/*
  Dinosaur Game

  Arduino implementation of Google Chrome's Dinosaur Game

  https://palmacas.com/mesa-automatica
*/

#include <U8g2lib.h>
#include <EEPROM.h>

// SSD1306 constructor using I2C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C display(U8G2_R0);

// Images
// dinosaur, 21x22px
const unsigned char bitmap_dinosaur [] PROGMEM = {
  0x00, 0xf8, 0x0f, 0x00, 0xfc, 0x1f, 0x00, 0xec, 0x1f, 0x00, 
  0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0x7e, 
  0x00, 0x01, 0xff, 0x03, 0x81, 0x3f, 0x00, 0xc3, 0x3f, 0x00, 
  0xe7, 0xff, 0x00, 0xff, 0xbf, 0x00, 0xff, 0x3f, 0x00, 0xff, 
  0x3f, 0x00, 0xfe, 0x3f, 0x00, 0xfc, 0x1f, 0x00, 0xf8, 0x0f, 
  0x00, 0xf0, 0x07, 0x00, 0xe0, 0x06, 0x00, 0x60, 0x04, 0x00, 
  0x20, 0x04, 0x00, 0x60, 0x0c, 0x00 };

// dinosaur left, 21x22px
const unsigned char bitmap_dinosaur_left [] PROGMEM = {
  0x00, 0xf8, 0x0f, 0x00, 0xfc, 0x1f, 0x00, 0xec, 0x1f, 0x00, 
  0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0x7e, 
  0x00, 0x01, 0xff, 0x03, 0x81, 0x3f, 0x00, 0xc3, 0x3f, 0x00, 
  0xe7, 0xff, 0x00, 0xff, 0xbf, 0x00, 0xff, 0x3f, 0x00, 0xff, 
  0x3f, 0x00, 0xfe, 0x3f, 0x00, 0xfc, 0x1f, 0x00, 0xf8, 0x0f, 
  0x00, 0xf0, 0x07, 0x00, 0x60, 0x06, 0x00, 0xe0, 0x04, 0x00, 
  0x00, 0x04, 0x00, 0x00, 0x0c, 0x00, };

// dinosaur right, 21x22px
const unsigned char bitmap_dinosaur_right [] PROGMEM = {
  0x00, 0xf8, 0x0f, 0x00, 0xfc, 0x1f, 0x00, 0xec, 0x1f, 0x00, 
  0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0xfc, 0x1f, 0x00, 0x7e, 
  0x00, 0x01, 0xff, 0x03, 0x81, 0x3f, 0x00, 0xc3, 0x3f, 0x00, 
  0xe7, 0xff, 0x00, 0xff, 0xbf, 0x00, 0xff, 0x3f, 0x00, 0xff, 
  0x3f, 0x00, 0xfe, 0x3f, 0x00, 0xfc, 0x1f, 0x00, 0xf8, 0x0f, 
  0x00, 0xf0, 0x07, 0x00, 0xe0, 0x0c, 0x00, 0x60, 0x00, 0x00, 
  0x20, 0x00, 0x00, 0x60, 0x00, 0x00, };

// pterodactyl up, 22x14px
const unsigned char bitmap_pterodactyl_up [] PROGMEM = {
  0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0e, 0x00, 0x00, 
  0x1e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x30, 0x7e, 
  0x00, 0x78, 0xfe, 0x00, 0xfc, 0xfe, 0x01, 0xfe, 0xfe, 0x03, 
  0xff, 0xff, 0x3f, 0xc0, 0xff, 0x07, 0x00, 0xff, 0x1f, 0x00, 
  0xfc, 0x03, };

// pterodactyl down, 22x15px
const unsigned char bitmap_pterodactyl_down [] PROGMEM = {
  0x30, 0x00, 0x00, 0x78, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfe, 
  0x00, 0x00, 0xff, 0xff, 0x3f, 0xc0, 0xff, 0x07, 0x00, 0xff, 
  0x1f, 0x00, 0xfe, 0x03, 0x00, 0xfe, 0x00, 0x00, 0x3e, 0x00, 
  0x00, 0x1e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x06, 0x00, 0x00, 
  0x06, 0x00, 0x00, 0x02, 0x00, };

