REM GALAGA enemy behavior. The C engine loads this file, runs it once so the REM definitions exist, and then calls one UPDATE function per enemy per frame. REM There is no top-level code: definitions, then END. REM REM Names the engine binds before every call: REM SELF@ - this enemy's record (ENEMY): the state machine's memory REM ACTOR@ - the engine's live actor (ACTOR): position is the real thing REM GAME@ - shared frame state (GAME): player position, wave, randomness REM REM SELF@.STATE# bits: 1 = entering 2 = in formation 4 = diving REM REM Two rules of this dialect that bite here, both from docs/03: REM - the LEFT operand decides integer or float arithmetic, so a float REM always goes first: SELF@.T% * 150 + 260, never 260 + 150 * SELF@.T% REM - RETURN at the start of a line ends the DEF body, so every early REM return rides an IF ... THEN, and only the last RETURN starts a line REM Ease toward the formation slot, with a little entry swirl. REM Answers 1 once the slot is reached, else 0. DEF GLIDEHOME(DT%) DX% = SELF@.HOMEX% - ACTOR@.X% DY% = SELF@.HOMEY% - ACTOR@.Y% K% = DT% * 4.5 IF K% > 1 THEN K% = 1 ACTOR@.X% = ACTOR@.X% + DX% * K% + SIN(SELF@.T% * 6) * 90 * DT% ACTOR@.Y% = ACTOR@.Y% + DY% * K% IF ABS(DX%) < 3 AND ABS(DY%) < 3 THEN RETURN 1 RETURN 0 REM One frame of a dive: accelerate downward, weave, lean toward the REM player's column, and glide back in from the top after falling out. DEF DIVESTEP(DT%, WEAVE%, LEAD%) SPD% = SELF@.T% * 150 + 260 ACTOR@.Y% = ACTOR@.Y% + SPD% * DT% ACTOR@.X% = ACTOR@.X% + SIN(SELF@.T% * 4) * WEAVE% * DT% DX% = GAME@.PLAYERX% - ACTOR@.X% IF DX% > 220 THEN DX% = 220 IF DX% < -220 THEN DX% = -220 ACTOR@.X% = ACTOR@.X% + DX% * LEAD% * DT% IF ACTOR@.Y% > 1040 THEN BEGIN ACTOR@.Y% = 0.0 - 90 SELF@.STATE# = 1 SELF@.T% = 0 BEND RETURN 0 REM Raise the fire flag when diving roughly above the player. The engine REM consumes FIRE# and does the spawning; the script only wishes. DEF DECIDEFIRE(DT%) DX% = GAME@.PLAYERX% - ACTOR@.X% IF ABS(DX%) > 140 THEN RETURN 0 IF ACTOR@.Y% > GAME@.PLAYERY% THEN RETURN 0 IF SELF@.RND% < DT% * 1.5 THEN SELF@.FIRE# = 1 RETURN 0 REM Bee: enter, breathe in formation, occasionally dive nearly straight. DEF UPDATEBEE(DT%) SELF@.T% = SELF@.T% + DT% IF SELF@.T% < 0 THEN RETURN 0 S# = SELF@.STATE# IF (S# AND 1) > 0 THEN BEGIN R# = GLIDEHOME(DT%) IF R# = 1 THEN SELF@.STATE# = 2 : SELF@.T% = 0 BEND IF (S# AND 2) > 0 THEN BEGIN ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.7) * 16 ACTOR@.Y% = SELF@.HOMEY% IF SELF@.RND% < DT% * 0.04 THEN SELF@.STATE# = 4 : SELF@.T% = 0 BEND IF (S# AND 4) > 0 THEN BEGIN R# = DIVESTEP(DT%, 130, 0.2) R# = DECIDEFIRE(DT%) BEND RETURN 0 REM Butterfly: the same machine with a wide lateral weave on the dive. DEF UPDATEBFLY(DT%) SELF@.T% = SELF@.T% + DT% IF SELF@.T% < 0 THEN RETURN 0 S# = SELF@.STATE# IF (S# AND 1) > 0 THEN BEGIN R# = GLIDEHOME(DT%) IF R# = 1 THEN SELF@.STATE# = 2 : SELF@.T% = 0 BEND IF (S# AND 2) > 0 THEN BEGIN ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 2.1) * 24 ACTOR@.Y% = SELF@.HOMEY% IF SELF@.RND% < DT% * 0.05 THEN SELF@.STATE# = 4 : SELF@.T% = 0 BEND IF (S# AND 4) > 0 THEN BEGIN R# = DIVESTEP(DT%, 260, 0.1) R# = DECIDEFIRE(DT%) BEND RETURN 0 REM Boss: two hit points, a slow sway, and a dive that leads the player. REM At one hit point it raises actor state bit 13 (8192), and the engine's REM character mapping swaps the sprite - the boundary crossed the other way. DEF UPDATEBOSS(DT%) SELF@.T% = SELF@.T% + DT% IF SELF@.T% < 0 THEN RETURN 0 IF SELF@.HP# = 1 THEN ACTOR@.STATE# = ACTOR@.STATE# OR 8192 S# = SELF@.STATE# IF (S# AND 1) > 0 THEN BEGIN R# = GLIDEHOME(DT%) IF R# = 1 THEN SELF@.STATE# = 2 : SELF@.T% = 0 BEND IF (S# AND 2) > 0 THEN BEGIN ACTOR@.X% = SELF@.HOMEX% + SIN(SELF@.T% * 1.1) * 10 ACTOR@.Y% = SELF@.HOMEY% IF SELF@.RND% < DT% * 0.03 THEN SELF@.STATE# = 4 : SELF@.T% = 0 BEND IF (S# AND 4) > 0 THEN BEGIN R# = DIVESTEP(DT%, 60, 0.9) R# = DECIDEFIRE(DT%) BEND RETURN 0 END