' INFILTRATOR 2000
'
' A SHOOT 'EM UP GAME INSPIRED BY CLASSICS
' LIKE XEVIOUS & URIDIUM
'
' WRITTEN ENTIRELY ON AN IPHONE!
'
' CODE & GRAPHICS COPYRIGHT (C) 2019 NAT PRYCE
' MUSIC (C) 2019 DESBYC
' LICENSE: CC BY-NC-SA 4.0


'==========================================
' INITIALISE THE HARDWARE

GAMEPAD 1
DISPLAY(,1)

' AUDIO VOICES
' 0: PLAYER SFX (SHIELDS, SHOTS, EXPLODING)
' 1: ENEMIES EXPLODING
' 2: ENEMY SHOTS
' 3: AUDIO CUES ABOUT ENEMY BEHAVIOUR

' SOUNDS
' 0: PLAYER SHOOT
' 1: ENEMY EXPLODE
' 2: SHIELDS ON
' 3: SHIELDS OFF
' 4: ENEMY LAY MINE
' 5: ENEMY SHOOT
' 6: ENEMY TURN ROUND FOR REAR ATTACK
' 7: BULLET HITS BARRIER


'==========================================
' CONSTANTS & UTILITIES

GLOBAL DIAGONAL, TRUE, FALSE, NONE

DIAGONAL = 1/SQR(2)
FALSE = 0
TRUE = NOT FALSE

' FOR EMPTY LISTS OR MISSING ELEMENTS OF
' ARRAYS & LISTS
NONE = -1



SUB FAIL(FNAME$, MSG$)
  ATTR (,0,0,1)
  SCROLL 0, 0, 0
  
  TEXT 0,1, "ERROR!"
  TEXT 1,2, FNAME$
  TEXT 1,3, MSG$
  END
END SUB


SUB INSORT(A(), N)
  ' INSERTION SORT
  
  I = I
  WHILE I < N
    J = I
    DO
       ' NO SHORTCUT LOGICAL OPERATORS :-(
       IF J <= 0 THEN GOTO ENDINSERT
       IF A(J-1) <= A(J) THEN GOTO ENDINSERT
       
       SWAP A(J), A(J-1)
       J = J - 1
    LOOP
    
    ENDINSERT:
    
    I = I + 1
  WEND
END SUB



'==========================================
' DOUBLY-LINKED LISTS OF NODES STORED IN 
' MULTIDIMENSIONAL ARRAYS.
' 
' MULTIPLE LISTS CAN USE NODES FROM IN THE
' SAME ARRAY
'
' AN EMPTY LIST IS REPRESENTED BY NONE (-1)

' LIST ELEMENT FIELD INDICES
GLOBAL _NXT, _PRV
_NXT = 0
_PRV = 1


SUB L_INIT(NODES(), MAX_INDEX)
  ' INITIALISE AN ARRAY AS A LINKED LIST
  ' POST: 0 IS THE HEAD OF THE LIST
  
  FOR I=0 TO MAX_INDEX
    NODES(I,_NXT) = I+1
    NODES(I,_PRV) = I-1
  NEXT I
  NODES(MAX_INDEX,_NXT) = NONE
END SUB


SUB L_ADD(NODES(), HEAD, N)
  ' ADD A NODE TO THE FRONT OF A LIST
  ' PRE: N IS NOT IN A LIST
  ' POST: HEAD = N
  
  NODES(N,_NXT) = HEAD
  NODES(N,_PRV) = NONE
  
  IF HEAD >= 0 THEN
    NODES(HEAD,_PRV) = N
  END IF
  HEAD = N
END SUB


SUB L_UNLINK(NODES(), HEAD, N)
  ' REMOVE A NODE FROM A LINKED LIST
  ' PRE: HEAD CONTAINS E
  ' POST: HEAD DOES NOT CONTAIN E
  ' POST: NEXT & PREV POINTERS OF E ARE NONE
  
  IF HEAD < 0 THEN
    CALL FAIL("L_UNLINK", "LIST EMPTY")
  END IF
  
  NXT = NODES(N,_NXT)
  PRV = NODES(N,_PRV)
  
  IF NXT >= 0 THEN
    NODES(NXT,_PRV) = PRV
  END IF
  
  IF PRV >= 0 THEN
    NODES(PRV,_NXT) = NXT
  ELSE
    HEAD = NXT
  END IF
  
  NODES(N,_NXT) = NONE
  NODES(N,_PRV) = NONE
END SUB


SUB L_MV(NODES(),FROM_LIST, E, TO_LIST)
  ' MOVE NODE E FROM ONE LIST TO ANOTHER
  ' PRE: FROM_LIST <> NONE
  ' PRE; FROM_LIST CONTAINS E
  ' POST: TO_LIST = E
  
  CALL L_UNLINK(NODES(), FROM_LIST, E)
  CALL L_ADD(NODES(), TO_LIST, E)
END SUB



'==========================================
' SPRITES
'
' DYNAMICALLY ALLOCATED SPRITES WITH
' FLOATING POINT VIRTUAL COORDINATES THAT
' CAN BE OUTSIDE THE PHYSICAL SCREEN 
' WITHOUT WRAPPING ROUND.

DIM GLOBAL SPRITES(63,8)
' FIELDS:
' 0: NEXT POINTER
' 1: PREV POINTER
' 2: X COORD
' 3: Y COORD
' 4-8: DEPENDS ON CONTROL SUBROUTINE

' SPRITE FIELD INDICES
GLOBAL _X, _Y
_X = 2
_Y = 3


' THE LIST OF UNUSED SPRITES
GLOBAL SPRITE_FREE
SPRITE_FREE = NONE


SUB SPRITE_INIT
  ' INITIALISE THE SPRITE ALLOCATOR
  
  CALL L_INIT(SPRITES(),63)
  SPRITE_FREE = 0
  SPRITE OFF
END SUB


SUB SPRITE_ALLOC(S_OUT)
  ' ALLOCATE A SPRITE FROM THE FREE LIST &
  ' RETURN ITS ID IN S_OUT

  S_OUT = SPRITE_FREE
  CALL L_UNLINK(SPRITES(), SPRITE_FREE, S_OUT)
END SUB


SUB SPRITE_ALLOC_TO(LIST)
  ' ALLOCATE A SPRITE AND ADD IT TO THE HEAD
  ' OF LIST
  
  S = SPRITE_FREE
  CALL L_MV(SPRITES(), SPRITE_FREE, S, LIST)
END SUB

SUB SPRITE_ADD(S, TO_L)
  ' ADD AN ALLOCATED SPRITE TO LIST TO_L
  ' 
  ' PRE: S IS NOT ALREADY IN A LIST
  ' POST: TO_L = S
  
  CALL L_ADD(SPRITES(), S, TO_L)
END SUB


SUB SPRITE_REALLOC(FROM_L, S, TO_L)
  ' REALLOCATE A SPRITE THAT HAS BEEN
  ' ALLOCATED, MOVING IT FROM LIST FROM_L
  ' TO THE HEAD OF LIST TO_L
  '
  ' POST: TO_L = S
  
  CALL L_MV(SPRITES(), FROM_L, S, TO_L)
END SUB


SUB SPRITE_RELEASE(SLIST, S)
  ' REMOVE SPRITE S FROM SLIST & ADD IT TO
  ' THE FREE LIST
  '
  ' YOU CANNOT USE S AFTER THIS CALL. IT IS
  ' SET TO NONE TO CATCH USE-AFTER-RELEASE
  ' BUGS.
  ' 
  ' POST: S = NONE
  
  SPRITE OFF S
  CALL SPRITE_REALLOC(SLIST, S, SPRITE_FREE)
  S = NONE
END SUB


SUB SPRITE_RELEASE1(S)
  ' ADD S TO THE FREE LIST
  '
  ' YOU CANNOT USE S AFTER THIS CALL. IT IS
  ' SET TO NONE TO CATCH USE-AFTER-RELEASE
  ' BUGS.
  ' 
  ' POST: S = NONE
  
  SPRITE OFF S 
  CALL SPRITE_ADD(S, SPRITE_FREE)
  S = NONE
END SUB



SUB SPRITE_SETPOS(S, X, Y)
  ' MOVE A SPRITE TO AN ABSOLUTE POSITION
  
  SPRITES(S,_X) = X
  SPRITES(S,_Y) = Y
  IF X < -32 OR Y < -32 OR X>160 OR Y>128 THEN
    SPRITE OFF S
  ELSE
    SPRITE S,X,Y,
  END IF
END SUB


SUB SPRITE_MOVE_BY(S, DX, DY)
  ' MOVE A SPRITE BY A RELATIVE VECTOR
  
  NEWX = SPRITES(S,_X) + DX
  NEWY = SPRITES(S,_Y) + DY
  CALL SPRITE_SETPOS(S, NEWX, NEWY)
END SUB


SUB SPRITE_CLAMPXY(S, MINX,MINY,MAXX,MAXY)
  X = SPRITES(S,_X)
  Y = SPRITES(S,_Y)
  NEWX = MIN(MAX(MINX,X),MAXX)
  NEWY = MIN(MAX(MINY,Y),MAXY)
  CALL SPRITE_SETPOS(S, NEWX,NEWY)
END SUB


SUB SPRITE_FRAME(S, C)
  SPRITE S,,,C
END SUB


SUB SPRITE_MOVE_TO(S, OX, OY, SPEED, DONE)
  SX = OX - SPRITES(S,_X)
  SY = OY - SPRITES(S,_Y)
  
  D = SQR(SX^2 + SY^2)
  
  IF D <= SPEED THEN
    CALL SPRITE_SETPOS(S, OX, OY)
    DONE = TRUE
  ELSE
    DX = SPEED * (SX/D)
    DY = SPEED * (SY/D)
    CALL SPRITE_MOVE_BY(S, DX, DY)
    DONE = FALSE
  END IF
END SUB


SUB SPRITE_CHASE(E, OTHER, SPEED, DONE)
  OX = SPRITES(OTHER,_X)
  OY = SPRITES(OTHER,_Y)
  CALL SPRITE_MOVE_TO(E, OX, OY, SPEED, DONE)
END SUB


SUB SPRITE_SETRELPOS(S, RELTO, DX, DY)
  X = SPRITES(RELTO,_X) + DX
  Y = SPRITES(RELTO,_Y) + DY
  CALL SPRITE_SETPOS(S, X, Y)
END SUB


'==========================================
' BACKGROUND

SUB BG_INIT
  ' INITIALISE THE GAME BACKGROUND
  
  BG 0
  FOR Y = 0 TO 31
    CALL BG_RND_ROW(Y)
  NEXT Y
END SUB


SUB BG_SCROLL
   BG 0
   T = GTIMER
   Y = -T*2 MOD 256
   SCROLL 0,0,Y
   
   IF Y MOD 8 = 0 THEN
      NEXT_ROW=Y\8 - 1
      CALL BG_RND_ROW(NEXT_ROW)
   END IF
END SUB


SUB BG_RND_ROW(Y)
  FOR X = 0 TO 31
    CALL BG_RND_CELL(X,Y)
  NEXT X
END SUB


SUB BG_RND_CELL(X,Y)
  IF RND*ZONE_LEN < (GTIMER - ZONE_LEN) THEN
    CALL BG_RND_METAL_CELL(X,Y)
  ELSE
    CALL BG_RND_GRASS_CELL(X,Y)
  END IF
