Arnov Sharma
Published © MIT

Candy Box Gameboy

The ATTBOY is a very basic Attiny85-based game console that is housed in a candy box and runs the space invaders game.

BeginnerFull instructions provided1 hour580
Candy Box Gameboy

Things used in this project

Story

Read more

Schematics

sch

Code

ATTBOY.ino

C/C++
#include <EEPROM.h>
#include "font6x8AJ.h"
#include <avr/sleep.h>
#include <avr/interrupt.h> // needed for the additional interrupt

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define SSD1306XLED_H
#define SSD1306_SCL   PORTB4  // SCL, Pin 4 on SSD1306 Board - for webbogles board
#define SSD1306_SDA   PORTB3  // SDA, Pin 3 on SSD1306 Board - for webbogles board
#define SSD1306_SA    0x78  // Slave address

// Function prototypes
void resetAliens(void);
void drawPlatform(void);
void sendBlock(int);
void playSpaceAttack(void);
void beep(int,int);
void levelUp(int);
void drawFire(int x, int y);
void doDrawLS(long, byte);
void doDrawRS(long, byte);
void clearAlienArea(int row, int lastActive);
void doNumber (int x, int y, int value);
void ssd1306_init(void);
void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void ssd1306_setpos(uint8_t x, uint8_t y);
void ssd1306_fillscreen(uint8_t fill_Data);
void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]);

long firecounter = 0; // Timer for managing fire
long aliencounter = 0; // Timer for alien movement
int mothercounter = 0; // Timer for movement of the mothership
int level = 1; // Game level - incremented every time you clear the screen
int mothershipX = 0; // position of mothership
int mothership = 0; // is mothership active?
int mothershipWidth = 8; // mothership width in pixels
int fireXidx, fireYidx; // mapping of player fire locaiton onto array of aliens
int leftLimit; // furtherst left in x-axis pixels that the aliens are currently
int positionNow = 0; // current position of the alien array (as steps from the left)
boolean alienDirection = 1; // current direction of travel for alien swarm - 1 is right 0 is left
int alienRow = 0; // which alien row are we considering
int alienFire[5][3]; // max 5 lots of alien fire  - indices are active, xpos, ypos
int playerFire[3]; // one lot of player fire - indices are active, xpos, ypos
boolean row[4][10]; // on-off array of aliens
int firstAlien = 0; // index of first live alien in the array
int lastAlien = 8; // index of last live alien in the array
int newFirst = firstAlien; // as above when it changes
int newLast = lastAlien; // ...
int aliensDead = 0; // how many aliens have been killed in total on this level?
int lastActiveRow = 2; // what's the lowest row in which we will find live aliens?
int deadOn1 = 0; // how many aliens are dead on row one (middle row)
int deadOn2 = 0; // how many aliens are dead on row two (bottom row)

boolean fire = 0;
int topScoreB = 0;
int player; //0 to 128-platformWidth  - this is the position of the player
int platformWidth = 16; 
boolean stopAnimate = 0; // this is set to 1 when a collision is detected
boolean mute = 0;
boolean newHigh = 0;
int score = 0; // score - this affects the difficulty of the game
int top = 0;

// Interrupt handlers
ISR(PCINT0_vect){ // PB0 pin button interrupt           
   if (digitalRead(2)==1) fire = 1;
}

void playerIncSpaceAttack(){ // PB2 pin button interrupt
  if (digitalRead(0)==1) fire = 1;
}

// Arduino stuff - setup
void setup() {
  DDRB = 0b00000010;    // set PB1 as output (for the speaker)
  PCMSK = 0b00000001; // pin change mask: listen to portb bit 1
  GIMSK |= 0b00100000;  // enable PCINT interrupt 
  sei();          // enable all interrupts
}

