#include #include #include #include #ifdef CROSS # include # define sgttyb termio #else # include #endif /* * Assign human-readable names to some common 16-bit color values: */ #define BLACK 0x0000 #define BLUE 0x002F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF /* * Redraw every 50 msec */ #define DRAW_LOOP_INTERVAL 50 /* * Data from external font files. */ extern const struct gpanel_font_t font_lucidasans15; int wing; int fx, fy, fallRate; int pillarPos, gapPos; int score; int highScore = 0; int running = 0; int crashed = 0; int scrPress = 0; time_t nextDrawLoopRunTime; void rect(int color, int x, int y, int w, int h) { gpanel_rect(color, x, y, x+w-1, y+h-1); } void fill(int color, int x, int y, int w, int h) { gpanel_fill(color, x, y, x+w-1, y+h-1); } void drawPillar(int x, int gap) { fill(GREEN, x+2, 2, 46, gap-4); fill(GREEN, x+2, gap+92, 46, 136-gap); rect(BLACK, x, 0, 50, gap); rect(BLACK, x+1, 1, 48, gap-2); rect(BLACK, x, gap+90, 50, 140-gap); rect(BLACK, x+1, gap+91, 48, 138-gap); } void clearPillar(int x, int gap) { // "cheat" slightly and just clear the right hand pixels // to help minimise flicker, the rest will be overdrawn fill(BLUE, x+45, 0, 5, gap); fill(BLUE, x+45, gap+90, 5, 140-gap); } void clearFlappy(int x, int y) { fill(BLUE, x, y, 34, 24); } void drawFlappy(int x, int y) { // Upper & lower body fill(BLACK, x+2, y+8, 2, 10); fill(BLACK, x+4, y+6, 2, 2); fill(BLACK, x+6, y+4, 2, 2); fill(BLACK, x+8, y+2, 4, 2); fill(BLACK, x+12, y, 12, 2); fill(BLACK, x+24, y+2, 2, 2); fill(BLACK, x+26, y+4, 2, 2); fill(BLACK, x+28, y+6, 2, 6); fill(BLACK, x+10, y+22, 10, 2); fill(BLACK, x+4, y+18, 2, 2); fill(BLACK, x+6, y+20, 4, 2); // Body fill fill(YELLOW, x+12, y+2, 6, 2); fill(YELLOW, x+8, y+4, 8, 2); fill(YELLOW, x+6, y+6, 10, 2); fill(YELLOW, x+4, y+8, 12, 2); fill(YELLOW, x+4, y+10, 14, 2); fill(YELLOW, x+4, y+12, 16, 2); fill(YELLOW, x+4, y+14, 14, 2); fill(YELLOW, x+4, y+16, 12, 2); fill(YELLOW, x+6, y+18, 12, 2); fill(YELLOW, x+10, y+20, 10, 2); // Eye fill(BLACK, x+18, y+2, 2, 2); fill(BLACK, x+16, y+4, 2, 6); fill(BLACK, x+18, y+10, 2, 2); fill(BLACK, x+18, y+4, 2, 6); fill(BLACK, x+20, y+2, 4, 10); fill(BLACK, x+24, y+4, 2, 8); fill(BLACK, x+26, y+6, 2, 6); fill(BLACK, x+24, y+6, 2, 4); // Beak fill(BLACK, x+20, y+12, 12, 2); fill(BLACK, x+18, y+14, 2, 2); fill(RED, x+20, y+14, 12, 2); fill(BLACK, x+32, y+14, 2, 2); fill(BLACK, x+16, y+16, 2, 2); fill(RED, x+18, y+16, 2, 2); fill(BLACK, x+20, y+16, 12, 2); fill(BLACK, x+18, y+18, 2, 2); fill(RED, x+20, y+18, 10, 2); fill(BLACK, x+30, y+18, 2, 2); fill(BLACK, x+20, y+20, 10, 2); } // Wing down void drawWing1(int x, int y) { fill(BLACK, x, y+14, 2, 6); fill(BLACK, x+2, y+20, 8, 2); fill(BLACK, x+2, y+12, 10, 2); fill(BLACK, x+12, y+14, 2, 2); fill(BLACK, x+10, y+16, 2, 2); fill(WHITE, x+2, y+14, 8, 6); fill(BLACK, x+8, y+18, 2, 2); fill(WHITE, x+10, y+14, 2, 2); } // Wing middle void drawWing2(int x, int y) { fill(BLACK, x+2, y+10, 10, 2); fill(BLACK, x+2, y+16, 10, 2); fill(BLACK, x, y+12, 2, 4); fill(BLACK, x+12, y+12, 2, 4); fill(WHITE, x+2, y+12, 10, 4); } // Wing up void drawWing3(int x, int y) { fill(BLACK, x+2, y+6, 8, 2); fill(BLACK, x, y+8, 2, 6); fill(BLACK, x+10, y+8, 2, 2); fill(BLACK, x+12, y+10, 2, 4); fill(BLACK, x+10, y+14, 2, 2); fill(BLACK, x+2, y+14, 2, 2); fill(BLACK, x+4, y+16, 6, 2); fill(WHITE, x+2, y+8, 8, 6); fill(WHITE, x+4, y+14, 6, 2); fill(WHITE, x+10, y+10, 2, 4); } time_t millis() { struct timeval tv; gettimeofday(&tv, 0); return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); } void startGame() { fx = 50; fy = 125; fallRate = 1; pillarPos = 320; gapPos = 60; crashed = 0; score = 0; gpanel_clear(BLUE, 0, 0); gpanel_text(&font_lucidasans15, WHITE, BLUE, 5, 5, "Flappy Bird: tap to begin"); //TODO: read high score value from file. highScore = 0; char scoreLine[80]; sprintf(scoreLine, "High Score : %u", highScore); gpanel_text(&font_lucidasans15, GREEN, BLUE, 60, 60, scoreLine); // Draw Ground int tx, ty = 230; for (tx = 0; tx <= 300; tx +=20) { gpanel_fill_triangle(GREEN, tx, ty, tx+10, ty, tx, ty+10); gpanel_fill_triangle(YELLOW, tx+10, ty+10, tx+10, ty, tx, ty+10); gpanel_fill_triangle(YELLOW, tx+10, ty, tx+20, ty, tx+10, ty+10); gpanel_fill_triangle(GREEN, tx+20, ty+10, tx+20, ty, tx+10, ty+10); } } void drawLoop() { // clear moving items clearPillar(pillarPos, gapPos); clearFlappy(fx, fy); // move items if (running) { fy += fallRate; fallRate++; pillarPos -=5; if (pillarPos == 0) { score++; } else if (pillarPos < -50) { pillarPos = 320; gapPos = 20 + random() % 100; } } // draw moving items & animate drawPillar(pillarPos, gapPos); drawFlappy(fx, fy); switch (wing) { case 0: case 1: drawWing1(fx, fy); break; case 2: case 3: drawWing2(fx, fy); break; case 4: case 5: drawWing3(fx, fy); break; } wing++; if (wing == 6) wing = 0; } void checkCollision() { // Collision with ground if (fy > 206) crashed = 1; // Collision with pillar if (fx + 34 > pillarPos && fx < pillarPos + 50) if (fy < gapPos || fy + 24 > gapPos + 90) crashed = 1; if (crashed) { //TODO: use larger font. gpanel_text(&font_lucidasans15, RED, BLUE, 75, 75, "Game Over!"); char scoreLine[80]; sprintf(scoreLine, "%u", score); gpanel_text(&font_lucidasans15, RED, BLUE, 75, 125, "Score:"); gpanel_text(&font_lucidasans15, RED, BLUE, 220, 125, scoreLine); if (score > highScore) { highScore = score; gpanel_text(&font_lucidasans15, RED, BLUE, 75, 175, "NEW HIGH!"); //TODO: Write high score value to file. } // stop animation running = 0; // delay to stop any last minute clicks from restarting immediately sleep(1); } } #if 1 struct sgttyb origtty, newtty; int restore_input() { #ifdef CROSS if (newtty.c_cc[VMIN] != 0) ioctl(0, TCSETAW, &origtty); #else if (newtty.sg_flags != 0) ioctl(0, TIOCSETP, &origtty); #endif } /* * Return 1 when any key is pressed on console. */ int get_input() { #ifdef CROSS if (newtty.c_cc[VMIN] == 0) { ioctl(0, TCGETA, &origtty); newtty = origtty; newtty.c_lflag &= ~(ICANON | ECHO); newtty.c_oflag &= ~ONLCR; newtty.c_cc[VMIN] = 1; newtty.c_cc[VTIME] = 0; ioctl(0, TCSETAW, &newtty); } #else if (newtty.sg_flags == 0) { ioctl(0, TIOCGETP, &origtty); newtty = origtty; newtty.sg_flags &= ~(ECHO|CRMOD|XTABS); newtty.sg_flags |= CBREAK; ioctl(0, TIOCSETP, &newtty); } #endif int nchars = 0; ioctl(0, FIONREAD, &nchars); if (nchars <= 0) return 0; char c; read(0, &c, 1); return 1; } #endif int main() { char *devname = "/dev/tft0"; int xsize = 320, ysize = 240; if (gpanel_open(devname) < 0) { printf("Cannot open %s\n", devname); exit(-1); } gpanel_clear(BLUE, &xsize, &ysize); startGame(); nextDrawLoopRunTime = millis() + DRAW_LOOP_INTERVAL; for (;;) { if (millis() > nextDrawLoopRunTime && !crashed) { drawLoop(); checkCollision(); nextDrawLoopRunTime += DRAW_LOOP_INTERVAL; } // Get user input. int user_input = get_input(); // Process "user input" if (user_input > 0 && !scrPress) { if (crashed) { // restart game startGame(); } else if (!running) { // clear text & start scrolling gpanel_fill(BLUE, 0, 0, 320-1, 80); running = 1; } else { // fly up fallRate = -8; scrPress = 1; } } else if (user_input == 0 && scrPress) { // Attempt to throttle presses scrPress = 0; } } }