// cactus  1, 12x26px
const unsigned char bitmap_cactus_1 [] PROGMEM = {
  0x60, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x04, 
  0xf2, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 
  0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 
  0xf7, 0x07, 0xf7, 0x03, 0xf7, 0x00, 0xfe, 0x00, 0xfc, 0x00, 
  0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 
  0xf0, 0x00, };

// cactus  2, 12x26px
const unsigned char bitmap_cactus_2 [] PROGMEM = {
  0x60, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf2, 0x00, 
  0xf7, 0x00, 0xf7, 0x00, 0xf7, 0x04, 0xf7, 0x0e, 0xf7, 0x0e, 
  0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 0xf7, 0x0e, 
  0xfe, 0x0e, 0xfc, 0x0e, 0xf0, 0x0e, 0xf0, 0x0e, 0xf0, 0x07, 
  0xf0, 0x03, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 
  0xf0, 0x00, };

// Button pins
#define BTN 4
int button_state = 0;
unsigned long last_press = 0;

// Buzzer pin
#define BUZ 9

// Variables
char *project = "Dinosaur Game";
char *version = "v1.0";
char *author = "palmacas 2023";
int dino_x = 10; // initial x position of the dinosaur
int dino_y = 62-23; // initial y position of the dinosaur
int jump_height = 20; // height of the dinosaur jump
int obstacle_x = 128; // initial x position of the obstacle
int obstacle_width = 12; // withd of the obstacle
int score = 0; // player's score
int game_speed = 1; // game initial speed

void setup() {
  // Project data
  display.begin();
  display.firstPage();
  do {
    display.setFont(u8g2_font_t0_15_tr);
    display.drawStr(64 - display.getStrWidth(project)/2, 15, project);
    display.drawStr(64 - display.getStrWidth(version)/2, 45, version);
    display.drawStr(64 - display.getStrWidth(author)/2, 60, author);
  } while (display.nextPage());

  // Startup sound
  pinMode(BUZ, OUTPUT);
  tone(BUZ, 1568, 100); // G6
  delay(250);
  tone(BUZ, 1047, 100); // C6
  delay(250);
  tone(BUZ, 1568, 100); // G6
  delay(100);
  noTone(BUZ);
  delay(100);

  // Splash screen
  /*display.firstPage();
  do {
    display.drawXBM(0, 0, 128, 64, splash_screen);
  } while (display.nextPage());*/
  delay(1000);

  pinMode(BTN, INPUT_PULLUP);
}

void loop() {
  button_state = readButton();

  // if button is pressed the dinosaur jumps
  if (button_state == HIGH)
  {
    dino_y -= jump_height;
  }

  // updates obstacle position
  obstacle_x -= game_speed;

  // collision detection
  if (dino_x + 21 >= obstacle_x && dino_x <= obstacle_x + obstacle_width && dino_y + 22 >= 62-26)
  {
    gameOver();
  }

  // updates score
  if (obstacle_x + obstacle_width < 0)
  {
    score++;
    obstacle_x = 128;
  }

  display.firstPage();
  do {
    display.setDrawColor(1);
    display.setBitmapMode(1);
    display.setFont(u8g2_font_t0_15_tr);
    display.setCursor(0, 10);
    display.print("Score: ");
    display.print(score);
    display.drawLine(0, 59, 128, 59); // draws the floor line
    display.drawXBMP(dino_x, dino_y, 21, 22, bitmap_dinosaur);
    display.drawXBMP(obstacle_x, 62-26, 12, 26, bitmap_cactus_1);

  } while (display.nextPage());

  //gameOver();
  delay(1);
}

// display game over message
void gameOver() {
  display.firstPage();
  do {
    display.setFont(u8g2_font_t0_15_tr);
    display.drawStr(64 - display.getStrWidth("Game Over!")/2, 20, "Game Over!");
    display.drawStr(64 - display.getStrWidth("Score: ")/2, 35, "Score: ");
    display.setCursor(60, 50);
    display.print(score);
  } while (display.nextPage());

  dino_y = 62-23;
  obstacle_x = 128;
  score = 0;

  while (button_state == LOW)
  {
    button_state = readButton();
  }
}

int readButton() {
  int btn_state = 0;
  if (digitalRead(BTN) == LOW) {
    if (millis() - last_press > 100) {
      btn_state = 1;
      playTone();
    }
    last_press = millis();
  }
  delay(1);
  return btn_state;
}

int playTone() {
  tone(BUZ, 1047, 100); // C6
  delay(100);
  noTone(BUZ);
}