root/modules/games/gui_tetris.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mkdir_if_not_exist
  2. platformInit
  3. platformEnd
  4. platformReadInput
  5. platformRenderGame
  6. platformGetSystemTime
  7. setMatrixCells
  8. setTetramino
  9. startGame
  10. createGame
  11. gameInit
  12. gameEnd
  13. deleteGame
  14. rotateTetramino
  15. checkCollision
  16. onFilledRows
  17. moveTetramino
  18. dropTetramino
  19. gameUpdate
  20. gui_tetris_init
  21. basic_module_init
  22. gui_tetris_draw
  23. gui_tetris_kbd_process

   1 #include "camera_info.h"
   2 #include "keyboard.h"
   3 #include "clock.h"
   4 #include "backlight.h"
   5 #include "lang.h"
   6 #include "gui.h"
   7 #include "gui_draw.h"
   8 #include "gui_lang.h"
   9 #include "gui_batt.h"
  10 #include "gui_mbox.h"
  11 #include "modes.h"
  12 #include "time.h"
  13 
  14 #include "module_def.h"
  15 
  16 void gui_game_menu_kbd_process();
  17 int gui_tetris_kbd_process();
  18 void gui_tetris_draw();
  19 
  20 gui_handler GUI_MODE_TETRIS = 
  21     /*GUI_MODE_TETRIS*/ { GUI_MODE_MODULE, gui_tetris_draw, gui_tetris_kbd_process, gui_game_menu_kbd_process, 0, GUI_MODE_FLAG_NODRAWRESTORE };
  22 
  23 #define BOARD_WIDTH     (10)
  24 #define BOARD_HEIGHT    (22)
  25 /* Initial time delay (in miliseconds) between falling moves */
  26 #define INI_DELAY_FALL  (540)
  27 /* Score points given by filled rows */
  28 #define SCORE_1_FILLED_ROW  (40)
  29 #define SCORE_2_FILLED_ROW  (100)
  30 #define SCORE_3_FILLED_ROW  (300)
  31 #define SCORE_4_FILLED_ROW  (1200)
  32 /* Number of filled rows required to increase the game level */
  33 #define FILLED_ROWS_FOR_LEVEL_UP    (10)
  34 /* The falling delay is multiplied by this factor with every level up */
  35 #define DELAY_FACTOR_FOR_LEVEL_UP   (0.9)
  36 
  37 #define GAME_ERROR_NONE         (0)     /* Everything is OK, oh wonders!      */
  38 #define GAME_ERROR_USER_QUITS   (1)     /* The user quits (bored?), our fail  */
  39 #define GAME_ERROR_NO_MEMORY    (-1)    /* Not enough memory                  */
  40 #define GAME_ERROR_NO_VIDEO     (-2)    /* Video system was not initialized   */
  41 #define GAME_ERROR_NO_IMAGES    (-3)    /* Problem loading the image files    */
  42 #define GAME_ERROR_ASSERT       (-100)  /* Something went very very wrong...  */
  43 
  44 #define EVENT_NONE          (0)
  45 #define EVENT_MOVE_DOWN     (1 << 1)
  46 #define EVENT_MOVE_LEFT     (1 << 2)
  47 #define EVENT_MOVE_RIGHT    (1 << 3)
  48 #define EVENT_ROTATE_CW     (1 << 4)    /* rotate clockwise         */
  49 #define EVENT_ROTATE_CCW    (1 << 5)    /* rotate counter-clockwise */
  50 #define EVENT_DROP          (1 << 6)
  51 #define EVENT_PAUSE         (1 << 7)
  52 #define EVENT_RESTART       (1 << 8)
  53 #define EVENT_SHOW_NEXT     (1 << 9)    /* toggle show next tetromino */
  54 // Tetromino definitions (used as indexes: must be between 0-6)
  55 #define TETROMINO_I     (0)
  56 #define TETROMINO_O     (1)
  57 #define TETROMINO_T     (2)
  58 #define TETROMINO_S     (3)
  59 #define TETROMINO_Z     (4)
  60 #define TETROMINO_J     (5)
  61 #define TETROMINO_L     (6)
  62 /* Tetromino colors */
  63 #define TETRIS_COLOR_CYAN      COLOR_CYAN
  64 #define TETRIS_COLOR_RED       COLOR_RED
  65 #define TETRIS_COLOR_BLUE      COLOR_BLUE
  66 #define TETRIS_COLOR_ORANGE    COLOR_WHITE
  67 #define TETRIS_COLOR_GREEN     COLOR_GREEN
  68 #define TETRIS_COLOR_YELLOW    COLOR_YELLOW
  69 #define TETRIS_COLOR_PURPLE    COLOR_MAGENTA
  70 #define EMPTY_CELL  (-1)
  71 /* screen colors */
  72 #define TETRIS_COLOR_BG        COLOR_GREY_DK
  73 #define TETRIS_COLOR_BOARD     COLOR_GREY
  74 #define TETRIS_COLOR_TEXT      COLOR_WHITE
  75 
  76 typedef struct StcTetramino {
  77     int cells[4][4];
  78     int x;
  79     int y;
  80     int size;
  81     int type;
  82 } StcTetramino;
  83 
  84 typedef struct StcPlatform StcPlatform;
  85 
  86 typedef struct StcGame {
  87     int map[BOARD_WIDTH][BOARD_HEIGHT];
  88 
  89     StcTetramino nextBlock;     /* next tetromino               */
  90     StcTetramino fallingBlock;  /* current falling tetromino    */
  91     StcPlatform *platform;      /* platform hidden data         */
  92     int errorCode;              /* game error code              */
  93     long systemTime;            /* system time in miliseconds   */
  94     int delay;          /* delay time for falling tetrominoes   */
  95     int isOver;         /* 1 if the game is over, 0 otherwise   */
  96     int isPaused;       /* 1 if the game is paused, 0 otherwise */
  97     int showPreview;    /* 1 if we must show preview tetramino  */
  98     long lastFallTime;  /* last time the game moved the falling tetromino */
  99 
 100     int events;
 101 
 102     struct {
 103         long score;         /* user score for current game      */
 104         long high;          /* high score                       */
 105         int lines;          /* total number of lines cleared    */
 106         int totalPieces;    /* total number of tetraminoes used */
 107         int pieces[7];      /* number of tetraminoes per type   */
 108         int level;          /* current game level               */
 109     } stats;
 110 } StcGame;
 111 
 112 StcGame *createGame();
 113 void deleteGame(StcGame *pGame);
 114 int gameInit(StcGame *gameInstance);
 115 void gameEnd();
 116 void gameUpdate(StcGame *gameInstance);
 117 
 118 StcGame *game = 0;
 119 
 120 // No need to import such simple
 121 long mkdir_if_not_exist(const char *dirname) 
 122 {
 123     // Check if directory exists and create it if it does not.
 124     if (stat(dirname,0) != 0) return mkdir(dirname);
 125     return 0;   // Success
 126 }
 127 
 128 int platformInit() { return GAME_ERROR_NONE; }
 129 void platformEnd() {}
 130 
 131 /* Read input device and notify game */
 132 void platformReadInput() {}
 133 
 134 int tile_size;
 135 #define PREVIEW_X (tile_size*15)
 136 #define PREVIEW_Y (tile_size)
 137 #define BOARD_X (tile_size)
 138 #define BOARD_Y (tile_size)
 139 #define TILE_SIZE (tile_size)
 140 /* Render the state of the game */
 141 int tmp[BOARD_WIDTH][BOARD_HEIGHT];
 142 int tmp2[BOARD_WIDTH][BOARD_HEIGHT];
 143 
 144 int prevNextBlockType = -1;
 145 void platformRenderGame()
 146 {
 147     int i, j;
 148 
 149     for (i = 0; i < BOARD_WIDTH; ++i)
 150     {
 151         for (j = 0; j < BOARD_HEIGHT; ++j)
 152         {
 153             tmp[i][j] = EMPTY_CELL;
 154             tmp2[i][j] = EMPTY_CELL;
 155         }
 156     }
 157 
 158     /* Draw preview block */
 159     if (game->nextBlock.type != prevNextBlockType)
 160     {
 161         prevNextBlockType = game->nextBlock.type;
 162         for (i = 0; i < 4; ++i)
 163         {
 164             for (j = 0; j < 4; ++j)
 165             {
 166                 if (game->nextBlock.cells[i][j] != EMPTY_CELL)
 167                 {
 168                     draw_rectangle(camera_screen.disp_left + PREVIEW_X + (TILE_SIZE * i),
 169                                    PREVIEW_Y + (TILE_SIZE * j),
 170                                    camera_screen.disp_left + PREVIEW_X + (TILE_SIZE * i) + TILE_SIZE - 1,
 171                                    PREVIEW_Y + (TILE_SIZE * j) + TILE_SIZE - 1,
 172                                    MAKE_COLOR(game->nextBlock.cells[i][j], game->nextBlock.cells[i][j]), RECT_BORDER0 | DRAW_FILLED);
 173                 }
 174                 else
 175                 {
 176                     draw_rectangle(camera_screen.disp_left + PREVIEW_X + (TILE_SIZE * i),
 177                                    PREVIEW_Y + (TILE_SIZE * j),
 178                                    camera_screen.disp_left + PREVIEW_X + (TILE_SIZE * i) + TILE_SIZE - 1,
 179                                    PREVIEW_Y + (TILE_SIZE * j) + TILE_SIZE - 1,
 180                                    MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_BG), RECT_BORDER0 | DRAW_FILLED);
 181                 }
 182             }
 183         }
 184     }
 185 
 186     /* Draw the cells in the board */
 187     for (i = 0; i < BOARD_WIDTH; ++i)
 188     {
 189         for (j = 0; j < BOARD_HEIGHT; ++j)
 190         {
 191             if (game->map[i][j] != EMPTY_CELL)
 192             {
 193                 tmp2[i][j] = game->map[i][j];
 194             }
 195         }
 196     }
 197     /* Draw falling tetromino */
 198     for (i = 0; i < 4; ++i)
 199     {
 200         for (j = 0; j < 4; ++j)
 201         {
 202             if (game->fallingBlock.cells[i][j] != EMPTY_CELL)
 203             {
 204                 tmp[i + game->fallingBlock.x][j + game->fallingBlock.y] = game->fallingBlock.cells[i][j];
 205             }
 206         }
 207     }
 208 
 209     for (i = 0; i < BOARD_WIDTH; ++i)
 210     {
 211         for (j = 0; j < BOARD_HEIGHT; ++j)
 212         {
 213             if (tmp[i][j] != EMPTY_CELL)
 214             {
 215                 draw_rectangle(camera_screen.disp_left + BOARD_X + (TILE_SIZE * i),
 216                                BOARD_Y + (TILE_SIZE * j),
 217                                camera_screen.disp_left + BOARD_X + (TILE_SIZE * i) + TILE_SIZE - 1,
 218                                BOARD_Y + (TILE_SIZE * j) + TILE_SIZE - 1,
 219                                MAKE_COLOR(tmp[i][j], tmp[i][j]), RECT_BORDER0 | DRAW_FILLED);
 220             }
 221             else if (tmp2[i][j] != EMPTY_CELL)
 222             {
 223                 draw_rectangle(camera_screen.disp_left + BOARD_X + (TILE_SIZE * i),
 224                                BOARD_Y + (TILE_SIZE * j),
 225                                camera_screen.disp_left + BOARD_X + (TILE_SIZE * i) + TILE_SIZE - 1,
 226                                BOARD_Y + (TILE_SIZE * j) + TILE_SIZE - 1,
 227                                MAKE_COLOR(tmp2[i][j], tmp2[i][j]), RECT_BORDER0 | DRAW_FILLED);
 228             }
 229             else
 230             {
 231                 draw_rectangle(camera_screen.disp_left + BOARD_X + (TILE_SIZE * i),
 232                                BOARD_Y + (TILE_SIZE * j),
 233                                camera_screen.disp_left + BOARD_X + (TILE_SIZE * i) + TILE_SIZE - 1,
 234                                BOARD_Y + (TILE_SIZE * j) + TILE_SIZE - 1,
 235                                MAKE_COLOR(TETRIS_COLOR_BOARD, TETRIS_COLOR_BOARD), RECT_BORDER0 | DRAW_FILLED);
 236             }
 237         }
 238     }
 239 
 240     /* output game info */
 241     int tx = camera_screen.disp_right - 22 * FONT_WIDTH;
 242     twoColors cl = MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT);
 243     int yo = FONT_HEIGHT;
 244     char str_buf[100];
 245     static struct tm *ttm;
 246     sprintf(str_buf, "High:    %5d", game->stats.high);
 247     draw_string(tx, camera_screen.height - yo * 11, str_buf, cl);
 248     sprintf(str_buf, "Points:  %5d", game->stats.score);
 249     draw_string(tx, camera_screen.height - yo * 10, str_buf, cl);
 250     sprintf(str_buf, "Lines:   %5d", game->stats.lines);
 251     draw_string(tx, camera_screen.height - yo * 9, str_buf, cl);
 252     sprintf(str_buf, "Level:   %5d", game->stats.level);
 253     draw_string(tx, camera_screen.height - yo * 8, str_buf, cl);
 254     sprintf(str_buf, "UP  -> Pause");
 255     draw_string(tx, camera_screen.height - yo * 6, str_buf, cl);
 256     sprintf(str_buf, "SET -> Rotate");
 257     draw_string(tx, camera_screen.height - yo * 5, str_buf, cl);
 258 
 259     ttm = get_localtime();
 260     sprintf(str_buf, "Time:    %2u:%02u", ttm->tm_hour, ttm->tm_min);
 261     draw_string(tx, camera_screen.height - yo * 3, str_buf, cl);
 262     sprintf(str_buf, "Batt:     %3d%%", get_batt_perc());
 263     draw_string(tx, camera_screen.height - yo * 2, str_buf, cl);
 264 }
 265 
 266 /* Return the current system time in milliseconds */
 267 long platformGetSystemTime() { return get_tick_count(); }
 268 
 269 /* Set matrix elements to indicated value */
 270 static void setMatrixCells(int *matrix, int width, int height, int value)
 271 {
 272     int i, j;
 273     for (i = 0; i < width; ++i)
 274     {
 275         for (j = 0; j < height; ++j)
 276         {
 277             *(matrix + i + (j * width)) = value;
 278         }
 279     }
 280 }
 281 
 282 /* Initialize tetromino cells for every tipe of tetromino */
 283 static void setTetramino(int indexTetramino, StcTetramino *tetramino)
 284 {
 285 
 286     /* Initialize tetromino cells to empty cells */
 287     setMatrixCells(&tetramino->cells[0][0], 4, 4, EMPTY_CELL);
 288 
 289     /* Almost all the blocks have size 3 */
 290     tetramino->size = 3;
 291 
 292     /* Initial configuration from: http://www.tetrisconcept.com/wiki/index.php/SRS */
 293     switch (indexTetramino)
 294     {
 295         case TETROMINO_I:
 296             tetramino->cells[0][1] = TETRIS_COLOR_CYAN;
 297             tetramino->cells[1][1] = TETRIS_COLOR_CYAN;
 298             tetramino->cells[2][1] = TETRIS_COLOR_CYAN;
 299             tetramino->cells[3][1] = TETRIS_COLOR_CYAN;
 300             tetramino->size = 4;
 301             break;
 302         case TETROMINO_O:
 303             tetramino->cells[0][0] = TETRIS_COLOR_YELLOW;
 304             tetramino->cells[0][1] = TETRIS_COLOR_YELLOW;
 305             tetramino->cells[1][0] = TETRIS_COLOR_YELLOW;
 306             tetramino->cells[1][1] = TETRIS_COLOR_YELLOW;
 307             tetramino->size = 2;
 308             break;
 309         case TETROMINO_T:
 310             tetramino->cells[0][1] = TETRIS_COLOR_PURPLE;
 311             tetramino->cells[1][0] = TETRIS_COLOR_PURPLE;
 312             tetramino->cells[1][1] = TETRIS_COLOR_PURPLE;
 313             tetramino->cells[2][1] = TETRIS_COLOR_PURPLE;
 314             break;
 315         case TETROMINO_S:
 316             tetramino->cells[0][1] = TETRIS_COLOR_GREEN;
 317             tetramino->cells[1][0] = TETRIS_COLOR_GREEN;
 318             tetramino->cells[1][1] = TETRIS_COLOR_GREEN;
 319             tetramino->cells[2][0] = TETRIS_COLOR_GREEN;
 320             break;
 321         case TETROMINO_Z:
 322             tetramino->cells[0][0] = TETRIS_COLOR_RED;
 323             tetramino->cells[1][0] = TETRIS_COLOR_RED;
 324             tetramino->cells[1][1] = TETRIS_COLOR_RED;
 325             tetramino->cells[2][1] = TETRIS_COLOR_RED;
 326             break;
 327         case TETROMINO_J:
 328             tetramino->cells[0][0] = TETRIS_COLOR_BLUE;
 329             tetramino->cells[0][1] = TETRIS_COLOR_BLUE;
 330             tetramino->cells[1][1] = TETRIS_COLOR_BLUE;
 331             tetramino->cells[2][1] = TETRIS_COLOR_BLUE;
 332             break;
 333         case TETROMINO_L:
 334             tetramino->cells[0][1] = TETRIS_COLOR_ORANGE;
 335             tetramino->cells[1][1] = TETRIS_COLOR_ORANGE;
 336             tetramino->cells[2][0] = TETRIS_COLOR_ORANGE;
 337             tetramino->cells[2][1] = TETRIS_COLOR_ORANGE;
 338             break;
 339     }
 340     tetramino->type = indexTetramino;
 341 }
 342 
 343 /*  Start a new game */
 344 static void startGame(StcGame *game)
 345 {
 346     int i;
 347 
 348     /* Initialize game data */
 349     game->errorCode = GAME_ERROR_NONE;
 350     game->systemTime = platformGetSystemTime();
 351     game->lastFallTime = game->systemTime;
 352     game->isOver = 0;
 353     game->isPaused = 0;
 354     game->showPreview = 1;
 355     game->events = EVENT_NONE;
 356     game->delay = INI_DELAY_FALL;
 357     /* Initialize game statistics */
 358     game->stats.score = 0;
 359     game->stats.lines = 0;
 360     game->stats.totalPieces = 0;
 361     game->stats.level = 0;
 362     for (i = 0; i < 7; ++i)
 363     {
 364         game->stats.pieces[i] = 0;
 365     }
 366 
 367     /* Initialize rand generator */
 368     srand(game->systemTime);
 369 
 370     /* Initialize game tile map */
 371     setMatrixCells(&game->map[0][0], BOARD_WIDTH, BOARD_HEIGHT, EMPTY_CELL);
 372 
 373     /* Initialize falling tetromino */
 374     setTetramino(rand() % 7, &game->fallingBlock);
 375     game->fallingBlock.x = (BOARD_WIDTH - game->fallingBlock.size) / 2;
 376     game->fallingBlock.y = 0;
 377 
 378     /* Initialize preview tetromino */
 379     setTetramino(rand() % 7, &game->nextBlock);
 380 }
 381 
 382 /* Create new game object */
 383 StcGame *createGame()
 384 {
 385     /* Allocate space for our game object */
 386     StcGame *game = (StcGame *) malloc(sizeof(StcGame));
 387     return game;
 388 }
 389 
 390 /*
 391  * Initializes the game, if there are no problems returns GAME_ERROR_NONE.
 392  */
 393 int gameInit(StcGame *game)
 394 {
 395     int errorCode;
 396 
 397     errorCode = platformInit();
 398     if (errorCode == GAME_ERROR_NONE)
 399     {
 400         startGame(game);
 401         return GAME_ERROR_NONE;
 402     }
 403     return errorCode;
 404 };
 405 
 406 void gameEnd()
 407 {
 408     /* Free platform resources */
 409     platformEnd();
 410 }
 411 
 412 void deleteGame(StcGame *game)
 413 {
 414     free(game);
 415 }
 416 
 417 /*
 418  * Rotate falling tetromino. If there are no collisions when the
 419  * tetromino is rotated this modifies the tetramino's cell buffer.
 420  */
 421 void rotateTetramino(StcGame *game, int clockwise)
 422 {
 423     int i, j;
 424     int rotated[4][4]; /* temporary array to hold rotated cells */
 425 
 426     /* If TETRAMINO_O is falling return immediately */
 427     if (game->fallingBlock.type == TETROMINO_O)
 428     {
 429         return; /* rotation doesn't require any changes */
 430     }
 431 
 432     /* Initialize rotated cells to blank */
 433     setMatrixCells(&rotated[0][0], 4, 4, EMPTY_CELL);
 434 
 435     /* Copy rotated cells to the temporary array */
 436     for (i = 0; i < game->fallingBlock.size; ++i)
 437     {
 438         for (j = 0; j < game->fallingBlock.size; ++j)
 439         {
 440             if (clockwise)
 441             {
 442                 rotated[game->fallingBlock.size - j - 1][i] = game->fallingBlock.cells[i][j];
 443             }
 444             else
 445             {
 446                 rotated[j][game->fallingBlock.size - i - 1] = game->fallingBlock.cells[i][j];
 447             }
 448         }
 449     }
 450     /* Check collision of the temporary array */
 451     for (i = 0; i < game->fallingBlock.size; ++i)
 452     {
 453         for (j = 0; j < game->fallingBlock.size; ++j)
 454         {
 455             if (rotated[i][j] != EMPTY_CELL)
 456             {
 457                 /* Check collision with left, right or bottom borders of the map */
 458                 if ((game->fallingBlock.x + i < 0) || (game->fallingBlock.x + i >= BOARD_WIDTH) || (game->fallingBlock.y + j >= BOARD_HEIGHT))
 459                 {
 460                     return; /* there was collision therefore return */
 461                 }
 462                 /* Check collision with existing cells in the map */
 463                 if (game->map[i + game->fallingBlock.x][j + game->fallingBlock.y] != EMPTY_CELL)
 464                 {
 465                     return; /* there was collision therefore return */
 466                 }
 467             }
 468         }
 469     }
 470     /* There are no collisions, replace tetramino cells with rotated cells */
 471     for (i = 0; i < 4; ++i)
 472     {
 473         for (j = 0; j < 4; ++j)
 474         {
 475             game->fallingBlock.cells[i][j] = rotated[i][j];
 476         }
 477     }
 478 }
 479 
 480 /*
 481  * Check if tetromino will collide with something if it is moved in the requested direction.
 482  * If there are collisions returns 1 else returns 0.
 483  */
 484 static int checkCollision(StcGame *game, int dx, int dy)
 485 {
 486     int newx, newy, i, j;
 487 
 488     newx = game->fallingBlock.x + dx;
 489     newy = game->fallingBlock.y + dy;
 490 
 491     for (i = 0; i < game->fallingBlock.size; ++i)
 492     {
 493         for (j = 0; j < game->fallingBlock.size; ++j)
 494         {
 495             if (game->fallingBlock.cells[i][j] != EMPTY_CELL)
 496             {
 497                 /* Check the tetramino would be inside the left, right and bottom borders */
 498                 if ((newx + i < 0) || (newx + i >= BOARD_WIDTH) || (newy + j >= BOARD_HEIGHT))
 499                 {
 500                     return 1;
 501                 }
 502                 /* Check the tetromino won't collide with existing cells in the map */
 503                 if (game->map[newx + i][newy + j] != EMPTY_CELL)
 504                 {
 505                     return 1;
 506                 }
 507             }
 508         }
 509     }
 510     return 0;
 511 }
 512 
 513 /* Game scoring: http://www.tetrisconcept.com/wiki/index.php/Scoring */
 514 static void onFilledRows(StcGame *game, int filledRows)
 515 {
 516     /* Update total number of filled rows */
 517     game->stats.lines += filledRows;
 518 
 519     /* Increase score accordingly to the number of filled rows */
 520     switch (filledRows)
 521     {
 522         case 1:
 523             game->stats.score += (SCORE_1_FILLED_ROW * (game->stats.level));
 524             break;
 525         case 2:
 526             game->stats.score += (SCORE_2_FILLED_ROW * (game->stats.level));
 527             break;
 528         case 3:
 529             game->stats.score += (SCORE_3_FILLED_ROW * (game->stats.level));
 530             break;
 531         case 4:
 532             game->stats.score += (SCORE_4_FILLED_ROW * (game->stats.level));
 533             break;
 534         default:
 535             game->errorCode = GAME_ERROR_ASSERT; /* This can't happen */
 536     }
 537     /* Check if we need to update level */
 538     if (game->stats.lines >= FILLED_ROWS_FOR_LEVEL_UP * (game->stats.level))
 539     {
 540         game->stats.level++;
 541 
 542         /* Increase speed for falling tetrominoes */
 543         game->delay *= DELAY_FACTOR_FOR_LEVEL_UP;
 544     }
 545 }
 546 
 547 /*
 548  * Move tetramino in direction especified by (x, y) (in tile units)
 549  * This function detects if there are filled rows or if the move 
 550  * lands a falling tetromino, also checks for game over condition.
 551  */
 552 static void moveTetramino(StcGame *game, int x, int y)
 553 {
 554     int i, j, hasFullRow, numFilledRows;
 555 
 556     /* Check if the move would create a collision */
 557     if (checkCollision(game, x, y))
 558     {
 559         /* In case of collision check if move was downwards (y == 1) */
 560         if (y == 1)
 561         {
 562             /* Check if collision occur when the falling
 563              * tetromino is in the 1st or 2nd row */
 564             if (game->fallingBlock.y <= 1)
 565             {
 566                 game->isOver = 1; /* if this happens the game is over */
 567             }
 568             else
 569             {
 570                 /* The falling tetromino has reached the bottom,
 571                  * so we copy their cells to the board map */
 572                 for (i = 0; i < game->fallingBlock.size; ++i)
 573                 {
 574                     for (j = 0; j < game->fallingBlock.size; ++j)
 575                     {
 576                         if (game->fallingBlock.cells[i][j] != EMPTY_CELL)
 577                         {
 578                             game->map[game->fallingBlock.x + i][game->fallingBlock.y + j] = game->fallingBlock.cells[i][j];
 579                         }
 580                     }
 581                 }
 582 
 583                 /* Check if the landing tetromino has created full rows */
 584                 numFilledRows = 0;
 585                 for (j = 1; j < BOARD_HEIGHT; ++j)
 586                 {
 587                     hasFullRow = 1;
 588                     for (i = 0; i < BOARD_WIDTH; ++i)
 589                     {
 590                         if (game->map[i][j] == EMPTY_CELL)
 591                         {
 592                             hasFullRow = 0;
 593                             break;
 594                         }
 595                     }
 596                     /* If we found a full row we need to remove that row from the map
 597                      * we do that by just moving all the above rows one row below */
 598                     if (hasFullRow)
 599                     {
 600                         for (x = 0; x < BOARD_WIDTH; ++x)
 601                         {
 602                             for (y = j; y > 0; --y)
 603                             {
 604                                 game->map[x][y] = game->map[x][y - 1];
 605                             }
 606                         }
 607                         numFilledRows++; /* increase filled row counter */
 608                     }
 609                 }
 610 
 611                 /* Update game statistics */
 612                 if (numFilledRows)
 613                 {
 614                     onFilledRows(game, numFilledRows);
 615                 }
 616                 game->stats.totalPieces++;
 617                 game->stats.pieces[game->fallingBlock.type]++;
 618 
 619                 /* Use preview tetromino as falling tetromino.
 620                  * Copy preview tetramino for falling tetramino */
 621                 for (i = 0; i < 4; ++i)
 622                 {
 623                     for (j = 0; j < 4; ++j)
 624                     {
 625                         game->fallingBlock.cells[i][j] = game->nextBlock.cells[i][j];
 626                     }
 627                 }
 628                 game->fallingBlock.size = game->nextBlock.size;
 629                 game->fallingBlock.type = game->nextBlock.type;
 630 
 631                 /* Reset position */
 632                 game->fallingBlock.y = 0;
 633                 game->fallingBlock.x = (BOARD_WIDTH - game->fallingBlock.size) / 2;
 634 
 635                 /* Create next preview tetromino */
 636                 setTetramino(rand() % 7, &game->nextBlock);
 637             }
 638         }
 639     }
 640     else
 641     {
 642         /* There are no collisions, just move the tetramino */
 643         game->fallingBlock.x += x;
 644         game->fallingBlock.y += y;
 645     }
 646 }
 647 
 648 /* Hard drop */
 649 static void dropTetramino(StcGame *game)
 650 {
 651     int y;
 652     y = 1;
 653     /* Calculate number of cells to drop */
 654     while (!checkCollision(game, 0, y))
 655     {
 656         y++;
 657     }
 658     moveTetramino(game, 0, y - 1);
 659 }
 660 
 661 /*
 662  * Main function game called every frame
 663  */
 664 void gameUpdate(StcGame *game)
 665 {
 666     long sysTime;
 667 
 668     if (!game)
 669     {
 670         return;
 671     }
 672 
 673     /* Read user input */
 674     platformReadInput();
 675 
 676     /* Update game state */
 677     if (game->isOver)
 678     {
 679 
 680         if (game->stats.score > game->stats.high)
 681         {
 682             game->stats.high = game->stats.score;
 683             FILE * f;
 684             long buf;
 685             buf = game->stats.score;
 686 
 687             mkdir_if_not_exist("A/CHDK/GAMES");
 688             f = fopen("A/CHDK/GAMES/TETRIS.SCO", "wb");
 689             fwrite(&buf, 1, sizeof(buf), f);
 690             fclose(f);
 691         }
 692 
 693         //if (game->events & EVENT_RESTART) {
 694         if (game->events & EVENT_PAUSE)
 695         {
 696 
 697             //TurnOnBackLight();
 698 
 699             game->isOver = 0;
 700             startGame(game);
 701 
 702         }
 703     }
 704     else
 705     {
 706         sysTime = platformGetSystemTime();
 707 
 708         /* Always handle pause event */
 709         if (game->events & EVENT_PAUSE)
 710         {
 711             game->isPaused = !game->isPaused;
 712             game->events = EVENT_NONE;
 713         }
 714 
 715         /* Check if the game is paused */
 716         if (game->isPaused)
 717         {
 718             /* We achieve the effect of pausing the game
 719              * adding the last frame duration to lastFallTime */
 720             game->lastFallTime += (sysTime - game->systemTime);
 721         }
 722         else
 723         {
 724             if (game->events != EVENT_NONE)
 725             {
 726                 if (game->events & EVENT_SHOW_NEXT)
 727                 {
 728                     game->showPreview = !game->showPreview;
 729                 }
 730                 if (game->events & EVENT_DROP)
 731                 {
 732                     dropTetramino(game);
 733                 }
 734                 if (game->events & EVENT_ROTATE_CW)
 735                 {
 736                     rotateTetramino(game, 1);
 737                 }
 738                 if (game->events & EVENT_MOVE_RIGHT)
 739                 {
 740                     moveTetramino(game, 1, 0);
 741                 }
 742                 else if (game->events & EVENT_MOVE_LEFT)
 743                 {
 744                     moveTetramino(game, -1, 0);
 745                 }
 746                 if (game->events & EVENT_MOVE_DOWN)
 747                 {
 748                     moveTetramino(game, 0, 1);
 749                 }
 750                 game->events = EVENT_NONE;
 751             }
 752             /* Check if it's time to move downwards the falling tetromino */
 753             if (sysTime - game->lastFallTime >= game->delay)
 754             {
 755                 moveTetramino(game, 0, 1);
 756                 game->lastFallTime = sysTime;
 757             }
 758         }
 759         game->systemTime = sysTime;
 760     }
 761     /* Draw game state */
 762     platformRenderGame();
 763 }
 764 
 765 void gui_tetris_init()
 766 {
 767     tile_size = camera_screen.height / (BOARD_HEIGHT+2);
 768 
 769     draw_rectangle(camera_screen.disp_left, 0, camera_screen.disp_right, camera_screen.height - 1, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_BG), RECT_BORDER0 | DRAW_FILLED);
 770     draw_rectangle(camera_screen.disp_left + BOARD_X - 1, BOARD_Y - 1, camera_screen.disp_left + BOARD_WIDTH * TILE_SIZE + TILE_SIZE, BOARD_HEIGHT * TILE_SIZE + TILE_SIZE, MAKE_COLOR(TETRIS_COLOR_TEXT, TETRIS_COLOR_TEXT), RECT_BORDER1);
 771     game = createGame();
 772     gameInit(game);
 773 
 774     long buf;
 775     FILE *f;
 776 
 777     f = fopen("A/CHDK/GAMES/TETRIS.SCO", "rb");
 778     if (!f)
 779     {
 780         game->stats.high = 0;
 781     }
 782     else
 783     {
 784         fread(&buf, 1, sizeof(buf), f);
 785         game->stats.high = buf;
 786     }
 787 
 788     fclose(f);
 789     startGame(game);
 790 }
 791 
 792 int basic_module_init()
 793 {
 794     gui_set_mode(&GUI_MODE_TETRIS);
 795     gui_tetris_init();
 796     return 1;
 797 }
 798 
 799 void gui_tetris_draw()
 800 {
 801     gameUpdate(game);
 802 }
 803 
 804 int gui_tetris_kbd_process()
 805 {
 806     if (!game)
 807     {
 808         return 0;
 809     }
 810     switch (kbd_get_autoclicked_key())
 811     {
 812         case KEY_UP:
 813             if ((game->isPaused) || (game->isOver))
 814             {
 815                 TurnOnBackLight();
 816             }
 817             else
 818             {
 819                 TurnOffBackLight();
 820             }
 821             game->events |= EVENT_PAUSE;
 822             break;
 823         case KEY_LEFT:
 824             game->events |= EVENT_MOVE_LEFT;
 825             break;
 826         case KEY_RIGHT:
 827             game->events |= EVENT_MOVE_RIGHT;
 828             break;
 829         case KEY_DOWN:
 830             game->events |= EVENT_MOVE_DOWN;
 831             break;
 832             //case KEY_SET:
 833             //game->events |= EVENT_RESTART;
 834             //break;
 835         case KEY_DISPLAY:
 836         case KEY_ERASE:
 837         case KEY_SET:
 838             game->events |= EVENT_ROTATE_CW;
 839             break;
 840         default:
 841             break;
 842     }
 843     return 0;
 844 }
 845 
 846 #include "simple_game.c"
 847 
 848 /******************** Module Information structure ******************/
 849 
 850 ModuleInfo _module_info =
 851 {
 852     MODULEINFO_V1_MAGICNUM,
 853     sizeof(ModuleInfo),
 854     SIMPLE_MODULE_VERSION,              // Module version
 855 
 856     ANY_CHDK_BRANCH, 0, OPT_ARCHITECTURE,                       // Requirements of CHDK version
 857     ANY_PLATFORM_ALLOWED,               // Specify platform dependency
 858 
 859     -LANG_MENU_GAMES_TETRIS,    // Module name
 860     MTYPE_GAME,
 861 
 862     &_librun.base,
 863 
 864     ANY_VERSION,                // CONF version
 865     CAM_SCREEN_VERSION,         // CAM SCREEN version
 866     ANY_VERSION,                // CAM SENSOR version
 867     ANY_VERSION,                // CAM INFO version
 868 
 869     0,
 870 };
 871 
 872 /*************** END OF AUXILARY PART *******************/

/* [<][>][^][v][top][bottom][index][help] */