123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966 |
- #include <SPI.h>
- #include <GD.h>
- // ----------------------------------------------------------------------
- // qrand: quick random numbers
- // ----------------------------------------------------------------------
- static uint16_t lfsr = 1;
- static void qrandSeed(int seed)
- {
- if (seed) {
- lfsr = seed;
- } else {
- lfsr = 0x947;
- }
- }
- static byte qrand1() // a random bit
- {
- lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & 0xb400);
- return lfsr & 1;
- }
- static byte qrand(byte n) // n random bits
- {
- byte r = 0;
- while (n--)
- r = (r << 1) | qrand1();
- return r;
- }
- // ----------------------------------------------------------------------
- // controller: buttons on Arduino pins 3,4,5,6
- // ----------------------------------------------------------------------
- static void controller_init()
- {
- // Configure input pins with internal pullups
- byte i;
- for (i = 3; i < 7; i++) {
- pinMode(i, INPUT);
- digitalWrite(i, HIGH);
- }
- }
- #define CONTROL_LEFT 1
- #define CONTROL_RIGHT 2
- #define CONTROL_UP 4
- #define CONTROL_DOWN 8
- static int screenshot_i; // JCB
- #define PLAYBACK 0 // read controls from Serial - JCB
- #define SCREENSHOT 0 // JCB
- static byte controller_sense(uint16_t clock)
- {
- byte r = 0;
- #if PLAYBACK // JCB
- Serial.write(0x50); // request key // JCB
- Serial.write(lowByte(clock)); // JCB
- Serial.write(highByte(clock)); // JCB
- while (!Serial.available()) // JCB
- ; // JCB
- r = Serial.read(); // JCB
- return r; // JCB
- #endif // JCB
- if (!digitalRead(5))
- r |= CONTROL_DOWN;
- if (!digitalRead(4))
- r |= CONTROL_UP;
- if (!digitalRead(6))
- r |= CONTROL_LEFT;
- if (!digitalRead(3))
- r |= CONTROL_RIGHT;
- #if PLAYBACK // JCB
- Serial.write(lowByte(clock)); // JCB
- Serial.write(r); // JCB
- #endif // JCB
- return r;
- }
- // Swap color's red and blue channels
- uint16_t swapRB(uint16_t color)
- {
- byte r = 31 & (color >> 10);
- byte g = 31 & (color >> 5);
- byte b = 31 & color;
- return (color & 0x8000) | (b << 10) | (g << 5) | r;
- }
- // Swap color's red and green channels
- uint16_t swapRG(uint16_t color)
- {
- byte r = 31 & (color >> 10);
- byte g = 31 & (color >> 5);
- byte b = 31 & color;
- return (color & 0x8000) | (g << 10) | (r << 5) | b;
- }
- #include "asteroidgraphics.h"
- #include "splitscreen.h"
- static void update_score();
- // [int(127 * math.sin(math.pi * 2 * i / 16)) for i in range(16)]
- static flash_uint8_t charsin[16] = {0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48};
- #define qsin(a) (char)pgm_read_byte_near(charsin + ((a) & 15))
- #define qcos(a) qsin((a) + 4)
- static char spr2obj[256]; // Maps sprites to owning objects
- /*
- The general idea is that an object table ``objects`` has an entry for
- each drawn thing on screen (e.g. player, missile, rock, explosion).
- Each class of object has a ``handler`` function that does the necessary
- housekeeping and draws the actual sprites.
- As part of the behavior, some classes need to know if they have collided
- with anything. In particular the rocks need to know if they have collided
- with the player or a missile. The `collide` member points to the
- colliding sprite.
- */
- struct object {
- int x, y;
- byte handler, state;
- byte collide;
- } objects[128];
- #define COORD(c) ((c) << 4)
- static char explosions = -1;
- static char enemies = -1;
- static char missiles = -1;
- static void push(char *stk, byte i)
- {
- objects[i].state = *stk;
- *stk = i;
- }
- static char pop(char *stk)
- {
- char r = *stk;
- if (0 <= r) {
- *stk = objects[r].state;
- }
- return r;
- }
- byte rr[4] = { 0,3,6,5 };
- static struct {
- byte boing, boom, pop;
- byte thrust;
- byte bass;
- } sounds;
- static int player_vx, player_vy; // Player velocity
- static int player_invincible, player_dying;
- static byte lives;
- static long score;
- static byte level;
- // Move object po by velocity (vx, vy), optionally keeping in
- // player's frame.
- // Returns true if the object wrapped screen edge
- static bool move(struct object *po, char vx, char vy, byte playerframe = 1)
- {
- bool r = 0;
- if (playerframe) {
- po->x += (vx - player_vx);
- po->y += (vy - player_vy);
- } else {
- po->x += vx;
- po->y += vy;
- }
- if (po->x > COORD(416))
- r = 1, po->x -= COORD(432);
- else if (po->x < COORD(-16))
- r = 1, po->x += COORD(432);
- if (po->y > COORD(316))
- r = 1, po->y -= COORD(332);
- else if (po->y < COORD(-16))
- r = 1, po->y += COORD(332);
- return r;
- }
- #define HANDLE_NULL 0
- #define HANDLE_ROCK0 1
- #define HANDLE_ROCK1 2
- #define HANDLE_BANG0 3
- #define HANDLE_BANG1 4
- #define HANDLE_PLAYER 5
- #define HANDLE_MISSILE 6
- // Expire object i, and return it to the free stk
- static void expire(char *stk, byte i)
- {
- objects[i].handler = HANDLE_NULL;
- push(stk, i);
- }
- static void handle_null(byte i, byte state, uint16_t clock)
- {
- }
- static void handle_player(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- byte angle = (po->state & 15);
- byte rot1 = (angle & 3);
- byte rot2 = rr[3 & (angle >> 2)];
- if (!player_dying && (player_invincible & 1) == 0)
- draw_player(200, 150, rot1, rot2);
- static byte prev_control;
- byte control = controller_sense(clock);
- char thrust_x, thrust_y;
- if (!player_dying && control & CONTROL_DOWN) { // thrust
- byte flame_angle = angle ^ 8;
- byte d;
- for (d = 9; d > 5; d--) {
- int flamex = 201 - (((d + (clock&3)) * qsin(flame_angle)) >> 5);
- int flamey = 150 - (((d + (clock&3)) * qcos(flame_angle)) >> 5);
- if ((player_invincible & 1) == 0)
- draw_sparkr(flamex, flamey, rot1, rot2, 1); // collision class K
- }
- thrust_x = -qsin(angle);
- thrust_y = -qcos(angle);
- sounds.thrust = 1;
- } else {
- thrust_x = thrust_y = 0;
- sounds.thrust = 0;
- }
- player_vx = ((31 * player_vx) + thrust_x) / 32;
- player_vy = ((31 * player_vy) + thrust_y) / 32;
- po->x += player_vx;
- po->y += player_vy;
- if (clock & 1) {
- char rotate = (512 - analogRead(0)) / 400;
- if (control & CONTROL_LEFT)
- rotate++;
- if (control & CONTROL_RIGHT)
- rotate--;
- po->state = ((angle + rotate) & 15);
- }
- if (!player_dying &&
- !(prev_control & CONTROL_UP) &&
- (control & CONTROL_UP)) { // shoot!
- char e = pop(&missiles);
- if (0 <= e) {
- objects[e].x = COORD(200);
- objects[e].y = COORD(150);
- objects[e].state = po->state;
- objects[e].handler = HANDLE_MISSILE;
- }
- sounds.boing = 1;
- }
- prev_control = control;
- if (player_invincible)
- --player_invincible;
- if (player_dying) {
- if (--player_dying == 0) {
- --lives;
- update_score();
- if (lives != 0) {
- player_invincible = 48;
- }
- }
- }
- }
- static void handle_missile(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- byte angle = (po->state & 15);
- byte rot1 = (angle & 3);
- byte rot2 = rr[3 & (angle >> 2)];
- draw_sparkr(po->x >> 4, po->y >> 4, rot1, rot2);
- char vx = -qsin(po->state), vy = -qcos(po->state);
- if (move(po, vx, vy, 0)) {
- expire(&missiles, i);
- }
- }
- static void explodehere(struct object *po, byte handler, uint16_t clock)
- {
- char e = pop(&explosions);
- if (0 <= e) {
- objects[e].x = po->x;
- objects[e].y = po->y;
- objects[e].handler = handler;
- objects[e].state = clock;
- }
- }
- static void killplayer(uint16_t clock)
- {
- if (!player_invincible && !player_dying) {
- char e = pop(&explosions);
- if (0 <= e) {
- objects[e].x = COORD(200);
- objects[e].y = COORD(150);
- objects[e].handler = HANDLE_BANG1;
- objects[e].state = clock;
- }
- player_dying = 2 * 36;
- sounds.boom = 1;
- sounds.pop = 1;
- }
- }
- static byte commonrock(uint16_t clock, byte i, byte speed, void df(int x, int y, byte anim, byte rot, byte jk))
- {
- struct object *po = &objects[i];
- byte move_angle = po->state >> 4;
- char vx = (speed * -qsin(move_angle)) >> 6, vy = (speed * -qcos(move_angle)) >> 6;
- move(po, vx, vy);
- byte angle = (clock * speed) >> 4;
- if (po->state & 1)
- angle = ~angle;
- byte rot1 = (angle & 3);
- byte rot2 = rr[3 & (angle >> 2)];
- df(po->x >> 4, po->y >> 4, rot1, rot2, 1);
- if (po->collide != 0xff) {
- struct object *other = &objects[po->collide];
- switch (other->handler) {
- case HANDLE_PLAYER:
- killplayer(clock);
- break;
- case HANDLE_MISSILE:
- expire(&missiles, po->collide); // missile is dead
- expire(&enemies, i);
- return 1;
- }
- }
- return 0;
- }
- static void handle_rock0(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- byte speed = 12 + (po->state & 7);
- if (commonrock(clock, i, speed, draw_rock0r)) {
- explodehere(po, HANDLE_BANG0, clock);
- score += 10;
- sounds.pop = 1;
- }
- }
- static void handle_rock1(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- byte speed = 6 + (po->state & 3);
- if (commonrock(clock, i, speed, draw_rock1r)) {
- int j;
- for (j = 0; j < 4; j++) {
- char e = pop(&enemies);
- if (0 < e) {
- objects[e].x = po->x;
- objects[e].y = po->y;
- objects[e].handler = HANDLE_ROCK0;
- objects[e].state = (j << 6) + qrand(6); // spread fragments across 4 quadrants
- }
- }
- explodehere(po, HANDLE_BANG1, clock);
- score += 30;
- sounds.boom = 1;
- }
- }
- static void handle_bang0(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- move(po, 0, 0);
- byte anim = ((0xff & clock) - state) >> 1;
- if (anim < EXPLODE16_FRAMES)
- draw_explode16(po->x >> 4, po->y >> 4, anim, 0);
- else
- expire(&explosions, i);
- }
- static void handle_bang1(byte i, byte state, uint16_t clock)
- {
- struct object *po = &objects[i];
- move(po, 0, 0);
- byte anim = ((0xff & clock) - state) >> 1;
- byte rot = 7 & i;
- if (anim < EXPLODE32_FRAMES)
- draw_explode32(po->x >> 4, po->y >> 4, anim, rot);
- else
- expire(&explosions, i);
- }
- typedef void (*handler)(byte, byte, uint16_t);
- static handler handlers[] = {
- handle_null,
- handle_rock0,
- handle_rock1,
- handle_bang0,
- handle_bang1,
- handle_player,
- handle_missile
- };
- class GDflashbits {
- public:
- void begin(flash_uint8_t *s) {
- src = s;
- mask = 0x01;
- }
- byte get1(void) {
- byte r = (pgm_read_byte_near(src) & mask) != 0;
- mask <<= 1;
- if (!mask) {
- mask = 1;
- src++;
- }
- return r;
- }
- unsigned short getn(byte n) {
- unsigned short r = 0;
- while (n--) {
- r <<= 1;
- r |= get1();
- }
- return r;
- }
- private:
- flash_uint8_t *src;
- byte mask;
- };
- static GDflashbits GDFB;
- static void GD_uncompress(unsigned int addr, flash_uint8_t *src)
- {
- GDFB.begin(src);
- byte b_off = GDFB.getn(4);
- byte b_len = GDFB.getn(4);
- byte minlen = GDFB.getn(2);
- unsigned short items = GDFB.getn(16);
- while (items--) {
- if (GDFB.get1() == 0) {
- GD.wr(addr++, GDFB.getn(8));
- } else {
- int offset = -GDFB.getn(b_off) - 1;
- int l = GDFB.getn(b_len) + minlen;
- while (l--) {
- GD.wr(addr, GD.rd(addr + offset));
- addr++;
- }
- }
- }
- }
- // uncompress one line of the title banner into buffer dst
- // title banner lines are run-length encoded
- static void titlepaint(char *dst, byte src, byte mask)
- {
- if (src != 0xff) {
- flash_uint8_t *psrc = title_runs + 2 * src;
- byte a, b;
- do {
- a = pgm_read_byte_near(psrc++);
- b = pgm_read_byte_near(psrc++);
- while (a < (b & 0x7f))
- dst[a++] |= mask;
- } while (!(b & 0x80));
- }
- }
- // draw a title banner column src (0-511) to screen column dst (0-63).
- static void column(byte dst, byte src)
- {
- static char scratch[76];
- memset(scratch, 0, sizeof(scratch));
- uint8_t line = pgm_read_byte_near(title + 2 * src);
- titlepaint(scratch, line, 1);
- line = pgm_read_byte_near(title + 2 * src + 1);
- titlepaint(scratch, line, 2);
- byte j;
- for (j = 0; j < 38; j++) {
- GD.wr(dst + (j << 6),
- (((dst + j) & 15) << 4) +
- scratch[2 * j] +
- (scratch[2 * j + 1] << 2));
- }
- }
- static void setup_sprites()
- {
- GD.copy(PALETTE16A, palette16a, sizeof(palette16a));
- GD.copy(PALETTE4A, palette4a, sizeof(palette4a));
- GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
- // Use the first two 256-color palettes as pseudo-16 color palettes
- int i;
- for (i = 0; i < 256; i++) {
- // palette 0 decodes low nibble, hence (i & 15)
- uint16_t rgb = pgm_read_word_near(palette256a + ((i & 15) << 1));
- GD.wr16(RAM_SPRPAL + (i << 1), rgb);
- // palette 1 decodes nigh nibble, hence (i >> 4)
- rgb = pgm_read_word_near(palette256a + ((i >> 4) << 1));
- GD.wr16(RAM_SPRPAL + 512 + (i << 1), rgb);
- }
- GD_uncompress(RAM_SPRIMG, asteroid_images_compressed);
- GD.wr(JK_MODE, 1);
- }
- // Run the object handlers, keeping track of sprite ownership in spr2obj
- static void runobjects(uint16_t r)
- {
- int i;
- // JCB-SPR_PAGEW-A
- GD.__wstartspr((r & 1) ? 256 : 0); // write sprites to other frame
- // JCB-SPR_PAGEW-B
- for (i = 0; i < 128; i++) {
- struct object *po = &objects[i];
- handler h = (handler)handlers[po->handler];
- byte loSpr = GD.spr;
- (*h)(i, po->state, r);
- while (loSpr < GD.spr) {
- spr2obj[loSpr++] = i;
- }
- }
- // Hide all the remaining sprites
- do
- GD.xhide();
- while (GD.spr);
- GD.__end();
- }
- // ----------------------------------------------------------------------
- // map
- // ----------------------------------------------------------------------
- static byte loaded[8];
- static byte scrap;
- // copy a (w,h) rectangle from the source image (x,y) into picture RAM
- static void rect(unsigned int dst, byte x, byte y, byte w, byte h)
- {
- flash_uint8_t *src = bg_pic + (16 * y) + x;
- while (h--) {
- GD.copy(dst, src, w);
- dst += 64;
- src += 16;
- }
- }
- static void map_draw(byte strip)
- {
- strip &= 63; // Universe is finite but unbounded: 64 strips
- byte s8 = (strip & 7); // Destination slot for this strip (0-7)
- if (loaded[s8] != strip) {
- qrandSeed(level ^ (strip * 77)); // strip number is the hash...
- // Random star pattern is made from characters 1-15
- GD.__wstart(s8 * (8 * 64));
- int i;
- for (i = 0; i < (8 * 64); i++) {
- byte r;
- if (qrand(3) == 0)
- r = qrand(4);
- else
- r = 0;
- SPI.transfer(r);
- }
- GD.__end();
- // Occasional planet, copied from the background char map
- if (qrand(2) == 0) {
- uint16_t dst = (qrand(3) * 8) + (s8 * (8 * 64));
- switch (qrand(2)) {
- case 0:
- rect(dst, 0, 1, 6, 6);
- break;
- case 1:
- rect(dst, 7, 1, 6, 6);
- break;
- case 2:
- rect(dst, 0, 7, 8, 4);
- break;
- case 3:
- rect(dst, 8, 7, 5, 5);
- break;
- }
- }
- loaded[s8] = strip;
- }
- }
- static void map_coldstart()
- {
- memset(loaded, 0xff, sizeof(loaded));
- scrap = 0xff;
- byte i;
- for (i = 0; i < 8; i++)
- map_draw(i);
- }
- static int atxy(int x, int y)
- {
- return (y << 6) + x;
- }
- static void update_score()
- {
- flash_uint8_t* digitcodes = bg_pic + (16 * 30);
- unsigned long s = score;
- uint16_t a = atxy(49, scrap << 3);
- byte i;
- for (i = 0; i < 6; i++) {
- GD.wr(a--, pgm_read_byte_near(digitcodes + (s % 10)));
- s /= 10;
- }
- GD.wr(atxy(0, scrap << 3), pgm_read_byte_near(digitcodes + lives));
- }
- static void map_refresh(byte strip)
- {
- byte i;
- byte newscrap = 7 & (strip + 7);
- if (scrap != newscrap) {
- scrap = newscrap;
- uint16_t scrapline = scrap << 6;
- GD.wr16(COMM+2, 0x8000 | scrapline); // show scrapline at line 0
- GD.wr16(COMM+14, 0x8000 | (0x1ff & ((scrapline + 8) - 291))); // show scrapline+8 at line 291
- GD.fill(atxy(0, scrap << 3), 0, 50);
- update_score();
- GD.fill(atxy(0, 1 + (scrap << 3)), 0, 64);
- rect(atxy(0, 1 + (scrap << 3)), 0, 31, 16, 1);
- rect(atxy(32, 1 + (scrap << 3)), 0, 31, 16, 1);
- loaded[scrap] = 0xff;
- }
- delay(1); // wait for raster to pass the top line before overwriting it
- for (i = 0; i < 6; i++)
- map_draw(strip + i);
- }
- static void start_level()
- {
- int i;
- for (i = 0; i < 128; i++)
- objects[i].handler = 0;
- player_vx = 0;
- player_vy = 0;
- player_invincible = 0;
- player_dying = 0;
- objects[0].x = 0;
- objects[0].y = 0;
- objects[0].state = 0;
- objects[0].handler = HANDLE_PLAYER;
- // Set up the pools of objects for missiles, enemies, explosions
- missiles = 0;
- enemies = 0;
- explosions = 0;
- for (i = 1; i < 16; i++)
- push(&missiles, i);
- for (i = 16; i < 80; i++)
- push(&enemies, i);
- for (i = 80; i < 128; i++)
- push(&explosions, i);
- // Place asteroids in a ring around the edges of the screen
- for (i = 0; i < min(32, 3 + level); i++) {
- char e = pop(&enemies);
- if (random(2) == 0) {
- objects[e].x = random(2) ? COORD(32) : COORD(400-32);
- objects[e].y = random(COORD(300));
- } else {
- objects[e].x = random(COORD(400));
- objects[e].y = random(2) ? COORD(32) : COORD(300-32);
- }
- objects[e].handler = HANDLE_ROCK1;
- objects[e].state = qrand(8);
- }
- GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
- for (i = 0; i < 16; i++) {
- uint16_t a = PALETTE16B + 2 * i;
- uint16_t c = GD.rd16(a);
- if (level & 1)
- c = swapRB(c);
- if (level & 2)
- c = swapRG(c);
- if (level & 4)
- c = swapRB(c);
- GD.wr16(a, c);
- }
- map_coldstart();
- }
- void setup()
- {
- Serial.begin(1000000); // JCB
- GD.begin();
- controller_init();
- }
- static void title_banner()
- {
- GD.fill(VOICES, 0, 64 * 4);
- GD.wr(J1_RESET, 1);
- GD.wr(SPR_DISABLE, 1);
- GD.wr16(SCROLL_X, 0);
- GD.wr16(SCROLL_Y, 0);
- GD.fill(RAM_PIC, 0, 4096);
- setup_sprites();
- uint16_t i;
- uint16_t j;
- GD.__wstart(RAM_CHR);
- for (i = 0; i < 256; i++) {
- // bits control lit segments like this:
- // 0 1
- // 2 3
- byte a = (i & 1) ? 0x3f : 0;
- byte b = (i & 2) ? 0x3f : 0;
- byte c = (i & 4) ? 0x3f : 0;
- byte d = (i & 8) ? 0x3f : 0;
- for (j = 0; j < 3; j++) {
- SPI.transfer(a);
- SPI.transfer(b);
- }
- SPI.transfer(0);
- SPI.transfer(0);
- for (j = 0; j < 3; j++) {
- SPI.transfer(c);
- SPI.transfer(d);
- }
- SPI.transfer(0);
- SPI.transfer(0);
- }
- GD.__end();
- for (i = 0; i < 256; i++) {
- GD.setpal(4 * i + 0, RGB(0,0,0));
- uint16_t color = pgm_read_word_near(title_ramp + 2 * (i >> 4));
- GD.setpal(4 * i + 3, color);
- }
- for (i = 0; i < 64; i++) {
- column(i, i);
- }
- for (i = 0; i < 128; i++) {
- objects[i].handler = 0;
- objects[i].collide = 0xff;
- }
- for (i = 0; i < 128; i++)
- push(&enemies, i);
- for (i = 0; i < 40; i++) {
- char e = pop(&enemies);
- objects[e].x = COORD(random(400));
- objects[e].y = COORD(random(300));
- objects[e].handler = qrand1() ? HANDLE_ROCK1 : HANDLE_ROCK0;
- objects[e].state = qrand(8);
- }
- byte startgame = 0;
- for (i = 0; startgame < 50; i++) {
- for (j = 0; j < 256; j++) {
- byte index = 15 & ((-i >> 2) + (j >> 4));
- uint16_t color = pgm_read_word_near(title_ramp + 2 * index);
- GD.setpal(4 * j + 3, color);
- }
- if (!startgame &&
- (controller_sense(i) || (i == (2048 - 400)))) {
- // explode all rocks!
- for (j = 0; j < 128; j++) {
- byte h = objects[j].handler;
- if ((h == HANDLE_ROCK0) || (h == HANDLE_ROCK1))
- objects[j].handler = HANDLE_BANG1;
- objects[j].state = i;
- }
- startgame = 1;
- }
- if (startgame)
- startgame++;
- runobjects(i);
- GD.waitvblank();
- GD.wr(SPR_PAGE, (i & 1));
- GD.wr(SPR_DISABLE, 0);
- GD.wr16(SCROLL_X, i);
- if ((i & 7) == 0) {
- byte x = ((i >> 3) + 56);
- column(63 & x, 255 & x);
- }
- // if (SCREENSHOT && (i & 1)) // JCB
- // GD.screenshot(screenshot_i++); // JCB
- }
- // JCB-FADE-A
- for (i = 0; i < 32; i++) {
- for (j = 0; j < 256; j++) {
- uint16_t a = RAM_PAL + (8 * j) + 6;
- uint16_t pal = GD.rd16(a);
- byte r = 31 & (pal >> 10);
- byte g = 31 & (pal >> 5);
- byte b = 31 & pal;
- if (r) r--;
- if (g) g--;
- if (b) b--;
- pal = (r << 10) | (g << 5) | b;
- GD.wr16(a, pal);
- }
- GD.waitvblank();
- GD.waitvblank();
- }
- // JCB-FADE-B
- GD.fill(RAM_PIC, 0, 4096);
- }
- #define SOUNDCYCLE(state) ((state) = v ? ((state) + 1) : 0)
- void loop()
- {
- title_banner();
- GD_uncompress(RAM_CHR, bg_chr_compressed);
- GD_uncompress(RAM_PAL, bg_pal_compressed);
- GD.wr16(COMM+0, 0);
- GD.wr16(COMM+2, 0x8000);
- GD.wr16(COMM+4, 8); // split at line 8
- GD.wr16(COMM+6, 177);
- GD.wr16(COMM+8, 166);
- GD.wr16(COMM+10, 291); // split at line 291
- GD.wr16(COMM+12, 0);
- GD.wr16(COMM+14, 0x8000 | (0x1ff & (8 - 291))); // show line 8 at line 292
- GD.microcode(splitscreen_code, sizeof(splitscreen_code));
- setup_sprites();
- qrandSeed(34); // JCB - determinism
- randomSeed(34); // JCB
- memset(&sounds, 0, sizeof(sounds));
- level = 0;
- score = 0;
- lives = 3;
- unsigned int r = 0;
- start_level();
- while (lives) {
- int i, j;
- runobjects(r);
- for (i = 0; i < 128; i++)
- objects[i].collide = 0xff;
- // JCB-SPR_PAGE-A
- GD.waitvblank();
- // swap frames
- GD.wr(SPR_PAGE, (r & 1));
- // JCB-SPR_PAGE-B
- int scrollx = objects[0].x >> 4;
- int scrolly = objects[0].y >> 4;
- GD.wr16(COMM+6, scrollx & 0x1ff);
- GD.wr16(COMM+8, scrolly & 0x1ff);
- map_refresh(scrolly >> 6);
- update_score();
- GD.wr16(COMM+12, r); // horizontal scroll the bottom banner
- GD.waitvblank();
- GD.__start(COLLISION);
- for (i = 0; i < 256; i++) {
- byte c = SPI.transfer(0); // c is the colliding sprite number
- if (c != 0xff) {
- objects[spr2obj[i]].collide = spr2obj[c];
- }
- }
- GD.__end();
- if (SCREENSHOT) // JCB
- GD.screenshot(screenshot_i++); // JCB
- if (sounds.boing) {
- byte v = max(0, 16 - (sounds.boing - 1) * 2);
- GD.voice(0, 0, 4 * 4000 - 700 * sounds.boing, v/2, v/2);
- GD.voice(1, 1, 1000 - 100 * sounds.boing, v, v);
- SOUNDCYCLE(sounds.boing);
- }
- if (sounds.boom) {
- byte v = max(0, 96 - (sounds.boom - 1) * 6);
- GD.voice(2, 0, 220, v, v);
- GD.voice(3, 1, 220/8, v/2, v/2);
- SOUNDCYCLE(sounds.boom);
- }
- if (sounds.pop) {
- byte v = max(0, 32 - (sounds.pop - 1) * 3);
- GD.voice(4, 0, 440, v, v);
- GD.voice(5, 1, 440/8, v/2, v/2);
- SOUNDCYCLE(sounds.pop);
- }
- GD.voice(6, 1, 40, sounds.thrust ? 10 : 0, sounds.thrust ? 10 : 0);
- static byte tune;
- if (sounds.bass) {
- byte v = sounds.bass < 9 ? 63 : 0;
- int f0 = tune ? 130: 163 ;
- byte partials[] = { 71, 32, 14, 75, 20, 40 };
- byte i;
- for (i = 0; i < 6; i++) {
- byte a = (v * partials[i]) >> 8;
- GD.voice(7 + i, 0, f0 * (i + 1), a, a);
- }
- SOUNDCYCLE(sounds.bass);
- }
- static byte rhythm;
- if (++rhythm >= max(24 - level, 10)) {
- sounds.bass = 1;
- rhythm = 0;
- tune = !tune;
- }
- byte nenemies = 64;
- byte pe = enemies;
- while (pe) {
- pe = objects[pe].state;
- nenemies--;
- }
- if (nenemies == 0) {
- level++;
- start_level();
- }
- r++;
- }
- }
|