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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user