Space invaders with a nRF2401A wireless joypad

Dependencies:   Gameduino mbed nRF2401A

Fork of Gameduino_Invaders_game by Chris Dick

Gameduino and an nRF2401A hooked up to an mbed on an mbeduino:

/media/uploads/TheChrisyd/2014-03-08_22.53.54.jpg

graphics.cpp

Committer:
TheChrisyd
Date:
2014-03-09
Revision:
5:3ede9991d8e0
Parent:
4:bb78bedae411

File content as of revision 5:3ede9991d8e0:

#include "game.h"

SPI spigraphics(ARD_MOSI, ARD_MISO, ARD_SCK); // mosi, miso, sclk

typedef prog_uint8_t char_data;
typedef prog_uint16_t sprite_data;

extern GDClass GD;
/*---------------------------------------------
  Graphics - made by typing in the hex... :-)
---------------------------------------------*/
static PROGMEM sprite_data invaderT[] = {
  0x0c00,  0x0c00,
  0x1e00,  0x1e00,
  0x3f00,  0x3f00,
  0x6d80,  0x6d80,
  0x7f80,  0x7f80,
  0x1200,  0x2d00,
  0x2d00,  0x4080,
  0x5280,  0x2100
};
static PROGMEM sprite_data invaderM[] = {
  0x2080,  0x2080,
  0x9120,  0x1100,
  0xbfa0,  0x3f80,
  0xeee0,  0x6ec0,
  0xffe0,  0xffe0,
  0x7fc0,  0xbfa0,
  0x2080,  0xa0a0,
  0x4040,  0x1b00
};
static PROGMEM sprite_data invaderB[] = {
  0x0f00,  0x0f00, 
  0x7fe0,  0x7fe0,
  0xfff0,  0xfff0,
  0xe670,  0xe670,
  0xfff0,  0xfff0,
  0x1980,  0x1980,
  0x36c0,  0x6660,
  0xc030,  0x30c0
};
static PROGMEM sprite_data invader_explosion[] = {
  0x0880,
  0x4510,
  0x2020,
  0x1040,
  0xc018,
  0x1040,
  0x2520,
  0x4890
};
static PROGMEM sprite_data player[] = {
  0x0200,  0x1004,  0x0200,
  0x0700,  0x8219,  0x0010,
  0x0700,  0x10c0,  0x02a0,
  0x7ff0,  0x0202,  0x1200,
  0xfff8,  0x4b31,  0x01b0,
  0xfff8,  0x21c4,  0x45a8,
  0xfff8,  0x1ff0,  0x1fe4,
  0xfff8,  0x37f2,  0x3ff5
};
static PROGMEM sprite_data bullet[] = {
  0x0200,
  0x0200,
  0x0200,
  0x0200,
  0x0000,
  0x0000,
  0x0000,
  0x0000
};
static PROGMEM sprite_data bullet_blast[] = {
  0x1120,
  0x0440,
  0x0fc0,
  0x1fe0,
  0x1fe0,
  0x0fc0,
  0x0480,
  0x1220
};
static PROGMEM sprite_data saucer[] = {
  0x0000,  0x1281,  0x4800,
  0x07e0,  0x0806,  0x1000,
  0x1ff8,  0x51e3,  0x0000,
  0x3ffc,  0x03f9,  0xc800,
  0x6db6,  0x0754,  0xe400,
  0xffff,  0x11f1,  0x8000,
  0x399c,  0x40a3,  0x1000,
  0x1008,  0x1110,  0x8000
};
static PROGMEM sprite_data saucerScore[] = {
  0x007c,  0x1038,  0x107c,  0x7c38,
  0x0040,  0x3044,  0x3040,  0x0444,
  0x0078,  0x104c,  0x1078,  0x084c,
  0x0004,  0x1054,  0x1004,  0x1854,
  0x0004,  0x1064,  0x1004,  0x0464,
  0x0044,  0x1044,  0x1044,  0x4444,
  0x0038,  0x3838,  0x3838,  0x3838,
  0x0000,  0x0000,  0x0000,  0x0000
};
static PROGMEM sprite_data saucerZero[] = {
  0x3800,
  0x4400,
  0x4c00,
  0x5400,
  0x6400,
  0x4400,
  0x3800,
  0x0000
};
static PROGMEM sprite_data zigzagBomb[] = {
  0x0200,  0x0400,  0x0200,  0x0100,
  0x0400,  0x0300,  0x0100,  0x0200,
  0x0200,  0x0100,  0x0200,  0x0400,
  0x0100,  0x0200,  0x0400,  0x0200,
  0x0200,  0x0400,  0x0200,  0x0100,
  0x0400,  0x0200,  0x0100,  0x0200,
  0x0100,  0x0100,  0x0200,  0x0400,
  0x0000,  0x0000,  0x0000,  0x0000
};
static PROGMEM sprite_data barBomb[] = {
  0x0200,  0x0200,  0x0200,  0x0700,
  0x0200,  0x0200,  0x0200,  0x0200,
  0x0200,  0x0200,  0x0700,  0x0200,
  0x0200,  0x0700,  0x0200,  0x0200,
  0x0200,  0x0200,  0x0200,  0x0200,
  0x0700,  0x0200,  0x0200,  0x0200,
  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000
};
static PROGMEM sprite_data diagBomb[] = {
  0x0200,  0x0200,  0x0200,  0x0300,
  0x0200,  0x0200,  0x0200,  0x0600,
  0x0200,  0x0600,  0x0200,  0x0200,
  0x0200,  0x0300,  0x0200,  0x0300,
  0x0200,  0x0200,  0x0200,  0x0600,
  0x0200,  0x0600,  0x0200,  0x0200,
  0x0200,  0x0300,  0x0200,  0x0200,
  0x0000,  0x0000,  0x0000,  0x0000
};
static PROGMEM sprite_data bomb_blast[] = {
  0x0100,
  0x0440,
  0x01a0,
  0x03c0,
  0x05c0,
  0x03e0,
  0x05c0,
  0x02a0
};