// Arduino stuff - loop
void loop() { 
  ssd1306_init();
  ssd1306_fillscreen(0x00);
  
  // The lower case character set is seriously compromised because I've had to truncate the ASCII table
  // to release space for executable code - hence lower case y and w are remapped to h and / respectively.
  // There is no z in the table (or h!) as these aren't used anywhere in the text here and most of the 
  // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail)
  ssd1306_char_f6x8(0, 1, "A L I E N");
  ssd1306_char_f6x8(4, 2, "A T T A C K");
  ssd1306_char_f6x8(0, 4, "by A R N O V"); // see comments above !

  ssd1306_setpos(85,1);
  ssd1306_send_data_start();
  sendBlock(1);
  sendBlock(0);  
  sendBlock(1);
  ssd1306_send_data_stop();
  ssd1306_setpos(85,2);
  ssd1306_send_data_start();
  sendBlock(0);
  sendBlock(2);
  sendBlock(0);
  ssd1306_send_data_stop();
  ssd1306_setpos(85,3);
  ssd1306_send_data_start();
  sendBlock(0);
  sendBlock(0);  
  sendBlock(1);
  sendBlock(0);  
  sendBlock(1);
  ssd1306_send_data_stop();
  player = 96;
  drawPlatform();

  long startT = millis();
  long nowT =0;
  boolean sChange = 0;
  while(digitalRead(0) == HIGH) {
    nowT = millis();
    if (nowT - startT > 2000) {
      sChange = 1;     
      if (digitalRead(2) == HIGH) {
        EEPROM.write(0,0);
        EEPROM.write(1,0);
        ssd1306_char_f6x8(8, 0, "-HIGH SCORE RESET-");  
      } else if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(23, 0, "-- SOUND ON --");  }    
      break;
    }
    if (sChange == 1) break;
  }  
  while(digitalRead(0) == HIGH);


  if (sChange == 0) {
    for (byte mm = 112;mm>=1;mm--) {
      if ( (mm>=32) && (mm<56) ) drawFire(104,mm);
      if (mm == 32) {
        ssd1306_setpos(100,3);      
        ssd1306_send_data_start();
        sendBlock(0);
        ssd1306_send_data_stop();
        ssd1306_setpos(100,4);      
        ssd1306_send_data_start();
        sendBlock(0);
        ssd1306_send_data_stop();
        beep(30,100);
      }
      ssd1306_setpos(mm,0);      
      ssd1306_send_data_start();
      sendBlock(3);
      sendBlock(0);
      ssd1306_send_data_stop();
      drawPlatform();
      delay(20);
    }
    ssd1306_setpos(0,0);      
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    ssd1306_char_f6x8(0, 6, "R E T R O");  // see comments above !
    ssd1306_char_f6x8(0, 7, "/GAME CONSOLE");  // see comments above !
    delay(1500);
    ssd1306_init();
    ssd1306_fillscreen(0x00);
    stopAnimate = 0;
    score = 0;

    playSpaceAttack(); 
    top=topScoreB;
    
    ssd1306_fillscreen(0x00);
    ssd1306_char_f6x8(11, 1, "----------------");
    ssd1306_char_f6x8(11, 2, "G A M E  O V E R");
    ssd1306_char_f6x8(11, 3, "----------------");
    ssd1306_char_f6x8(37, 5, "SCORE:");
    doNumber(75, 5, score);
    if (!newHigh) {
      ssd1306_char_f6x8(21, 7, "HIGH SCORE:");
      doNumber(88, 7, top);
    }
    for (int i = 0; i<1000; i = i+ 50){
      beep(50,i);
    }
    delay(2000);
    if (newHigh) {
      ssd1306_fillscreen(0x00);
      ssd1306_char_f6x8(10, 1, "----------------");
      ssd1306_char_f6x8(10, 3, " NEW HIGH SCORE ");
      ssd1306_char_f6x8(10, 7, "----------------");
      doNumber(50,5,top);
      for (int i = 700; i>200; i = i - 50){
      beep(30,i);
      }
      newHigh = 0;
      delay(2700);    
    } 
  }
  system_sleep();
}

void doNumber (int x, int y, int value) {
    char temp[10] = {0,0,0,0,0,0,0,0,0,0};
    itoa(value,temp,10);
    ssd1306_char_f6x8(x, y, temp);
}

void ssd1306_init(void){
  DDRB |= (1 << SSD1306_SDA); // Set port as output
  DDRB |= (1 << SSD1306_SCL); // Set port as output

  ssd1306_send_command(0xAE); // display off
  ssd1306_send_command(0x00); // Set Memory Addressing Mode
  ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
  ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7
  ssd1306_send_command(0x81); // Set COM Output Scan Direction
  ssd1306_send_command(0xCF); // ---set low column address
  ssd1306_send_command(0xA1); // ---set high column address
  ssd1306_send_command(0xC8); // --set start line address
  ssd1306_send_command(0xA6); // --set contrast control register
  ssd1306_send_command(0xA8);
  ssd1306_send_command(0x3F); // --set segment re-map 0 to 127
  ssd1306_send_command(0xD3); // --set normal display
  ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64)
  ssd1306_send_command(0xD5); // 
  ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  ssd1306_send_command(0xD9); // -set display offset
  ssd1306_send_command(0xF1); // -not offset
  ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency
  ssd1306_send_command(0x12); // --set divide ratio
  ssd1306_send_command(0xDB); // --set pre-charge period
  ssd1306_send_command(0x40); // 
  ssd1306_send_command(0x20); // --set com pins hardware configuration
  ssd1306_send_command(0x02);
  ssd1306_send_command(0x8D); // --set vcomh
  ssd1306_send_command(0x14); // 0x20,0.77xVcc
  ssd1306_send_command(0xA4); // --set DC-DC enable
  ssd1306_send_command(0xA6); // 
  ssd1306_send_command(0xAF); // --turn on oled panel 
}