END SUB

SUB BG_RND_GRASS_CELL(X,Y)
  N = RND
  
  IF N > 0.995 THEN
    C = 7
  ELSE IF N > 0.99 THEN
    C=6
  ELSE IF N > 0.85 THEN
    C = 4+INT(RND*2)
  ELSE IF N > 0.6 THEN
    C = 1+INT(RND*3)
  ELSE
    C=0
  END IF
  
  ATTR (0,RND<0.5,0,0,)
  CELL X,Y,C+32
END SUB

SUB BG_RND_METAL_CELL(X,Y)
  INVERSE = RND < 0.5
  
  N = RND
  IF N > 0.995 THEN
    C = 15
  ELSE IF N > 0.9 THEN
    C=10+INT(RND*5)
  ELSE IF N > 0.6 THEN
    C = 8+INT(RND*2)
  ELSE
    C=0
  END IF
  
  ATTR (5,INVERSE,INVERSE,0,)
  CELL X,Y,C+32
END SUB


'==========================================
' SKY (CLOUDS ETC)


SUB SKY_GENERATE
  BG 1
  BG FILL 0, 0 TO 20, 15 CHAR 0
  
  ATTR (5,0,0,1,0)
    
  FLUFFINESS = 0.66
  
  W = 6 + INT(RND*6)
  X = 1 + INT(RND*(18-W))
  CALL SKY_TOP_ROWS(X, 7, W, FLUFFINESS)
  CALL SKY_BTM_ROWS(X, 8, W, FLUFFINESS)
  
  ATTR (,0,0,,)
END SUB

SUB SKY_TOP_ROWS(X, Y, W, F)
  CALL SKY_TOP_ROW(X, Y, W)

  LINEW = W
  LINEX = 0
  LINEY = Y
  DO
    STARTX = 1+INT(RND*(F*(LINEW-2)))
    ENDX = 1+INT(RND*(F*(LINEW-2)))
    LINEW = LINEW - (STARTX+ENDX)
    
    IF LINEW < 2 THEN EXIT SUB
    
    LINEX = LINEX + STARTX
    LINEY = LINEY - 1
    
    CALL SKY_TOP_ROW(X+LINEX, LINEY, LINEW)
  LOOP
END SUB

SUB SKY_TOP_ROW(X0, Y, W)
  ATTR (,0,0,,)
  
  FOR X = 1 TO W-2
    CELL X0+X, Y, 17
  NEXT X
  
  CELL X0, Y, 16
  CELL X0+W-1, Y, 18
END SUB


SUB SKY_BTM_ROWS(X, Y, W, F)
  CALL SKY_BTM_ROW(X, Y, W)

  LINEW = W
  LINEX = 0
  LINEY = Y
  DO
    STARTX = 1+INT(RND*(F*(LINEW-2)))
    ENDX = 1+INT(RND*(F*(LINEW-2)))
    LINEW = LINEW - (STARTX+ENDX)
    
    IF LINEW < 2 THEN EXIT SUB
    
    LINEX = LINEX + STARTX
    LINEY = LINEY + 1
    
    CALL SKY_BTM_ROW(X+LINEX, LINEY, LINEW)
  LOOP
END SUB


SUB SKY_BTM_ROW(X0, Y, W)
  ATTR (,0,0,,)
  
  FOR X = 1 TO W-2
    CELL X0+X, Y, 17
  NEXT X
  
  ATTR (,1,1,,)
  
  CELL X0, Y, 18
  CELL X0+W-1, Y, 16
END SUB


SUB SKY_SCROLL
  S = GTIMER*3 MOD 256
  SCROLL 1,0,-S
  
  IF S >= 128 AND S < 128+3 THEN
    ' RENDER NEW CLOUDS OFFSCREEN
    CALL SKY_GENERATE
  END IF
END SUB

SUB SKY_INIT
  ' INITIALISE THE SKY LAYER (CLOUDS)
  
  BG 1
  BG FILL 0,0 TO 32,32 CHAR 0
  CALL SKY_GENERATE
END SUB


'==========================================
' HEAD UP DISPLAY

GLOBAL HUD_LIVES
GLOBAL HUD_BOMBS
GLOBAL HUD_SCORE
GLOBAL HUD_ALERT


SUB HUD_SHOW_LIVES
  X = (5-PLAYER_LIVES)*-8
  CALL SPRITE_SETPOS(HUD_LIVES, X, -24)
END SUB


SUB HUD_SHOW_BOMBS
  X = 160 - PLAYER_BOMBS*8
  CALL SPRITE_SETPOS(HUD_BOMBS, X, -24)
END SUB


SUB HUD_SHOW_SCORE
  ' CLEAR LAST SCORE TEXT
  FILL $8000 + 56*16, 4*16, 0
  
  S = PLAYER_SCORE\1
  POS = 0
  
  REPEAT
    DIGIT = S MOD 10
    S = S \ 10
    
    SRCCH = 112+DIGIT
    DSTCH = 59 - POS\2
    SHIFTDIV = 15 * ((POS+1) MOD 2) + 1
    
    FOR Y = 0 TO 7
      SRC = $8000 + SRCCH*16 + Y*2
      DST = $8000 + DSTCH*16 + Y*2
      
      B0 = PEEK(DST)
      B1 = PEEK(DST+1)
      
      B0 = B0 OR (PEEK(SRC)\SHIFTDIV)
      B1 = B1 OR (PEEK(SRC+1)\SHIFTDIV)
      
      POKE DST, B0
      POKE DST+1, B1
    NEXT Y
    
    INC POS
  UNTIL S = 0
  
  SHOWIT:
  
  CALL SPRITE_SETPOS(HUD_SCORE, 64, -24)
END SUB


SUB HUD_INIT
  ' 32X32 PIXEL SPRITES WITH THE BOTTOM 8
  ' PIXEL ROWS ON SCREEN, TO DISPLAY THE
  ' SCORE & NUMBER OF EXTRA LIVES & BOMBS
  
  CALL SPRITE_ALLOC(HUD_LIVES)
  SPRITE.A HUD_LIVES,(7,0,0,1,3)
  CALL SPRITE_FRAME(HUD_LIVES,0)
  
  CALL SPRITE_ALLOC(HUD_BOMBS)
  SPRITE.A HUD_BOMBS,(7,0,0,1,3)
  CALL SPRITE_FRAME(HUD_BOMBS,4)
  
  CALL SPRITE_ALLOC(HUD_SCORE)
  SPRITE.A HUD_SCORE,(3,0,0,1,3)
  CALL SPRITE_FRAME(HUD_SCORE,8)
  
  HUD_ALERT = NONE

  CALL HUD_SHOW_LIVES
  CALL HUD_SHOW_BOMBS
  CALL HUD_SHOW_SCORE
END SUB


SUB HUD_SHOW_ALERT
  IF HUD_ALERT = NONE THEN
    CALL ENEMY_ALLOC(HUD_ALERT)
  END IF
  SPRITE HUD_ALERT, 72, 8, 110
  SPRITE.A HUD_ALERT, (7,0,0,1,1)
END SUB


SUB HUD_CLEAR_ALERT
  CALL SPRITE_RELEASE1(HUD_ALERT)
END SUB


SUB HUD_ANIMATE_ALERT
  IF HUD_ALERT = NONE THEN EXIT SUB
  
  SHOWN = (GTIMER MOD 60)\30
  IF SHOWN THEN
    SPRITE HUD_ALERT, 72, 8,
  ELSE
    SPRITE OFF HUD_ALERT
  END IF
END SUB


'==========================================
' PLAYER MOVEMENT AND SHOOTING

' SPRITES
GLOBAL PLAYER
GLOBAL PLAYER_SHADOW


GLOBAL BULLETS_FREE, BULLETS 
GLOBAL BULLET_MIN
GLOBAL BULLET_MAX

GLOBAL PLAYER_IS_STARTING
PLAYER_IS_STARTING = 1
GLOBAL PLAYER_IS_UNSHIELDED
PLAYER_IS_UNSHIELDED = 2
GLOBAL PLAYER_IS_SHIELDED
PLAYER_IS_SHIELDED = 3
GLOBAL PLAYER_IS_EXPLODING
PLAYER_IS_EXPLODING = 4

GLOBAL PLAYER_STATE
GLOBAL PLAYER_COOLDOWN
GLOBAL PLAYER_BOMBS
GLOBAL PLAYER_LIVES
GLOBAL PLAYER_SHOTS
GLOBAL PLAYER_MISSES
GLOBAL PLAYER_SPEED
GLOBAL PLAYER_SCORE


SUB PLAYER_INIT
  ' INITIALISE THE PLAYER SPRITES & HUD
  '
  ' PRE: SPRITE_INIT MUST HAVE BEEN CALLED
  ' NOTE: MUST BE CALLED BEFORE ENEMY_INIT
  
  PLAYER_SPEED = 1
  PLAYER_BOMBS = 3
  PLAYER_LIVES = 5
  PLAYER_SHOTS = 0
  PLAYER_MISSES = 0
  PLAYER_SCORE = 0
  
  ' HUD IS ALLOCATED FIRST TO ENSURE IT
  ' IS DRAWN OVER EVERYTHING ELSE
  CALL HUD_INIT

  CALL SPRITE_ALLOC(PLAYER)
  CALL SPRITE_ALLOC(PLAYER_SHADOW)
  SPRITE.A PLAYER_SHADOW,(2)
  CALL PLAYER_SHADOW
  
  ' ALLOCATE A CONTIGUOUS RANGE OF SPRITES
  ' FOR PLAYER BULLETS TO MAKE COLLISION
  ' DETECTION EASY
  
  BULLETS = NONE
  BULLETS_FREE = NONE
  BULLET_MIN = SPRITE_FREE
  BULLET_MAX = SPRITE_FREE + 3
  
  FOR I = BULLET_MIN TO BULLET_MAX
    CALL SPRITE_ALLOC_TO(BULLETS_FREE)
    CALL SPRITE_FRAME(BULLETS_FREE,7)
    SPRITE.A BULLETS_FREE,(3)
  NEXT I
    
  CALL PLAYER_START_LIFE
END SUB


SUB PLAYER_START_LIFE
  PLAYER_STATE = PLAYER_IS_STARTING
  PLAYER_COOLDOWN = 1
  CALL HUD_SHOW_LIVES
  
  SPRITE.A PLAYER,(1, 0, 0, 0, 0)
  
  CALL SPRITE_SETPOS(PLAYER, 76, 132)
  CALL SPRITE_FRAME(PLAYER, 2)
  SPRITE OFF PLAYER_SHADOW
END SUB


SUB PLAYER_LOSE_LIFE
  PLAYER_LIVES = PLAYER_LIVES - 1
  CALL PLAYER_START_LIFE
END SUB


SUB PLAYER_SCORE(N)
  HITS = (PLAYER_SHOTS-PLAYER_MISSES)
  ACCURACY = HITS/PLAYER_SHOTS
  ADD PLAYER_SCORE, 10*N*ACCURACY
  CALL HUD_SHOW_SCORE
END SUB