static PROGMEM sprite_data shield[] = {
  0x0fff, 0xc000,
  0x1fff, 0xe000,
  0x3fff, 0xf000,
  0x7fff, 0xf800,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xffff, 0xfc00,
  0xfe03, 0xfc00,
  0xfc01, 0xfc00,
  0xf800, 0xfc00,
  0xf800, 0xfc00
};

/*---------------------------------------------
  Turn raw data into Gameduino sprites
  
  We're using four color sprites so each
  sprite can contain four images. This
  function interleaves the data...
---------------------------------------------*/
void makeSprite(byte index, byte height, const sprite_data* g1, byte s1=1,  const sprite_data* g2=0, byte s2=0,  const sprite_data* g3=0, byte s3=0,  const sprite_data* g4=0, byte s4=0)
{
  const unsigned int topBit = 0x8000;
  unsigned int d1=0,d2=0,d3=0,d4=0;
  unsigned int dest = RAM_SPRIMG+(index*256);
  for (byte i=0; i<height; ++i) {              // Space invaders are only 8 pixels tall
    d1 = *g1;                          g1 += s1;
    if (g2) { d2 = *g2;  g2 += s2;  }
    if (g3) { d3 = *g3;  g3 += s3;  }
    if (g4) { d4 = *g4;  g4 += s4;  }
    for (byte j=0; j<16; ++j) {
      unsigned int m1 = (d1&topBit)>>15;
      unsigned int m2 = (d2&topBit)>>13;
      unsigned int m3 = (d3&topBit)>>11;
      unsigned int m4 = (d4&topBit)>>9;
      GD.wr(dest++,m1+m2+m3+m4);
      d1 <<= 1;
      d2 <<= 1;
      d3 <<= 1;
      d4 <<= 1;
    }
  }
}
void makeInvader(byte gr, const sprite_data *data)
{
  makeSprite(gr,8,data,2,data+1,2,invader_explosion,1);
}
void makeSp3(byte gr, const sprite_data *d)
{
  makeSprite(gr,8, d,3, d+1,3, d+2,3);
}
void makeSp4(byte gr, const sprite_data *d)
{
  makeSprite(gr,8, d,4, d+1,4, d+2,4, d+3,4);
}

/*---------------------------------------------
  Char data
---------------------------------------------*/
static PROGMEM char_data floorChar[] = {
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0xff,
  0x00
};
static PROGMEM char_data playerLeft[] = {
  0x02,
  0x07,
  0x07,
  0x7f,
  0xff,
  0xff,
  0xff,
  0xff
};
static PROGMEM char_data playerRight[] = {
  0x00,
  0x00,
  0x00,
  0xf0,
  0xf8,
  0xf8,
  0xf8,
  0xf8
};

/*---------------------------------------------
  Make a character
---------------------------------------------*/
static PROGMEM prog_uint8_t stretch[16] = {
  0x00, 0x03, 0x0c, 0x0f,
  0x30, 0x33, 0x3c, 0x3f,
  0xc0, 0xc3, 0xcc, 0xcf,
  0xf0, 0xf3, 0xfc, 0xff
};
static void makeChar(byte index, const char_data *ch, unsigned int color)
{
  // Bitmap
  GD.__wstart(RAM_CHR+(16*index));
  for (int i=0; i<8; i++) {
    byte b = *ch++;
    spigraphics.write((*stretch+(b >> 4)));
    spigraphics.write((*stretch+(b & 15)));
  }
  GD.__end();
  // Colors
  GD.setpal((4*index)+3, color);
}

