diff --git a/lib/libgpanel/Makefile b/lib/libgpanel/Makefile index 0460d42..ca410d4 100644 --- a/lib/libgpanel/Makefile +++ b/lib/libgpanel/Makefile @@ -5,8 +5,8 @@ vpath %.c $(TOPSRC)/src/libgpanel CFLAGS = -Os -B$(TOPSRC)/lib/ $(DEFS) -Wa,-x -Wall -Werror -OBJS = open.o clear.o pixel.o line.o rect.o fill.o circle.o \ - image.o char.o text.o text_width.o +OBJS = open.o clear.o pixel.o line.o rect.o fill.o fill_triangle.o \ + circle.o image.o char.o text.o text_width.o all: ../libgpanel.a diff --git a/rootfs.manifest b/rootfs.manifest index 618ccce..dd20282 100644 --- a/rootfs.manifest +++ b/rootfs.manifest @@ -1117,6 +1117,7 @@ file /share/examples/gpanel/Makefile file /share/examples/gpanel/circle.c file /share/examples/gpanel/color.c file /share/examples/gpanel/fill.c +file /share/examples/gpanel/flappy.c file /share/examples/gpanel/font.c file /share/examples/gpanel/line.c file /share/examples/gpanel/pixel.c diff --git a/share/examples/gpanel/Makefile b/share/examples/gpanel/Makefile index a89ca24..37c6a3b 100644 --- a/share/examples/gpanel/Makefile +++ b/share/examples/gpanel/Makefile @@ -1,6 +1,6 @@ CC = cc LIBS = -lgpanel -PROG = tft tftetris pixel line rect fill circle font color speed +PROG = tft tftetris pixel line rect fill circle font color speed flappy FONTS = 5x7.o 6x9.o digits20.o digits32.o lucidasans11.o lucidasans15.o \ lucidasans7.o lucidasans9.o verdana7.o @@ -37,6 +37,9 @@ color: color.c speed: speed.c lucidasans15.o $(CC) $(CFLAGS) -o $@ $(LDFLAGS) speed.c lucidasans15.o $(LIBS) +flappy: flappy.c lucidasans15.o + $(CC) $(CFLAGS) -o $@ $(LDFLAGS) flappy.c lucidasans15.o $(LIBS) + font: font.c $(FONTS) $(CC) $(CFLAGS) -o $@ $(LDFLAGS) font.c $(FONTS) $(LIBS) diff --git a/share/examples/gpanel/flappy.c b/share/examples/gpanel/flappy.c new file mode 100644 index 0000000..a4248ba --- /dev/null +++ b/share/examples/gpanel/flappy.c @@ -0,0 +1,376 @@ +#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; + } + } +}