SUB PLAYER_EXPLODE
  ' OFFSET BY (-4,-4) BECAUSE EXPLOSION
  ' SPRITE IS 16X16 AND PLAYER SPRITE IS 8X8
  CALL SPRITE_MOVE_BY(PLAYER, -4, -4)
  CALL SPRITE_FRAME(PLAYER, 128)
  SPRITE.A PLAYER,(3,RND<0.5,RND<0.5,,1)
  
  CALL FX_BIGEXPLOSION(PLAYER)
  
  PLAYER_STATE = PLAYER_IS_EXPLODING
  PLAYER_COOLDOWN = 0
  SPRITE OFF PLAYER_SHADOW
END SUB

SUB PLAYER_SHIELD_OFF
  IF PLAYER_STATE <> PLAYER_IS_UNSHIELDED THEN
    PLAYER_STATE = PLAYER_IS_UNSHIELDED
    PLAYER_COOLDOWN = 0
    PLAY 0, 30, 30 SOUND 3
  END IF
END SUB


SUB PLAYER_MOVE
  S = PLAYER_STATE
  IF S = PLAYER_IS_STARTING THEN
    CALL PLAYER_ACT_STARTING
  ELSE IF S = PLAYER_IS_EXPLODING THEN
    CALL PLAYER_ACT_EXPLODING
  ELSE
    CALL PLAYER_ACT_ACTIVE
  END IF
END SUB


SUB PLAYER_ACT_STARTING
  DX = LEFT(0) - RIGHT(0)
  CALL SPRITE_MOVE_BY(PLAYER, DX, -0.25)
  
  IF SPRITES(PLAYER,_Y) < 100 THEN
    CALL PLAYER_SHIELD_OFF
  ELSE IF TIMER MOD 2 THEN
      SPRITE OFF PLAYER
  END IF
END SUB


SUB PLAYER_ACT_ACTIVE
  P = PLAYER
  
  IF PLAYER_COOLDOWN > 0 THEN
    PLAYER_COOLDOWN = PLAYER_COOLDOWN - 1
    IF PLAYER_COOLDOWN = 0 THEN
      CALL PLAYER_SHIELD_OFF
    END IF
  END IF
  
  DX = LEFT(0) - RIGHT(0)
  DY = UP(0) - DOWN(0)
  IF DX <> 0 OR DY <> 0 THEN
    S = PLAYER_SPEED / SQR(DX^2 + DY^2)
    CALL SPRITE_MOVE_BY(P, DX*S, DY*S)
  END IF
  CALL SPRITE_CLAMPXY(P, 0, 0, 152, 116)
  CALL SPRITE_FRAME(P, 2+DX)
  
  IF PLAYER_STATE = PLAYER_IS_UNSHIELDED THEN
    CALL PLAYER_SHADOW
  ELSE
    IF TIMER MOD 2 THEN
      SPRITE OFF P
    END IF
    SPRITE OFF PLAYER_SHADOW
  END IF
END SUB


SUB PLAYER_ACT_EXPLODING
  F = PLAYER_COOLDOWN\5
  IF F < 8 THEN
    CALL SPRITE_FRAME(PLAYER, 128+F*2)
    PLAYER_COOLDOWN = PLAYER_COOLDOWN+1
    
    IF PLAYER_LIVES = 1 AND F > 0 THEN
      ' LOSING LAST LIFE, SO EPIC SLO-MO
      WAIT F
    END IF
  ELSE
    CALL PLAYER_LOSE_LIFE
  END IF
END SUB


SUB PLAYER_COLLIDE(E)
  IF PLAYER_STATE = PLAYER_IS_UNSHIELDED THEN
    ETYPE = SPRITES(E,_TYPE)
    IF NOT ETYPE AND INVULNERABLE THEN
      CALL ENEMY_EXPLODE(E, 0, 0.25)
    END IF
    CALL PLAYER_EXPLODE
  END IF
END SUB


SUB PLAYER_SHADOW
  ' POSITION A SEMI-TRANSPARENT SHADOW
  ' UNDER THE PLAYER'S SHIP
  
  X = SPRITE.X(PLAYER)+3
  Y = SPRITE.Y(PLAYER)+3
  C = SPRITE.C(PLAYER)+3
  
  IF TIMER MOD 2 THEN
    SPRITE PLAYER_SHADOW,X,Y,C
  ELSE
    SPRITE OFF PLAYER_SHADOW
  END IF
END SUB


SUB PLAYER_SHOOT
  IF BUTTON(0,0) THEN
    CALL PLAYER_SHOOT_BULLET
  END IF
  
  IF BUTTON TAP(0,1) THEN
    CALL PLAYER_SMART_BOMB
  END IF
END SUB


SUB PLAYER_SHOOT_BULLET
  IF PLAYER_COOLDOWN > 0 THEN EXIT SUB
  
  X = SPRITES(PLAYER,_X)
  Y = SPRITES(PLAYER,_Y) - 6
  
  B = BULLETS_FREE
  CALL SPRITE_REALLOC(BULLETS_FREE,B,BULLETS)
  CALL SPRITE_SETPOS(B, X, Y)
  
  PLAYER_COOLDOWN = 16
  PLAYER_SHOTS = PLAYER_SHOTS + 1
  
  PLAY 0, 61, 10 SOUND 0
END SUB

SUB BULLETS_MOVE
  B = BULLETS
  WHILE B <> NONE
    NEXT_B = SPRITES(B,_NXT)
    CALL BULLET_MOVE(B)
    B = NEXT_B
  WEND
END SUB


SUB BULLET_MOVE(B)
  Y = SPRITES(B,_Y)
  IF Y < -8 THEN
    CALL BULLET_RELEASE(B)
    PLAYER_MISSES = PLAYER_MISSES + 1
  ELSE
    CALL SPRITE_MOVE_BY(B,0,-2)
  END IF
END SUB


SUB BULLET_RELEASE(B)
  SPRITE OFF B
  CALL SPRITE_REALLOC(BULLETS,B,BULLETS_FREE)
END SUB


SUB PLAYER_SMART_BOMB
  IF PLAYER_BOMBS <= 0 THEN EXIT SUB
  IF ENEMY_LIST = NONE THEN EXIT SUB
  
  E = ENEMY_LIST
  WHILE E <> NONE
    NXT = SPRITES(E,_NXT)
    INV = SPRITES(E,_TYPE) AND INVULNERABLE
    IF INV = FALSE THEN
      CALL ENEMY_EXPLODE(E, 0, 0)
    END IF
    E = NXT
  WEND
  
  PLAYER_BOMBS = PLAYER_BOMBS - 1
  
  CALL HUD_SHOW_BOMBS
END SUB


'==========================================
' ENEMY MOVEMENT AND SHOOTING

GLOBAL INVULNERABLE
INVULNERABLE = $8000

' ENEMY ACTIVITY TYPES
GLOBAL ACT_DISC
ACT_DISC = 1
GLOBAL ACT_CROSS
ACT_CROSS = 2
GLOBAL ACT_SEEKER
ACT_SEEKER = 3
GLOBAL ACT_WAVY
ACT_WAVY = 4
GLOBAL ACT_SHOOTER
ACT_SHOOTER = 5
GLOBAL ACT_BARRIER
ACT_BARRIER = 6
GLOBAL ACT_LAYER
ACT_LAYER = 7
GLOBAL ACT_EXPLODE
ACT_EXPLODE = 8
GLOBAL ACT_BULLET
ACT_BULLET = 9
GLOBAL ACT_WITHDRAW
ACT_WITHDRAW = 10
GLOBAL ACT_REVERSE
ACT_REVERSE = 11
GLOBAL ACT_MINE
ACT_MINE = 12

GLOBAL ACT_SNAKE_SLITHER
ACT_SNAKE_SLITHER = 13 + INVULNERABLE
GLOBAL ACT_SNAKE_CHARGE
ACT_SNAKE_CHARGE = 14 + INVULNERABLE
GLOBAL ACT_SNAKE_TAIL
ACT_SNAKE_TAIL = 15 + INVULNERABLE
GLOBAL ACT_SNAKE_FINAL
ACT_SNAKE_FINAL = 16 + INVULNERABLE


' ENEMY SPRITE PROPERTIES
' 4: ENEMY TYPE
' 5: START TIME (GTIMER WHEN STATE STARTED)
' 6: PROPA (USE DEPENDS ON THE TYPE)
' 7: PROPB (USE DEPENDS ON THE TYPE)
' 8: PROPC (USE DEPENDS ON THE TYPE)

GLOBAL _TYPE, _START, _PROPA, _PROPB
_TYPE = 4
_START = 5
_PROPA = 6
_PROPB = 7
_PROPC = 8

' COMMON USES OF PROPS A, B & C
GLOBAL  _DX, _DY, _FLASH
_DX = _PROPA
_DY = _PROPB
_FLASH = _PROPC

GLOBAL ENEMY_LIST
GLOBAL ENEMY_MIN

SUB ENEMY_INIT
  ' INITIALISE THE LIST OF ENEMY SPRITES
  '
  ' PRE: SPRITE_INIT MUST HAVE BEEN CALLED
  ' PRE: PLAYER_INIT MUST HAVE BEEN CALLED
  
  ENEMY_LIST = NONE
  ENEMY_MIN = SPRITE_FREE
END SUB


SUB ENEMY_ALLOC(E)
  CALL SPRITE_ALLOC(E)
  CALL L_ADD(SPRITES(), ENEMY_LIST, E)
END SUB


SUB ENEMY_RELEASE(E)
  ' RELEASES AN ENEMY SPRITE BACK TO THE
  ' FREE SPRITE POOL
  '
  ' POST: E = NONE
  
  CALL SPRITE_RELEASE(ENEMY_LIST, E)
END SUB


SUB ENEMY_START(E, STATE)
  SPRITES(E,_TYPE) = STATE
  CALL ENEMY_RESET(E)
END SUB


SUB ENEMY_RESET(E)
  SPRITES(E,_START) = GTIMER
END SUB


SUB ENEMY_ANIMATE_ALL
  E = ENEMY_LIST
  
  WHILE E >= 0
    NEXT_E = SPRITES(E,0)
    CALL ENEMY_ANIMATE(E)
    E = NEXT_E
  WEND
END SUB


SUB ENEMY_MOVEP(E, DXPROP, DYPROP)
  ' MOVE ENEMY BY DX,DY STORED IN ITS
  ' SPRITE PROPERTIES
  
  IF E = NONE THEN EXIT SUB
  
  IF DXPROP <> NONE THEN
    DX = SPRITES(E,DXPROP)
  ELSE
    DX = 0
  END IF
  IF DYPROP <> NONE THEN
    DY = SPRITES(E,DYPROP)
  ELSE
    DY = 0
  END IF
  
   CALL SPRITE_MOVE_BY(E, DX, DY)
END SUB


SUB ENEMY_RM_OFFSCREEN(E)
  IF E = NONE THEN EXIT SUB
  
  X = SPRITES(E,_X)
  Y = SPRITES(E,_Y)
  
  IF X<=-8 OR X>160 OR Y<=-8 OR Y>128 THEN
    CALL ENEMY_RELEASE(E)
  END IF
END SUB


SUB ENEMY_DIE_IF_SHOT(E)
  IF E = NONE THEN EXIT SUB
  
  H = SPRITE HIT(E,BULLET_MIN TO BULLET_MAX) 
  IF H THEN
    CALL BULLET_RELEASE(HIT)
    CALL ENEMY_EXPLODE(E, 0, -0.25)
    CALL PLAYER_SCORE(1)
    E = NONE
  END IF
