From 851b03bcf6491698d01892ae9ce7a53b9102afcd Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Sun, 2 Aug 2026 13:26:47 -0400 Subject: [PATCH] Give chapter 18 the six pieces a reader had to invent A reader building the game from the chapter alone had to guess at all of these, and three of them are code the chapter describes and never shows: - the HUD eraser stamp, promised in Step 6 as "make a second one while you are here" and then never given - the gem roll at the end of ONEBRICK, without which nothing ever drops - BX1# and BY1#, which SPAWNGEM reads and only ONEBRICK can set And three rules the chapter leans on without stating: - MID and INSTR count from zero, which is what makes a glyph number an array index with no adjustment - sprite n sets bit 2^(n-1), which is where 16, 32 and 64 come from - states 5 and 6 have no tick routine on purpose; that is what a pause is Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL --- docs/18-tutorial-breakout-artwork.md | 40 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/18-tutorial-breakout-artwork.md b/docs/18-tutorial-breakout-artwork.md index 22b34ff..5802838 100644 --- a/docs/18-tutorial-breakout-artwork.md +++ b/docs/18-tutorial-breakout-artwork.md @@ -411,7 +411,18 @@ RETURN Make a second one the width of the HUD strip while you are here, for the same reason: the strip is rewritten whenever a number in it changes, and the old digits have to go -somewhere before the new ones are drawn. +somewhere before the new ones are drawn. It is the same code with a bigger rectangle: + +```basic norun +FOR K# = 0 TO 59 + DRAW 1, 0, 160 + K# TO 799, 160 + K# +NEXT K# +SSHAPE Z$, 0, 160, 800, 220 : HBL$ = Z$ +``` + +Both blocks belong at the end of `DRAWPROTOS` in Step 4, drawn below the six brick +prototypes and captured with them, so the eight `SSHAPE` slots are all spent in one place. +`HBL$` is stamped at the top of the HUD redraw and `BL$` by `ERASEBRICK`. **This is what makes the whole field a draw-once job.** Draw the walls and all sixty bricks when a level is laid out, and after that touch only what changes. A program that had to @@ -504,7 +515,11 @@ DATA 4, 0,6, 0,30, 3,33, 6,36 4-unit `SCORE` in the HUD. Turning a character into a glyph number is one call: `INSTR(ALPHA$, MID(TX$, TXI#, 1))` -over an alphabet string. That is why the order of the `DATA` lines matters — they have to +over an alphabet string. **`MID` and `INSTR` both count from zero in this dialect**, which +is the opposite of Commodore BASIC and the opposite of most other BASICs — `MID(A$, 0, 1)` +is the first character, and `INSTR` answers 0 for a match at the front and −1 for no match +at all. That is what makes the glyph number an array index with no adjustment, and it is +also why `MID("0123456789", D#, 1)` turns a digit straight into its character. That is why the order of the `DATA` lines matters — they have to match the order of the characters in `ALPHA$`. The finished game uses a 41-character alphabet covering `A` to `Z`, `0` to `9`, space, colon, dash, full stop and exclamation mark. @@ -700,7 +715,8 @@ RETURN ``` **The mask is by sprite, and the balls are sprites 5, 6 and 7** — so their bits are 16, 32 -and 64, not 1, 2 and 4. That is the same `5 + B#` arithmetic `MOVSPR` uses for them +and 64, not 1, 2 and 4. Sprite *n* sets bit 2 to the power of *n* − 1: sprite 1 is 1, +sprite 2 is 2, sprite 3 is 4, sprite 4 is 8, sprite 5 is 16, and so on to sprite 8 at 128. That is the same `5 + B#` arithmetic `MOVSPR` uses for them everywhere else, and getting it wrong costs nothing visible: the handler runs, tests bits nothing sets, and returns. @@ -724,11 +740,23 @@ SOLID N# + 1 BRN# = BRN# - 1 SCORE# = SCORE# + BRV#(R#) SOUND 1, 17175 - R# * 2100, 4 +BX1# = BRKX# + C# * 72 +BY1# = BRKY# + R# * 24 GOSUB ERASEBRICK GOSUB HUDTEXT +IF GMON# = 1 THEN RETURN +RNMAX# = 7 +GOSUB NEXTRAND +IF RNVAL# = 0 THEN GOSUB SPAWNGEM RETURN ``` +`BX1#` and `BY1#` are the broken brick's top-left corner in pixels, and they are set here +because this is the only routine that knows it — Step 9's `SPAWNGEM` starts the gem falling +from that spot. The last four lines are the whole of "a broken brick drops a gem about one +time in seven": one roll, and `IF GMON# = 1 THEN RETURN` first because there is one gem +sprite and a gem already falling means no roll at all. + Fields 2, 3 and 4 push the ball exactly clear along the contact normal. Field 7 is which axis to reverse — **the minimum translation axis**, which is why a ball clipping the end of a row goes sideways rather than straight back down. Working that out by hand means building @@ -976,6 +1004,12 @@ Each is a subroutine called once a frame while that state is current. This is a shape from Chapter 17's branch targets, and it works here because nothing jumps out of a loop to reach it. +There are seven state numbers and only five tick routines. **5 is game over and 6 is +paused, and neither has one on purpose**: the frame loop's `IF STATE# = n THEN GOSUB` list +simply has no line for them, so the frame draws whatever is on the screen, reads the +keyboard and does nothing else. That is exactly what a pause is, and writing an empty +subroutine to say so would cost a label. + ```basic norun LABEL TITLETICK STIMER# = STIMER# + 1