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:
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Breakout, written entirely in BASIC for the AKGL BASIC interpreter, using downloaded CC0
|
Breakout, written entirely in BASIC for the AKGL BASIC interpreter, using downloaded CC0
|
||||||
sprite artwork for the paddles, the balls and the powerup gems. The bricks, the HUD and
|
sprite artwork for the paddles, the balls and the powerup gems. The bricks, the HUD and
|
||||||
the lettering are drawn, captured with `SSHAPE` and installed as sprites — because a
|
the lettering are drawn — `WINDOW` moves the text layer to two rows at the bottom, and
|
||||||
sprite is the one thing this interpreter puts on the screen for nothing. The text layer
|
everything the program draws then stays on the screen without being redrawn or captured.
|
||||||
owns every row of the window unless a program takes some back with `WINDOW`, and even
|
|
||||||
then a drawing has to be re-issued every frame; a sprite is drawn from state the
|
The bricks are also **collidable without being sprites**: `SOLID` registers a rectangle
|
||||||
interpreter keeps.
|
the interpreter collides against, so sixty of them cost none of the eight slots.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
../../../build-akgl/basic breakout.bas
|
../../../build-akgl/basic breakout.bas
|
||||||
|
|||||||
@@ -3,43 +3,45 @@ REM BREAKOUT, for the AKGL BASIC interpreter, using sprite artwork.
|
|||||||
REM
|
REM
|
||||||
REM Run it with the SDL build: build-akgl/basic breakout-sprites.bas
|
REM Run it with the SDL build: build-akgl/basic breakout-sprites.bas
|
||||||
REM
|
REM
|
||||||
REM Five things about this interpreter shape everything below, and they
|
REM Four things about this interpreter shape everything below, and they are
|
||||||
REM are worth knowing before the first line stops making sense.
|
REM worth knowing before the first line stops making sense.
|
||||||
REM
|
REM
|
||||||
REM 1. The text layer repaints every row of the window opaque black after
|
REM 1. The text layer repaints every row it owns, opaque, after the
|
||||||
REM the program's steps have run, so by default anything DRAW or BOX
|
REM program's steps have run -- and by default it owns the whole window,
|
||||||
REM put on the screen is wiped before it is ever presented. Sprites are
|
REM so anything DRAW or BOX put on the screen would be wiped before it
|
||||||
REM drawn after the text layer, so a sprite is the one thing that is
|
REM was ever seen. WINDOW moves it to two rows at the bottom, which is
|
||||||
REM seen for nothing. Everything drawn here is therefore captured with
|
REM the first executable line of this program, and everything below
|
||||||
REM SSHAPE and installed as a sprite with SPRSAV -- that is what
|
REM then draws into a layer the frame composites and *keeps*.
|
||||||
REM sprites 1 and 2 are. They are the screen.
|
|
||||||
REM
|
REM
|
||||||
REM (WINDOW can shrink the text area and hand the rest of the window to
|
REM Nothing here is captured into a sprite. It used to be: sprites 1
|
||||||
REM the drawing verbs; it could not be reached from this build when
|
REM and 2 held an SSHAPE of the HUD strip and of the whole play field,
|
||||||
REM this was written. It would not help here anyway: a drawing has to
|
REM because a sprite was the one thing drawn from state the interpreter
|
||||||
REM be re-issued every frame and fit inside one batch, and a sprite has
|
REM kept and a drawing lasted exactly one frame. Both slots are free
|
||||||
REM neither constraint.)
|
REM now, and the field is drawn once per level rather than rebuilt.
|
||||||
REM
|
REM
|
||||||
REM 2. The host runs 256 source lines and then presents the frame, and a
|
REM 2. Nothing has a drawing deadline. The host runs 256 source lines and
|
||||||
REM present throws the drawing buffer away. So all the drawing between
|
REM then presents, and a present used to throw the drawing buffer away
|
||||||
REM a GRAPHIC 1, 1 and its SSHAPE has to fit inside one of those
|
REM -- so a capture spanning that boundary came back half empty, and a
|
||||||
REM batches or the capture comes back half empty. Waiting for TI# to
|
REM PACE routine spun on TI# to start each frame's drawing at the top
|
||||||
REM tick lands us on a batch boundary -- see PACE -- and every draw
|
REM of a batch. With nothing captured, a drawing that spans two batches
|
||||||
REM routine below is written to stay under about two hundred lines.
|
REM simply arrives over two frames and the layer keeps both halves.
|
||||||
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
|
REM
|
||||||
REM 3. There are eight sprites and no more. Two are the screen, five are
|
REM What survives from that era is worth keeping anyway: the lettering
|
||||||
REM the artwork, one is the gem. That is the whole budget, and it is
|
REM is turned into a list of strokes when a number changes and merely
|
||||||
REM why the bricks are drawn rather than made of art.
|
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
|
||||||
REM 4. There are 128 variables and 64 labels, and this program is close
|
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 enough to both that a constant is spelled out rather than named
|
||||||
REM and several routines that would read better as subroutines are
|
REM and several routines that would read better as subroutines are
|
||||||
REM BEGIN blocks instead.
|
REM BEGIN blocks instead.
|
||||||
REM
|
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 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 FIXED, and are marked below; the shape of this listing is still
|
||||||
REM theirs, which is why they are still written down:
|
REM theirs, which is why they are still written down:
|
||||||
@@ -138,13 +140,8 @@ GMY% = 0
|
|||||||
|
|
||||||
REM --------------------------------------------------------------- bricks
|
REM --------------------------------------------------------------- bricks
|
||||||
DIM BRK#(60)
|
DIM BRK#(60)
|
||||||
DIM LX#(60)
|
|
||||||
DIM LY#(60)
|
|
||||||
DIM RS#(6)
|
|
||||||
DIM RC#(6)
|
|
||||||
DIM BRC#(6)
|
DIM BRC#(6)
|
||||||
DIM BRV#(6)
|
DIM BRV#(6)
|
||||||
LN# = 0
|
|
||||||
BRN# = 0
|
BRN# = 0
|
||||||
|
|
||||||
REM ------------------------------------------------------- shapes and dirt
|
REM ------------------------------------------------------- shapes and dirt
|
||||||
@@ -165,7 +162,6 @@ S5$ = ""
|
|||||||
Z$ = ""
|
Z$ = ""
|
||||||
REM 99 means the stamps are gone and have to be rebuilt before anything
|
REM 99 means the stamps are gone and have to be rebuilt before anything
|
||||||
REM can be drawn with them.
|
REM can be drawn with them.
|
||||||
SHN# = 99
|
|
||||||
DHUD# = 1
|
DHUD# = 1
|
||||||
DPLAY# = 1
|
DPLAY# = 1
|
||||||
BAN$ = ""
|
BAN$ = ""
|
||||||
@@ -244,6 +240,19 @@ HIT# = 0
|
|||||||
SEED# = 1
|
SEED# = 1
|
||||||
RNMAX# = 2
|
RNMAX# = 2
|
||||||
RNVAL# = 0
|
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 =====================================================================
|
||||||
REM Start up
|
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 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 in the file, so whichever loader runs first gets the DATA that is
|
||||||
REM written first. The tables are written first.
|
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 LOADTABLES
|
||||||
GOSUB LOADFONT
|
GOSUB LOADFONT
|
||||||
SEED# = MOD(1 + TI# * 7919, 2147483648)
|
SEED# = MOD(1 + TI# * 7919, 2147483648)
|
||||||
@@ -283,6 +300,11 @@ VOL 8
|
|||||||
ENVELOPE 0, 0, 6, 0, 4
|
ENVELOPE 0, 0, 6, 0, 4
|
||||||
TEMPO 12
|
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
|
GOSUB TITLESCREEN
|
||||||
|
|
||||||
REM =====================================================================
|
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 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 with a deadline; then the game, which may take as long as it likes.
|
||||||
REM =====================================================================
|
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
|
LABEL FRAME
|
||||||
GOSUB PACE
|
|
||||||
GOSUB DRAWJOB
|
GOSUB DRAWJOB
|
||||||
GOSUB READKEYS
|
GOSUB READKEYS
|
||||||
IF STATE# = 0 THEN GOSUB TITLETICK
|
IF STATE# = 0 THEN GOSUB TITLETICK
|
||||||
@@ -312,30 +337,6 @@ IF SCORE# > HISCORE# THEN HISCORE# = SCORE#
|
|||||||
PRINT "FINAL SCORE " + SCORE# + " BEST " + HISCORE#
|
PRINT "FINAL SCORE " + SCORE# + " BEST " + HISCORE#
|
||||||
END
|
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 =====================================================================
|
||||||
REM One capture per frame, and only one
|
REM One capture per frame, and only one
|
||||||
REM
|
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 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 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 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
|
LABEL DRAWJOB
|
||||||
IF SHN# < 14 THEN GOTO DRAWJOB2
|
|
||||||
GOSUB DRAWPROTOS
|
|
||||||
RETURN
|
|
||||||
LABEL DRAWJOB2
|
LABEL DRAWJOB2
|
||||||
IF DPLAY# = 0 THEN GOTO DRAWJOB3
|
IF DPLAY# = 0 THEN GOTO DRAWJOB3
|
||||||
GOSUB DRAWPLAY
|
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 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.
|
REM four milliseconds a call, which is an eighth of a frame.
|
||||||
LABEL DRAWPROTOS
|
LABEL DRAWPROTOS
|
||||||
GRAPHIC 5
|
|
||||||
GRAPHIC 1, 1
|
GRAPHIC 1, 1
|
||||||
WIDTH 1
|
WIDTH 1
|
||||||
SHN# = 99
|
|
||||||
FOR R# = 0 TO 5
|
FOR R# = 0 TO 5
|
||||||
COLOR 1, BRC#(R#)
|
COLOR 1, BRC#(R#)
|
||||||
T1# = R# * 20
|
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#
|
DRAW 1, 0, T1# + K# TO 67, T1# + K# : DRAW 1, 0, T2# + K# TO 67, T2# + K#
|
||||||
NEXT K#
|
NEXT K#
|
||||||
NEXT R#
|
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, 0, 68, 16 : S0$ = Z$
|
||||||
SSHAPE Z$, 0, 20, 68, 36 : S1$ = Z$
|
SSHAPE Z$, 0, 20, 68, 36 : S1$ = Z$
|
||||||
SSHAPE Z$, 0, 40, 68, 56 : S2$ = Z$
|
SSHAPE Z$, 0, 40, 68, 56 : S2$ = Z$
|
||||||
SSHAPE Z$, 0, 60, 68, 76 : S3$ = Z$
|
SSHAPE Z$, 0, 60, 68, 76 : S3$ = Z$
|
||||||
SSHAPE Z$, 0, 80, 68, 96 : S4$ = Z$
|
SSHAPE Z$, 0, 80, 68, 96 : S4$ = Z$
|
||||||
SSHAPE Z$, 0, 100, 68, 116 : S5$ = 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
|
DPLAY# = 1
|
||||||
DHUD# = 1
|
DHUD# = 1
|
||||||
RETURN
|
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 ---------------------------------------------------------------------
|
||||||
REM The field: walls, whatever bricks are still standing, and whatever
|
REM The field: walls, whatever bricks are still standing, and whatever
|
||||||
REM lettering belongs on the field. Nothing here computes anything it
|
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 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
|
||||||
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
|
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
|
||||||
BOX 5, 2, 62, 797, 597 : BOX 1, 6, 66, 793, 593
|
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 Every brick still standing, stamped where it belongs. This runs when a
|
||||||
REM brick would cost a line per brick inside the one routine in this
|
REM level is laid out and not again -- a broken brick is erased in place by
|
||||||
REM program that has a deadline. The live list is built row by row, so a
|
REM ERASEBRICK, so there is no reason to redraw sixty of them to remove one.
|
||||||
REM row is a contiguous run of it: RS# is where the run starts and RC# how
|
REM That is what took away the flattened live list, its four arrays, and the
|
||||||
REM long it is.
|
REM run-per-row loop that walked it.
|
||||||
Z$ = S0$ : T1# = RS#(0) : T2# = RC#(0) : GOSUB STAMPROW
|
FOR R# = 0 TO 5
|
||||||
Z$ = S1$ : T1# = RS#(1) : T2# = RC#(1) : GOSUB STAMPROW
|
IF R# = 0 THEN Z$ = S0$
|
||||||
Z$ = S2$ : T1# = RS#(2) : T2# = RC#(2) : GOSUB STAMPROW
|
IF R# = 1 THEN Z$ = S1$
|
||||||
Z$ = S3$ : T1# = RS#(3) : T2# = RC#(3) : GOSUB STAMPROW
|
IF R# = 2 THEN Z$ = S2$
|
||||||
Z$ = S4$ : T1# = RS#(4) : T2# = RC#(4) : GOSUB STAMPROW
|
IF R# = 3 THEN Z$ = S3$
|
||||||
Z$ = S5$ : T1# = RS#(5) : T2# = RC#(5) : GOSUB STAMPROW
|
IF R# = 4 THEN Z$ = S4$
|
||||||
REM A skipped BEGIN block holding a FOR used to break the RETURN at the end
|
IF R# = 5 THEN Z$ = S5$
|
||||||
REM of the routine holding it, so this loop is guarded by a GOTO instead.
|
FOR C# = 0 TO 9
|
||||||
REM Fixed since; left alone because a GOTO guard costs nothing.
|
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# = 1 THEN DRAW PC#(0), PX1#(0), PY1#(0) TO PX2#(0), PY2#(0)
|
||||||
IF PN# < 2 THEN GOTO PLDONE
|
IF PN# < 2 THEN GOTO PLDONE
|
||||||
FOR I# = 0 TO PN# - 1
|
FOR I# = 0 TO PN# - 1
|
||||||
DRAW PC#(I#), PX1#(I#), PY1#(I#) TO PX2#(I#), PY2#(I#)
|
DRAW PC#(I#), PX1#(I#), PY1#(I#) TO PX2#(I#), PY2#(I#)
|
||||||
NEXT I#
|
NEXT I#
|
||||||
LABEL PLDONE
|
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
|
RETURN
|
||||||
|
|
||||||
REM ---------------------------------------------------------------------
|
REM ---------------------------------------------------------------------
|
||||||
REM The HUD strip. Same shape as DRAWPLAY: one pass over a stroke list
|
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.
|
REM that was built on some earlier frame, when there was time.
|
||||||
LABEL DRAWHUD
|
LABEL DRAWHUD
|
||||||
GRAPHIC 1, 1
|
GRAPHIC 1, 0
|
||||||
WIDTH 1
|
WIDTH 1
|
||||||
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
|
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
|
||||||
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
|
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
|
BOX 5, 0, 56, 799, 57
|
||||||
IF HN# = 1 THEN DRAW HC#(0), HX1#(0), HY1#(0) TO HX2#(0), HY2#(0)
|
IF HN# = 1 THEN DRAW HC#(0), HX1#(0), HY1#(0) TO HX2#(0), HY2#(0)
|
||||||
IF HN# < 2 THEN GOTO HUDONE
|
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#)
|
DRAW HC#(I#), HX1#(I#), HY1#(I#) TO HX2#(I#), HY2#(I#)
|
||||||
NEXT I#
|
NEXT I#
|
||||||
LABEL HUDONE
|
LABEL HUDONE
|
||||||
SSHAPE Z$, 0, 0, 800, 60
|
|
||||||
SPRSAV Z$, 1
|
|
||||||
SPRITE 1, 1, 2
|
|
||||||
MOVSPR 1, 0, 0
|
|
||||||
SHN# = SHN# + 1
|
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM =====================================================================
|
REM =====================================================================
|
||||||
@@ -580,7 +588,6 @@ IF BLST#(B#) = 0 THEN BEGIN
|
|||||||
BEND
|
BEND
|
||||||
IF BLON#(B#) = 0 THEN RETURN
|
IF BLON#(B#) = 0 THEN RETURN
|
||||||
IF BLST#(B#) = 0 THEN BEGIN
|
IF BLST#(B#) = 0 THEN BEGIN
|
||||||
GOSUB BALLBRICKS
|
|
||||||
GOSUB BALLPADDLE
|
GOSUB BALLPADDLE
|
||||||
BEND
|
BEND
|
||||||
MOVSPR 5 + B#, BLX%(B#), BLY%(B#)
|
MOVSPR 5 + B#, BLX%(B#), BLY%(B#)
|
||||||
@@ -610,70 +617,58 @@ SPRITE 5 + B#, 0
|
|||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM ---------------------------------------------------------------------
|
REM ---------------------------------------------------------------------
|
||||||
REM Brick collision. A ball can only be over four cells at once, so the
|
REM Brick collision, all of it.
|
||||||
REM cells are worked out from its box rather than by walking sixty
|
REM
|
||||||
REM bricks. DO rather than FOR throughout: a FOR whose bounds are equal
|
REM COLLISION 2 fires between source lines when a ball overlaps one of the
|
||||||
REM does not run its body in this dialect, and a ball inside one column is
|
REM rectangles SOLID registered, and RCOLLISION says which and which way out.
|
||||||
REM exactly that case.
|
REM That replaced a bounding-box narrowing that turned the ball's box into a
|
||||||
LABEL BALLBRICKS
|
REM range of candidate cells, a nested DO walking them, and a cell test that
|
||||||
IF BRN# = 0 THEN RETURN
|
REM computed an overlap rectangle and compared its width to its height to pick
|
||||||
LFT# = BLX%(B#)
|
REM an axis -- about sixty lines.
|
||||||
TOP# = BLY%(B#)
|
REM
|
||||||
RGT# = LFT# + 21
|
REM The handler cannot know which ball hit, so it asks each live one. Three is
|
||||||
BOT# = TOP# + 21
|
REM not a loop worth writing.
|
||||||
IF BOT# < BRKY# THEN RETURN
|
REM The mask is by *sprite*, and the balls are sprites 5, 6 and 7 -- so their
|
||||||
IF TOP# > BRKY# + 6 * 24 THEN RETURN
|
REM bits are 16, 32 and 64, not 1, 2 and 4. Ball B# is sprite 5 + B#, which is
|
||||||
CC1# = (LFT# - BRKX#) / 72
|
REM the same arithmetic MOVSPR and RCOLLISION use for them everywhere else.
|
||||||
CC2# = (RGT# - BRKX#) / 72
|
LABEL BRICKHIT
|
||||||
RR1# = (TOP# - BRKY#) / 24
|
MB# = BUMP(2)
|
||||||
RR2# = (BOT# - BRKY#) / 24
|
IF MB# = 0 THEN RETURN
|
||||||
IF CC1# < 0 THEN CC1# = 0
|
B# = 0
|
||||||
IF CC2# > 9 THEN CC2# = 9
|
IF (MB# AND 16) <> 0 THEN GOSUB ONEBRICK
|
||||||
IF RR1# < 0 THEN RR1# = 0
|
B# = 1
|
||||||
IF RR2# > 5 THEN RR2# = 5
|
IF (MB# AND 32) <> 0 THEN GOSUB ONEBRICK
|
||||||
IF CC2# < CC1# THEN RETURN
|
B# = 2
|
||||||
IF RR2# < RR1# THEN RETURN
|
IF (MB# AND 64) <> 0 THEN GOSUB ONEBRICK
|
||||||
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
|
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM Reflect off whichever face the ball has less of itself past: the
|
REM One ball against whatever it hit. B# is which ball; sprite 5 + B# is its
|
||||||
REM standard box resolution, and the reason a ball clipping the end of a
|
REM slot, and RCOLLISION is keyed on the sprite number.
|
||||||
REM row goes sideways instead of straight back down. T1# to T4# are the
|
LABEL ONEBRICK
|
||||||
REM overlapping rectangle; its width against its height is the whole test.
|
IF BLON#(B#) = 0 THEN RETURN
|
||||||
LABEL TESTCELL
|
N# = RCOLLISION(5 + B#, 1)
|
||||||
N# = R# * 10 + C#
|
IF N# < 1 THEN RETURN
|
||||||
|
N# = N# - 1
|
||||||
IF BRK#(N#) = 0 THEN RETURN
|
IF BRK#(N#) = 0 THEN RETURN
|
||||||
BX1# = BRKX# + C# * 72
|
DP% = RCOLLISION(5 + B#, 4)
|
||||||
BY1# = BRKY# + R# * 24
|
BLX%(B#) = BLX%(B#) + (RCOLLISION(5 + B#, 2) * DP%)
|
||||||
IF RGT# < BX1# THEN RETURN
|
BLY%(B#) = BLY%(B#) + (RCOLLISION(5 + B#, 3) * DP%)
|
||||||
IF LFT# > BX1# + 67 THEN RETURN
|
AX# = RCOLLISION(5 + B#, 7)
|
||||||
IF BOT# < BY1# THEN RETURN
|
IF AX# = 1 THEN BLVX%(B#) = 0.0 - BLVX%(B#)
|
||||||
IF TOP# > BY1# + 15 THEN RETURN
|
IF AX# = 2 THEN BLVY%(B#) = 0.0 - BLVY%(B#)
|
||||||
T1# = RGT# : IF BX1# + 67 < T1# THEN T1# = BX1# + 67
|
R# = N# / 10
|
||||||
T2# = LFT# : IF BX1# > T2# THEN T2# = BX1#
|
C# = MOD(N#, 10)
|
||||||
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#)
|
|
||||||
BRK#(N#) = 0
|
BRK#(N#) = 0
|
||||||
|
SOLID N# + 1
|
||||||
BRN# = BRN# - 1
|
BRN# = BRN# - 1
|
||||||
SCORE# = SCORE# + BRV#(R#)
|
SCORE# = SCORE# + BRV#(R#)
|
||||||
REM The top row rings at C6 and each row down drops about a third, so the
|
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.
|
REM wall plays itself down a scale as it comes apart.
|
||||||
SOUND 1, 17175 - R# * 2100, 4
|
SOUND 1, 17175 - R# * 2100, 4
|
||||||
HIT# = 1
|
BX1# = BRKX# + C# * 72
|
||||||
GOSUB BUILDLIVE
|
BY1# = BRKY# + R# * 24
|
||||||
|
GOSUB ERASEBRICK
|
||||||
GOSUB HUDTEXT
|
GOSUB HUDTEXT
|
||||||
DPLAY# = 1
|
|
||||||
IF GMON# = 1 THEN RETURN
|
IF GMON# = 1 THEN RETURN
|
||||||
RNMAX# = 7
|
RNMAX# = 7
|
||||||
GOSUB NEXTRAND
|
GOSUB NEXTRAND
|
||||||
@@ -915,7 +910,7 @@ BRN# = 0
|
|||||||
FOR I# = 0 TO 59
|
FOR I# = 0 TO 59
|
||||||
BRK#(I#) = 0
|
BRK#(I#) = 0
|
||||||
NEXT I#
|
NEXT I#
|
||||||
GOSUB BUILDLIVE
|
SOLID
|
||||||
SPRITE 3, 0
|
SPRITE 3, 0
|
||||||
SPRITE 5, 0
|
SPRITE 5, 0
|
||||||
PN# = 0
|
PN# = 0
|
||||||
@@ -962,7 +957,7 @@ BRN# = 0
|
|||||||
FOR I# = 0 TO 59
|
FOR I# = 0 TO 59
|
||||||
BRK#(I#) = 0
|
BRK#(I#) = 0
|
||||||
NEXT I#
|
NEXT I#
|
||||||
GOSUB BUILDLIVE
|
SOLID
|
||||||
SPRITE 3, 0
|
SPRITE 3, 0
|
||||||
SPRITE 5, 0
|
SPRITE 5, 0
|
||||||
SPRITE 6, 0
|
SPRITE 6, 0
|
||||||
@@ -1006,10 +1001,15 @@ BRN# = 0
|
|||||||
FOR I# = 0 TO 59
|
FOR I# = 0 TO 59
|
||||||
BRK#(I#) = 0
|
BRK#(I#) = 0
|
||||||
NEXT I#
|
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
|
FOR R# = 0 TO 5
|
||||||
IF R# < T3# THEN GOSUB FILLROW
|
IF R# < T3# THEN GOSUB FILLROW
|
||||||
NEXT R#
|
NEXT R#
|
||||||
GOSUB BUILDLIVE
|
|
||||||
GOSUB RESETBALL
|
GOSUB RESETBALL
|
||||||
GOSUB HUDTEXT
|
GOSUB HUDTEXT
|
||||||
GMTYP# = 0
|
GMTYP# = 0
|
||||||
@@ -1020,10 +1020,17 @@ DPLAY# = 1
|
|||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM Again a subroutine rather than a block, for the same reason.
|
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
|
LABEL FILLROW
|
||||||
FOR C# = 0 TO 9
|
FOR C# = 0 TO 9
|
||||||
BRK#(R# * 10 + C#) = 1
|
BRK#(R# * 10 + C#) = 1
|
||||||
BRN# = BRN# + 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#
|
NEXT C#
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
@@ -1032,30 +1039,6 @@ SPD% = 5.6 + 0.45 * LEVEL#
|
|||||||
IF SPD% > 11.0 THEN SPD% = 11.0
|
IF SPD% > 11.0 THEN SPD% = 11.0
|
||||||
RETURN
|
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
|
LABEL RESETBALL
|
||||||
FOR B# = 0 TO 2
|
FOR B# = 0 TO 2
|
||||||
BLON#(B#) = 0
|
BLON#(B#) = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user