END SUB


SUB ENEMY_FLASH_IF_SHOT(E, WAS_HIT)
  IF E = NONE THEN EXIT SUB

  H = SPRITE HIT(E,BULLET_MIN TO BULLET_MAX) 
  
  IF H THEN
    CALL BULLET_RELEASE(HIT)
    SPRITE.A E,(7)
    SPRITES(E,_FLASH) = 6
    PLAY 1, 40, 20 SOUND 7
    
  ELSE IF SPRITES(E,_FLASH) > 0 THEN
    SPRITES(E,_FLASH) = SPRITES(E,_FLASH)-1
    IF SPRITES(E,_FLASH) = 0 THEN
      SPRITE.A E,(2)
    END IF
  END IF
  
  WAS_HIT = H
END SUB


SUB ENEMY_SHOOT_AT(FROM, AT, SPEED)
  ' SHOOT A BULLET TRAVELLING AT THE GIVEN
  ' SPEED FROM SPRITE `FROM` TO THE CURRENT
  ' POSITION OF SPRITE `AT`
  
  X = SPRITES(FROM,_X)
  Y = SPRITES(FROM,_Y)
  XX = SPRITES(AT,_X) - X
  YY = SPRITES(AT,_Y) - Y
  D = SQR(XX^2 + YY^2)
  IF D <> 0 THEN
    DX = SPEED * XX/D
    DY = SPEED * YY/D
  
    CALL ENEMY_SHOOT(X, Y, DX, DY)
  END IF
END SUB


SUB ENEMY_SHOOT_RND(E, SPEED)
  ' SHOOT IN A RANDOM DIRECTION FROM E
  
  X = SPRITES(E,_X)
  Y = SPRITES(E,_Y)
  RX = RND*2 - 1
  RY = RND*2 - 1
  RM = SQR(RX^2 + RY^2)
  DX = SPEED * RX/RM
  DY = SPEED * RY/RM
  
  CALL ENEMY_SHOOT(X, Y, DX, DY)
END SUB

SUB ENEMY_SHOOT(X, Y, DX, DY)
  ' SHOOT A BULLET FROM (X,Y) WITH VELOCITY
  ' (DX,DY) PER FRAME

  ' AVOID RUNNING OUT OF MEMORY
  IF SPRITE_FREE = NONE THEN EXIT SUB
  
  E = NONE
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, X, Y)
  CALL SPRITE_FRAME(E, 8)
  SPRITE.A E,(3,0,0,0,0)
  SPRITES(E,_DX) = DX
  SPRITES(E,_DY) = DY
  CALL ENEMY_START(E, ACT_BULLET)
  
  PLAY 2, 48, 8 SOUND 5
END SUB

SUB ENEMY_ANIMATE(E)
  TYPE = SPRITES(E,_TYPE)
  T = GTIMER - SPRITES(E,5)
  
  IF TYPE = ACT_DISC THEN
    CALL ENEMY_ACT_DISC(E, T)
  ELSE IF TYPE = ACT_WAVY THEN
    CALL ENEMY_ACT_WAVY(E, T)
  ELSE IF TYPE = ACT_SHOOTER THEN
    CALL ENEMY_ACT_SHOOTER(E, T)
  ELSE IF TYPE = ACT_BARRIER THEN
    CALL ENEMY_ACT_BARRIER(E, T)
  ELSE IF TYPE = ACT_BULLET THEN
    CALL ENEMY_ACT_BULLET(E, T)
  ELSE IF TYPE = ACT_EXPLODE THEN
    CALL ENEMY_ACT_EXPLODE(E,T)
  ELSE IF TYPE = ACT_REVERSE THEN
    CALL ENEMY_ACT_REVERSE(E, T)
  ELSE IF TYPE = ACT_SEEKER THEN
    CALL ENEMY_ACT_SEEKER(E, T)
  ELSE IF TYPE = ACT_WITHDRAW THEN
    CALL ENEMY_ACT_WITHDRAW(E, T)
  ELSE IF TYPE = ACT_CROSS THEN
    CALL ENEMY_ACT_CROSS(E, T)
  ELSE IF TYPE = ACT_LAYER THEN
    CALL ENEMY_ACT_LAYER(E, T)
  ELSE IF TYPE = ACT_MINE THEN
    CALL ENEMY_ACT_MINE(E, T)
  ELSE IF TYPE = ACT_SNAKE_SLITHER THEN
    CALL SNAKE_ACT_SLITHER(E, T)
  ELSE IF TYPE = ACT_SNAKE_CHARGE THEN
    CALL SNAKE_ACT_CHARGE(E, T)
  ELSE IF TYPE = ACT_SNAKE_TAIL THEN
    CALL SNAKE_ACT_TAIL(E, T)
  ELSE IF TYPE = ACT_SNAKE_FINAL THEN
    CALL SNAKE_ACT_FINAL(E, T)
  ELSE
    ERR$ = "UNKNOWN TYPE " + STR$(TYPE)
    CALL FAIL("ENEMY_ANIMATE", ERR$)
  END IF
  
  IF E <> NONE AND TYPE <> ACT_EXPLODE THEN
    IF SPRITE HIT(E,PLAYER) THEN
      CALL PLAYER_COLLIDE(E)
    END IF
  END IF
END SUB



' DISC FLOATS DOWN THE SCREEN

SUB ENEMY_SPAWN_DISC(E)
  X = SPRITES(PLAYER,_X) + RND*16 - 8
  
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, X, -8)
  CALL SPRITE_FRAME(E, 64)
  SPRITE.A E,(2,0,0,0,0)
  
  ' FRAME DIRECTION
  IF RND < 0.5 THEN
    FD = 1
  ELSE
    FD = -1
  END IF
  
  SPRITES(E,_PROPB) = FD
  CALL ENEMY_START(E, ACT_DISC)
END SUB

SUB ENEMY_ACT_DISC(E, T)
  CALL SPRITE_MOVE_BY(E, 0, 1.25)
  CALL ENEMY_RM_OFFSCREEN(E)
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE THEN
    DF = SPRITES(E,_PROPB)
    F = (4 + (T/6 MOD 4)*DF) MOD 4
    CALL SPRITE_FRAME(E,64+F)
  END IF
END SUB


' WAVY FOLLOWS A SINUSOIDAL PATH UNTIL IT
' REACHES THE BOTTOM OF THE SCREEN, THEN
' IT SHOOTS UP AND OFF THE TOP

SUB ENEMY_SPAWN_WAVY(E)
  X = 16 + RND*96
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, RND*128,-8)
  CALL SPRITE_FRAME(E, 76)
  SPRITE.A E,(2,0,0,0,0)
  SPRITES(E,_PROPA) = X
  CALL ENEMY_START(E, ACT_WAVY)
END SUB

SUB ENEMY_ACT_WAVY(E, T)
  Y = T-8
  IF Y < 180 THEN
    P = 90
    XOFFSET = COS(2*PI * (T MOD P)/P)
    
    X = SPRITES(E,_PROPA) + XOFFSET*16
    F = 76 + (2+INT(XOFFSET*2))
    
    CALL SPRITE_SETPOS(E, X, Y)
    CALL SPRITE_FRAME(E,F)
    CALL ENEMY_DIE_IF_SHOT(E)
  ELSE
    X = SPRITES(PLAYER,_X)
    CALL SPRITE_SETPOS(E, X, 180)
    CALL ENEMY_START(E, ACT_REVERSE)
    PLAY 3, 48, 20 SOUND 6
  END IF
END SUB

SUB ENEMY_ACT_REVERSE(E, T)
  CALL SPRITE_MOVE_BY(E, 0, -2)
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE THEN
    ' PLAYER IS STARTING
    PS = PLAYER_STATE = PLAYER_IS_STARTING
    ' PLAYER IS EXPLODING
    PE = PLAYER_STATE = PLAYER_IS_EXPLODING
    ' OFF SCREEN
    OS = SPRITES(E,_Y) <= -8
    
    IF OS OR PS OR PE THEN
      CALL ENEMY_RELEASE(E)
    ELSE
      CALL SPRITE_FRAME(E,68+(T/6 MOD 4))
    END IF
  END IF
END SUB


' SHOOTER FLOATS ACROSS THE SCREEN
' HORIZONTALLY FIRING BULLETS

SUB ENEMY_SPAWN_SHOOTER(E)
  IF RND < 0.5 THEN
    X = -8
    DX = 0.5
  ELSE
    X = 160
    DX = -0.5
  END IF
  Y = RND*64
  
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, X, Y)
  CALL SPRITE_FRAME(E, 72)
  SPRITE.A E,(2,0,0,0,0)
  SPRITES(E,_DX) = DX
  CALL ENEMY_START(E, ACT_SHOOTER)
END SUB

SUB ENEMY_ACT_SHOOTER(E, T)
  CALL ENEMY_MOVEP(E, _DX, NONE)
  CALL ENEMY_RM_OFFSCREEN(E)
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE THEN
    F = T/6 MOD 4
    CALL SPRITE_FRAME(E,72+F)
    
    IF T MOD 60 = 0 THEN
      U = PLAYER_STATE = PLAYER_IS_UNSHIELDED
      IF U THEN
        CALL ENEMY_SHOOT_AT(E, PLAYER, 1)
      END IF
    END IF
  END IF
END SUB

SUB ENEMY_ACT_BULLET(E, T)
  CALL ENEMY_MOVEP(E, _DX, _DY)
  CALL ENEMY_RM_OFFSCREEN(E)
END SUB


' BARRIER - BULLETS DO NOT DESTROY IT BUT
' CAN PUSH IT BACK OFF THE TOP OF THE SCREEN

SUB ENEMY_SPAWN_BARRIER(E)
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, RND*128,-8)
  CALL SPRITE_FRAME(E, 84)
  SPRITE.A E,(2,0,0,0,0)
  CALL ENEMY_START(E, ACT_BARRIER)
END SUB

SUB ENEMY_ACT_BARRIER(E, T)
  CALL SPRITE_MOVE_BY(E, 0, 0.25)
  CALL ENEMY_RM_OFFSCREEN(E)
  
  IF E <> NONE THEN
    F = T/6 MOD 4
    CALL SPRITE_FRAME(E,84+F)
    
    WAS_HIT = FALSE
    CALL ENEMY_FLASH_IF_SHOT(E, WAS_HIT)
    
    IF WAS_HIT THEN
      CALL SPRITE_MOVE_BY(E, 0, -8)
      CALL ENEMY_RM_OFFSCREEN(E)
      IF E = NONE THEN
        CALL PLAYER_SCORE(3)
      END IF
    END IF
  END IF
END SUB


' SEEKER - HOMES IN ON THE PLAYER SHIP

SUB ENEMY_SPAWN_SEEKER(E)
  CALL ENEMY_ALLOC(E)
  
  Y = RND*64
  IF RND < 0.5 THEN
    X = -8
  ELSE
    X = 160
  END IF
  
  CALL SPRITE_SETPOS(E, X, Y)
  CALL SPRITE_FRAME(E, 84)
  SPRITE.A E,(2,0,0,0,0)
  CALL ENEMY_START(E, ACT_SEEKER)
END SUB