void ssd1306_xfer_start(void){
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
}

void ssd1306_xfer_stop(void){
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
}

void ssd1306_send_byte(uint8_t byte){
  uint8_t i;
  for(i=0; i<8; i++)
  {
    if((byte << i) & 0x80)
      DIGITAL_WRITE_HIGH(SSD1306_SDA);
    else
      DIGITAL_WRITE_LOW(SSD1306_SDA);
    
    DIGITAL_WRITE_HIGH(SSD1306_SCL);
    DIGITAL_WRITE_LOW(SSD1306_SCL);
  }
  DIGITAL_WRITE_HIGH(SSD1306_SDA);
  DIGITAL_WRITE_HIGH(SSD1306_SCL);
  DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command(uint8_t command){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
  ssd1306_send_byte(0x00);  // write command
  ssd1306_send_byte(command);
  ssd1306_xfer_stop();
}

void ssd1306_send_data_start(void){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);
  ssd1306_send_byte(0x40);  //write data
}

void ssd1306_send_data_stop(void){
  ssd1306_xfer_stop();
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
  if (y>7) return;
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  //Slave address,SA0=0
  ssd1306_send_byte(0x00);  //write command

  ssd1306_send_byte(0xb0+y);
  ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10
  ssd1306_send_byte((x&0x0f)|0x01); // |0x01

  ssd1306_xfer_stop();
}

void ssd1306_fillscreen(uint8_t fill_Data){
  uint8_t m,n;
  for(m=0;m<8;m++)
  {
    ssd1306_send_command(0xb0+m); //page0-page1
    ssd1306_send_command(0x00);   //low column start address
    ssd1306_send_command(0x10);   //high column start address
    ssd1306_send_data_start();
    for(n=0;n<128;n++)
    {
      ssd1306_send_byte(fill_Data);
    }
    ssd1306_send_data_stop();
  }
}

void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){
  uint8_t c,i,j=0;
  while(ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c >0) c = c - 12;
    if (c >15) c = c - 6;
    if (c>40) c=c-9;
    if(x>126)
    {
      x=0;
      y++;
    }
    ssd1306_setpos(x,y);
    ssd1306_send_data_start();
    for(i=0;i<6;i++)
    {
      ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i]));
    }
    ssd1306_send_data_stop();
    x += 6;
    j++;
  }
}

void system_sleep() {
  ssd1306_fillscreen(0x00);
  ssd1306_send_command(0xAE);
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON  
  ssd1306_send_command(0xAF);
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}digitalWrite(1,LOW);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}}
}



/* ------------------------
 *  SpaceAttack Code
 */
