Take libakerror 2.0.2, libakstdlib and libakgl 0.9.0 #59
@@ -1049,17 +1049,17 @@ every program. It remains a useful from-scratch PRNG example:
|
|||||||
```basic norun
|
```basic norun
|
||||||
SEED# = 12345
|
SEED# = 12345
|
||||||
RMAX# = 6
|
RMAX# = 6
|
||||||
RND# = 0
|
ROLL# = 0
|
||||||
I# = 0
|
I# = 0
|
||||||
FOR I# = 1 TO 5
|
FOR I# = 1 TO 5
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
PRINT "ROLL " + (RND# + 1)
|
PRINT "ROLL " + (ROLL# + 1)
|
||||||
NEXT I#
|
NEXT I#
|
||||||
END
|
END
|
||||||
|
|
||||||
LABEL RANDOM
|
LABEL RANDOM
|
||||||
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
||||||
RND# = MOD((SEED# / 65536), RMAX#)
|
ROLL# = MOD((SEED# / 65536), RMAX#)
|
||||||
RETURN
|
RETURN
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ whole development loop; the engine never rebuilds.
|
|||||||
- **[Step 2](#step-2-bind-the-engines-own-actor)** — bind the engine's own
|
- **[Step 2](#step-2-bind-the-engines-own-actor)** — bind the engine's own
|
||||||
actor as the second type, which is the point of the whole exercise
|
actor as the second type, which is the point of the whole exercise
|
||||||
- **[Step 3](#step-3-share-the-frame-and-the-dice)** — share the frame state,
|
- **[Step 3](#step-3-share-the-frame-and-the-dice)** — share the frame state,
|
||||||
and give the script randomness it cannot make itself
|
and hand the script dice the engine controls
|
||||||
- **[Step 4](#step-4-why-bindings-and-not-arguments)** — see why the structures
|
- **[Step 4](#step-4-why-bindings-and-not-arguments)** — see why the structures
|
||||||
are bindings rather than function arguments
|
are bindings rather than function arguments
|
||||||
- **[Step 5](#step-5-the-shape-of-the-script)** — learn the three language
|
- **[Step 5](#step-5-the-shape-of-the-script)** — learn the three language
|
||||||
@@ -75,7 +75,7 @@ typedef struct galaga_Enemy
|
|||||||
float t; /* parametric clock for the current maneuver */
|
float t; /* parametric clock for the current maneuver */
|
||||||
int32_t hp;
|
int32_t hp;
|
||||||
int32_t fire; /* outbox: script sets 1, engine consumes */
|
int32_t fire; /* outbox: script sets 1, engine consumes */
|
||||||
float rnd; /* inbox: engine writes fresh 0..1 each call */
|
float rnd; /* inbox: fresh 0..1 each call; ROLL% in BASIC */
|
||||||
} galaga_Enemy;
|
} galaga_Enemy;
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ static const akbasic_HostField ENEMY_FIELDS[] = {
|
|||||||
AKBASIC_HOST_FIELD( galaga_Enemy, t, "T%", AKBASIC_HOSTFIELD_FLOAT ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, t, "T%", AKBASIC_HOSTFIELD_FLOAT ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, hp, "HP#", AKBASIC_HOSTFIELD_INT32 ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, hp, "HP#", AKBASIC_HOSTFIELD_INT32 ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, fire, "FIRE#", AKBASIC_HOSTFIELD_INT32 ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, fire, "FIRE#", AKBASIC_HOSTFIELD_INT32 ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, rnd, "RND%", AKBASIC_HOSTFIELD_FLOAT )
|
AKBASIC_HOST_FIELD( galaga_Enemy, rnd, "ROLL%", AKBASIC_HOSTFIELD_FLOAT )
|
||||||
};
|
};
|
||||||
static const akbasic_HostType ENEMY_TYPE = {
|
static const akbasic_HostType ENEMY_TYPE = {
|
||||||
"ENEMY", sizeof(galaga_Enemy), ENEMY_FIELDS, 8
|
"ENEMY", sizeof(galaga_Enemy), ENEMY_FIELDS, 8
|
||||||
@@ -194,7 +194,7 @@ typedef struct galaga_Shared
|
|||||||
float playerx; /* the player actor's position, this frame */
|
float playerx; /* the player actor's position, this frame */
|
||||||
float playery;
|
float playery;
|
||||||
int32_t wave;
|
int32_t wave;
|
||||||
float rnd; /* fresh 0..1 each frame; the issue #16 route */
|
float rnd; /* fresh 0..1 each frame; ROLL% to the script */
|
||||||
} galaga_Shared;
|
} galaga_Shared;
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -203,13 +203,24 @@ the engine refreshes it at the top of every frame. The boss reads
|
|||||||
`GAME@.PLAYERX%` to lead its dive; the fire decision reads it to know whether
|
`GAME@.PLAYERX%` to lead its dive; the fire decision reads it to know whether
|
||||||
anything is worth shooting at.
|
anything is worth shooting at.
|
||||||
|
|
||||||
The `rnd` fields — one here per frame, one on each enemy per call — exist
|
The `rnd` fields — one here per frame, one on each enemy per call — carry the
|
||||||
because the engine's PRNG is the script's **only** source of randomness: write
|
engine's PRNG into the script: write `SELF@.ROLL% < DT% * 1.5` and an enemy's
|
||||||
`SELF@.RND% < DT% * 1.5` and an enemy's trigger finger is a dice roll. There
|
trigger finger is a dice roll.
|
||||||
is no `RND` verb in this dialect; issue #16 tracks adding one, and Chapter
|
|
||||||
17's breakout hand-rolls a linear congruential generator in BASIC as the other
|
The dialect does now have a native `RND` function — issue #16 closed, and
|
||||||
route. Here the engine fills the field, which also keeps a headless run the
|
[Chapter 12](12-function-reference.md) documents it — so this is no longer the
|
||||||
same game on every machine — the PRNG is the example's own, not libc's.
|
*only* route; Chapter 17's breakout hand-rolls a linear congruential generator
|
||||||
|
in BASIC as a third. The engine keeps filling the field here on purpose,
|
||||||
|
because it buys something `RND` cannot: the numbers come from the example's own
|
||||||
|
PRNG rather than libc's, so a headless run is the same game on every machine,
|
||||||
|
which is what makes `example_galaga` a test and not just a demo.
|
||||||
|
|
||||||
|
**The BASIC name is `ROLL%`, not `RND%`.** A host field is a bare word and
|
||||||
|
shares a namespace with every verb and function, so once `RND` became a
|
||||||
|
function name a field could no longer be called that — the scanner refuses it
|
||||||
|
with *"Reserved word in variable name"*. The C member stays `rnd`; only the
|
||||||
|
name the script sees had to move. [Chapter 16](16-structures.md) has the same
|
||||||
|
rule for `TYPE` declarations.
|
||||||
|
|
||||||
## Step 4: Why bindings, and not arguments
|
## Step 4: Why bindings, and not arguments
|
||||||
|
|
||||||
@@ -351,7 +362,7 @@ DEF DECIDEFIRE(DT%)
|
|||||||
DX% = GAME@.PLAYERX% - ACTOR@.X%
|
DX% = GAME@.PLAYERX% - ACTOR@.X%
|
||||||
IF ABS(DX%) > 140 THEN RETURN 0
|
IF ABS(DX%) > 140 THEN RETURN 0
|
||||||
IF ACTOR@.Y% > GAME@.PLAYERY% THEN RETURN 0
|
IF ACTOR@.Y% > GAME@.PLAYERY% THEN RETURN 0
|
||||||
IF SELF@.RND% < DT% * 1.5 THEN SELF@.FIRE# = 1
|
IF SELF@.ROLL% < DT% * 1.5 THEN SELF@.FIRE# = 1
|
||||||
RETURN 0
|
RETURN 0
|
||||||
END
|
END
|
||||||
```
|
```
|
||||||
@@ -386,7 +397,7 @@ DEF UPDATEBEE(DT%)
|
|||||||
IF (S# AND 2) > 0 THEN BEGIN
|
IF (S# AND 2) > 0 THEN BEGIN
|
||||||
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.7) * 16
|
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.7) * 16
|
||||||
ACTOR@.Y% = SELF@.HOMEY%
|
ACTOR@.Y% = SELF@.HOMEY%
|
||||||
IF SELF@.RND% < DT% * 0.04 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
IF SELF@.ROLL% < DT% * 0.04 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
||||||
BEND
|
BEND
|
||||||
IF (S# AND 4) > 0 THEN BEGIN
|
IF (S# AND 4) > 0 THEN BEGIN
|
||||||
R# = DIVESTEP(DT%, 130, 0.2)
|
R# = DIVESTEP(DT%, 130, 0.2)
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ RX# = 0
|
|||||||
N# = 0
|
N# = 0
|
||||||
MROW# = 0
|
MROW# = 0
|
||||||
RMAX# = 2
|
RMAX# = 2
|
||||||
RND# = 0
|
ROLL# = 0
|
||||||
SND# = 0
|
SND# = 0
|
||||||
P$ = ""
|
P$ = ""
|
||||||
H$ = ""
|
H$ = ""
|
||||||
@@ -172,7 +172,8 @@ BSG$(4) = "[--]"
|
|||||||
BSG$(5) = "[--]"
|
BSG$(5) = "[--]"
|
||||||
|
|
||||||
REM --- the seed -------------------------------------------------------
|
REM --- the seed -------------------------------------------------------
|
||||||
REM There is no RND in this dialect. TI# is jiffies off the host's clock
|
REM RND exists now, but this program keeps its own LCG so a headless run
|
||||||
|
REM is the same game every time. TI# is jiffies off the host's clock
|
||||||
REM and is host uptime rather than zero-based, which makes it a fine seed.
|
REM and is host uptime rather than zero-based, which makes it a fine seed.
|
||||||
SEED# = TI#
|
SEED# = TI#
|
||||||
|
|
||||||
@@ -389,17 +390,18 @@ BY# = PY# - 10
|
|||||||
RMAX# = 2
|
RMAX# = 2
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
BVX# = BSPD#
|
BVX# = BSPD#
|
||||||
IF RND# = 0 THEN BVX# = 0 - BSPD#
|
IF ROLL# = 0 THEN BVX# = 0 - BSPD#
|
||||||
BVY# = 0 - BSPD#
|
BVY# = 0 - BSPD#
|
||||||
PDEC# = 0
|
PDEC# = 0
|
||||||
GOSUB SHOWSPR
|
GOSUB SHOWSPR
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM A linear congruential generator, because this dialect has no RND.
|
REM A linear congruential generator. RND(n) would do this in one token
|
||||||
|
REM now; the LCG stays because its sequence is reproducible.
|
||||||
REM The multiply stays inside int64 for any seed under 2^31.
|
REM The multiply stays inside int64 for any seed under 2^31.
|
||||||
LABEL RANDOM
|
LABEL RANDOM
|
||||||
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
||||||
RND# = MOD((SEED# / 65536), RMAX#)
|
ROLL# = MOD((SEED# / 65536), RMAX#)
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM ####################################################################
|
REM ####################################################################
|
||||||
@@ -632,7 +634,7 @@ NUDGE# = 0
|
|||||||
STALL# = 0
|
STALL# = 0
|
||||||
RMAX# = 4
|
RMAX# = 4
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
BVX# = (RND# * 3) - 6
|
BVX# = (ROLL# * 3) - 6
|
||||||
IF BVX# = 0 THEN BVX# = 3
|
IF BVX# = 0 THEN BVX# = 3
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
@@ -641,7 +643,7 @@ REM like something with a hand on the paddle rather than a mirror.
|
|||||||
LABEL DEMOAIM
|
LABEL DEMOAIM
|
||||||
RMAX# = 81
|
RMAX# = 81
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
DOFF# = RND# - 40
|
DOFF# = ROLL# - 40
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM ####################################################################
|
REM ####################################################################
|
||||||
|
|||||||
@@ -241,7 +241,8 @@ SLX# = 0
|
|||||||
SLY# = 0
|
SLY# = 0
|
||||||
SLI# = 0
|
SLI# = 0
|
||||||
REM The two eraser stamps. Declared here for exactly the same reason -- built
|
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 inside DRAWPROTOS and left undeclared, they were SHAPE:6 and SHAPE:7
|
||||||
|
REM inside
|
||||||
REM it and empty everywhere else.
|
REM it and empty everywhere else.
|
||||||
BL$ = ""
|
BL$ = ""
|
||||||
HBL$ = ""
|
HBL$ = ""
|
||||||
@@ -254,10 +255,13 @@ 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 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 out of the way before anything drawn can be seen. Two rows at the bottom
|
||||||
|
REM is
|
||||||
REM enough for the final score, and hands the other thirty-five to the drawing
|
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 verbs. Everything this game draws then simply stays there -- a drawing
|
||||||
REM into a layer the frame composites, so nothing here is captured into a sprite
|
REM goes
|
||||||
|
REM into a layer the frame composites, so nothing here is captured into a
|
||||||
|
REM sprite
|
||||||
REM and nothing is redrawn every frame.
|
REM and nothing is redrawn every frame.
|
||||||
WINDOW 0, 35, 49, 36
|
WINDOW 0, 35, 49, 36
|
||||||
|
|
||||||
@@ -293,7 +297,8 @@ ENVELOPE 0, 0, 6, 0, 4
|
|||||||
TEMPO 12
|
TEMPO 12
|
||||||
|
|
||||||
COLLISION 2, BRICKHIT
|
COLLISION 2, BRICKHIT
|
||||||
REM The stamps, once. They used to be rebuilt whenever the SSHAPE pool ran dry,
|
REM The stamps, once. They used to be rebuilt whenever the SSHAPE pool ran
|
||||||
|
REM dry,
|
||||||
REM because every frame's capture spent another slot; nothing captures now, so
|
REM because every frame's capture spent another slot; nothing captures now, so
|
||||||
REM eight slots are spent here and never again.
|
REM eight slots are spent here and never again.
|
||||||
GOSUB DRAWPROTOS
|
GOSUB DRAWPROTOS
|
||||||
@@ -374,7 +379,8 @@ COLOR 1, 1
|
|||||||
FOR K# = 0 TO 15
|
FOR K# = 0 TO 15
|
||||||
DRAW 1, 0, 130 + K# TO 67, 130 + K#
|
DRAW 1, 0, 130 + K# TO 67, 130 + K#
|
||||||
NEXT K#
|
NEXT K#
|
||||||
REM And an eighth the width of the HUD strip, for the same reason: the strip is
|
REM And an eighth the width of the HUD strip, for the same reason: the strip
|
||||||
|
REM is
|
||||||
REM rewritten whenever a number in it changes, and the old digits have to go
|
REM rewritten whenever a number in it changes, and the old digits have to go
|
||||||
REM somewhere first.
|
REM somewhere first.
|
||||||
FOR K# = 0 TO 59
|
FOR K# = 0 TO 59
|
||||||
@@ -393,7 +399,8 @@ 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 Take one brick off the screen: stamp the blank over it. Called when a
|
||||||
|
REM brick
|
||||||
REM breaks, so the field is never redrawn as a whole during play -- which is
|
REM breaks, so the field is never redrawn as a whole during play -- which is
|
||||||
REM what lets the whole live-list machinery go.
|
REM what lets the whole live-list machinery go.
|
||||||
LABEL ERASEBRICK
|
LABEL ERASEBRICK
|
||||||
@@ -444,7 +451,8 @@ 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 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 stays, which is the whole point -- so the digits that were there have to
|
||||||
|
REM be
|
||||||
REM stamped over before the new ones are drawn.
|
REM stamped over before the new ones are drawn.
|
||||||
Z$ = HBL$
|
Z$ = HBL$
|
||||||
GSHAPE Z$, 0, 0
|
GSHAPE Z$, 0, 0
|
||||||
@@ -495,7 +503,10 @@ IF SNDON# = 0 THEN VOL 0
|
|||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
LABEL PRESSPAUSE
|
LABEL PRESSPAUSE
|
||||||
IF STATE# = 2 THEN STATE# = 6 : GMTYP# = 0 : BAN$ = "PAUSED" : GOSUB SETBANNER : RETURN
|
IF STATE# <> 2 THEN GOTO PRESSPAUSE2
|
||||||
|
STATE# = 6 : GMTYP# = 0 : BAN$ = "PAUSED" : GOSUB SETBANNER
|
||||||
|
RETURN
|
||||||
|
LABEL PRESSPAUSE2
|
||||||
IF STATE# = 6 THEN STATE# = 2 : BAN$ = "" : GOSUB SETBANNER
|
IF STATE# = 6 THEN STATE# = 2 : BAN$ = "" : GOSUB SETBANNER
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
|
|||||||
@@ -71,10 +71,15 @@ static char *ENEMY_CHARACTER[GALAGA_ENEMY_KINDS] = {
|
|||||||
/* --------------------------------------------------------------- random --- */
|
/* --------------------------------------------------------------- random --- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No RND verb exists (issue #16), so the engine is the script's only source
|
* The engine is this script's source of randomness: it refreshes GAME@.ROLL%
|
||||||
* of randomness: it refreshes GAME@.RND% each frame and SELF@.RND% each call
|
* each frame and SELF@.ROLL% each call from this PRNG. A hand-rolled LCG
|
||||||
* from this PRNG. A hand-rolled LCG rather than rand() so a headless run is
|
* rather than rand() so a headless run is the same game on every libc, which
|
||||||
* the same game on every libc.
|
* is what lets interop_test.c assert exact counts. The dialect gained a native
|
||||||
|
* RND (issue #16) after this example was written; the field stays because RND
|
||||||
|
* would reintroduce exactly the per-machine variation this avoids.
|
||||||
|
*
|
||||||
|
* The BASIC-visible name is ROLL%, not RND%: host field names share a
|
||||||
|
* namespace with verbs and functions, so RND stopped being available as one.
|
||||||
*/
|
*/
|
||||||
static uint32_t PRNG_STATE = 0x12345678u;
|
static uint32_t PRNG_STATE = 0x12345678u;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ DEF DECIDEFIRE(DT%)
|
|||||||
DX% = GAME@.PLAYERX% - ACTOR@.X%
|
DX% = GAME@.PLAYERX% - ACTOR@.X%
|
||||||
IF ABS(DX%) > 140 THEN RETURN 0
|
IF ABS(DX%) > 140 THEN RETURN 0
|
||||||
IF ACTOR@.Y% > GAME@.PLAYERY% THEN RETURN 0
|
IF ACTOR@.Y% > GAME@.PLAYERY% THEN RETURN 0
|
||||||
IF SELF@.RND% < DT% * 1.5 THEN SELF@.FIRE# = 1
|
IF SELF@.ROLL% < DT% * 1.5 THEN SELF@.FIRE# = 1
|
||||||
RETURN 0
|
RETURN 0
|
||||||
|
|
||||||
REM Bee: enter, breathe in formation, occasionally dive nearly straight.
|
REM Bee: enter, breathe in formation, occasionally dive nearly straight.
|
||||||
@@ -65,7 +65,7 @@ DEF UPDATEBEE(DT%)
|
|||||||
IF (S# AND 2) > 0 THEN BEGIN
|
IF (S# AND 2) > 0 THEN BEGIN
|
||||||
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.7) * 16
|
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.7) * 16
|
||||||
ACTOR@.Y% = SELF@.HOMEY%
|
ACTOR@.Y% = SELF@.HOMEY%
|
||||||
IF SELF@.RND% < DT% * 0.04 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
IF SELF@.ROLL% < DT% * 0.04 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
||||||
BEND
|
BEND
|
||||||
IF (S# AND 4) > 0 THEN BEGIN
|
IF (S# AND 4) > 0 THEN BEGIN
|
||||||
R# = DIVESTEP(DT%, 130, 0.2)
|
R# = DIVESTEP(DT%, 130, 0.2)
|
||||||
@@ -85,7 +85,7 @@ DEF UPDATEBFLY(DT%)
|
|||||||
IF (S# AND 2) > 0 THEN BEGIN
|
IF (S# AND 2) > 0 THEN BEGIN
|
||||||
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 2.1) * 24
|
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 2.1) * 24
|
||||||
ACTOR@.Y% = SELF@.HOMEY%
|
ACTOR@.Y% = SELF@.HOMEY%
|
||||||
IF SELF@.RND% < DT% * 0.05 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
IF SELF@.ROLL% < DT% * 0.05 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
||||||
BEND
|
BEND
|
||||||
IF (S# AND 4) > 0 THEN BEGIN
|
IF (S# AND 4) > 0 THEN BEGIN
|
||||||
R# = DIVESTEP(DT%, 260, 0.1)
|
R# = DIVESTEP(DT%, 260, 0.1)
|
||||||
@@ -108,7 +108,7 @@ DEF UPDATEBOSS(DT%)
|
|||||||
IF (S# AND 2) > 0 THEN BEGIN
|
IF (S# AND 2) > 0 THEN BEGIN
|
||||||
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.1) * 10
|
ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.1) * 10
|
||||||
ACTOR@.Y% = SELF@.HOMEY%
|
ACTOR@.Y% = SELF@.HOMEY%
|
||||||
IF SELF@.RND% < DT% * 0.03 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
IF SELF@.ROLL% < DT% * 0.03 THEN SELF@.STATE# = 4 : SELF@.T% = 0
|
||||||
BEND
|
BEND
|
||||||
IF (S# AND 4) > 0 THEN BEGIN
|
IF (S# AND 4) > 0 THEN BEGIN
|
||||||
R# = DIVESTEP(DT%, 60, 0.9)
|
R# = DIVESTEP(DT%, 60, 0.9)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ typedef struct galaga_Enemy
|
|||||||
float t; /* parametric clock for the current maneuver */
|
float t; /* parametric clock for the current maneuver */
|
||||||
int32_t hp;
|
int32_t hp;
|
||||||
int32_t fire; /* outbox: script sets 1, engine consumes */
|
int32_t fire; /* outbox: script sets 1, engine consumes */
|
||||||
float rnd; /* inbox: engine writes fresh 0..1 each call */
|
float rnd; /* inbox: fresh 0..1 each call; ROLL% in BASIC */
|
||||||
} galaga_Enemy;
|
} galaga_Enemy;
|
||||||
|
|
||||||
/** @brief Frame state every enemy may read. Bound once as GAME@. */
|
/** @brief Frame state every enemy may read. Bound once as GAME@. */
|
||||||
@@ -86,7 +86,7 @@ typedef struct galaga_Shared
|
|||||||
float playerx; /* the player actor's position, this frame */
|
float playerx; /* the player actor's position, this frame */
|
||||||
float playery;
|
float playery;
|
||||||
int32_t wave;
|
int32_t wave;
|
||||||
float rnd; /* fresh 0..1 each frame; the issue #16 route */
|
float rnd; /* fresh 0..1 each frame; ROLL% to the script */
|
||||||
} galaga_Shared;
|
} galaga_Shared;
|
||||||
|
|
||||||
/* --------------------------------------------------------------- screens --- */
|
/* --------------------------------------------------------------- screens --- */
|
||||||
|
|||||||
@@ -491,7 +491,8 @@ static akerr_ErrorContext *frame(bool *running)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The shared frame state, refreshed before any enemy thinks. The engine
|
/* The shared frame state, refreshed before any enemy thinks. The engine
|
||||||
* filling GAME@.RND% is the issue #16 route: no RND verb exists. */
|
* fills GAME@.ROLL% from its own PRNG rather than letting the script call
|
||||||
|
* the native RND, so a headless run is the same game on every machine. */
|
||||||
galaga_shared.playerx = galaga_game.player->x + 50.0f;
|
galaga_shared.playerx = galaga_game.player->x + 50.0f;
|
||||||
galaga_shared.playery = galaga_game.player->y;
|
galaga_shared.playery = galaga_game.player->y;
|
||||||
galaga_shared.rnd = galaga_random();
|
galaga_shared.rnd = galaga_random();
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ static const akbasic_HostField ENEMY_FIELDS[] = {
|
|||||||
AKBASIC_HOST_FIELD( galaga_Enemy, t, "T%", AKBASIC_HOSTFIELD_FLOAT ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, t, "T%", AKBASIC_HOSTFIELD_FLOAT ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, hp, "HP#", AKBASIC_HOSTFIELD_INT32 ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, hp, "HP#", AKBASIC_HOSTFIELD_INT32 ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, fire, "FIRE#", AKBASIC_HOSTFIELD_INT32 ),
|
AKBASIC_HOST_FIELD( galaga_Enemy, fire, "FIRE#", AKBASIC_HOSTFIELD_INT32 ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Enemy, rnd, "RND%", AKBASIC_HOSTFIELD_FLOAT )
|
AKBASIC_HOST_FIELD( galaga_Enemy, rnd, "ROLL%", AKBASIC_HOSTFIELD_FLOAT )
|
||||||
};
|
};
|
||||||
static const akbasic_HostType ENEMY_TYPE = {
|
static const akbasic_HostType ENEMY_TYPE = {
|
||||||
"ENEMY", sizeof(galaga_Enemy), ENEMY_FIELDS, 8
|
"ENEMY", sizeof(galaga_Enemy), ENEMY_FIELDS, 8
|
||||||
@@ -87,7 +87,7 @@ static const akbasic_HostField GAME_FIELDS[] = {
|
|||||||
AKBASIC_HOST_FIELD( galaga_Shared, playerx, "PLAYERX%", AKBASIC_HOSTFIELD_FLOAT ),
|
AKBASIC_HOST_FIELD( galaga_Shared, playerx, "PLAYERX%", AKBASIC_HOSTFIELD_FLOAT ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Shared, playery, "PLAYERY%", AKBASIC_HOSTFIELD_FLOAT ),
|
AKBASIC_HOST_FIELD( galaga_Shared, playery, "PLAYERY%", AKBASIC_HOSTFIELD_FLOAT ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Shared, wave, "WAVE#", AKBASIC_HOSTFIELD_INT32 ),
|
AKBASIC_HOST_FIELD( galaga_Shared, wave, "WAVE#", AKBASIC_HOSTFIELD_INT32 ),
|
||||||
AKBASIC_HOST_FIELD( galaga_Shared, rnd, "RND%", AKBASIC_HOSTFIELD_FLOAT )
|
AKBASIC_HOST_FIELD( galaga_Shared, rnd, "ROLL%", AKBASIC_HOSTFIELD_FLOAT )
|
||||||
};
|
};
|
||||||
static const akbasic_HostType GAME_TYPE = {
|
static const akbasic_HostType GAME_TYPE = {
|
||||||
"GAME", sizeof(galaga_Shared), GAME_FIELDS, 4
|
"GAME", sizeof(galaga_Shared), GAME_FIELDS, 4
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ REM A demoscene production for the akbasic interpreter, written as if
|
|||||||
REM it were 1985 and this dialect were the machine under the tree.
|
REM it were 1985 and this dialect were the machine under the tree.
|
||||||
REM It leans on every corner of the interpreter on purpose:
|
REM It leans on every corner of the interpreter on purpose:
|
||||||
REM
|
REM
|
||||||
REM - there is no RND, so it carries chapter 17's LCG and seeds it
|
REM - it carries chapter 17's LCG rather than the native RND, and seeds it
|
||||||
REM from the jiffy clock
|
REM from the jiffy clock
|
||||||
REM - the palette cannot be rewritten, so every "colour cycle" is an
|
REM - the palette cannot be rewritten, so every "colour cycle" is an
|
||||||
REM honest redraw of the same strokes in the next colour
|
REM honest redraw of the same strokes in the next colour
|
||||||
@@ -81,7 +81,7 @@ REM machine and a predeclared scratch variable occupies one forever,
|
|||||||
REM where a scoped one gives its slot back. The first cut of this
|
REM where a scoped one gives its slot back. The first cut of this
|
||||||
REM program predeclared everything and ran the pool dry.
|
REM program predeclared everything and ran the pool dry.
|
||||||
RMAX# = 0
|
RMAX# = 0
|
||||||
RND# = 0
|
ROLL# = 0
|
||||||
TX% = 0
|
TX% = 0
|
||||||
TB# = 0
|
TB# = 0
|
||||||
|
|
||||||
@@ -816,11 +816,12 @@ REM =====================================================================
|
|||||||
REM Subroutines.
|
REM Subroutines.
|
||||||
REM =====================================================================
|
REM =====================================================================
|
||||||
|
|
||||||
REM Chapter 17's generator, verbatim: there is no RND in this dialect.
|
REM Chapter 17's historical generator, verbatim. RND(n) is built in now;
|
||||||
REM Answers 0 to RMAX#-1 in RND#, from the middle bits of the seed.
|
REM this stays so the demo runs identically on every machine.
|
||||||
|
REM Answers 0 to RMAX#-1 in ROLL#, from the middle bits of the seed.
|
||||||
LABEL RANDOM
|
LABEL RANDOM
|
||||||
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648)
|
||||||
RND# = MOD((SEED# / 65536), RMAX#)
|
ROLL# = MOD((SEED# / 65536), RMAX#)
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
REM NS# stars in four brightnesses. Two LCG pulls a star, and the
|
REM NS# stars in four brightnesses. Two LCG pulls a star, and the
|
||||||
@@ -830,10 +831,10 @@ SI# = 0
|
|||||||
DO WHILE SI# < NS#
|
DO WHILE SI# < NS#
|
||||||
RMAX# = W#
|
RMAX# = W#
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
X# = RND#
|
X# = ROLL#
|
||||||
RMAX# = H#
|
RMAX# = H#
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
Y# = RND#
|
Y# = ROLL#
|
||||||
B# = MOD(SEED#, 4)
|
B# = MOD(SEED#, 4)
|
||||||
COLOR 1, STC#(B#)
|
COLOR 1, STC#(B#)
|
||||||
DRAW 1, X#, Y#
|
DRAW 1, X#, Y#
|
||||||
@@ -1291,7 +1292,7 @@ LABEL REHOME
|
|||||||
MOVSPR L2#, 0.5 * W#, 0.5 * H#
|
MOVSPR L2#, 0.5 * W#, 0.5 * H#
|
||||||
RMAX# = 360
|
RMAX# = 360
|
||||||
GOSUB RANDOM
|
GOSUB RANDOM
|
||||||
SBG#(L2#) = RND#
|
SBG#(L2#) = ROLL#
|
||||||
MOVSPR L2#, SBG#(L2#) # 5
|
MOVSPR L2#, SBG#(L2#) # 5
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user