SUB ENEMY_ACT_SEEKER(E, T)
  CALL SPRITE_CHASE(E, PLAYER, 0.5, -1)
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE THEN  
    CALL SPRITE_FRAME(E, 80 + T/6 MOD 4)
  END IF
  
  IF PLAYER_STATE = PLAYER_IS_EXPLODING THEN
    CALL ENEMY_START(E, ACT_WITHDRAW)
  END IF
END SUB

SUB ENEMY_ACT_WITHDRAW(E, T)
  PX = SPRITES(PLAYER,_X)
  DX = SGN(SPRITES(E,_X)-PX) OR 1
  
  CALL SPRITE_MOVE_BY(E, DX*1.5, 0)
  CALL ENEMY_RM_OFFSCREEN(E) 
  CALL ENEMY_DIE_IF_SHOT(E)
  IF E <> NONE THEN
    CALL SPRITE_FRAME(E, 80 + T/6 MOD 4)
  END IF
END SUB


' CROSS - LAUNCHES TOWARDS THE PLAYER
'         POSITION, SHOOTING DIAGONALLY.
'         FIRES UP TO FOUR DIAGONAL BULLETS
'         WHEN SHOT.

SUB ENEMY_SPAWN_CROSS(E)
  CALL ENEMY_ALLOC(E)
  SPRITE.A E,(2,0,0,0,0)
  
  SPEED = 0.5
  
  X = RND*152
  Y = -8
  SX = SPRITES(PLAYER,_X) - X
  SY = SPRITES(PLAYER,_Y) - Y
  D = SQR(SX^2 + SY^2)
  
  CALL SPRITE_SETPOS(E, X, Y)
  SPRITES(E,_DX) = (SPEED * SX)/D
  SPRITES(E,_DY) = (SPEED * SY)/D
  CALL ENEMY_START(E, ACT_CROSS)
END SUB


SUB ENEMY_ACT_CROSS(E, T)
  DX = SPRITES(E,_DX)
  DY = SPRITES(E,_DY)
  
  CALL SPRITE_MOVE_BY(E, DX, DY)
  CALL ENEMY_DIE_IF_SHOT(E)
  CALL ENEMY_RM_OFFSCREEN(E)
  
  IF E <> NONE THEN
    CALL SPRITE_FRAME(E, 88+((T/6)MOD 4))
    
    P = 40
    IF T MOD P = 0 THEN
      DIR = INT((T MOD (P*4))/P)
      IF DIR = 3 THEN
        BDX = -1
        BDY = -1
      ELSE IF DIR = 2 THEN
        BDX = 1
        BDY = -1
      ELSE IF DIR = 1 THEN
        BDX = 1
        BDY = 1
      ELSE IF DIR = 0 THEN
        BDX = -1
        BDY = 1
      END IF

      X = SPRITES(E,_X) + BDX*4
      Y = SPRITES(E,_Y) + BDY*4
      S = DIAGONAL
      
      CALL ENEMY_SHOOT(X,Y,DX+BDX*S,DY+BDY*S)
      
    END IF
  END IF
END SUB


' LAYER - SPAWNS A LINE OF STATIC MINES

SUB ENEMY_SPAWN_LAYER(E)
  CALL ENEMY_ALLOC(E)
  SPRITE.A E, (2,0,0,0,0)
  
  IF RND < 0.5 THEN
    X0 = -8
    X1 = 160
  ELSE
    X0 = 160
    X1 = -8
  END IF
  
  Y0 = RND*112
  Y1 = RND*96
  
  SPEED = 0.25
  
  PATHDX = X1-X0
  PATHDY = Y1-Y0
  S = SQR(PATHDX^2 + PATHDY^2) 
  
  CALL SPRITE_SETPOS(E, X0, Y0)
  SPRITES(E,_DX) = SPEED * PATHDX/S
  SPRITES(E,_DY) = SPEED * PATHDY/S
  
  CALL ENEMY_START(E, ACT_LAYER)
END SUB

SUB ENEMY_ACT_LAYER(E, T)
  CALL ENEMY_MOVEP(E, _DX, _DY)
  CALL ENEMY_RM_OFFSCREEN(E)
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE THEN
    CALL SPRITE_FRAME(E, 92+((T/60) MOD 2))
    IF T MOD 150 = 0 THEN
      CALL ENEMY_LAY_MINE(E)
    END IF
  END IF
END SUB

SUB ENEMY_LAY_MINE(E)
  IF SPRITE_FREE = NONE THEN EXIT SUB
  
  X = SPRITES(E,_X)
  Y = SPRITES(E,_Y)
  
  IF X > 8 AND X < 152 THEN
    CALL ENEMY_SPAWN_MINE(X, Y)
    PLAY 2, 56, 20 SOUND 4
  END IF
END SUB

SUB ENEMY_SPAWN_MINE(X, Y)
  E = NONE
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, X, Y)
  SPRITE.A E, (2,0,0,0,0)
  CALL ENEMY_START(E, ACT_MINE)
END SUB

SUB ENEMY_ACT_MINE(E, T)
  CALL SPRITE_FRAME(E, 94+((T/60) MOD 2))
  CALL ENEMY_DIE_IF_SHOT(E)
  
  IF E <> NONE AND T >= 300 THEN
    FOR I = 1 TO 3
      CALL ENEMY_SHOOT_RND(E, 1)
    NEXT I
      
    CALL ENEMY_EXPLODE(E, 0, 0)
  END IF
END SUB


' EXPLOSIONS

SUB ENEMY_EXPLODE(E, DX, DY)
  ' OFFSET BY (-4,-4) BECAUSE EXPLOSION
  ' SPRITE IS 16X16 AND ENEMY SPRITE IS 8X8
  
  CALL SPRITE_MOVE_BY(E, -4, -4)
  CALL SPRITE_FRAME(E, 128)
  SPRITE.A E,(3, RND<0.5, RND<0.5, 1, 1)
  SPRITES(E,_DX) = DX
  SPRITES(E,_DY) = DY
  CALL ENEMY_START(E, ACT_EXPLODE)
  
  PLAY 1, 12, 10 SOUND 1
END SUB


SUB ENEMY_SPAWN_EXPLODE(X, Y, DX, DY)
  IF SPRITE_FREE = NONE THEN EXIT SUB
  
  E = NONE
  CALL ENEMY_ALLOC(E)
  CALL SPRITE_SETPOS(E, X, Y)
  CALL ENEMY_EXPLODE(E, DX, DY)
END SUB


SUB ENEMY_ACT_EXPLODE(E, T)
  F = T\5
  IF F < 8 THEN
    CALL ENEMY_MOVEP(E, _DX, _DY)
    CALL SPRITE_FRAME(E, 128+F*2)
  ELSE
    CALL ENEMY_RELEASE(E)
  END IF
END SUB


SUB FX_BIGEXPLOSION(S)
  FOR I = 1 TO 4
    X = SPRITES(S,_X) + RND*4
    Y = SPRITES(S,_Y) + RND*4
    A = RND*2*PI
    DX = RND*0.5*COS(A)
    DY = RND*0.5*SIN(A)
    CALL ENEMY_SPAWN_EXPLODE(X, Y, DX, DY)
  NEXT I
END SUB


'==========================================
' BOSS BATTLE ENEMY BEHAVIOUR

' SNAKE PROPERTIES (ALSO USE _FLASH)
GLOBAL _HEAD, _ACTIVE, _CHARGEX, _CHARGEY
_HEAD = _PROPA
_ACTIVE = _PROPB
_CHARGEX = _PROPA
_CHARGEY = _PROPB

SUB SPAWN_BOSS
  X = 76
  Y = -8
  
  TAIL_SIZE=12
  
  DIM SPR(TAIL_SIZE)
  FOR I = 0 TO TAIL_SIZE
    CALL SPRITE_ALLOC(SPR(I))
    SPRITE.A SPR(I), (2,0,0,0,0)
    CALL SPRITE_SETPOS(SPR(I), X, Y)
  NEXT I
  
  ' SORT INTO DISPLAY ORDER
  CALL INSORT(SPR(), TAIL_SIZE+1)
  
  ' HEAD IS HIGHEST IN DISPLAY ORDER
  HEAD = SPR(0)
  CALL SNAKE_START_SLITHER(HEAD)
  
  
  ' TAIL
  FOR I = 1 TO TAIL_SIZE
    CALL ENEMY_START(SPR(I), ACT_SNAKE_TAIL)
    SPRITES(SPR(I),_HEAD) = HEAD
    SPRITES(SPR(I),_ACTIVE) = 0
  NEXT I
  
  ' ADD TO ENEMY LIST IN REVERSE ORDER 
  ' SO THAT THE SPRITES THAT ARE HIGHER IN
  ' THE DISPLAY ORDER ARE CHECKED FOR HITS
  ' BEFORE THOSE LOWER, AND SO SHIELD 
  ' SPRITES THAT ARE NOT VISIBLE TO THE
  ' PLAYER
  FOR I = TAIL_SIZE TO 0 STEP -1
    CALL L_ADD(SPRITES(), ENEMY_LIST, SPR(I))
  NEXT I
END SUB


SUB SNAKE_ACTIVATE(E, ACTIVE_TIME)
  SPRITES(E,_ACTIVE) = ACTIVE_TIME
END SUB


SUB SNAKE_COUNTDOWN(E, NXT, ACTIVE_TIME)
  IF SPRITES(E,_ACTIVE) > 0 THEN
    SPRITES(E,_ACTIVE) = SPRITES(E,_ACTIVE)-1
    IF SPRITES(E,_ACTIVE) = 0 THEN
      CALL SNAKE_ACTIVATE(NXT, ACTIVE_TIME)
    END IF
  END IF
END SUB


SUB SNAKE_START_SLITHER(E)
  CALL ENEMY_START(E, ACT_SNAKE_SLITHER)
  CALL SNAKE_ACTIVATE(E, 20)
END SUB


SUB SNAKE_ACT_SLITHER(E, T)
  N = SPRITES(E,_NXT)
  
  IF SPRITES(N,_TYPE) = ACT_SNAKE_TAIL THEN
    CALL SNAKE_MOVE_SLITHER(E, T)
    CALL SNAKE_COUNTDOWN(E, N, 10)
  
    IF SPRITES(E,_ACTIVE) THEN
      CALL SPRITE_FRAME(E, 97)
    ELSE 
      CALL SPRITE_FRAME(E, 96)
    END IF
    
    IS_HIT = FALSE
    CALL ENEMY_FLASH_IF_SHOT(E, IS_HIT)
    IF IS_HIT THEN
      CALL SPRITE_MOVE_BY(E, 0, -4)
    END IF
  ELSE
    CALL SNAKE_START_FINAL(E)
  END IF
END SUB


SUB SNAKE_MOVE_SLITHER(E, T)
  TX = 152/2 + SIN(T/420 * 2*PI)*152/2
  TY = 32 + COS(T/90 * 2*PI)*32
  
  CALL SPRITE_MOVE_TO(E, TX, TY, 1.5, -1)
END SUB


SUB SNAKE_START_CHARGE(E)
  CALL ENEMY_START(E, ACT_SNAKE_CHARGE)
  SPRITES(E,_CHARGEX) = SPRITES(PLAYER,_X)
  SPRITES(E,_CHARGEY) = SPRITES(PLAYER,_Y)
END SUB