/*---------------------------------------------
  Make all the game sprites
---------------------------------------------*/
void makeGraphics()
{
  // Sprites
  makeInvader(GR_INVADER_T,invaderT);
  makeInvader(GR_INVADER_M,invaderM);
  makeInvader(GR_INVADER_B,invaderB);
  makeSp3(GR_PLAYER,player);
  makeSprite(GR_BULLET,8,bullet,1,bullet_blast,1);
  makeSprite(GR_BULLET,8,bullet,1,bullet_blast,1, saucerZero,1);
  makeSp3(GR_SAUCER,saucer);
  makeSp4(GR_BOMB_ZIGZAG,zigzagBomb);
  makeSp4(GR_BOMB_BARS,  barBomb);
  makeSp4(GR_BOMB_DIAG,  diagBomb);
  makeSp4(GR_SAUCER_SCORE,saucerScore);
  makeSprite(GR_BOMB_OTHER,8,bomb_blast,1);
  GD.wr16(PALETTE4A+0,0x8000);  // Transparent
  GD.wr16(PALETTE4A+2,0x7fff);  // White
  // Charset
  GD.ascii();
  //const unsigned int red = 0x7c00;
  const unsigned int green = 0x03e0;
  makeChar(CH_FLOOR,floorChar,green);
  makeChar(CH_PLAYERL,playerLeft,green);
  makeChar(CH_PLAYERR,playerRight,green);
}


/*---------------------------------------------------
  The shields are sprites but we use them as bitmaps
  so we need to be able to shoot/bomb/rebuild them
  on demand.
---------------------------------------------------*/
void remakeShields()
{
  for (int i=0; i<4; ++i) {
    makeSprite(GR_SHIELD1+i,16,shield,2,shield+1,2);
  }
}

int8_t zapShield(int8_t n, int8_t x, bool withBullet)
{
  int y = 0;
  if ((n >= 0) and (n <= 3)) {
    n += GR_SHIELD1;
    const unsigned int spriteMem = RAM_SPRIMG+(n*256);
    int col = x+6;    // The pixels in the bullet are in column 6 of the graphic
    if (col < 0 ) col = 0;
    if (col > 31) col = 31;
    // Scan the shield and find the top/bottom pixel in column 'x'
    byte pixelMask = 0x03;
    if (col > 15) {
      pixelMask = 0x0c;
      col &= 15;
    }
    unsigned int s = spriteMem+col;
    const sprite_data *blastMap = bullet_blast;
    if (withBullet) {
      // Go from the top, find lowest pixel
      for (int8_t r=0; r<16; ++r) {
        if ((GD.rd(s)&pixelMask)!=0) {
          y = r;
        }
        s += 16;
      }
      x += 3;
      y -= 5;
    }
    else {
      // Go from the bottom, find highest pixel
      y = 16;
      s += 256;
      int8_t offset = 3;
      for (int8_t r=0; r<16; ++r) {
        s -= 16;
        // Bombs are wider...we check three columns
        if ((GD.rd(s)&pixelMask)!=0) {
          y = 15-r;
          offset = 2;
        }
        else if ((col!=0) and ((GD.rd(s-1)&pixelMask)!=0)) {
          y = 15-r;
          offset = 1;
        }
        else if ((col!=31) and ((GD.rd(s+1)&pixelMask)!=0)) {
          y = 15-r;
          offset = 3;
        }
      }
      x += offset;
      y -= 4;
      blastMap = bomb_blast;
    }
    // Blast a hole in it
    for (int8_t j=0; j<8; ++j) {  // 8 lines tall
      const int py = y+j;
      if ((py>=0) and (py<=15)) {
        unsigned int blastMask = 0x1000;
        unsigned int blastGraphic = pgm_read_word_near(blastMap);
        for (int8_t i=0; i<8; ++i) {    // 8 pixels wide...
          if ((blastGraphic&blastMask)!=0) {
            // Set shield pixel to 0 where there's a 1 in the source graphic
            int px = x+i;
            if ((px>=0) and (px<=31)) {
              byte pixelMask = 0xfc;
              if (px > 15) {
                px &= 15;
                pixelMask = 0xf3;
              }
              unsigned int s = spriteMem+(py*16)+px;
              GD.wr(s,GD.rd(s)&pixelMask);
            }
          }
          blastMask >>= 1;
        }
      }
      ++blastMap;
    }
    
  }
  return y;
}