void playSpaceAttack() {

  firecounter = 0; // Timer for managing fire
  level = 1; // Game level - incremented every time you clear the screen
  topScoreB = 0; // highscore
  score = 0; // obvious
  
  // Initialisations
  for (byte i = 0; i<5;i++) {
    alienFire[i][0] = 0;
  }
  resetAliens();

  levelUp(1); // This also does various essential initialisations
  
  attachInterrupt(0,playerIncSpaceAttack,CHANGE);

  while (stopAnimate == 0) {
    while(1) {
    aliencounter++;
    firecounter++;
    mothercounter++;
    
    // deal with inputs
    if(analogRead(0) < 940) fire =1; // this reads the reset pin (pin 1) of the Attiny85 - to use it, see comments section above

    if(digitalRead(2)==1) {
      if (digitalRead(0)==1) fire = 1;
      if(player < 127-platformWidth) {
        player++;
        } 
    }
    if (digitalRead(0)==1){
      if (digitalRead(2)==1) fire = 1;
      if (player >1){
        player--;
        } 
     }

    if ( (mothership == 0) && (random(0,1000) > 998) && (alienRow>0) ) {
      mothership = 1;
      mothershipX= 127-16;
    }

    // draw aliens
    for (int inc = 0; inc <= lastActiveRow; inc ++) {
      ssd1306_setpos((positionNow*8),inc+alienRow);
      ssd1306_send_data_start();
      for (int bl = firstAlien; bl <=lastAlien; bl++){
        if(row[inc][bl]==1){
          if (inc == lastActiveRow) {
            if(random(0,1000) > (999-level)) { // this alien is going to fire!
              byte afIndex = 0;
              while (alienFire[afIndex][0] == 1) {
                afIndex++; // search for empty alien fire option
                if (afIndex == 5) break;
              } 
              if (afIndex < 5) { // we've found a slot
                alienFire[afIndex][0] = 1; // activate fire on this slot
                alienFire[afIndex][1] = ( (positionNow*8) + ((bl-firstAlien) * 8) + 4); // x position
                alienFire[afIndex][2] = (inc+alienRow+1)*8; // Where the fire starts
              }
            } // end of this alien firing
          } // only if we are on the lowest row of live aliens
          if ( (inc == 0) || (inc == 2) ) {
            sendBlock(1);
          } else {
            sendBlock(2);
          }
        }else {
          sendBlock(0);
        }
       }   
      ssd1306_send_data_stop();
    }


    // Display the score
    doNumber(0,6,score);
    
    // Burn clock cycles to keep game at constant (ish) speed when there are low numbers of live aliens 
    int burnLimit = (8-(lastAlien-firstAlien));
    for (int burn = 0; burn < burnLimit; burn+=2) {                  
        drawPlatform();    
        }

    // Display the mothership
    if (mothercounter >= 3) {
      mothercounter = 0;    
      // draw mothership
      if (mothership) {
        ssd1306_setpos(mothershipX,0);
        ssd1306_send_data_start();
        sendBlock(3);
        sendBlock(0);
        ssd1306_send_data_stop();
        mothershipX --;
        if (mothershipX == 0) {
          mothership = 0;                      
          ssd1306_setpos(mothershipX,0);
          ssd1306_send_data_start();
          sendBlock(0);
          sendBlock(0);
          ssd1306_send_data_stop();
        }
      }
    }

    // Move the aliens
    if (aliencounter >= (92-((level-1)*5)) ) {
        aliencounter = 0;
      if(alienDirection) { // Moving right
        // move down a row
        if (positionNow >= 6+(8-(lastAlien-firstAlien))) {
          alienDirection = 0;
          clearAlienArea(alienRow,lastActiveRow);                                   
          alienRow++;
        } else {
          positionNow++;
        }
      } else { // Moving left
        // move down a row
        if (positionNow <= 0) {
          alienDirection = 1;
          clearAlienArea(alienRow,lastActiveRow);                                   
          alienRow++;
        } else {
          positionNow --;
        }
      } 
      clearAlienArea(alienRow,lastActiveRow);                                   
    }

    // Fire !
    if ((fire == 1) && (playerFire[0] == 0)) { // fire has been pressed and we're not currently firing - initiate fire!!
      playerFire[0] = 1;
      playerFire[1] = player+platformWidth/2; // xposition of new fire!
      playerFire[2] = 56;
    }

    // Handle all firing-related stuff (in both directions!)
    if (firecounter >= 2) {                  
      firecounter = 0;
      // --- Deal with player Firing --- 
      if (playerFire[0] == 1) {                      
          drawFire(playerFire[1], playerFire[2]);
          if (playerFire[2] == 0) {
            ssd1306_setpos(playerFire[1],0);
            uint8_t temp = B00000000;
            ssd1306_send_data_start();
            ssd1306_send_byte(temp);  
            ssd1306_send_data_stop();                                                          
            playerFire[0] = 0;
            fire = 0;
          } else {
            playerFire[2] = playerFire[2] - 1;                          
          }
      }
      
      // aliens are at positionNow * 8 + 8* their index
      
      leftLimit = positionNow*8;
      if ((fire == 1)) {
        fireXidx = firstAlien + floor((playerFire[1]-positionNow*8)/8);                    
        fireYidx=  floor(floor(playerFire[2]/8) - alienRow);
        
        if((mothership == 1) && (playerFire[1] >= mothershipX) && (playerFire[1] <= mothershipX +8) && playerFire[2] <=8) {
          long scm = random(1,100);
          if (scm<30) {
            score +=50;
          } else if (scm<60) {
            score += 100;
          } else if (scm<90) {
            score += 150;
          } else {
            score += 300;
          }
            
          beep(30,400);
          beep(30,300);          
          beep(30,200);
          beep(30,100);
          mothership = 0;
          ssd1306_setpos(mothershipX,0);
          ssd1306_send_data_start();
          sendBlock(0);
          sendBlock(0);
          ssd1306_send_data_stop();
        }

        // Alien has been hit
        if ((playerFire[1] >= leftLimit) && (fireYidx>=0) && (fireYidx<=lastActiveRow) && (fireXidx>=0) && (fireXidx<9)) {                    
          if (row[fireYidx][fireXidx] == 1) { 

            int lastActiveToClear = lastActiveRow; // if we kill the last alien on a row - we still need to clear that row (end of this fn)
            
            if (fireYidx == 2) deadOn2++;
            if (fireYidx == 1) deadOn1++;
            
            if (deadOn2 == 5) {lastActiveRow = 1;}
            if ((deadOn1 == 4) && (deadOn2 == 5)) {lastActiveRow = 0;}

            score = score + (int)((3-fireYidx) * 10);
                        
            aliensDead++;                      
            ssd1306_setpos(playerFire[1],alienRow+fireYidx+1);
            uint8_t temp = B00000000;
            ssd1306_send_data_start();
            ssd1306_send_byte(temp);  
            ssd1306_send_data_stop();                                                          
            beep(30,100);
            
            fire = 0;
            playerFire[0] = 0;
            playerFire[1] = 0;
            playerFire[2] = 7;
            row[fireYidx][fireXidx] = 0;
            
            if (fireXidx == firstAlien) { // this is the first Alien - sweep back and reset to new first alien
              for (int xi = lastAlien; xi>=firstAlien;xi--) {
                if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newFirst = xi;
              }
              positionNow += newFirst - firstAlien;
              firstAlien = newFirst;
            }
            
            if (fireXidx == lastAlien) { // this is the last Alien - sweep thru and reset to new last alien
              for (int xi = firstAlien; xi<=lastAlien;xi++) {
                if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newLast = xi;
              }
              lastAlien = newLast;
            }
            clearAlienArea(alienRow,lastActiveToClear);
                                               
          }         
        }
      }


      // --- Deal with Alien Firing --- 
      for (byte afIndex = 0; afIndex<5; afIndex++) {
        if(alienFire[afIndex][0] == 1) {
          drawFire(alienFire[afIndex][1],alienFire[afIndex][2]);
          alienFire[afIndex][2] = alienFire[afIndex][2] + 1;

          if (alienFire[afIndex][2] >= 56) {
            ssd1306_setpos(alienFire[afIndex][1],7);
            uint8_t temp = B00000000;
            ssd1306_send_data_start();
            ssd1306_send_byte(temp);  
            ssd1306_send_data_stop();                                                          
            alienFire[afIndex][0] = 0; // the fire's got to the end
            if( ((alienFire[afIndex][1]) > player) && ( (alienFire[afIndex][1]) < player+platformWidth) ) { // you've been hit!!
              stopAnimate = 1;
              goto die;
              }
          }
        }                  
      } // end of aliens firing                  
    }

    if (aliensDead == 14) {
      for (int ai = 0; ai <= 5; ai++) alienFire[ai][0] = 0;

      level++;
      if (level>15) level = 15;
      levelUp(level);

      resetAliens();
    }
    
    if( ((alienRow == 5) && (lastActiveRow == 2))||  ( (alienRow == 6) && (lastActiveRow == 1)) || ((alienRow == 7) && (lastActiveRow == 0)) || stopAnimate) {
      stopAnimate = 1;
      break;
    }
    // draw the player
    drawPlatform();
    }
 }
die:
  topScoreB = EEPROM.read(0);
  topScoreB = topScoreB << 8;
  topScoreB = topScoreB |  EEPROM.read(1);

  if (score > topScoreB) { 
    topScoreB = score;
    EEPROM.write(1,score & 0xFF); 
    EEPROM.write(0,(score>>8) & 0xFF); 
    newHigh = 1;
    }
  }

