' INFILTRATOR 2000 ' ' A SHOOT 'EM UP GAME INSPIRED BY CLASSICS ' LIKE XEVIOUS & URIDIUM ' ' WRITTEN ENTIRELY ON AN IPHONE! ' ' COPYRIGHT (C) 2019 NAT PRYCE ' LICENSE: CC BY-NC-SA 4.0 ' BUGS: ' * EXPLOSIONS DRAWN ABOVE CLOUDS ' - NOT AN EASY FIX ' (NO WAY TO LAYER SPRITES WITH PRIORITY ' *AND* PLACE SPRITES BELOW A TILE MAP. ' WOULD NEED TO ALLOCATE EXPLOSIONS FROM ' LOW TO HIGH AND ENEMIES THE OTHER WAY) ' ' ' TODO: ' * FINAL BATTLE AT ENEMY BASE ' * PROCEDURAL ENEMY WAVES THAT GET ' HARDER AS THE GAME PROGRESSES ' * LASER BARRIERS IN THE ASSIMILATED ZONE ' - GFX DONE, NEED LOGIC TO CHANGE BG TILES ' AND CHECK COLLISIONS ' * VICTORY SCREEN ' * PROCEDURAL CLOUDS ' * SOUND FX ' * MUSIC? ' LEVEL STRUCTURE ' 0 - 256 = EASE IN, NO ENEMIES '========================================== ' INITIALISE THE HARDWARE GAMEPAD 1 DISPLAY(,1) '========================================== ' 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(F$, MSG$) TRACE "ERROR!" TRACE "", F$ TRACE "", MSG$ END 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,0) = 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,7) ' FIELDS: ' 0: NEXT POINTER ' 1: PREV POINTER ' 2: X COORD ' 3: Y COORD ' 4-7: 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_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 L_MV(SPRITES(),SLIST,S,SPRITE_FREE) S = NONE END SUB SUB SPRITE_MOVE_TO(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_MOVE_TO(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_MOVE_TO(S, NEWX,NEWY) END SUB SUB SPRITE_FRAME(S, C) SPRITE S,,,C 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 = TIMER Y = -T*2 MOD 256 SCROLL 0,0,Y IF Y MOD 8 = 0 THEN NEXT_ROW=INT(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*3600 < (GTIMER - 3600) 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_INIT ' INITIALISE THE SKY LAYER (CLOUDS) BG 1 BG COPY 0,0,32,32 TO 0,0 CALL SKY_SCROLL END SUB SUB SKY_SCROLL SCROLL 1,0,-TIMER*3 MOD 256 END SUB '========================================== ' PLAYER MOVEMENT AND SHOOTING ' SPRITES GLOBAL PLAYER GLOBAL PLAYER_SHADOW GLOBAL HUD_LIVES 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_SHIELDS GLOBAL PLAYER_LIVES GLOBAL PLAYER_SHOTS GLOBAL PLAYER_MISSES SUB PLAYER_INIT ' INITIALISE THE PLAYER SPRITES ' PRE: SPRITE_INIT MUST HAVE BEEN CALLED ' NOTE: MUST BE CALLED BEFORE ENEMY_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 ' A 32X32 PIXEL SPRITE WITH THE BOTTOM 8 ' PIXEL ROWS ON SCREEN, TO DISPLAY THE ' NUMBER OF EXTRA LIVES CALL SPRITE_ALLOC(HUD_LIVES) SPRITE.A HUD_LIVES,(7,0,0,1,3) CALL SPRITE_FRAME(HUD_LIVES,0) PLAYER_SHIELDS = 1 PLAYER_LIVES = 5 PLAYER_SHOTS = 0 PLAYER_MISSES = 0 CALL PLAYER_START_LIFE END SUB SUB PLAYER_START_LIFE PLAYER_STATE = PLAYER_IS_STARTING PLAYER_COOLDOWN = 1 CALL PLAYER_SHOW_LIVES SPRITE.A PLAYER,(1, 0, 0, 0, 0) CALL SPRITE_MOVE_TO(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_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) FOR I = 1 TO 4 IF SPRITE_FREE <> NONE THEN X = SPRITES(PLAYER,_X) + RND*4 Y = SPRITES(PLAYER,_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) END IF NEXT I PLAYER_STATE = PLAYER_IS_EXPLODING PLAYER_COOLDOWN = 0 SPRITE OFF PLAYER_SHADOW 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 PLAYER_STATE = PLAYER_IS_UNSHIELDED PLAYER_COOLDOWN = 0 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 PLAYER_STATE = PLAYER_IS_UNSHIELDED END IF END IF DX = LEFT(0) - RIGHT(0) DY = UP(0) - DOWN(0) IF DX AND DY THEN SCALE = DIAGONAL ELSE SCALE = 1 END IF CALL SPRITE_MOVE_BY(P, DX*SCALE, DY*SCALE) 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 = INT(PLAYER_COOLDOWN/5) IF F < 8 THEN CALL SPRITE_FRAME(PLAYER, 128+F*2) PLAYER_COOLDOWN = PLAYER_COOLDOWN+1 ELSE CALL PLAYER_LOSE_LIFE END IF END SUB SUB PLAYER_COLLIDE(E) IF PLAYER_STATE = PLAYER_IS_UNSHIELDED THEN CALL ENEMY_EXPLODE(E, 0, 0.25) 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_SHOW_LIVES X = (5-PLAYER_LIVES)*-8 CALL SPRITE_MOVE_TO(HUD_LIVES, X, -24) END SUB SUB PLAYER_SHOOT IF PLAYER_COOLDOWN > 0 THEN EXIT SUB END IF IF BUTTON(0,0) THEN X = SPRITES(PLAYER,_X) Y = SPRITES(PLAYER,_Y) - 6 B = BULLETS_FREE CALL SPRITE_REALLOC(BULLETS_FREE, B, BULLETS) CALL SPRITE_MOVE_TO(B, X, Y) PLAYER_COOLDOWN = 16 PLAYER_SHOTS = PLAYER_SHOTS + 1 END IF IF BUTTON(0,1) AND PLAYER_SHIELDS > 0 THEN PLAYER_STATE = PLAYER_IS_SHIELDED PLAYER_COOLDOWN = 180 PLAYER_SHIELDS = PLAYER_SHIELDS - 1 END IF 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 '========================================== ' ENEMY MOVEMENT AND SHOOTING ' ENEMY ACTIONS THAT CAN BE SPAWNED GLOBAL ACT_DISC ACT_DISC = 0 GLOBAL ACT_CROSS ACT_CROSS = 1 GLOBAL ACT_WAVY ACT_WAVY = 2 GLOBAL ACT_SHOOTER ACT_SHOOTER = 3 GLOBAL ACT_BARRIER ACT_BARRIER = 4 GLOBAL ACT_SEEKER ACT_SEEKER = 5 GLOBAL ACT_LAYER ACT_LAYER = 6 GLOBAL ENEMY_TYPES ENEMY_TYPES=7 ' ACTIONS ENEMIES CAN DO AFTER SPAWNING GLOBAL ACT_EXPLODE ACT_EXPLODE = 100 GLOBAL ACT_BULLET ACT_BULLET = 101 GLOBAL ACT_WITHDRAW ACT_WITHDRAW = 102 GLOBAL ACT_REVERSE ACT_REVERSE = 103 GLOBAL ACT_MINE ACT_MINE = 104 ' ENEMY SPRITE PROPERTIES ' 4: ACTION STATE & CONTROL BITS ' 00-07 ACTION STATE ' 08-15 DEPENDS ON THE ACTION TYPE ' 5: START TIME (GTIMER WHEN STATE STARTED) ' 6: PROPA (USE DEPENDS ON THE ACTION TYPE) ' 7: PROPB (USE DEPENDS ON THE ACTION TYPE) GLOBAL _PROPA, _PROPB, _DX, _DY _PROPA = 6 _PROPB = 7 ' COMMON USES OF PROPA & PROPB _DX = _PROPA _DY = _PROPB 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) IF E = NONE THEN TRACE "E = NONE STARTING STATE", STATE END END IF SPRITES(E,4) = STATE SPRITES(E,5) = 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 END IF 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 END IF 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 END IF H = SPRITE HIT(E,BULLET_MIN TO BULLET_MAX) IF H THEN CALL BULLET_RELEASE(HIT) CALL ENEMY_EXPLODE(E, 0, -0.25) E = NONE END IF END SUB SUB ENEMY_ANIMATE(E) ACTION = SPRITES(E,4) AND $FF T = GTIMER - SPRITES(E,5) IF ACTION = ACT_DISC THEN CALL ENEMY_ACT_DISC(E, T) ELSE IF ACTION = ACT_WAVY THEN CALL ENEMY_ACT_WAVY(E, T) ELSE IF ACTION = ACT_SHOOTER THEN CALL ENEMY_ACT_SHOOTER(E, T) ELSE IF ACTION = ACT_BARRIER THEN CALL ENEMY_ACT_BARRIER(E, T) ELSE IF ACTION = ACT_BULLET THEN CALL ENEMY_ACT_BULLET(E, T) ELSE IF ACTION = ACT_EXPLODE THEN CALL ENEMY_ACT_EXPLODE(E,T) ELSE IF ACTION = ACT_REVERSE THEN CALL ENEMY_ACT_REVERSE(E, T) ELSE IF ACTION = ACT_SEEKER THEN CALL ENEMY_ACT_SEEKER(E, T) ELSE IF ACTION = ACT_WITHDRAW THEN CALL ENEMY_ACT_WITHDRAW(E, T) ELSE IF ACTION = ACT_CROSS THEN CALL ENEMY_ACT_CROSS(E, T) ELSE IF ACTION = ACT_LAYER THEN CALL ENEMY_ACT_LAYER(E, T) ELSE IF ACTION = ACT_MINE THEN CALL ENEMY_ACT_MINE(E, T) ELSE CALL FAIL("ENEMY_ANIMATE", "UNKNOWN TYPE") END IF IF E <> NONE AND ACTION <> ACT_EXPLODE THEN IF SPRITE HIT(E,PLAYER) THEN CALL PLAYER_COLLIDE(E) END IF END IF END SUB SUB ENEMY_SPAWN T = MAX(0, GTIMER) N = MIN(INT(T/1024), ENEMY_TYPES) TYPE = INT(RND*N) CALL ENEMY_SPAWN_TYPE(TYPE) END SUB SUB ENEMY_SPAWN_TYPE(TYPE) IF SPRITE_FREE = NONE OR GTIMER < 256 THEN EXIT SUB END IF E = NONE IF TYPE = ACT_DISC THEN CALL ENEMY_SPAWN_DISC(E) ELSE IF TYPE = ACT_WAVY THEN CALL ENEMY_SPAWN_WAVY(E) ELSE IF TYPE = ACT_SHOOTER THEN CALL ENEMY_SPAWN_SHOOTER(E) ELSE IF TYPE = ACT_BARRIER THEN CALL ENEMY_SPAWN_BARRIER(E) ELSE IF TYPE = ACT_SEEKER THEN CALL ENEMY_SPAWN_SEEKER(E) ELSE IF TYPE = ACT_CROSS THEN CALL ENEMY_SPAWN_CROSS(E) ELSE IF TYPE = ACT_LAYER THEN CALL ENEMY_SPAWN_LAYER(E) ELSE CALL FAIL("ENEMY_SPAWN_TYPE", "UNKNOWN TYPE") 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_MOVE_TO(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_MOVE_TO(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_MOVE_TO(E, X, Y) CALL SPRITE_FRAME(E,F) CALL ENEMY_DIE_IF_SHOT(E) ELSE X = SPRITES(PLAYER,_X) CALL SPRITE_MOVE_TO(E, X, 180) CALL ENEMY_START(E, ACT_REVERSE) 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_MOVE_TO(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 AND PLAYER_STATE = PLAYER_IS_UNSHIELDED THEN CALL ENEMY_SHOOT_AT(E, PLAYER, 1) END IF END IF END SUB SUB ENEMY_SHOOT_AT(FROM, AT, SPEED) X = SPRITES(FROM,_X) Y = SPRITES(FROM,_Y) XX = SPRITES(AT,_X) - X YY = SPRITES(AT,_Y) - Y D = SQR(XX*XX + YY*YY) DX = SPEED * XX/D DY = SPEED * YY/D CALL ENEMY_SHOOT(X, Y, DX, DY) END SUB SUB ENEMY_SHOOT(X, Y, DX, DY) ' AVOID RUNNING OUT OF MEMORY IF SPRITE_FREE = NONE THEN EXIT SUB END IF E = NONE CALL ENEMY_ALLOC(E) CALL SPRITE_MOVE_TO(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) 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_MOVE_TO(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 H = SPRITE HIT(E,BULLET_MIN TO BULLET_MAX) IF H THEN CALL BULLET_RELEASE(HIT) CALL SPRITE_MOVE_BY(E, 0, -8) SPRITE.A E,(7) SPRITES(E,_PROPA) = 20 ELSE IF SPRITES(E,_PROPA) > 0 THEN SPRITES(E,_PROPA) = SPRITES(E,_PROPA)-1 IF SPRITES(E,_PROPA) = 0 THEN SPRITE.A E,(2) END IF END IF F = T/6 MOD 4 CALL SPRITE_FRAME(E,84+F) 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_MOVE_TO(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) SPEED = 0.5 SX = SPRITES(PLAYER,_X) - SPRITES(E,_X) SY = SPRITES(PLAYER,_Y) - SPRITES(E,_Y) D = SQR(SX^2 + SY^2) DX = SPEED * (SX/D) DY = SPEED * (SY/D) CALL SPRITE_MOVE_BY(E, DX, DY) 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_MOVE_TO(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 = 30 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*112 SPEED = 0.75 PATHDX = X1-X0 PATHDY = Y1-Y0 S = SQR(PATHDX^2 + PATHDY^2) CALL SPRITE_MOVE_TO(E, X0, Y0) SPRITES(E,_DX) = PATHDX/S SPRITES(E,_DY) = 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-30) MOD 40 = 0 THEN X = SPRITES(E,_X) Y = SPRITES(E,_Y) IF SPRITE_FREE <> NONE THEN CALL ENEMY_SPAWN_MINE(X, Y) END IF END IF END IF END SUB SUB ENEMY_SPAWN_MINE(X, Y) E = NONE CALL ENEMY_ALLOC(E) CALL SPRITE_MOVE_TO(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) 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) END SUB SUB ENEMY_SPAWN_EXPLODE(X, Y, DX, DY) E = NONE CALL ENEMY_ALLOC(E) CALL SPRITE_MOVE_TO(E, X, Y) CALL ENEMY_EXPLODE(E, DX, DY) END SUB SUB ENEMY_ACT_EXPLODE(E, T) F = INT(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 '========================================== ' TITLE & INFO SCREENS DIM GLOBAL TITLE_LINE_OFFSETS(6) SUB TITLE_SCREEN SPRITE OFF CLS BG 1 ATTR (6,0,0,0) BG FILL 0,0 TO 32,32 CHAR 40 BG 0 ATTR(3,0,0,0) PRINT TEXT 0, 02, " INFILTRATOR 2000" TEXT 0, 13, " PRESS: A TO PLAY" TEXT 0, 14, " B NEXT PAGE" ATTR (0,0,0) ON RASTER CALL TITLE_CENTER_LINES 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$ TEXT 0, 5+I, L$ OX = (20-LEN(L$))*4 TITLE_LINE_OFFSETS(I) = -OX NEXT I ELSE T = T + 1 END IF SCROLL 1, 0, -T/4 WAIT VBL UNTIL BUTTON TAP(0,0) ON RASTER OFF END SUB SUB TITLE_CENTER_LINES L = RASTER/8 IF L >= 5 AND L <= 11 THEN SCROLL 0, TITLE_LINE_OFFSETS(L-5), 0 ELSE SCROLL 0, 0, 0 END IF END SUB SUB GAME_OVER SPRITE OFF CLS BG 1 ATTR (6,0,0,0,0) BG FILL 0,0 TO 32,32 CHAR 40 IF PLAYER_SHOTS = 0 THEN ACCURACY = 0 ELSE ACCURACY=1-PLAYER_MISSES/PLAYER_SHOTS END IF ACCURACY_PCT = INT(ACCURACY*100) BG 0 ATTR (3) TEXT 0, 2, " GAME OVER" ATTR (0) 'TEXT 0, 5, " ACHIEVEMENTS" TEXT 0, 7, " DISTANCE:" NUMBER 12, 7, GTIMER, 5 TEXT 0, 9, " ACCURACY:" NUMBER 12, 9, ACCURACY_PCT, 2 TEXT 14, 9, "%" ATTR (3) TEXT 0, 13, " PRESS ANY BUTTON" TEXT 0, 14, " TO PLAY AGAIN" REPEAT SCROLL 1, 0, -TIMER/4 WAIT VBL UNTIL BUTTON TAP(0) END SUB '========================================== ' GAME ' TIMER COUNTING FROM THE START OF THE GAME GLOBAL GTIMER SUB GAME_FRAME RANDOMIZE GTIMER CALL BG_SCROLL CALL SKY_SCROLL CALL PLAYER_MOVE CALL BULLETS_MOVE CALL PLAYER_SHOOT CALL ENEMY_ANIMATE_ALL ' TEMPORARY UNTIL WAVES ARE IMPLEMENTED IF GTIMER MOD 60 = 0 THEN CALL ENEMY_SPAWN END IF GTIMER = GTIMER + 1 END SUB SUB GAME_INIT GTIMER = 0 CALL BG_INIT CALL SKY_INIT CALL SPRITE_INIT CALL PLAYER_INIT CALL ENEMY_INIT END SUB SUB MAIN DO CALL TITLE_SCREEN CALL GAME_INIT REPEAT CALL GAME_FRAME WAIT VBL UNTIL PLAYER_LIVES <= 0 CALL GAME_OVER LOOP 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_SHIELDS = 0 DO IF BUTTON TAP(0,1) THEN E = NONE CALL ENEMY_SPAWN_LAYER(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 '========================================== ' WHERE IT ALL STARTS ENTRY_POINT: 'CALL ENEMY_TESTBED CALL MAIN INSTRUCTIONS: DATA 240 DATA "THE ROGUE AI 'FANG'" DATA "THREATENS HUMANITY" DATA "" DATA "ONLY YOU" DATA "CAN STOP IT" DATA "" DATA 240 DATA "FLY YOUR AIRCRAFT" DATA "TO THE MAINFRAME" DATA "" DATA "DESTROY THE CORES" DATA "" DATA "SAVE THE WORLD" DATA 240 DATA "YOU MUST CROSS" DATA "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 = PHASE SHIELD" 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 CAN ATTACK" DATA "FROM BEHIND" DATA "" DATA 120 DATA "" DATA "" DATA "GOOD LUCK, PILOT" DATA "" DATA "" DATA "" DATA 300 DATA "DESIGN &" DATA "PROGRAMMING" DATA "NAT PRYCE" DATA "" DATA "LICENSE" DATA "CC BY-NC-SA 4.0" 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 010A050A050A05000400100010000002 00A050A050A040804000000000001000 00000000000000000000000000000000 FFAA00FFFF0055FFFFFFFF0000FFFFFF FF0055FFFFAA00FFFFFFAA000055FFFF FF00AAFFFF5500FFFFFF550000AAFFFF D99BD99BD99BD99BE7E7E7E7E7E7E7E7 9DB99DB99DB99DB9E3C7E3C7E3C7E3C7 B99DB99DB99DB99DC7E3C7E3C7E3C7E3 9B9818FFFF1819D9E7E7E70000E7E7E7 B9187DFFFFBE389DC7E782000041C7E3 9D38BEFFFF7D1CB9E3C741000082E3C7 00F6040455AEFFFBFB0DFFFFFFFFFFFB 0000000000000000FFFFFFFFFFFFFFFF 21210A4A40121200DEFFF5BFFFEDFFFF 2024444809212404DFFBBFF7FEDFFBFF 0404105042829010FBFFEFBFFD7FEFFF 2020240545494808DFFFFBFEBFF7FFFF 084C4D55B1B4A404F7BBFEEF5FFBFFFF 0404104A6C3D9E80FBFFEFB5D3E77FFF 20268CD9DB7F3600DFF977AEACC9FFFF 888822228888222277FFDDFF77FFDDFF CC003300CC003300BBFFEEFFBBFFEEFF FF818181818181FF017F7F7F7F7F7FFF 007E7E66667E7E00FF81839F9F9FBFFF 0018187E76181800FFEFEF89FFEFEFFF 0018347A5E2C1800FFEFCF8FFFFFFFFF 18347AF5AF5E2C18EFCF8F0FFFFFFFFF 3C42BDA5A5BD423CC3BD475F5F7FBFFF 0008302041221408003E4F5F7F3E1C08 0008302041221408003E4F5F7F3E1C08 0008302041221408003E4F5F7F3E1C08 0008302041221408003E4F5F7F3E1C08 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 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 42C300040418C34200247E3C3C7E2400 002466000066240042C3183C3C18C342 00013C24243C010242E742181842E742 000102181802010242E77E24247EE742 0000031B13073E3C3C7EFFFFFFFF7E3C 0000031B1B073E3C3C7EFFE7E7FF7E3C 00000004041800000000183C3C180000 0000001C1C1800000000182424180000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 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 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 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 #3:CLOUDS 00001410000000000000000000000000 00000000000400000000000000000000 00000000000000000000000000000000 00000000000000000000000000000024 00000000002510251225002000000000 00240024000000000000000000000000 00000000000000000005132511251125 1125133D000500000000000000000000 00000000000000000000000000040000 00000004000000000024000000000000 00000000000000000000000000240000 00000024000000040000000400000024 00000000000400000000000000000000 00240000002400000000000000000000 00000000000000000000000000000000 00000000000000000005001D1025113D 1225003D001900190019000000000024 0000000000240000000000000000001D 10251125113D113D113D113D1225001D 00190024000000240000002400000004 0000000000000000123D1125113D113D 113D113D103D00190019001900000000 00000000002400000000000000240000 003D123D11251125113D103D00050019 00240019001900190019000000190024 00000000000000000000002400000000 000000000004000000000000001D1025 113D113D1225001D0000000000000000 00000024002400240024000400000019 0019003D1025113D113D113D11251225 00190000002400240024000000000000 000000240019001910251125113D113D 1125113D113D113D1225000900000000 00000000000000000004002400040024 123D113D113D1125112511251125113D 103D0019000000000000000000000000 00000019000000000000000000191011 11111111101900190019000000000019 00190019001900190019001900190019 00190019001900190019001900190019 00190000 #4:ENDGAME 00001410000000000000000000000004 11041104000000000000000011041104 00040000000000000000000000000004 00000000000000041104110400000000 00000000110411040004000400000000 00000000000000041104110400000004 00000000000000000000000000000000 00040004110411040004000000000004 11041104000000000000000000000000 00000000000000000000000411041104 00040000000000040005000500000000 00000000000000000000000000000000 00000004000000000004000000000000 00000000000000000000000000000000 00000000000000000000000400000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000020042004 20042004200420042004200420042004 20042004200420042004200420042004 20042004201428142814281428142814 28142814281428142814281428142814 28142814281428142814201420142814 28142814281428142814281428142814 28142814281428142814281428142814 28142014201428142814281428142814 28142814281428142814281428142814 28142814281428142814201420142814 28142814281428142814281428142814 28142814281428142814281428142814 28142014201428142814281428142814 28142814281428142814281428142814 28142814281428142814201420142814 28142814281428142814281428142814 28142814281428142814281428142814 28142014201428142814281428142814 28142814281428142814281428142814 28142814281428142814201420142004 20142014201420142014201420142014 20142014201420142014201420142014 20142014