SUB SNAKE_ACT_CHARGE(E, T)
  CALL SPRITE_FRAME(E, 96 + (T\10) MOD 2)
  
  PX = SPRITES(E,_CHARGEX)
  PY = SPRITES(E,_CHARGEY)
  FIN = FALSE
  CALL SPRITE_MOVE_TO(E, PX, PY, 2.5, FIN)
  
  IS_HIT = FALSE
  CALL ENEMY_FLASH_IF_SHOT(E, IS_HIT)
  IF IS_HIT THEN
    CALL SPRITE_MOVE_BY(E, 0, -4)
  END IF
  
  PSAFE = PLAYER_STATE<>PLAYER_IS_UNSHIELDED
  
  IF FIN OR PSAFE THEN
    CALL SNAKE_START_SLITHER(E)
  END IF
END SUB


SUB SNAKE_START_FINAL(E)
  HEALTH = 6

  HUD = NONE
  CALL SPRITE_ALLOC(HUD)
  SPRITE.A HUD, (2,0,0,1,1)
  CALL SPRITE_FRAME(HUD, 160+2*HEALTH)
  
  SPRITES(E,_PROPA) = HEALTH
  SPRITES(E,_PROPB) = HUD
  CALL ENEMY_START(E, ACT_SNAKE_FINAL)
END SUB


SUB SNAKE_ACT_FINAL(E, T)
  HUD = SPRITES(E,_PROPB)
  
  PS = PLAYER_STATE<>PLAYER_IS_UNSHIELDED
  SHOOT = (T/60) MOD 3 = 0

  IF SHOOT AND NOT PS THEN
    CALL SPRITE_FRAME(E, 97)
    CALL SPRITE_CHASE(E, PLAYER, 0.75, -1)
    
    IF T MOD 2 = 0 THEN
      ANGLE = ((T MOD 60) / 11)*PI
      SPEED = 1.5
      DX = SPEED*COS(ANGLE)
      DY = SPEED*SIN(ANGLE)
      
      X = SPRITES(E,_X)
      Y = SPRITES(E,_Y)
      CALL ENEMY_SHOOT(X, Y, DX, DY)
    END IF
    
  ELSE
    CALL SNAKE_MOVE_SLITHER(E, T)
    CALL SPRITE_FRAME(E, 96 + (T\10) MOD 2)
  END IF
    
  IS_HIT = FALSE
  CALL ENEMY_FLASH_IF_SHOT(E, IS_HIT)
  
  IF IS_HIT THEN
    CALL SPRITE_MOVE_BY(E, 0, -4)
    EX = SPRITES(E,_X)-4
    EY = SPRITES(E,_Y)-4
    CALL ENEMY_SPAWN_EXPLODE(EX,EY,RND,RND)
    SPRITES(E,_PROPA) = SPRITES(E,_PROPA) - 1
    CALL PLAYER_SCORE(10)
  END IF
  
  HEALTH = SPRITES(E,_PROPA)
  
  IF HEALTH = 0 THEN
    CALL SPRITE_RELEASE1(HUD)
    CALL ENEMY_EXPLODE(E, 0, 0)
    CALL FX_BIGEXPLOSION(E)
    CALL PLAYER_SCORE(100)
  ELSE
    CALL SPRITE_FRAME(HUD, 160+2*HEALTH)
    CALL SPRITE_SETRELPOS(HUD, E, -4,-20)
  END IF
END SUB


SUB SNAKE_ACT_TAIL(E, T)
  HEAD = SPRITES(E,_HEAD)
  MODE = SPRITES(HEAD,_TYPE)
  N = SPRITES(E,_NXT)
  
  ' NO SHORTCUT BOOLEAN OPERATORS!
  IF N = NONE THEN
    ISTIP = TRUE
  ELSE 
    NTYPE = SPRITES(N,_TYPE)
    ISTIP = NTYPE <> ACT_SNAKE_TAIL
  END IF
  
  P = SPRITES(E,_PRV)
  PX = SPRITES(P,_X)
  PY = SPRITES(P,_Y)
  
  X = SPRITES(E,_X)
  Y = SPRITES(E,_Y)
  
  DX = PX - X
  DY = PY - Y
  
  FLEX = 6
  CALL SPRITE_MOVE_BY(E, DX/FLEX, DY/FLEX)
  
  ' ONLY SHOOT IN SLITHER MODE
  IF MODE <> ACT_SNAKE_SLITHER THEN
    SPRITES(E,_ACTIVE) = 0
  END IF
  
  IF SPRITES(E,_ACTIVE) THEN
    CALL SPRITE_FRAME(E, 99)
  ELSE
    CALL SPRITE_FRAME(E, 98)
  END IF
  
  IF ISTIP THEN
    ISLAST = SPRITES(E,_PRV) = HEAD
    
    CALL ENEMY_DIE_IF_SHOT(E)
    
    IF E = NONE THEN
      IF ISLAST THEN
        CALL SNAKE_START_FINAL(HEAD)
      ELSE
        CALL SNAKE_START_CHARGE(HEAD)
      END IF
    ELSE
      SHOOT = SPRITES(E,_ACTIVE) = 1
      B = PLAYER_STATE=PLAYER_IS_UNSHIELDED
      IF B AND SHOOT THEN
        CALL ENEMY_SHOOT_AT(E, PLAYER, 1)
      END IF
      
      CALL SNAKE_COUNTDOWN(E, HEAD, 20)
    END IF
  ELSE
    CALL ENEMY_FLASH_IF_SHOT(E, 0)
    CALL SNAKE_COUNTDOWN(E, N, 10)
  END IF
END SUB



'==========================================
' TITLE & INFO SCREENS

DIM GLOBAL TITLE_LINE_OFFSETS(16)

SUB TITLE_INIT
  SPRITE OFF
  CLS
  FOR I = 0 TO 16
    TITLE_LINE_OFFSETS(I) = 0
  NEXT I
  
  ON RASTER CALL TITLE_CENTER_LINES
END SUB

SUB TITLE_CENTER_LINES
  L = RASTER/8
  SCROLL 0, TITLE_LINE_OFFSETS(L), 0
END SUB

SUB TITLE_END
  ON RASTER OFF
END SUB

SUB CTEXT(Y, L$)
  TEXT 0, Y, L$
  TITLE_LINE_OFFSETS(Y) = -(20-LEN(L$))*4
END SUB


SUB TITLE_SCREEN
  CALL TITLE_INIT
  
  SOUND SOURCE ROM(14)
  MUSIC
  
  BG 1
  ATTR (6,0,0,0)
  BG FILL 0,0 TO 32,32 CHAR 40
  
  BG 0
  
  ATTR(3,0,0,0)
  
  CALL CTEXT(2, "INFILTRATOR 2000")
  
  TEXT 0, 13, " PRESS: A TO PLAY"
  TEXT 0, 14, "        B NEXT PAGE"
  
  ATTR (0,0,0)
  
  RESTORE INSTRUCTIONS
  D = 1
  T = 1
  REPEAT
    IF T MOD D = 0 OR BUTTON TAP(0,1) THEN
      T = 1
      
      READ D
      IF D = -1 THEN
        RESTORE INSTRUCTIONS
        READ D
      END IF
      
      BG FILL 0,5 TO 28,10 CHAR 0
      FOR I = 0 TO 5
        READ L$
        CALL CTEXT(5+I, L$)
      NEXT I
    ELSE
      T = T + 1
    END IF
    
    SCROLL 1, 0, -T/4
    
    WAIT VBL
  UNTIL BUTTON TAP(0,0)
  
  CALL TITLE_END
  STOP
END SUB


SUB STAT(Y, NAME$, V, VMAX)
  IF VMAX = 0 THEN
    PCT = 0
  ELSE
    PCT = MIN(100, (100*V)\VMAX)
  END IF
  CALL CTEXT(Y, NAME$+": "+STR$(PCT)+"%")
END SUB


SUB GAME_OVER
  IS_HISCORE = FALSE
  CALL HISCORE_UPDATE(IS_HISCORE)

  CALL TITLE_INIT
  
  BG 1
  ATTR (6,0,0,0,0)
  BG FILL 0,0 TO 32,32 CHAR 40
    
  BG 0
  ATTR (3)
  CALL CTEXT(2, "GAME OVER")
  ATTR (0)
  CALL STAT(5, "DISTANCE", GTIMER, GAME_LEN)
  
  ZONE = GTIMER\ZONE_LEN
  IF ZONE = 0 THEN
    ZONE$ = "(WILDERNESS ZONE)"
  ELSE IF ZONE = 1 THEN
    ZONE$ = "(CORRUPTED ZONE)"
  ELSE IF ZONE = 2 THEN
    ZONE$ = "(ASSIMILATED ZONE)"
  ELSE
    ZONE$ = "(ROBOT LEADER)"
  END IF
  
  CALL CTEXT(6, ZONE$)
  
  SCORE = PLAYER_SCORE\1
  CALL CTEXT(8, "SCORE: "+STR$(SCORE))
  CALL CTEXT(9,"BEST: "+STR$(HIGH_SCORE))
  IF IS_HISCORE THEN
    CALL CTEXT(11, "HIGH SCORE!")
  END IF
  
  ATTR (3)
  CALL CTEXT(13, "PRESS ANY BUTTON")
  CALL CTEXT(14, "TO PLAY AGAIN")
  
  REPEAT
    SCROLL 1, 0, -TIMER/4
    WAIT VBL
  UNTIL BUTTON TAP(0)
  
  CALL TITLE_END
END SUB



SUB VICTORY
  CALL TITLE_INIT
  
  BG 1
  ATTR (6,0,0,0,0)
  BG FILL 0,0 TO 32,32 CHAR 40
  
  HITS = PLAYER_SHOTS - PLAYER_MISSES    
  
  BG 0
  ATTR (3)
  CALL CTEXT(2, "VICTORY!")
  
  ATTR (0)
  
  CALL CTEXT(5, "YOU HAVE DEFEATED")
  CALL CTEXT(6, "THE ROBOT HORDES.")
  CALL CTEXT(7, "THE WORLD IS SAFE")
  
  CALL STAT(10,"ACCURACY",HITS,PLAYER_SHOTS)
  ATTR (3)
  CALL CTEXT(13, "PRESS ANY BUTTON")
  CALL CTEXT(14, "TO PLAY AGAIN")
  
  REPEAT
    SCROLL 1, 0, -TIMER/4
    WAIT VBL
  UNTIL BUTTON TAP(0)
  
  CALL TITLE_END
END SUB



'==========================================
' SPAWNING INCREASINGLY DIFFICULT ENEMIES

GLOBAL ENEMY_SPAWN_TYPES
ENEMY_SPAWN_TYPES=7


SUB ENEMY_SPAWN
  T = GTIMER
  
  IF T MOD 48 = 0 THEN
    ' ENSURE REPEATABLE SEQUENCE OF ENEMIES
    ' EVEN IF OTHER RANDOM ACTIVITY HAS
    ' OCCURRED THIS FRAME
    RANDOMIZE T
  
    N = MIN(T\768, ENEMY_SPAWN_TYPES)
    TYPE = INT(RND*N)
    CALL ENEMY_SPAWN_TYPE(TYPE)
  END IF
  
END SUB