void drawPlatform(){
 ssd1306_setpos(player,7);
 ssd1306_send_data_start();
 ssd1306_send_byte(B00000000);   
 ssd1306_send_byte(B00000000);   
 for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);}                
 for (byte pw = 0; pw <4; pw++){ssd1306_send_byte(B11110000);}                
 for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);}                
 ssd1306_send_byte(B00000000);   
 ssd1306_send_byte(B00000000);   
 ssd1306_send_data_stop();  
}

void sendBlock(int fill){
  if (fill == 1) {
   ssd1306_send_byte(B10011000);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10011000);
  } else if (fill == 2) {
   ssd1306_send_byte(B00110000);
   ssd1306_send_byte(B00111110);
   ssd1306_send_byte(B10110011);
   ssd1306_send_byte(B01011101);
   ssd1306_send_byte(B01011101);
   ssd1306_send_byte(B10110011);
   ssd1306_send_byte(B00111110);
   ssd1306_send_byte(B00110000);  
  } else if (fill == 3) {
   ssd1306_send_byte(B00011000);
   ssd1306_send_byte(B00111000);
   ssd1306_send_byte(B00110100);
   ssd1306_send_byte(B00110100);
   ssd1306_send_byte(B00110100);
   ssd1306_send_byte(B00110100);
   ssd1306_send_byte(B00111000);
   ssd1306_send_byte(B00011000);  
  } else if (fill == 0) {
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
  } 
}

