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 "stdlib.h"
   3 #include "keyboard.h"
   4 #include "clock.h"
   5 #include "backlight.h"
   6 #include "lang.h"
   7 #include "gui.h"
   8 #include "gui_draw.h"
   9 #include "gui_lang.h"
  10 #include "gui_batt.h"
  11 #include "gui_mbox.h"
  12 #include "modes.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 StcGame *createGame();
 112 void deleteGame(StcGame *pGame);
 113 int gameInit(StcGame *gameInstance);
 114 void gameEnd(StcGame *gameInstance);
 115 void gameUpdate(StcGame *gameInstance);
 116 
 117 
 118 StcGame *game;
 119 
 120 
 121 // No need to import such simple
 122 long mkdir_if_not_exist(const char *dirname) 
 123 {
 124     // Check if directory exists and create it if it does not.
 125     if (stat(dirname,0) != 0) return mkdir(dirname);
 126     return 0;   // Success
 127 }
 128 
 129 
 130 int platformInit(StcGame *gameInstance){ return GAME_ERROR_NONE;}
 131 void platformEnd(StcGame *gameInstance){}
 132 
 133 /* Read input device and notify game */
 134 void platformReadInput(StcGame *gameInstance){}
 135 #define PREVIEW_X 150
 136 #define PREVIEW_Y 10
 137 #define BOARD_X 10
 138 #define BOARD_Y 10
 139 #define TILE_SIZE 10
 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(StcGame *gameInstance){
 146     int i, j;
 147 
 148     for(i = 0; i < BOARD_WIDTH; ++i) {
 149         for (j = 0; j < BOARD_HEIGHT; ++j){
 150           tmp[i][j] = EMPTY_CELL;
 151           tmp2[i][j] = EMPTY_CELL;
 152         }
 153     }
 154 
 155     /* Draw preview block */
 156     if(game->nextBlock.type != prevNextBlockType){
 157         prevNextBlockType = game->nextBlock.type;
 158         for (i = 0; i < 4; ++i) {
 159             for (j = 0; j < 4; ++j) {
 160                 if (game->nextBlock.cells[i][j] != EMPTY_CELL) {
 161                     draw_rectangle(camera_screen.disp_left+PREVIEW_X + (TILE_SIZE * i),
 162                             PREVIEW_Y + (TILE_SIZE * j),
 163                             camera_screen.disp_left+PREVIEW_X + (TILE_SIZE * i)+TILE_SIZE-1,
 164                             PREVIEW_Y + (TILE_SIZE * j)+TILE_SIZE-1,
 165                             MAKE_COLOR(game->nextBlock.cells[i][j], game->nextBlock.cells[i][j]), RECT_BORDER0|DRAW_FILLED);
 166                 }else{
 167                     draw_rectangle(camera_screen.disp_left+PREVIEW_X + (TILE_SIZE * i),
 168                             PREVIEW_Y + (TILE_SIZE * j),
 169                             camera_screen.disp_left+PREVIEW_X + (TILE_SIZE * i)+TILE_SIZE-1,
 170                             PREVIEW_Y + (TILE_SIZE * j)+TILE_SIZE-1,
 171                             MAKE_COLOR(TETRIS_COLOR_BG,TETRIS_COLOR_BG), RECT_BORDER0|DRAW_FILLED);
 172                 }
 173             }
 174         }
 175     }
 176     
 177     /* Draw the cells in the board */
 178     for (i = 0; i < BOARD_WIDTH; ++i) {
 179         for (j = 0; j < BOARD_HEIGHT; ++j){
 180             if (game->map[i][j] != EMPTY_CELL) {
 181                 tmp2[i][j] = game->map[i][j];
 182             }
 183         }
 184     }
 185     /* Draw falling tetromino */
 186     for (i = 0; i<4; ++i) {
 187         for (j = 0; j < 4; ++j) {
 188             if (game->fallingBlock.cells[i][j] != EMPTY_CELL) {
 189               tmp[i+game->fallingBlock.x][j+game->fallingBlock.y] = game->fallingBlock.cells[i][j];
 190             }
 191         }
 192     }
 193 
 194     for (i = 0; i < BOARD_WIDTH; ++i) {
 195         for (j = 0; j < BOARD_HEIGHT; ++j){
 196                     if(tmp[i][j] != EMPTY_CELL){
 197                     draw_rectangle(camera_screen.disp_left+BOARD_X + (TILE_SIZE * i),
 198                             BOARD_Y + (TILE_SIZE * j),
 199                             camera_screen.disp_left+BOARD_X + (TILE_SIZE * i)+TILE_SIZE-1,
 200                             BOARD_Y + (TILE_SIZE * j)+TILE_SIZE-1,
 201                             MAKE_COLOR(tmp[i][j], tmp[i][j]), RECT_BORDER0|DRAW_FILLED);
 202                     }else if(tmp2[i][j] != EMPTY_CELL){
 203                     draw_rectangle(camera_screen.disp_left+BOARD_X + (TILE_SIZE * i),
 204                             BOARD_Y + (TILE_SIZE * j),
 205                             camera_screen.disp_left+BOARD_X + (TILE_SIZE * i)+TILE_SIZE-1,
 206                             BOARD_Y + (TILE_SIZE * j)+TILE_SIZE-1,
 207                             MAKE_COLOR(tmp2[i][j], tmp2[i][j]), RECT_BORDER0|DRAW_FILLED);
 208                     }else{
 209                     draw_rectangle(camera_screen.disp_left+BOARD_X + (TILE_SIZE * i),
 210                             BOARD_Y + (TILE_SIZE * j),
 211                             camera_screen.disp_left+BOARD_X + (TILE_SIZE * i)+TILE_SIZE-1,
 212                             BOARD_Y + (TILE_SIZE * j)+TILE_SIZE-1,
 213                             MAKE_COLOR(TETRIS_COLOR_BOARD,TETRIS_COLOR_BOARD), RECT_BORDER0|DRAW_FILLED);
 214                     }
 215         }
 216     }
 217     /* output game info */
 218     char str_buf[100];
 219     static struct tm *ttm;
 220     sprintf(str_buf,"High:    %5d",game->stats.high);
 221     draw_string(camera_screen.disp_left+150,35,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 222     sprintf(str_buf,"Points:  %5d",game->stats.score);
 223     draw_string(camera_screen.disp_left+150,55,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 224     sprintf(str_buf,"Lines:   %5d",game->stats.lines);
 225     draw_string(camera_screen.disp_left+150,75,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 226     sprintf(str_buf,"Level:   %5d",game->stats.level);
 227     draw_string(camera_screen.disp_left+150,95,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 228     sprintf(str_buf,"UP  -> Pause");
 229     draw_string(camera_screen.disp_left+150,135,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 230     sprintf(str_buf,"SET -> Rotate");
 231     draw_string(camera_screen.disp_left+150,155,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 232     ttm = get_localtime();
 233     sprintf(str_buf,"Time:    %2u:%02u", ttm->tm_hour, ttm->tm_min);
 234     draw_string(camera_screen.disp_left+150,195,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 235     sprintf(str_buf,"Batt:     %3d%%", get_batt_perc());
 236     draw_string(camera_screen.disp_left+150,215,str_buf, MAKE_COLOR(TETRIS_COLOR_BG, TETRIS_COLOR_TEXT));
 237 }
 238 
 239 /* Return the current system time in milliseconds */
 240 long platformGetSystemTime(){return get_tick_count();}
 241 
 242 /* Set matrix elements to indicated value */
 243 static void setMatrixCells(int *matrix, int width, int height, int value) {
 244     int i, j;
 245     for (i = 0; i < width; ++i) {
 246         for (j = 0; j < height; ++j) {
 247             *(matrix + i + (j * width)) = value;
 248         }
 249     }
 250 }
 251 
 252 /* Initialize tetromino cells for every tipe of tetromino */
 253 static void setTetramino(int indexTetramino, StcTetramino *tetramino) {
 254 
 255     /* Initialize tetromino cells to empty cells */
 256     setMatrixCells(&tetramino->cells[0][0], 4, 4, EMPTY_CELL);
 257 
 258     /* Almost all the blocks have size 3 */
 259     tetramino->size = 3;
 260 
 261     /* Initial configuration from: http://www.tetrisconcept.com/wiki/index.php/SRS */
 262     switch (indexTetramino) {
 263     case TETROMINO_I:
 264         tetramino->cells[0][1] = TETRIS_COLOR_CYAN;
 265         tetramino->cells[1][1] = TETRIS_COLOR_CYAN;
 266         tetramino->cells[2][1] = TETRIS_COLOR_CYAN;
 267         tetramino->cells[3][1] = TETRIS_COLOR_CYAN;
 268         tetramino->size = 4;
 269         break;
 270     case TETROMINO_O:
 271         tetramino->cells[0][0] = TETRIS_COLOR_YELLOW;
 272         tetramino->cells[0][1] = TETRIS_COLOR_YELLOW;
 273         tetramino->cells[1][0] = TETRIS_COLOR_YELLOW;
 274         tetramino->cells[1][1] = TETRIS_COLOR_YELLOW;
 275         tetramino->size = 2;
 276         break;
 277     case TETROMINO_T:
 278         tetramino->cells[0][1] = TETRIS_COLOR_PURPLE;
 279         tetramino->cells[1][0] = TETRIS_COLOR_PURPLE;
 280         tetramino->cells[1][1] = TETRIS_COLOR_PURPLE;
 281         tetramino->cells[2][1] = TETRIS_COLOR_PURPLE;
 282         break;
 283     case TETROMINO_S:
 284         tetramino->cells[0][1] = TETRIS_COLOR_GREEN;
 285         tetramino->cells[1][0] = TETRIS_COLOR_GREEN;
 286         tetramino->cells[1][1] = TETRIS_COLOR_GREEN;
 287         tetramino->cells[2][0] = TETRIS_COLOR_GREEN;
 288         break;
 289     case TETROMINO_Z:
 290         tetramino->cells[0][0] = TETRIS_COLOR_RED;
 291         tetramino->cells[1][0] = TETRIS_COLOR_RED;
 292         tetramino->cells[1][1] = TETRIS_COLOR_RED;
 293         tetramino->cells[2][1] = TETRIS_COLOR_RED;
 294         break;
 295     case TETROMINO_J:
 296         tetramino->cells[0][0] = TETRIS_COLOR_BLUE;
 297         tetramino->cells[0][1] = TETRIS_COLOR_BLUE;
 298         tetramino->cells[1][1] = TETRIS_COLOR_BLUE;
 299         tetramino->cells[2][1] = TETRIS_COLOR_BLUE;
 300         break;
 301     case TETROMINO_L:
 302         tetramino->cells[0][1] = TETRIS_COLOR_ORANGE;
 303         tetramino->cells[1][1] = TETRIS_COLOR_ORANGE;
 304         tetramino->cells[2][0] = TETRIS_COLOR_ORANGE;
 305         tetramino->cells[2][1] = TETRIS_COLOR_ORANGE;
 306         break;
 307     }
 308     tetramino->type = indexTetramino;
 309 }
 310 
 311 /*  Start a new game */
 312 static void startGame(StcGame *game) {
 313     int i;
 314 
 315     /* Initialize game data */
 316     game->errorCode = GAME_ERROR_NONE;
 317     game->systemTime = platformGetSystemTime();
 318     game->lastFallTime = game->systemTime;
 319     game->isOver = 0;
 320     game->isPaused = 0;
 321     game->showPreview = 1;
 322     game->events = EVENT_NONE;
 323     game->delay = INI_DELAY_FALL;
 324     /* Initialize game statistics */
 325     game->stats.score = 0;
 326     game->stats.lines = 0;
 327     game->stats.totalPieces = 0;
 328     game->stats.level = 0;
 329     for (i = 0; i < 7; ++i) {
 330         game->stats.pieces[i] = 0;
 331     }
 332 
 333     /* Initialize rand generator */
 334     srand(game->systemTime);
 335 
 336     /* Initialize game tile map */
 337     setMatrixCells(&game->map[0][0], BOARD_WIDTH, BOARD_HEIGHT, EMPTY_CELL);
 338 
 339     /* Initialize falling tetromino */
 340     setTetramino(rand() % 7, &game->fallingBlock);
 341     game->fallingBlock.x = (BOARD_WIDTH - game->fallingBlock.size) / 2;
 342     game->fallingBlock.y = 0;
 343 
 344     /* Initialize preview tetromino */
 345     setTetramino(rand() % 7, &game->nextBlock);
 346 }
 347 
 348 /* Create new game object */
 349 StcGame *createGame() {
 350     /* Allocate space for our game object */
 351     StcGame *game = (StcGame *) malloc(sizeof(StcGame));
 352     return game;
 353 }
 354 
 355 /*
 356  * Initializes the game, if there are no problems returns GAME_ERROR_NONE.
 357  */
 358 int gameInit(StcGame *game) {
 359     int errorCode;
 360 
 361     errorCode = platformInit(game);
 362     if (errorCode == GAME_ERROR_NONE) {
 363         startGame(game);
 364         return GAME_ERROR_NONE;
 365     }
 366     return errorCode;
 367 };
 368 
 369 void gameEnd(StcGame *game) {
 370     /* Free platform resources */
 371     platformEnd(game);
 372 }
 373 
 374 void deleteGame(StcGame *game) {
 375     free(game);
 376 }
 377 
 378 /*
 379  * Rotate falling tetromino. If there are no collisions when the
 380  * tetromino is rotated this modifies the tetramino's cell buffer.
 381  */
 382 void rotateTetramino(StcGame *game, int clockwise) {
 383     int i, j;
 384     int rotated[4][4];  /* temporary array to hold rotated cells */
 385 
 386     /* If TETRAMINO_O is falling return immediately */
 387     if (game->fallingBlock.type == TETROMINO_O) {
 388         return; /* rotation doesn't require any changes */
 389     }
 390 
 391     /* Initialize rotated cells to blank */
 392     setMatrixCells(&rotated[0][0], 4, 4, EMPTY_CELL);
 393 
 394     /* Copy rotated cells to the temporary array */
 395     for (i = 0; i < game->fallingBlock.size; ++i) {
 396         for (j = 0; j < game->fallingBlock.size; ++j) {
 397             if (clockwise) {
 398                 rotated[game->fallingBlock.size - j - 1][i] = game->fallingBlock.cells[i][j];
 399             } else {
 400                 rotated[j][game->fallingBlock.size - i - 1] = game->fallingBlock.cells[i][j];
 401             }
 402         }
 403     }
 404     /* Check collision of the temporary array */
 405     for (i = 0; i < game->fallingBlock.size; ++i) {
 406         for (j = 0; j < game->fallingBlock.size; ++j) {
 407             if (rotated[i][j] != EMPTY_CELL) {
 408                 /* Check collision with left, right or bottom borders of the map */
 409                 if ((game->fallingBlock.x + i < 0) || (game->fallingBlock.x + i >= BOARD_WIDTH)
 410                         || (game->fallingBlock.y + j >= BOARD_HEIGHT)) {
 411                     return; /* there was collision therefore return */
 412                 }
 413                 /* Check collision with existing cells in the map */
 414                 if (game->map[i + game->fallingBlock.x][j + game->fallingBlock.y] != EMPTY_CELL) {
 415                     return; /* there was collision therefore return */
 416                 }
 417             }
 418         }
 419     }
 420     /* There are no collisions, replace tetramino cells with rotated cells */
 421     for (i = 0; i < 4; ++i) {
 422         for (j = 0; j < 4; ++j) {
 423             game->fallingBlock.cells[i][j] = rotated[i][j];
 424         }
 425     }
 426 }
 427 
 428 /*
 429  * Check if tetromino will collide with something if it is moved in the requested direction.
 430  * If there are collisions returns 1 else returns 0.
 431  */
 432 static int checkCollision(StcGame *game, int dx, int dy) {
 433     int newx, newy, i, j;
 434 
 435     newx = game->fallingBlock.x + dx;
 436     newy = game->fallingBlock.y + dy;
 437 
 438     for (i = 0; i < game->fallingBlock.size; ++i) {
 439         for (j = 0; j < game->fallingBlock.size; ++j) {
 440             if (game->fallingBlock.cells[i][j] != EMPTY_CELL) {
 441                 /* Check the tetramino would be inside the left, right and bottom borders */
 442                 if ((newx + i < 0) || (newx + i >= BOARD_WIDTH)
 443                     || (newy + j >= BOARD_HEIGHT)) {
 444                     return 1;
 445                 }
 446                 /* Check the tetromino won't collide with existing cells in the map */
 447                 if (game->map[newx + i][newy + j] != EMPTY_CELL) {
 448                     return 1;
 449                 }
 450             }
 451         }
 452     }
 453     return 0;
 454 }
 455 
 456 /* Game scoring: http://www.tetrisconcept.com/wiki/index.php/Scoring */
 457 static void onFilledRows(StcGame *game, int filledRows) {
 458     /* Update total number of filled rows */
 459     game->stats.lines += filledRows;
 460 
 461     /* Increase score accordingly to the number of filled rows */
 462     switch (filledRows) {
 463     case 1:
 464         game->stats.score += (SCORE_1_FILLED_ROW * (game->stats.level));
 465         break;
 466     case 2:
 467         game->stats.score += (SCORE_2_FILLED_ROW * (game->stats.level));
 468         break;
 469     case 3:
 470         game->stats.score += (SCORE_3_FILLED_ROW * (game->stats.level));
 471         break;
 472     case 4:
 473         game->stats.score += (SCORE_4_FILLED_ROW * (game->stats.level));
 474         break;
 475     default:
 476         game->errorCode = GAME_ERROR_ASSERT;    /* This can't happen */
 477     }
 478     /* Check if we need to update level */
 479     if (game->stats.lines >= FILLED_ROWS_FOR_LEVEL_UP * (game->stats.level)) {
 480         game->stats.level++;
 481 
 482         /* Increase speed for falling tetrominoes */
 483         game->delay *= DELAY_FACTOR_FOR_LEVEL_UP;
 484     }
 485 }
 486 
 487 /*
 488  * Move tetramino in direction especified by (x, y) (in tile units)
 489  * This function detects if there are filled rows or if the move 
 490  * lands a falling tetromino, also checks for game over condition.
 491  */
 492 static void moveTetramino(StcGame *game, int x, int y) {
 493     int i, j, hasFullRow, numFilledRows;
 494     
 495     /* Check if the move would create a collision */
 496     if (checkCollision(game, x, y)) {
 497         /* In case of collision check if move was downwards (y == 1) */
 498         if (y == 1) {
 499             /* Check if collision occur when the falling
 500              * tetromino is in the 1st or 2nd row */
 501             if (game->fallingBlock.y <= 1) {
 502                 game->isOver = 1;   /* if this happens the game is over */
 503             }
 504             else {
 505                 /* The falling tetromino has reached the bottom,
 506                  * so we copy their cells to the board map */
 507                 for (i = 0; i < game->fallingBlock.size; ++i) {
 508                     for (j = 0; j < game->fallingBlock.size; ++j) {
 509                         if (game->fallingBlock.cells[i][j] != EMPTY_CELL) {
 510                             game->map[game->fallingBlock.x + i][game->fallingBlock.y + j]
 511                                     = game->fallingBlock.cells[i][j];
 512                         }
 513                     }
 514                 }
 515 
 516                 /* Check if the landing tetromino has created full rows */
 517                 numFilledRows = 0;
 518                 for (j = 1; j < BOARD_HEIGHT; ++j) {
 519                     hasFullRow = 1;
 520                     for (i = 0; i < BOARD_WIDTH; ++i) {
 521                         if (game->map[i][j] == EMPTY_CELL) {
 522                             hasFullRow = 0;
 523                             break;
 524                         }
 525                     }
 526                     /* If we found a full row we need to remove that row from the map
 527                      * we do that by just moving all the above rows one row below */
 528                     if (hasFullRow) {
 529                         for (x = 0; x < BOARD_WIDTH; ++x) {
 530                             for (y = j; y > 0; --y) {
 531                                 game->map[x][y] = game->map[x][y - 1];
 532                             }
 533                         }
 534                         numFilledRows++;    /* increase filled row counter */
 535                     }
 536                 }
 537 
 538                 /* Update game statistics */
 539                 if (numFilledRows) {
 540                     onFilledRows(game, numFilledRows);
 541                 }
 542                 game->stats.totalPieces++;
 543                 game->stats.pieces[game->fallingBlock.type]++;
 544                 
 545                 /* Use preview tetromino as falling tetromino.
 546                  * Copy preview tetramino for falling tetramino */
 547                 for (i = 0; i < 4; ++i) {
 548                     for (j = 0; j < 4; ++j) {
 549                         game->fallingBlock.cells[i][j] = game->nextBlock.cells[i][j];
 550                     }
 551                 }
 552                 game->fallingBlock.size = game->nextBlock.size;
 553                 game->fallingBlock.type = game->nextBlock.type;
 554 
 555                 /* Reset position */
 556                 game->fallingBlock.y = 0;
 557                 game->fallingBlock.x = (BOARD_WIDTH - game->fallingBlock.size) / 2;
 558 
 559                 /* Create next preview tetromino */
 560                 setTetramino(rand() % 7, &game->nextBlock);
 561             }
 562         }
 563     }
 564     else {
 565         /* There are no collisions, just move the tetramino */
 566         game->fallingBlock.x += x;
 567         game->fallingBlock.y += y;
 568     }
 569 }
 570 
 571 /* Hard drop */
 572 static void dropTetramino(StcGame *game) {
 573    int y;
 574    y = 1;
 575    /* Calculate number of cells to drop */
 576    while (!checkCollision(game, 0, y)) {
 577        y++;
 578    }
 579    moveTetramino(game, 0, y - 1);
 580 }
 581 
 582 /*
 583  * Main function game called every frame
 584  */
 585 void gameUpdate(StcGame *game) {
 586     long sysTime;
 587     /* Read user input */
 588     platformReadInput(game);
 589 
 590     /* Update game state */
 591     if (game->isOver) {
 592                 
 593                 if (game->stats.score > game->stats.high) {
 594                         game->stats.high = game->stats.score;
 595                         FILE * f;
 596                         long buf;
 597                         buf = game->stats.score;
 598 
 599                         mkdir_if_not_exist("A/CHDK/GAMES");
 600                         f = fopen ( "A/CHDK/GAMES/TETRIS.SCO" , "wb" );
 601                         fwrite (&buf , 1 , sizeof(buf) , f );
 602                         fclose (f);
 603                 }
 604                         
 605                 
 606         //if (game->events & EVENT_RESTART) {
 607                 if (game->events & EVENT_PAUSE) {
 608                     
 609                         //TurnOnBackLight();
 610                         
 611             game->isOver = 0;
 612                         startGame(game);
 613                         
 614         }
 615     }
 616     else {
 617         sysTime = platformGetSystemTime();
 618 
 619         /* Always handle pause event */
 620         if (game->events & EVENT_PAUSE) {
 621             game->isPaused = !game->isPaused;
 622             game->events = EVENT_NONE;
 623         }
 624 
 625         /* Check if the game is paused */
 626         if (game->isPaused) {
 627             /* We achieve the effect of pausing the game
 628              * adding the last frame duration to lastFallTime */
 629             game->lastFallTime += (sysTime - game->systemTime);
 630         }
 631         else {
 632             if (game->events != EVENT_NONE) {
 633                 if (game->events & EVENT_SHOW_NEXT) {
 634                     game->showPreview = !game->showPreview;
 635                 }
 636                 if (game->events & EVENT_DROP) {
 637                     dropTetramino(game);
 638                 }
 639                 if (game->events & EVENT_ROTATE_CW) {
 640                     rotateTetramino(game, 1);
 641                 }
 642                 if (game->events & EVENT_MOVE_RIGHT) {
 643                     moveTetramino(game, 1, 0);
 644                 }
 645                 else if (game->events & EVENT_MOVE_LEFT) {
 646                     moveTetramino(game, -1, 0);
 647                 }
 648                 if (game->events & EVENT_MOVE_DOWN) {
 649                     moveTetramino(game, 0, 1);
 650                 }
 651                 game->events = EVENT_NONE;
 652             }
 653             /* Check if it's time to move downwards the falling tetromino */
 654             if (sysTime - game->lastFallTime >= game->delay) {
 655                 moveTetramino(game, 0, 1);
 656                 game->lastFallTime = sysTime;
 657             }
 658         }
 659         game->systemTime = sysTime;
 660     }
 661     /* Draw game state */
 662     platformRenderGame(game);
 663 }
 664 
 665 void gui_tetris_init(){
 666     draw_rectangle(camera_screen.disp_left, 0,
 667                    camera_screen.disp_right, camera_screen.height-1, MAKE_COLOR(TETRIS_COLOR_BG,TETRIS_COLOR_BG), RECT_BORDER0|DRAW_FILLED);
 668     draw_rectangle(camera_screen.disp_left+BOARD_X-1,BOARD_Y-1,
 669                    camera_screen.disp_left+BOARD_WIDTH*TILE_SIZE+10,BOARD_HEIGHT*TILE_SIZE+10, MAKE_COLOR(TETRIS_COLOR_TEXT,TETRIS_COLOR_TEXT), RECT_BORDER1);
 670     game = createGame();
 671     gameInit(game);
 672   
 673     long buf;
 674     FILE *f;    
 675 
 676     f=fopen("A/CHDK/GAMES/TETRIS.SCO","rb");
 677     if(!f) {
 678         game->stats.high = 0;
 679     } else {
 680     
 681     fread( &buf, 1, sizeof( buf ), f );
 682     game->stats.high = buf; 
 683     
 684     }
 685 
 686     fclose (f);
 687     startGame(game);
 688 }
 689 
 690 int basic_module_init() {
 691   gui_set_mode(&GUI_MODE_TETRIS);
 692   gui_tetris_init();
 693   return 1;
 694 }
 695 
 696 void gui_tetris_draw(){
 697   gameUpdate(game);
 698 }
 699 
 700 int gui_tetris_kbd_process() {
 701         switch ( kbd_get_autoclicked_key() )
 702         {
 703             case KEY_UP:
 704             if ((game->isPaused) || (game->isOver)) {
 705                 TurnOnBackLight();
 706             } else { TurnOffBackLight(); }
 707                 game->events |= EVENT_PAUSE;
 708                 break;
 709             case KEY_LEFT:
 710                 game->events |= EVENT_MOVE_LEFT;
 711                 break;
 712             case KEY_RIGHT:
 713                 game->events |= EVENT_MOVE_RIGHT;
 714                 break;
 715             case KEY_DOWN:
 716                 game->events |= EVENT_MOVE_DOWN;
 717                 break;
 718             //case KEY_SET:
 719             //game->events |= EVENT_RESTART;
 720             //break;
 721             case KEY_DISPLAY:
 722             case KEY_ERASE:
 723             case KEY_SET:
 724                 game->events |= EVENT_ROTATE_CW;
 725                 break;
 726             default:
 727                 break;
 728         }
 729         return 0;
 730 }
 731 
 732 #include "simple_game.c"
 733 
 734 /******************** Module Information structure ******************/
 735 
 736 ModuleInfo _module_info =
 737 {
 738     MODULEINFO_V1_MAGICNUM,
 739     sizeof(ModuleInfo),
 740     SIMPLE_MODULE_VERSION,              // Module version
 741 
 742     ANY_CHDK_BRANCH, 0, OPT_ARCHITECTURE,                       // Requirements of CHDK version
 743     ANY_PLATFORM_ALLOWED,               // Specify platform dependency
 744 
 745     -LANG_MENU_GAMES_TETRIS,    // Module name
 746     MTYPE_GAME,
 747 
 748     &_librun.base,
 749 
 750     ANY_VERSION,                // CONF version
 751     CAM_SCREEN_VERSION,         // CAM SCREEN version
 752     ANY_VERSION,                // CAM SENSOR version
 753     ANY_VERSION,                // CAM INFO version
 754 };
 755 
 756 /*************** END OF AUXILARY PART *******************/

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