SUB ENEMY_SPAWN_TYPE(TYPE)
  E = NONE
  
  IF SPRITE_FREE = NONE THEN 
    EXIT SUB
  ELSE IF TYPE = 0 THEN
    CALL ENEMY_SPAWN_DISC(E)
  ELSE IF TYPE = 1 THEN
    CALL ENEMY_SPAWN_CROSS(E)
  ELSE IF TYPE = 2 THEN
    CALL ENEMY_SPAWN_SEEKER(E)
  ELSE IF TYPE = 3 THEN
    CALL ENEMY_SPAWN_WAVY(E)
  ELSE IF TYPE = 4 THEN
    CALL ENEMY_SPAWN_SHOOTER(E)
  ELSE IF TYPE = 5 THEN
    CALL ENEMY_SPAWN_BARRIER(E)
  ELSE IF TYPE = 6 THEN
    CALL ENEMY_SPAWN_LAYER(E)
  ELSE
    ERR$ = "UNKNOWN TYPE"
    CALL FAIL("ENEMY_SPAWN_TYPE", ERR$)
  END IF
END SUB




'==========================================
' GAME

GLOBAL ZONE_LEN, GAME_LEN
ZONE_LEN = 256*16
GAME_LEN = ZONE_LEN*3


' TIMER COUNTING FROM THE START OF THE GAME
GLOBAL GTIMER
GLOBAL GAME_OVER

GLOBAL GAME_PHASE
' 0 = PROCEDURAL ENEMY WAVES
' 1 = ALERT BEFORE BOSS
' 2 = BOSS

' HAVE WE JUMPED TO THE END GAME?
GLOBAL GAME_CHEATING


GLOBAL HIGHSCORE_FORMAT
HIGHSCORE_FORMAT = 1

GLOBAL HIGH_SCORE

SUB HISCORE_INIT
  BASE = $E000
  
  IF PEEKW(BASE) = HIGHSCORE_FORMAT THEN
    HIGH_SCORE = PEEKL(BASE+2)
  ELSE
    HIGH_SCORE = 0
  END IF
END SUB


SUB HISCORE_UPDATE(IS_HISCORE)
  IF GAME_CHEATING THEN
    IS_HISCORE = FALSE
    EXIT SUB
  END IF
  
  SCORE = PLAYER_SCORE\1
  
  IF SCORE > HIGH_SCORE THEN
    HIGH_SCORE = SCORE
    IS_HISCORE = TRUE
    
    BASE = $E000
    POKEW BASE, HIGHSCORE_FORMAT
    POKEL BASE+2, HIGH_SCORE
  ELSE
    IS_HIGHSCORE = FALSE
  END IF
END SUB


SUB GAME_FRAME
  RANDOMIZE GTIMER
  
  CALL BG_SCROLL
  CALL SKY_SCROLL
  
  CALL PLAYER_MOVE
  CALL BULLETS_MOVE
  CALL PLAYER_SHOOT
  
  IF GAME_PHASE = 0 THEN
    IF GTIMER >= GAME_LEN THEN
      GAME_PHASE = 1
      CALL HUD_SHOW_ALERT
    ELSE IF GTIMER > 256 THEN
      CALL ENEMY_SPAWN
    END IF
  ELSE IF GAME_PHASE = 1 THEN
    BOSSTIME = GTIMER >= GAME_LEN + 512
    IF BOSSTIME AND ENEMY_LIST = NONE THEN
      CALL HUD_CLEAR_ALERT
      GAME_PHASE = 2
      CALL SPAWN_BOSS
    ELSE
      CALL HUD_ANIMATE_ALERT
    END IF
  END IF
  
  CALL ENEMY_ANIMATE_ALL

  GTIMER = GTIMER + 1
END SUB


SUB GAME_INIT
  SOUND SOURCE ROM(15)
  
  GAME_OVER = FALSE
  GAME_PHASE = 0
  
  IF UP(0) THEN
    ' SUPER SECRET CHEAT MODE!
    GTIMER = GAME_LEN - 256
    GAME_CHEATING = TRUE
  ELSE
    GTIMER = 0
    GAME_CHEATING = FALSE
  END IF
  
  CALL BG_INIT
  CALL SKY_INIT
  
  CALL SPRITE_INIT
  CALL PLAYER_INIT
  CALL ENEMY_INIT
END SUB


SUB GAME
  CALL GAME_INIT
  REPEAT
    CALL GAME_FRAME
    
    IF GAME_PHASE=2 AND ENEMY_LIST=NONE THEN
      GAME_OVER = TRUE
    ELSE IF PLAYER_LIVES <= 0 THEN
      GAME_OVER = TRUE
    ELSE
      GAME_OVER = FALSE
    END IF
    
    WAIT VBL
  UNTIL GAME_OVER
END SUB


'==========================================
' TESTING

SUB ENEMY_TESTBED
  GTIMER = 0
  CALL BG_INIT
  CALL SPRITE_INIT
  CALL PLAYER_INIT
  CALL ENEMY_INIT
  
  ' ENSURE BUTTON 1 DOES NOT ACTIVATE SHIELDS
  PLAYER_BOMBS = 0
  
  DO
    IF BUTTON TAP(0,1) THEN
      E = NONE
      
      'CALL SPAWN_BOSS
      
      CALL ENEMY_ALLOC(E)
      CALL SPRITE_SETPOS(E, 76, -8)
      SPRITE.A E,(2,0,0,0,0)
      CALL SNAKE_START_FINAL(E)
      
    END IF
    
    RANDOMIZE GTIMER
  
    CALL BG_SCROLL
  
    CALL PLAYER_MOVE
    CALL BULLETS_MOVE
    CALL PLAYER_SHOOT
    CALL ENEMY_ANIMATE_ALL
  
    GTIMER = GTIMER + 1
    
    WAIT VBL
  LOOP
END SUB


SUB TEST_SCORE
  CALL SPRITE_INIT
  CALL HUD_INIT
  
  GTIMER = 0
  DO
    PLAYER_SCORE = GTIMER \ 10
    CALL HUD_SHOW_SCORE
    TRACE PLAYER_SCORE
    WAIT VBL
    INC GTIMER 
  LOOP
END SUB

SUB CLEAR_HISCORE
  FILL $E000, 4096, 0
END SUB


'==========================================
' WHAT ARE WE RUNNING?


SUB MAIN
  DO
    CALL HISCORE_INIT
    
    CALL TITLE_SCREEN
    
    CALL GAME
    
    IF PLAYER_LIVES > 0 THEN
      CALL VICTORY
    ELSE
      CALL GAME_OVER
    END IF
  LOOP
END SUB


ENTRY_POINT:
'CALL CLEAR_HISCORE
'CALL TEST_SCORE
'CALL ENEMY_TESTBED
CALL MAIN



INSTRUCTIONS:
DATA 240
DATA "ROBOTS ARE TAKING"
DATA "OVER THE WORLD"
DATA ""
DATA "ONLY YOU"
DATA "CAN SAVE US"
DATA ""

DATA 240
DATA "INFILTRATE THE"
DATA "ROBOT DEFENSES"
DATA ""
DATA "DESTROY THE"
DATA "ROBOT LEADER"
DATA ""

DATA 240
DATA "YOUR CRAFT IS ARMED"
DATA "WITH TWIN BLASTERS"
DATA "AND SMART BOMBS"
DATA "THAT DESTROY ALL"
DATA "ENEMIES IN SIGHT"
DATA ""

DATA 240
DATA "YOU WILL HAVE TO"
DATA "CROSS THREE ZONES"
DATA ""
DATA "WILDERNESS ZONE"
DATA "CORRUPTED ZONE"
DATA "ASSIMILATED ZONE"

DATA 300
DATA "CONTROLS"
DATA ""
DATA "PAD = MANOEVER    "
DATA "A   = SHOOT       "
DATA "B   = SMART BOMB  "
DATA ""

DATA 240
DATA "TIP"
DATA ""
DATA ""
DATA "KEEP MOVING"
DATA ""
DATA ""

DATA 240
DATA "TIP"
DATA ""
DATA "DON'T LOITER"
DATA "NEAR THE EDGE"
DATA "OF THE SCREEN"
DATA ""

DATA 240
DATA "TIP"
DATA ""
DATA "ROBOTS THAT GET"
DATA "PAST YOU MIGHT"
DATA "ATTACK FROM BEHIND"
DATA ""

DATA 240
DATA "TIP"
DATA ""
DATA "ACCURATE SHOOTING"
DATA "EARNS MORE POINTS"
DATA ""
DATA ""

DATA 240
DATA ""
DATA ""
DATA "GOOD LUCK, PILOT"
DATA ""
DATA ""
DATA ""

DATA 120
DATA ""
DATA ""
DATA "YOU'RE GONNA"
DATA "NEED IT"
DATA ""
DATA ""


DATA 300
DATA ""
DATA "DESIGN, PROGRAMMING"
DATA "NAT PRYCE"
DATA ""
DATA "MUSIC"
DATA "DESBYC"

DATA 300
DATA ""
DATA "LICENSE"
DATA ""
DATA "CC BY-NC-SA 4.0"
DATA ""
DATA "SHARE AND ENJOY"

DATA -1


#1:MAIN PALETTES
002D0804003F1B3400311601003C3824
002A1500003F2A15002B1601003A3020