void levelUp(int number) {

  fire = 0; // make sure no fire
  playerFire[0] = 0; // player fire inactive
  aliencounter=0;
  firecounter=0;
  mothercounter=0;
  firstAlien = 0;
  lastAlien = 8;
  newFirst = 0;
  newLast = 8;
  lastActiveRow = 2;
  deadOn1 = 0;
  deadOn2 = 0;
  aliensDead = 0;      
  mothership = 0;
  alienRow = 0;
  positionNow = 0;
  alienDirection = 1;
  player = 64;

  ssd1306_fillscreen(0x00);
  ssd1306_char_f6x8(16, 3, "--------------");
  ssd1306_char_f6x8(16, 4, " L E V E L ");
  ssd1306_char_f6x8(16, 5, "--------------");
  doNumber(85,4,number);
  for (int i = 800; i>200; i = i - 200){
  beep(30,i);
  }
  delay(700);    
  ssd1306_fillscreen(0x00);
}

void drawFire(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,y%8);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    doDrawRS(0,8-y%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,0);
    ssd1306_send_data_stop();
  }
}

// Drawing routine for the player and fire - with right-shifts
void doDrawRS(long P1, byte P2) {
  ssd1306_send_byte((B01111110 | P1)>>P2);
}

// Drawing routine for the player and fire - with left-shifts
void doDrawLS(long P1, byte P2) {
  ssd1306_send_byte((B01111110 | P1)<<P2);
}

void clearAlienArea(int row, int lastActive) {
  // clear alien area
  for (int inc = row; inc <=row+lastActive; inc ++) {
    ssd1306_setpos(0,inc);
    ssd1306_send_data_start();
    for (int bl = 0; bl <16; bl++){
      sendBlock(0);
      }   
      ssd1306_send_data_stop();
    }
}

void resetAliens(void) {
  for (byte i =0; i<10;i++){ // reset aliens
    row[0][i]=0; row[1][i]=0; row[2][i]=0;
  } 
  for (byte i =0; i<9;i+=2){ // reset aliens
    row[0][i]=1; row[2][i]=1;
  } 
  for (byte i =1; i<9;i+=2){ // reset aliens
    row[1][i]=1;
  }     
}

font6x8AJ.h

C Header File
/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 *
 * @file: font6x8.h
 * @created: 2014-08-12
 * @author: Neven Boyanov
 * 
 * Hacked by andy jackson to allow two games (originally by webboggles.com) to 
 * fit onto an ATTiny85 at the same time - hence several characters are missing
 * and a couple have been moved to limit the amount of software remapping required
 * to map ASCII values onto locations in this array.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ----------------------------------------------------------------------------

#include <avr/pgmspace.h>

// ----------------------------------------------------------------------------

/* Standard ASCII 6x8 font */
static const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
/*
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
  */
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w in place of /
  //0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
/*
  0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
  0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
  0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
  0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
  0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
 */
  0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
  0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
  0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
  0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
  0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
  0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
  0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
  0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
  0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
  0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
  0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
  0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
  0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
  0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
  0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
  0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
  0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
/*
  0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
  0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
  0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
  0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
  0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
  0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
  0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
  0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
  0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
*/
  0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
  0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
  0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
  0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
  0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
  0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
  0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
  0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y (in place of h)
  //0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
  0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
  0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
  0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
  0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
  0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
  0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
  0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
  0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
  0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
  0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
  0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
  0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
 // 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
 // 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
 // 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C // y
};

// ----------------------------------------------------------------------------

Credits

Arnov Sharma

Arnov Sharma

308 projects • 306 followers
Just your average MAKER

Comments