Asteroids game using a Gameduino

Dependencies:   Gameduino mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "GD.h"
00003 #include "shield.h"
00004 
00005 GDClass GD(ARD_MOSI, ARD_MISO, ARD_SCK, ARD_D9, USBTX, USBRX) ;
00006 SPI spimain(ARD_MOSI, ARD_MISO, ARD_SCK); // mosi, miso, sclk
00007 DigitalIn down(ARD_A2);//0
00008 DigitalIn up(ARD_A3);//1
00009 DigitalIn left(ARD_A1);//2
00010 DigitalIn right(ARD_A0);//3
00011 
00012 // ----------------------------------------------------------------------
00013 //     qrand: quick random numbers
00014 // ----------------------------------------------------------------------
00015 
00016 static uint16_t lfsr = 1;
00017 
00018 static void qrandSeed(int seed) {
00019     if (seed) {
00020         lfsr = seed;
00021     } else {
00022         lfsr = 0x947;
00023     }
00024 }
00025 
00026 static byte qrand1() {  // a random bit
00027     lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & 0xb400);
00028     return lfsr & 1;
00029 }
00030 
00031 static byte qrand(byte n) { // n random bits
00032     byte r = 0;
00033     while (n--)
00034         r = (r << 1) | qrand1();
00035     return r;
00036 }
00037 
00038 // ----------------------------------------------------------------------
00039 //     controller: buttons on Arduino pins 3,4,5,6
00040 // ----------------------------------------------------------------------
00041 
00042 static void controller_init() {
00043     // Configure input pins with internal pullups
00044 
00045     down.mode(PullUp);
00046     up.mode(PullUp);
00047     left.mode(PullUp);
00048     right.mode(PullUp);
00049 
00050 }
00051 
00052 #define CONTROL_LEFT  1
00053 #define CONTROL_RIGHT 2
00054 #define CONTROL_UP    4
00055 #define CONTROL_DOWN  8
00056 
00057 
00058 static byte controller_sense(uint16_t clock) {
00059     byte r = 0;
00060 
00061 
00062     if (!down)
00063         r |= CONTROL_DOWN;
00064     if (!up)
00065         r |= CONTROL_UP;
00066     if (!left)
00067         r |= CONTROL_LEFT;
00068     if (!right)
00069         r |= CONTROL_RIGHT;
00070 
00071     return r;
00072 }
00073 
00074 // Swap color's red and blue channels
00075 uint16_t swapRB(uint16_t color) {
00076     byte r = 31 & (color >> 10);
00077     byte g = 31 & (color >> 5);
00078     byte b = 31 & color;
00079     return (color & 0x8000) | (b << 10) | (g << 5) | r;
00080 }
00081 
00082 // Swap color's red and green channels
00083 uint16_t swapRG(uint16_t color) {
00084     byte r = 31 & (color >> 10);
00085     byte g = 31 & (color >> 5);
00086     byte b = 31 & color;
00087     return (color & 0x8000) | (g << 10) | (r << 5) | b;
00088 }
00089 
00090 #include "asteroidgraphics.h"
00091 #include "splitscreen.h"
00092 
00093 static void update_score();
00094 
00095 // [int(127 * math.sin(math.pi * 2 * i / 16)) for i in range(16)]
00096 static PROGMEM signed char charsin[16] = {0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48};
00097 char qsin(int a) {
00098 //har)pgm_read_byte_near(charsin + ((a) & 15))
00099     return charsin[(a & 15)];
00100 }
00101 #define qcos(a) qsin((a) + 4)
00102 
00103 static char spr2obj[256];   // Maps sprites to owning objects
00104 
00105 /*
00106 
00107 The general idea is that an object table ``objects`` has an entry for
00108 each drawn thing on screen (e.g. player, missile, rock, explosion).
00109 Each class of object has a ``handler`` function that does the necessary
00110 housekeeping and draws the actual sprites.
00111 
00112 As part of the behavior, some classes need to know if they have collided
00113 with anything. In particular the rocks need to know if they have collided
00114 with the player or a missile.  The `collide` member points to the
00115 colliding sprite.
00116 
00117 */
00118 
00119 struct object {
00120     int x, y;
00121     byte handler, state;
00122     byte collide;
00123 } objects[128];
00124 #define COORD(c) ((c) << 4)
00125 
00126 static signed char explosions = -1;
00127 static signed char enemies = -1;
00128 static signed char missiles = -1;
00129 
00130 static void push(signed char *stk, byte i) {
00131     objects[i].state = *stk;
00132     *stk = i;
00133 }
00134 
00135 static char pop(signed char *stk) {
00136     signed char r = *stk;
00137     if (0 <= r) {
00138         *stk = objects[r].state;
00139     }
00140     return r;
00141 }
00142 
00143 byte rr[4] = { 0,3,6,5 };
00144 
00145 static struct {
00146     byte boing, boom, pop;
00147     byte thrust;
00148     byte bass;
00149 } sounds;
00150 
00151 static int player_vx, player_vy;  // Player velocity
00152 static int player_invincible, player_dying;
00153 static byte lives;
00154 static long score;
00155 static byte level;
00156 
00157 // Move object po by velocity (vx, vy), optionally keeping in
00158 // player's frame.
00159 // Returns true if the object wrapped screen edge
00160 static bool move(struct object *po, char vx, char vy, byte playerframe = 1) {
00161     bool r = 0;
00162     if (playerframe) {
00163         po->x += (vx - player_vx);
00164         po->y += (vy - player_vy);
00165     } else {
00166         po->x += vx;
00167         po->y += vy;
00168     }
00169 
00170     if (po->x > COORD(416))
00171         r = 1, po->x -= COORD(432);
00172     else if (po->x < COORD(-16))
00173         r = 1, po->x += COORD(432);
00174 
00175     if (po->y > COORD(316))
00176         r = 1, po->y -= COORD(332);
00177     else if (po->y < COORD(-16))
00178         r = 1, po->y += COORD(332);
00179     return r;
00180 }
00181 
00182 
00183 #define HANDLE_NULL 0
00184 #define HANDLE_ROCK0 1
00185 #define HANDLE_ROCK1 2
00186 #define HANDLE_BANG0 3
00187 #define HANDLE_BANG1 4
00188 #define HANDLE_PLAYER 5
00189 #define HANDLE_MISSILE 6
00190 
00191 // Expire object i, and return it to the free stk
00192 static void expire(signed char *stk, byte i) {
00193     objects[i].handler = HANDLE_NULL;
00194     push(stk, i);
00195 }
00196 
00197 static void handle_null(byte i, byte state, uint16_t clock) {
00198 }
00199 
00200 static void handle_player(byte i, byte state, uint16_t clock) {
00201     struct object *po = &objects[i];
00202     byte angle = (po->state & 15);
00203     byte rot1 = (angle & 3);
00204     byte rot2 = rr[3 & (angle >> 2)];
00205     if (!player_dying && (player_invincible & 1) == 0)
00206         draw_player(200, 150, rot1, rot2);
00207 
00208     static byte prev_control;
00209     byte control = controller_sense(clock);
00210 
00211     char thrust_x, thrust_y;
00212     if (!player_dying && control & CONTROL_DOWN) { // thrust
00213         byte flame_angle = angle ^ 8;
00214         byte d;
00215         for (d = 9; d > 5; d--) {
00216             int flamex = 201 - (((d + (clock&3)) * qsin(flame_angle)) >> 5);
00217             int flamey = 150 - (((d + (clock&3)) * qcos(flame_angle)) >> 5);
00218             if ((player_invincible & 1) == 0)
00219                 draw_sparkr(flamex, flamey, rot1, rot2, 1);   // collision class K
00220         }
00221         thrust_x = -qsin(angle);
00222         thrust_y = -qcos(angle);
00223         sounds.thrust = 1;
00224     } else {
00225         thrust_x = thrust_y = 0;
00226         sounds.thrust = 0;
00227     }
00228 
00229     player_vx = ((31 * player_vx) + thrust_x) / 32;
00230     player_vy = ((31 * player_vy) + thrust_y) / 32;
00231 
00232     po->x += player_vx;
00233     po->y += player_vy;
00234 
00235     if (clock & 1) {
00236         char rotate = (512) / 400;
00237         if (control & CONTROL_LEFT)
00238             rotate++;
00239         if (control & CONTROL_RIGHT)
00240             rotate--;
00241         po->state = ((angle + rotate) & 15);
00242     }
00243 
00244     if (!player_dying &&
00245             !(prev_control & CONTROL_UP) &&
00246             (control & CONTROL_UP)) { // shoot!
00247         signed char e = pop(&missiles);
00248         if (0 <= e) {
00249             objects[e].x = COORD(200);
00250             objects[e].y = COORD(150);
00251             objects[e].state = po->state;
00252             objects[e].handler = HANDLE_MISSILE;
00253         }
00254         sounds.boing = 1;
00255     }
00256     prev_control = control;
00257     if (player_invincible)
00258         --player_invincible;
00259     if (player_dying) {
00260         if (--player_dying == 0) {
00261             --lives;
00262             update_score();
00263             if (lives != 0) {
00264                 player_invincible = 48;
00265             }
00266         }
00267     }
00268 }
00269 
00270 static void handle_missile(byte i, byte state, uint16_t clock) {
00271     struct object *po = &objects[i];
00272     byte angle = (po->state & 15);
00273     byte rot1 = (angle & 3);
00274     byte rot2 = rr[3 & (angle >> 2)];
00275     draw_sparkr(po->x >> 4, po->y >> 4, rot1, rot2);
00276     char vx = -qsin(po->state), vy = -qcos(po->state);
00277     if (move(po, vx, vy, 0)) {
00278         expire(&missiles, i);
00279     }
00280 }
00281 
00282 static void explodehere(struct object *po, byte handler, uint16_t clock) {
00283     signed char e = pop(&explosions);
00284     if (0 <= e) {
00285         objects[e].x = po->x;
00286         objects[e].y = po->y;
00287         objects[e].handler = handler;
00288         objects[e].state = clock;
00289     }
00290 }
00291 
00292 static void killplayer(uint16_t clock) {
00293     if (!player_invincible && !player_dying) {
00294         signed char e = pop(&explosions);
00295         if (0 <= e) {
00296             objects[e].x = COORD(200);
00297             objects[e].y = COORD(150);
00298             objects[e].handler = HANDLE_BANG1;
00299             objects[e].state = clock;
00300         }
00301         player_dying = 2 * 36;
00302         sounds.boom = 1;
00303         sounds.pop = 1;
00304     }
00305 }
00306 
00307 static byte commonrock(uint16_t clock, byte i, byte speed, void df(int x, int y, byte anim, byte rot, byte jk)) {
00308     struct object *po = &objects[i];
00309 
00310     byte move_angle = po->state >> 4;
00311     char vx = (speed * -qsin(move_angle)) >> 6, vy = (speed * -qcos(move_angle)) >> 6;
00312     move(po, vx, vy);
00313 
00314     byte angle = (clock * speed) >> 4;
00315     if (po->state & 1)
00316         angle = ~angle;
00317     byte rot1 = (angle & 3);
00318     byte rot2 = rr[3 & (angle >> 2)];
00319     df(po->x >> 4, po->y >> 4, rot1, rot2, 1);
00320     if (po->collide != 0xff) {
00321         struct object *other = &objects[po->collide];
00322         switch (other->handler) {
00323             case HANDLE_PLAYER:
00324                 killplayer(clock);
00325                 break;
00326             case HANDLE_MISSILE:
00327                 expire(&missiles, po->collide);   // missile is dead
00328                 expire(&enemies, i);
00329                 return 1;
00330         }
00331     }
00332     return 0;
00333 }
00334 
00335 static void handle_rock0(byte i, byte state, uint16_t clock) {
00336     struct object *po = &objects[i];
00337     byte speed = 12 + (po->state & 7);
00338     if (commonrock(clock, i, speed, draw_rock0r)) {
00339         explodehere(po, HANDLE_BANG0, clock);
00340         score += 10;
00341         sounds.pop = 1;
00342     }
00343 }
00344 
00345 static void handle_rock1(byte i, byte state, uint16_t clock) {
00346     struct object *po = &objects[i];
00347     byte speed = 6 + (po->state & 3);
00348     if (commonrock(clock, i, speed, draw_rock1r)) {
00349         int j;
00350         for (j = 0; j < 4; j++) {
00351             char e = pop(&enemies);
00352             if (0 < e) {
00353                 objects[e].x = po->x;
00354                 objects[e].y = po->y;
00355                 objects[e].handler = HANDLE_ROCK0;
00356                 objects[e].state = (j << 6) + qrand(6);   // spread fragments across 4 quadrants
00357             }
00358         }
00359         explodehere(po, HANDLE_BANG1, clock);
00360         score += 30;
00361         sounds.boom = 1;
00362     }
00363 }
00364 
00365 static void handle_bang0(byte i, byte state, uint16_t clock) {
00366     struct object *po = &objects[i];
00367     move(po, 0, 0);
00368     byte anim = ((0xff & clock) - state) >> 1;
00369     if (anim < EXPLODE16_FRAMES)
00370         draw_explode16(po->x >> 4, po->y >> 4, anim, 0);
00371     else
00372         expire(&explosions, i);
00373 }
00374 
00375 static void handle_bang1(byte i, byte state, uint16_t clock) {
00376     struct object *po = &objects[i];
00377     move(po, 0, 0);
00378     byte anim = ((0xff & clock) - state) >> 1;
00379     byte rot = 7 & i;
00380     if (anim < EXPLODE32_FRAMES)
00381         draw_explode32(po->x >> 4, po->y >> 4, anim, rot);
00382     else
00383         expire(&explosions, i);
00384 }
00385 
00386 typedef void (*handler)(byte, byte, uint16_t);
00387 static handler handlers[] = {
00388     handle_null,
00389     handle_rock0,
00390     handle_rock1,
00391     handle_bang0,
00392     handle_bang1,
00393     handle_player,
00394     handle_missile
00395 };
00396 
00397 
00398 // uncompress one line of the title banner into buffer dst
00399 // title banner lines are run-length encoded
00400 static void titlepaint(char *dst, byte src, byte mask) {
00401     if (src != 0xff) {
00402         PROGMEM prog_uchar *psrc = title_runs + 2 * src;
00403         byte a, b;
00404         do {
00405             a = *psrc++;
00406             b = *psrc++;
00407             while (a < (b & 0x7f))
00408                 dst[a++] |= mask;
00409         } while (!(b & 0x80));
00410     }
00411 }
00412 
00413 // draw a title banner column src (0-511) to screen column dst (0-63).
00414 static void column(byte dst, byte src) {
00415     static char scratch[76];
00416     memset(scratch, 0, sizeof(scratch));
00417     //PROGMEM prog_uchar* titleadd = title + 2 * src;
00418     prog_uchar line = title[2 * src];//*titleadd++;//pgm_read_byte_near(title + 2 * src);
00419     titlepaint(scratch, line, 1);
00420     line = title[2 * src+1];//*titleadd;//pgm_read_byte_near(title + 2 * src + 1);
00421     titlepaint(scratch, line, 2);
00422 
00423     byte j;
00424     for (j = 0; j < 38; j++) {
00425         GD.wr(dst + (j << 6),
00426               (((dst + j) & 15) << 4) +
00427               scratch[2 * j] +
00428               (scratch[2 * j + 1] << 2));
00429     }
00430 }
00431 
00432 static void setup_sprites() {
00433     GD.copy(PALETTE16A, palette16a, sizeof(palette16a));
00434     GD.copy(PALETTE4A, palette4a, sizeof(palette4a));
00435     GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
00436 
00437     // Use the first two 256-color palettes as pseudo-16 color palettes
00438     int i;
00439     for (i = 0; i < 256; i++) {
00440 
00441         // palette 0 decodes low nibble, hence (i & 15)
00442         //PROGMEM prog_uchar* pallow = palette256a + ((i & 15) << 1);
00443         uint16_t rgb = palette256a[((i & 15) << 1)];
00444         GD.wr16(RAM_SPRPAL + (i << 1), rgb);
00445 
00446         // palette 1 decodes nigh nibble, hence (i >> 4)
00447         //PROGMEM prog_uchar* palhigh = palette256a + ((i >> 4) << 1);
00448         rgb = palette256a[((i >> 4) << 1)];
00449         GD.wr16(RAM_SPRPAL + 512 + (i << 1), rgb);
00450     }
00451 
00452     GD.uncompress(RAM_SPRIMG, asteroid_images_compressed);
00453     GD.wr(JK_MODE, 1);
00454 }
00455 
00456 // Run the object handlers, keeping track of sprite ownership in spr2obj
00457 static void runobjects(uint16_t r) {
00458     int i;
00459     GD.__wstartspr(((r & 1) ? 256 : 0));  // write sprites to other frame
00460     for (i = 0; i < 128; i++) {
00461         struct object *po = &objects[i];
00462         handler h = (handler)handlers[po->handler];
00463         byte loSpr = GD.spr;
00464         (*h)(i, po->state, r);
00465         while (loSpr < GD.spr) {
00466             spr2obj[loSpr++] = i;
00467         }
00468     }
00469     // Hide all the remaining sprites
00470     do
00471         GD.xhide();
00472     while (GD.spr);
00473     GD.__end();
00474 }
00475 
00476 // ----------------------------------------------------------------------
00477 //     map
00478 // ----------------------------------------------------------------------
00479 
00480 static byte loaded[8];
00481 static byte scrap;
00482 
00483 // copy a (w,h) rectangle from the source image (x,y) into picture RAM
00484 static void rect(unsigned int dst, byte x, byte y, byte w, byte h) {
00485     PROGMEM prog_uchar *src = bg_pic + (16 * y) + x;
00486     while (h--) {
00487         GD.copy(dst, src, w);
00488         dst += 64;
00489         src += 16;
00490     }
00491 }
00492 
00493 static void map_draw(byte strip) {
00494     strip &= 63;              // Universe is finite but unbounded: 64 strips
00495     byte s8 = (strip & 7);    // Destination slot for this strip (0-7)
00496     if (loaded[s8] != strip) {
00497         qrandSeed(level ^ (strip * 77));    // strip number is the hash...
00498 
00499         // Random star pattern is made from characters 1-15
00500         GD.__wstart(s8 * (8 * 64));
00501         int i;
00502         for (i = 0; i < (8 * 64); i++) {
00503             byte r;
00504             if (qrand(3) == 0)
00505                 r = qrand(4);
00506             else
00507                 r = 0;
00508             spimain.write(r);
00509         }
00510         GD.__end();
00511 
00512         // Occasional planet, copied from the background char map
00513         if (qrand(2) == 0) {
00514             uint16_t dst = (qrand(3) * 8) + (s8 * (8 * 64));
00515             switch (qrand(2)) {
00516                 case 0:
00517                     rect(dst, 0, 1, 6, 6);
00518                     break;
00519                 case 1:
00520                     rect(dst, 7, 1, 6, 6);
00521                     break;
00522                 case 2:
00523                     rect(dst, 0, 7, 8, 4);
00524                     break;
00525                 case 3:
00526                     rect(dst, 8, 7, 5, 5);
00527                     break;
00528             }
00529         }
00530         loaded[s8] = strip;
00531     }
00532 }
00533 
00534 static void map_coldstart() {
00535     memset(loaded, 0xff, sizeof(loaded));
00536     scrap = 0xff;
00537     byte i;
00538     for (i = 0; i < 8; i++)
00539         map_draw(i);
00540 }
00541 
00542 static int atxy(int x, int y) {
00543     return (y << 6) + x;
00544 }
00545 
00546 static void update_score() {
00547     unsigned long s = score;
00548     uint16_t a = atxy(49, scrap << 3);
00549     byte i;
00550     //void* code;
00551     for (i = 0; i < 6; i++) {
00552         PROGMEM prog_uchar* digitcodes = (bg_pic + (16 * 30))+(s % 10);
00553 
00554         //code = &digitcodes + (s % 10);
00555         GD.wr(a--, *digitcodes);//pgm_read_byte_near(digitcodes + (s % 10)) ;
00556         s /= 10;
00557     }
00558     PROGMEM prog_uchar* digitcodes = bg_pic + (16 * 30) + lives;
00559     //code = &digitcodes + lives;
00560     GD.wr(atxy(0, scrap << 3), *digitcodes);// pgm_read_byte_near(digitcodes + lives));
00561 }
00562 
00563 static void map_refresh(byte strip) {
00564     byte i;
00565     byte newscrap = 7 & (strip + 7);
00566     if (scrap != newscrap) {
00567         scrap = newscrap;
00568 
00569         uint16_t scrapline = scrap << 6;
00570         GD.wr16(COMM+2, 0x8000 | scrapline);    // show scrapline at line 0
00571         GD.wr16(COMM+14, 0x8000 | (0x1ff & ((scrapline + 8) - 291))); // show scrapline+8 at line 291
00572 
00573         GD.fill(atxy(0, scrap << 3), 0, 50);
00574         update_score();
00575 
00576         GD.fill(atxy(0, 1 + (scrap << 3)), 0, 64);
00577         rect(atxy(0, 1 + (scrap << 3)), 0, 31, 16, 1);
00578         rect(atxy(32, 1 + (scrap << 3)), 0, 31, 16, 1);
00579 
00580         loaded[scrap] = 0xff;
00581     }
00582     wait_ms(1);   // wait for raster to pass the top line before overwriting it
00583     for (i = 0; i < 6; i++)
00584         map_draw(strip + i);
00585 }
00586 
00587 static void start_level() {
00588     int i;
00589 
00590     for (i = 0; i < 128; i++)
00591         objects[i].handler = 0;
00592 
00593     player_vx = 0;
00594     player_vy = 0;
00595     player_invincible = 0;
00596     player_dying = 0;
00597 
00598     objects[0].x = 0;
00599     objects[0].y = 0;
00600     objects[0].state = 0;
00601     objects[0].handler = HANDLE_PLAYER;
00602 
00603     // Set up the pools of objects for missiles, enemies, explosions
00604     missiles = 0;
00605     enemies = 0;
00606     explosions = 0;
00607     for (i = 1; i < 16; i++)
00608         push(&missiles, i);
00609     for (i = 16; i < 80; i++)
00610         push(&enemies, i);
00611     for (i = 80; i < 128; i++)
00612         push(&explosions, i);
00613 
00614     // Place asteroids in a ring around the edges of the screen
00615     int x;
00616     if ((level + 3) < 32) {
00617         x = level + 3;
00618     } else {
00619         x = 32;
00620     }
00621     for (i = 0; i < x; i++) {
00622         char e = pop(&enemies);
00623         if (rand()%2 == 0) {
00624             objects[e].x = rand()%2 ? COORD(32) : COORD(400-32);
00625             objects[e].y = rand()%COORD(300);
00626         } else {
00627             objects[e].x = rand()%COORD(400);
00628             objects[e].y = rand()%2 ? COORD(32) : COORD(300-32);
00629         }
00630         objects[e].handler = HANDLE_ROCK1;
00631         objects[e].state = qrand(8);
00632     }
00633 
00634     GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
00635     for (i = 0; i < 16; i++) {
00636         uint16_t a = PALETTE16B + 2 * i;
00637         uint16_t c = GD.rd16(a);
00638         if (level & 1)
00639             c = swapRB(c);
00640         if (level & 2)
00641             c = swapRG(c);
00642         if (level & 4)
00643             c = swapRB(c);
00644         GD.wr16(a, c);
00645     }
00646 
00647     map_coldstart();
00648 }
00649 
00650 void setup() {
00651 
00652     GD.begin();
00653     controller_init();
00654 
00655 }
00656 
00657 static void title_banner() {
00658     GD.fill(VOICES, 0, 64 * 4);
00659     GD.wr(J1_RESET, 1);
00660     GD.wr(SPR_DISABLE, 1);
00661     GD.wr16(SCROLL_X, 0);
00662     GD.wr16(SCROLL_Y, 0);
00663     GD.fill(RAM_PIC, 0, 4096);
00664     setup_sprites();
00665 
00666     uint16_t i;
00667     uint16_t j;
00668 
00669     GD.__wstart(RAM_CHR);
00670     for (i = 0; i < 256; i++) {
00671         // bits control lit segments like this:
00672         //   0   1
00673         //   2   3
00674         byte a = (i & 1) ? 0x3f : 0;
00675         byte b = (i & 2) ? 0x3f : 0;
00676         byte c = (i & 4) ? 0x3f : 0;
00677         byte d = (i & 8) ? 0x3f : 0;
00678         for (j = 0; j < 3; j++) {
00679             spimain.write(a);
00680             spimain.write(b);
00681         }
00682         spimain.write(0);
00683         spimain.write(0);
00684         for (j = 0; j < 3; j++) {
00685             spimain.write(c);
00686             spimain.write(d);
00687         }
00688         spimain.write(0);
00689         spimain.write(0);
00690     }
00691    
00692     GD.__end();
00693     
00694     for (i = 0; i < 256; i++) {
00695         GD.setpal(4 * i + 0, RGB(0,0,0));
00696         //prog_uchar* colptr = title_ramp + 2 * (i >> 4);
00697         prog_uchar color = title_ramp[(i >> 4)];//pgm_read_word_near(title_ramp + 2 * (i >> 4));
00698         GD.setpal(4 * i + 3, color);
00699     }
00700     
00701     for (i = 0; i < 64; i++) {
00702         column(i, i);
00703     }
00704 
00705     for (i = 0; i < 128; i++) {
00706         objects[i].handler = 0;
00707         objects[i].collide = 0xff;
00708     }
00709     
00710     for (i = 0; i < 128; i++)
00711         push(&enemies, i);
00712 
00713     for (i = 0; i < 40; i++) {
00714         char e = pop(&enemies);
00715         objects[e].x = COORD(rand()%400);
00716         objects[e].y = COORD(rand()%300);
00717         objects[e].handler = qrand1() ? HANDLE_ROCK1 : HANDLE_ROCK0;
00718         objects[e].state = qrand(8);
00719     }
00720 
00721     byte startgame = 0;
00722     for (i = 0; startgame < 50; i++) {
00723         for (j = 0; j < 256; j++) {
00724             byte index = 15 & ((-i >> 2) + (j >> 4));
00725             prog_uchar* colour = title_ramp + 2 * index;
00726             prog_uchar color = *colour;// pgm_read_word_near(title_ramp + 2 * index);
00727             GD.setpal(4 * j + 3, color);
00728         }
00729         
00730         if (!startgame &&
00731                 (controller_sense(i) || (i == (2048 - 400)))) {
00732             // explode all rocks!
00733             for (j = 0; j < 128; j++) {
00734                 byte h = objects[j].handler;
00735                 if ((h == HANDLE_ROCK0) || (h == HANDLE_ROCK1))
00736                     objects[j].handler = HANDLE_BANG1;
00737                 objects[j].state = i;
00738             }
00739             startgame = 1;
00740         }
00741         
00742         if (startgame){
00743             startgame++;
00744             }
00745             
00746         //runobjects(i);
00747         
00748         GD.waitvblank();
00749         GD.wr(SPR_PAGE, (i & 1));
00750         GD.wr(SPR_DISABLE, 0);
00751         GD.wr16(SCROLL_X, i);
00752         if ((i & 7) == 0) {
00753             byte x = ((i >> 3) + 56);
00754             column(63 & x, 255 & x);
00755         }
00756     }
00757 
00758     for (i = 0; i < 32; i++) {
00759         for (j = 0; j < 256; j++) {
00760             uint16_t a = RAM_PAL + (8 * j) + 6;
00761             uint16_t pal = GD.rd16(a);
00762             byte r = 31 & (pal >> 10);
00763             byte g = 31 & (pal >> 5);
00764             byte b = 31 & pal;
00765             if (r) r--;
00766             if (g) g--;
00767             if (b) b--;
00768             pal = (r << 10) | (g << 5) | b;
00769             GD.wr16(a, pal);
00770         }
00771         GD.waitvblank();
00772         GD.waitvblank();
00773         
00774     }
00775 
00776     GD.fill(RAM_PIC, 0, 4096);
00777     
00778 }
00779 
00780 #define SOUNDCYCLE(state) ((state) = v ? ((state) + 1) : 0)
00781 
00782 int main() {
00783    
00784     setup();
00785     while (1) {
00786      
00787         title_banner();
00788 
00789         GD.uncompress(RAM_CHR, bg_chr_compressed);
00790         GD.uncompress(RAM_PAL, bg_pal_compressed);
00791 
00792         GD.wr16(COMM+0, 0);
00793         GD.wr16(COMM+2, 0x8000);
00794         GD.wr16(COMM+4, 8);   // split at line 8
00795         GD.wr16(COMM+6, 177);
00796         GD.wr16(COMM+8, 166);
00797         GD.wr16(COMM+10, 291);   // split at line 291
00798         GD.wr16(COMM+12, 0);
00799         GD.wr16(COMM+14, 0x8000 | (0x1ff & (8 - 291))); // show line 8 at line 292
00800         GD.microcode(splitscreen_code, sizeof(splitscreen_code));
00801 
00802         setup_sprites();
00803 
00804 
00805         memset(&sounds, 0, sizeof(sounds));
00806         level = 0;
00807         score = 0;
00808         lives = 3;
00809         unsigned int r = 0;
00810         start_level();
00811 
00812         while (lives) {
00813             int i;//, j;
00814 
00815             runobjects(r);
00816 
00817             for (i = 0; i < 128; i++)
00818                 objects[i].collide = 0xff;
00819 
00820             GD.waitvblank();
00821             // swap frames
00822             GD.wr(SPR_PAGE, (r & 1));
00823             int scrollx = objects[0].x >> 4;
00824             int scrolly = objects[0].y >> 4;
00825             GD.wr16(COMM+6, scrollx & 0x1ff);
00826             GD.wr16(COMM+8, scrolly & 0x1ff);
00827             map_refresh(scrolly >> 6);
00828             update_score();
00829             GD.wr16(COMM+12, r);    // horizontal scroll the bottom banner
00830 
00831             GD.waitvblank();
00832 
00833             GD.__start(COLLISION);
00834             for (i = 0; i < 256; i++) {
00835                 byte c = spimain.write(0);   // c is the colliding sprite number
00836                 if (c != 0xff) {
00837                     objects[spr2obj[i]].collide = spr2obj[c];
00838                 }
00839             }
00840             GD.__end();
00841 
00842             if (sounds.boing) {
00843                 byte x;
00844                 if ((16 - (sounds.boing - 1) * 2) <0) {
00845                     x = 0;
00846                 } else {
00847                     x = 16 - (sounds.boing - 1) * 2;
00848                 }
00849                 byte v = x; //max(0, 16 - (sounds.boing - 1) * 2);
00850                 GD.voice(0, 0, 4 * 4000 - 700 * sounds.boing, v/2, v/2);
00851                 GD.voice(1, 1, 1000 - 100 * sounds.boing, v, v);
00852                 SOUNDCYCLE(sounds.boing);
00853             }
00854             if (sounds.boom) {
00855                 byte x;
00856                 if ((96 - (sounds.boom - 1) * 6) <0) {
00857                     x = 0;
00858                 } else {
00859                     x = 96 - (sounds.boom - 1) * 6;
00860                 }
00861                 byte v = x;//max(0, 96 - (sounds.boom - 1) * 6);
00862                 GD.voice(2, 0, 220, v, v);
00863                 GD.voice(3, 1, 220/8, v/2, v/2);
00864                 SOUNDCYCLE(sounds.boom);
00865             }
00866             if (sounds.pop) {
00867                 byte x;
00868                 if ((32 - (sounds.pop - 1) * 3) <0) {
00869                     x = 0;
00870                 } else {
00871                     x = 32 - (sounds.pop - 1) * 3;
00872                 }
00873                 byte v = x;//max(0, 32 - (sounds.pop - 1) * 3);
00874                 GD.voice(4, 0, 440, v, v);
00875                 GD.voice(5, 1, 440/8, v/2, v/2);
00876                 SOUNDCYCLE(sounds.pop);
00877             }
00878             GD.voice(6, 1, 40, sounds.thrust ? 10 : 0, sounds.thrust ? 10 : 0);
00879 
00880             static byte tune;
00881             if (sounds.bass) {
00882                 byte v = sounds.bass < 9 ? 63 : 0;
00883                 int f0 = tune ? 130: 163 ;
00884                 byte partials[] = { 71, 32, 14, 75, 20, 40 };
00885                 byte i;
00886                 for (i = 0; i < 6; i++) {
00887                     byte a = (v * partials[i]) >> 8;
00888                     GD.voice(7 + i, 0, f0 * (i + 1), a, a);
00889                 }
00890                 SOUNDCYCLE(sounds.bass);
00891             }
00892             static byte rhythm;
00893             byte x;
00894             if ((24 - level) <10) {
00895                 x = 10;
00896             } else {
00897                 x = 24 - level;
00898             }
00899             if (++rhythm >= x) {
00900                 sounds.bass = 1;
00901                 rhythm = 0;
00902                 tune = !tune;
00903             }
00904 
00905             byte nenemies = 64;
00906             byte pe = enemies;
00907             while (pe) {
00908                 pe = objects[pe].state;
00909                 nenemies--;
00910             }
00911             if (nenemies == 0) {
00912                 level++;
00913                 start_level();
00914             }
00915 
00916             r++;
00917         }
00918     }
00919 }