#2:MAIN CHARACTERS
00000000000000000000000000000000
18180C6C4E7E7E5A00183C5C7E7E7E5A
243C6676E7FFFF99003C7E6EFFFFFF99
18183078727E7E5A00183C747E7E7E5A
18183C7C7E7E7E5A18183C7C7E7E7E5A
243C7E7EFFFFFF99243C7E7EFFFFFF99
18183C7C7E7E7E5A18183C7C7E7E7E5A
5A245A00240000007E5A7E2424240024
0018245A5A24180000183C66663C1800
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
010A152A552A55AA0420400000800000
55AA55AA55AA55AA0000000000000000
00A050A854AA54AA4008040200000100
0420400000800000052A552A55AA55AA
000000000000000055AA55AA55AA55AA
400804020000010040A854AA54AA55AA
0343434343430603FFBFBFBFBFBF7EFF
432643434343433FBF5EBFBFBFBFBFFF
00FC00000002FFFDFD03FFFFFFFFFFFD
00BE40000040FFBFBF41BFFFFFFFFFBF
FFAA00FFFF0055FFFFFFFF0000FFFFFF
FF0055FFFFAA00FFFFFFAA000055FFFF
D99BD99BD99BD99BE7E7E7E7E7E7E7E7
9DB99DB99DB99DB9E3C7E3C7E3C7E3C7
9B9818FFFF1819D9E7E7E70000E7E7E7
B9187DFFFFBE389DC7E782000041C7E3
0000000000000000FFFFFFFFFFFFFFFF
21210A4A40121200DEFFF5BFFFEDFFFF
2024444809212404DFFBBFF7FEDFFBFF
0404105042829010FBFFEFBFFD7FEFFF
2020240545494808DFFFFBFEBFF7FFFF
084C4D55B1B4A404F7BBFEEF5FFBFFFF
0404104A6C3D9E80FBFFEFB5D3E77FFF
20268CD9DB7F3600DFF977AEACC9FFFF
888822228888222277FFDDFF77FFDDFF
CC003300CC003300BBFFEEFFBBFFEEFF
FF818181818181FF017F7F7F7F7F7FFF
007E7E66667E7E00FF81839F9F9FBFFF
0018187E76181800FFEFEF89FFEFEFFF
0018347A5E2C1800FFEFCF8FFFFFFFFF
18347AF5AF5E2C18EFCF8F0FFFFFFFFF
3C42BDA5A5BD423CC3BD475F5F7FBFFF
0008302041221408003E4F5F7F3E1C08
0008302041221408003E4F5F7F3E1C08
0008302041221408003E4F5F7F3E1C08
0008302041221408003E4F5F7F3E1C08
0000387C7C7C380000387CD6EED67C38
0000387C7C7C380000387CD6EED67C38
0000387C7C7C380000387CD6EED67C38
0000387C7C7C380000387CD6EED67C38
000000AA550000000000000000000000
000000AA550000000000000000000000
000000AA550000000000000000000000
000000AA550000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
001C6D6347373E3C3C62939DB9C9463C
002C7747236F3E3C3C5289BBDD914A3C
00347707636F3E3C3C4A89F99F91523C
00381B67675B1E3C3C46E59999A7623C
001C1C04040404081824243C3C3C3C18
00041C1C04040408183C24243C3C3C18
000404041C1C0408183C3C3C24243C18
00040404041C1C08183C3C3C3C242418
04022141412102043C7EE7C3C3E77E3C
04022159592102043C7EE7C3C3E77E3C
0402397D7D3902043C7EE7C3C3E77E3C
04023965653902043C7EE7C3C3E77E3C
00000060617E000000007E9F9F7E0000
00000030317E000000007ECFCF7E0000
0000000C0D7E000000007EF3F37E0000
00000006077E000000007EF9F97E0000
000000160C18100010387CEE7C381000
0000102E1C18100010386CD66C381000
0000382E3C181000103844D644381000
003844464C381000100038BA38001000
007E7E7E7E7E7E00007E7E7E7E7E7E00
00FF00FF7E7E000000FFFFFF7E7E0000
000000FFFF000000000000FFFF000000
0000000000FFFF0000007E7EFFFFFF00
000102181802010242E77E24247EE742
00013C24243C010242E742181842E742
002466000066240042C3183C3C18C342
42C300040418C34200247E3C3C7E2400
0000031B13073E3C3C7EFFFFFFFF7E3C
0000031B1B073E3C3C7EFFE7E7FF7E3C
00000004041800000000183C3C180000
0000001C1C1800000000182424180000
00180343470F3E3C3C7EFFFFFFFF7E3C
0018014343011A3C3C66FFBDBDFF663C
00000003070E181818183CFFFF3E1818
0000001B1F0E181818183CE7E73E1818
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
01030307070F0F1F01030307070E0E1E
80C0C0E0E0F0F0F880C0C0E0E0707078
00207050505070200000200000005020
00206060202070700000004000000070
00607010306070700000600010200070
00707030301070600000601020001060
00405050707010100000000000600010
00707060701070600000300060001060
00206060705070200000200020005020
00707010302020200000600010000020
00207070705070200000205020005020
00207050703030200000200040201020
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
1F3F3F7F7FFFFF7F1E3E3E7F7EFEFF7F
F8FCFCFEFEFFFFFE787C7CFE7E7FFFFE
000000000106050B000000000107060C
000000008060A0D00000000080E06030
0000030E181337270000030F1F1C3838
0000C07018C8ECE40000C0F0F8381C1C
030F1C306367CFCF030F1F3F7C78F0F0
C0F0380CC6E6F3F3C0F0F8FC3E1E0F0F
030F1C306367CFCF030F1F3F7C78F0F0
C0F0380CC6E6F3F3C0F0F8FC3E1E0F0F
030F18306061C3C6030F1F3F7F7EFCF9
C0F0380C068603F3C0F0F8FCFE7EFFFF
030F1F3C7070E1E3030F1F3F7F7FFFFF
C0F0F83C0EFEFF0FC0F0F8FCFEFEFF0F
030F1F2C7177ECE8030F1F2F7F7FFCF8
C0D0F8BCFE0C0201C0D0F8FCFE0C0201
030D1A28604080C0030D1A28604080C0
80D038040200000080D0380402000000
0B050601000000000C06070100000000
D0A06080000000003060E08000000000
273713180E03000038381C1F0F030000
E4ECC81870C000001C1C38F8F0C00000
CFCF6763301C0F03F0F0787C3F1F0F03
F3D386C60C38F0C00F2F7E3EFCF8F0C0
CFCF6662301C0F03F0F0797D3F1F0F03
836396966C38F0C07FFF9E9EFCF8F0C0
C5C36262331D0F03FBFF7E7E3F1F0F03
9B0F06060C98F0C09F0F06060C98F0C0
E6E464743C1E0F03FEFC7C7C3C1E0F03
07010202000000400701020200000040
F8B0706030080C01F8B0706030080C01
01010000000000000101000000000000
80004040000000008000404000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000003F3F00000000007F7F7F7F
0000000000FCFC0000000000FEFEFEFE
00000000003F3F00000000007F4F4F7F
0000000000FCFC0000000000FEFEFEFE
00000000003F3F00000000007F43437F
0000000000FCFC0000000000FEFEFEFE
00000000003F3F00000000007F40407F
0000000000FCFC0000000000FEFEFEFE
00000000003F3F00000000007F40407F
0000000000FCFC0000000000FE3E3EFE
00000000003F3F00000000007F40407F
0000000000FCFC0000000000FE0E0EFE
00000000003F3F00000000007F40407F
0000000000FCFC0000000000FE0202FE
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
3C3C3C3C3C3C3C3C3C242424243C243C
FEFEFEFE7E000000FE9292DA7E000000
7EFFFFFFFFFFFF7E7EDB81DBDB81DB7E
1C7F7F7F7F7F7F1C1C7741477141771C
F7FFFFFE7FFFFFEFF79D9BF66FD9B9EF
FCFCFCFFFFFFFEFEFC849487919B82FE
3C3C3C3C380000003C24242C38000000
1E3E7E7C7C7E3E1E1E32664C4C66321E
787C7E3E3E7E7C78784C663232664C78
007E7EFFFFFF7E7E007E5AE781E75A7E
003C3CFFFFFF3C3C003C24E781E7243C
0000003C3C3C3C380000003C24242C38
000000FFFFFF0000000000FF81FF0000
0000003C3C3C3C000000003C24243C00
0F1F3F7EFCF8F0E00F193366CC98B0E0
FFFFFFFFFFFFFFFFFF819991899981FF
7C7C7C3C3C7E7E7E7C4464242466427E
FFFFFFFFFFFFFFFFFF81F9819F9F81FF
FFFFFF3F3FFFFFFFFF81F92139F981FF
FEFEFFFFFF0E0E0EFE929381F302020E
FFFFFFFFFFFFFFFFFF819F81F9F981FF
FFFFFFFFFFFFFFFFFF819F81999981FF
FFFFFF3F3F3C3C3CFF81F9212724243C
FFFFFFFFFFFFFFFFFF819981999981FF
FFFFFFFFFF0F0F0FFF819981F909090F
3C3C3C3C3C3C3C003C24243C24243C00
3C3C3C3C3C3C3C383C24243C24242C38
001E3E7E7C7E3E1E001E32664C66321E
0000FFFFFFFFFF000000FF81FF81FF00
00787C7E3E7E7C7800784C6632664C78
FFFFFFFF3E3C3C3CFF8199F3263C243C
FFFFFFFFFFFFFFFFFF819999919F81FF
FFFFFFFFFFFFFFFFFF819999819999FF
FFFFFFFFFFFFFFFFFF819983999981FF
FFFFFFF0F0FFFFFFFF819F90909F81FF
FFFFFF7F7FFFFFFFFF81C94949C981FF
FFFFFFFCFCFFFFFFFF819F849C9F81FF
FFFFFFFCFCF0F0F0FF819F849C9090F0
FFFFFFFFFFFFFFFFFF819F91999981FF
FFFFFFFFFFFFFFFFFF999981999999FF
7E7E7E3C3C7E7E7E7E4266242466427E
FFFFFF0EFEFEFEFEFF81F908F89880FE
FFFFFFFEFEFFFFFFFF999386869399FF
F0F0F0F0F0FFFFFFF0909090909F81FF
E7FFFFFFFFFFFFFFE7BD9981819999FF
FFFFFFFFFFFFFFFFFF998981919999FF
FFFFFFFFFFFFFFFFFF819999999981FF
FFFFFFFFFFF0F0F0FF8199819F9090F0
FFFFFFFFFFFFFFFFFF819999959B85FF
FFFFFFFFFFFFFFFFFF819981879399FF
FFFFFFFFFFFFFFFFFF819F81F9F981FF
FFFFFF3C3C3C3C3CFF81E7242424243C
FFFFFFFFFFFFFFFFFF999999999981FF
FFFFFFFFFFFF7E3CFF99999999C3663C
FFFFFFFFFFFFFFE7FF9999818199BDE7
FFFFFF7EFFFFFFFFFF99C366C39999FF
FFFFFFFFFF3C3C3CFF999981E724243C
FFFFFF7EFCFFFFFFFF81F366CC9F81FF
7E7E7E78787E7E7E7E424E48484E427E
F0F8FC7E3F1F0F07F098CC6633190D07
7E7E7E1E1E7E7E7E7E4272121272427E
3C7EFFFFFF0000003C66C399FF000000
0000000000FFFFFF0000000000FF81FF

#14:TITLE MUSIC
4207F00F1CDA0F007008F09F1B5F0300
08006060000000002800400019FE0000
38003020000000007804905001000000
6207015801028000680201F7113F3300
0800000F000000000800000F00000000
0800000F000000000800000F00000000
0800000F000000000800000F00000000
0800000F000000000800000F00000000
4040024040400240C040020140030201
40000201400002014004400140044001
40004001400301014000020140000201
40000201400002014004024040040240
40044040400440010504400105000201
05000201050002010500020140840201
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
40404040404040404040404040404040
1B3F001B3F000000000000003F5F0000
00001B3F001B3F001B3F000000000000
000000003F5F001B3F00000000000000
1E3F000000001D3F000000003F5F0000
00000000000000001C3F001B3F000000
001B3F003F5F000000001B3F00000000
0F6F001B6F000F6F001B6F000F6F001B
6F000F6F000F6F00226F000F6F001B6F
000F6F00226F000F6F000F6F00206F00
0F6F001B6F000F6F00226F000F6F0016
6F001B6F000F6F00166F001B6F00166F
001B6F000F6F00226F000F6F00216F00
277F00000000297F00000000277F0000
00002A7F00000000287F00000000277F
000000002E7F000000001B7F00000000
417F00000000277F00000000337F0000
0000367F000000003A7F00000000397F
00000000397F00000000337F00000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
000000001B3F001B3F001B3F001B3F00
0000000000000000000000003F5F0000
00000000000000000000000000000000
000000003F5F00000000000000000000
0000000000000000000000003F5F0000
00000000000000000000000000000000
000000003F5F00000000000000000000
00000000000000000000000000000000
0000000000330F00000000330F000000
00330F00000000000000480F00470F00
460F00450F00440F00430F00420F0041
0F00400F003F0F003E0F003D0F000000
00000000000000000000000000000000

#15:MAIN SOUND
1200600F1CDA0F007008808019480900
2800848F0D7762002800848F09776200
1800202008DC05002800F03514CA0800
2E0082980CA764002000408E198CFF00
2800000F000000000800000F00000000
0800000F000000000800000F00000000
0800000F000000000800000F00000000
0800000F000000000800000F00000000

