PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/src/sdl/sdl_game.cpp

http://simple-tetris-clone.googlecode.com/
C++ | 309 lines | 240 code | 30 blank | 39 comment | 35 complexity | c5027433fbc9d4cbaf15bf5baf38c067 MD5 | raw file
  1. /* ========================================================================== */
  2. /* STC - SIMPLE TETRIS CLONE */
  3. /* -------------------------------------------------------------------------- */
  4. /* Simple pure SDL implementation. (No sound, no fonts) */
  5. /* Using SDL for game state rendering, user input and timing. */
  6. /* */
  7. /* Copyright (c) 2011 Laurens Rodriguez Oscanoa. */
  8. /* This code is licensed under the MIT license: */
  9. /* http://www.opensource.org/licenses/mit-license.php */
  10. /* -------------------------------------------------------------------------- */
  11. #include "sdl_game.hpp"
  12. #include <cstdlib>
  13. #include <ctime>
  14. #include <SDL_image.h>
  15. namespace stc
  16. {
  17. // Initializes platform, if there are no problems returns ERROR_NONE.
  18. int PlatformSdl::init(Game *game)
  19. {
  20. // Initialize the random number generator
  21. srand((unsigned int)(time(NULL)));
  22. // Start video system
  23. if (SDL_Init(SDL_INIT_VIDEO) < 0)
  24. {
  25. return Game::ERROR_NO_VIDEO;
  26. }
  27. // Create game video surface
  28. mScreen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT,
  29. SCREEN_BIT_DEPTH,
  30. SCREEN_VIDEO_MODE);
  31. if (mScreen == NULL)
  32. {
  33. return Game::ERROR_NO_VIDEO;
  34. }
  35. // Set window caption
  36. SDL_WM_SetCaption(STC_GAME_NAME " (C++)", STC_GAME_NAME);
  37. // Load images for blocks and background
  38. mBmpTiles = IMG_Load(STC_BMP_TILE_BLOCKS);
  39. mBmpBack = IMG_Load(STC_BMP_BACKGROUND);
  40. mBmpNumbers = IMG_Load(STC_BMP_NUMBERS);
  41. if (mBmpTiles == NULL || mBmpBack == NULL || mBmpNumbers == NULL)
  42. {
  43. return Game::ERROR_NO_IMAGES;
  44. }
  45. mGame = game;
  46. return Game::ERROR_NONE;
  47. }
  48. // Return the current system time in milliseconds
  49. long PlatformSdl::getSystemTime()
  50. {
  51. return SDL_GetTicks();
  52. }
  53. // Process events and notify game
  54. void PlatformSdl::processEvents()
  55. {
  56. SDL_Event event;
  57. // Grab events in the queue
  58. while (SDL_PollEvent(&event))
  59. {
  60. switch (event.type)
  61. {
  62. // On quit game
  63. case SDL_QUIT:
  64. mGame->onEventStart(Game::EVENT_QUIT);
  65. break;
  66. // On key pressed
  67. case SDL_KEYDOWN:
  68. switch (event.key.keysym.sym)
  69. {
  70. case SDLK_ESCAPE:
  71. mGame->onEventStart(Game::EVENT_QUIT);
  72. break;
  73. case SDLK_s:
  74. case SDLK_DOWN:
  75. mGame->onEventStart(Game::EVENT_MOVE_DOWN);
  76. break;
  77. case SDLK_w:
  78. case SDLK_UP:
  79. mGame->onEventStart(Game::EVENT_ROTATE_CW);
  80. break;
  81. case SDLK_a:
  82. case SDLK_LEFT:
  83. mGame->onEventStart(Game::EVENT_MOVE_LEFT);
  84. break;
  85. case SDLK_d:
  86. case SDLK_RIGHT:
  87. mGame->onEventStart(Game::EVENT_MOVE_RIGHT);
  88. break;
  89. case SDLK_SPACE:
  90. mGame->onEventStart(Game::EVENT_DROP);
  91. break;
  92. case SDLK_F5:
  93. mGame->onEventStart(Game::EVENT_RESTART);
  94. break;
  95. case SDLK_F1:
  96. mGame->onEventStart(Game::EVENT_PAUSE);
  97. break;
  98. case SDLK_F2:
  99. mGame->onEventStart(Game::EVENT_SHOW_NEXT);
  100. break;
  101. #ifdef STC_SHOW_GHOST_PIECE
  102. case SDLK_F3:
  103. mGame->onEventStart(Game::EVENT_SHOW_SHADOW);
  104. break;
  105. #endif // STC_SHOW_GHOST_PIECE
  106. default:
  107. break;
  108. }
  109. break;
  110. // On key released
  111. case SDL_KEYUP:
  112. switch (event.key.keysym.sym)
  113. {
  114. case SDLK_s:
  115. case SDLK_DOWN:
  116. mGame->onEventEnd(Game::EVENT_MOVE_DOWN);
  117. break;
  118. case SDLK_a:
  119. case SDLK_LEFT:
  120. mGame->onEventEnd(Game::EVENT_MOVE_LEFT);
  121. break;
  122. case SDLK_d:
  123. case SDLK_RIGHT:
  124. mGame->onEventEnd(Game::EVENT_MOVE_RIGHT);
  125. break;
  126. #ifdef STC_AUTO_ROTATION
  127. case SDLK_w:
  128. case SDLK_UP:
  129. mGame->onEventEnd(Game::EVENT_ROTATE_CW);
  130. break;
  131. #endif // STC_AUTO_ROTATION
  132. default:
  133. break;
  134. }
  135. default:
  136. break;
  137. }
  138. }
  139. }
  140. // Draw a tile from a tetromino
  141. void PlatformSdl::drawTile(int x, int y, int tile, bool shadow)
  142. {
  143. SDL_Rect recDestine;
  144. SDL_Rect recSource;
  145. recDestine.x = (Sint16)x;
  146. recDestine.y = (Sint16)y;
  147. recSource.x = (Sint16)(TILE_SIZE * tile);
  148. recSource.y = (TILE_SIZE + 1) * (shadow? 1 : 0);
  149. recSource.w = TILE_SIZE + 1;
  150. recSource.h = TILE_SIZE + 1;
  151. SDL_BlitSurface(mBmpTiles, &recSource, mScreen, &recDestine);
  152. }
  153. // Draw a number on the given position
  154. void PlatformSdl::drawNumber(int x, int y, long number, int length, int color)
  155. {
  156. SDL_Rect recDestine;
  157. SDL_Rect recSource;
  158. recSource.y = (Sint16)(NUMBER_HEIGHT * color);
  159. recSource.w = NUMBER_WIDTH;
  160. recSource.h = NUMBER_HEIGHT;
  161. recDestine.y = (Sint16)y;
  162. int pos = 0;
  163. do
  164. {
  165. recDestine.x = (Sint16)(x + NUMBER_WIDTH * (length - pos));
  166. recSource.x = NUMBER_WIDTH * (Sint16)(number % 10);
  167. SDL_BlitSurface(mBmpNumbers, &recSource, mScreen, &recDestine);
  168. number /= 10;
  169. } while (++pos < length);
  170. }
  171. // Render the state of the game using platform functions
  172. void PlatformSdl::renderGame()
  173. {
  174. int i, j;
  175. // Check if the game state has changed, if so redraw
  176. if (mGame->hasChanged())
  177. {
  178. // Draw background
  179. SDL_BlitSurface(mBmpBack, NULL, mScreen, NULL);
  180. // Draw preview block
  181. if (mGame->showPreview())
  182. {
  183. for (i = 0; i < Game::TETROMINO_SIZE; ++i)
  184. {
  185. for (j = 0; j < Game::TETROMINO_SIZE; ++j)
  186. {
  187. if (mGame->nextBlock().cells[i][j] != Game::EMPTY_CELL)
  188. {
  189. drawTile(PREVIEW_X + (TILE_SIZE * i),
  190. PREVIEW_Y + (TILE_SIZE * j),
  191. mGame->nextBlock().cells[i][j], false);
  192. }
  193. }
  194. }
  195. }
  196. #ifdef STC_SHOW_GHOST_PIECE
  197. // Draw shadow tetromino
  198. if (mGame->showShadow() && mGame->shadowGap() > 0)
  199. {
  200. for (i = 0; i < Game::TETROMINO_SIZE; ++i)
  201. {
  202. for (j = 0; j < Game::TETROMINO_SIZE; ++j)
  203. {
  204. if (mGame->fallingBlock().cells[i][j] != Game::EMPTY_CELL)
  205. {
  206. drawTile(BOARD_X + (TILE_SIZE * (mGame->fallingBlock().x + i)),
  207. BOARD_Y + (TILE_SIZE * (mGame->fallingBlock().y + mGame->shadowGap() + j)),
  208. mGame->fallingBlock().cells[i][j], true);
  209. }
  210. }
  211. }
  212. }
  213. #endif
  214. // Draw the cells in the board
  215. for (i = 0; i < Game::BOARD_TILEMAP_WIDTH; ++i)
  216. {
  217. for (j = 0; j < Game::BOARD_TILEMAP_HEIGHT; ++j)
  218. {
  219. if (mGame->getCell(i, j) != Game::EMPTY_CELL)
  220. {
  221. drawTile(BOARD_X + (TILE_SIZE * i),
  222. BOARD_Y + (TILE_SIZE * j),
  223. mGame->getCell(i, j), false);
  224. }
  225. }
  226. }
  227. // Draw falling tetromino
  228. for (i = 0; i < Game::TETROMINO_SIZE; ++i)
  229. {
  230. for (j = 0; j < Game::TETROMINO_SIZE; ++j)
  231. {
  232. if (mGame->fallingBlock().cells[i][j] != Game::EMPTY_CELL)
  233. {
  234. drawTile(BOARD_X + (TILE_SIZE * (mGame->fallingBlock().x + i)),
  235. BOARD_Y + (TILE_SIZE * (mGame->fallingBlock().y + j)),
  236. mGame->fallingBlock().cells[i][j], false);
  237. }
  238. }
  239. }
  240. // Draw game statistic data
  241. if (!mGame->isPaused())
  242. {
  243. drawNumber(LEVEL_X, LEVEL_Y, mGame->stats().level, LEVEL_LENGTH, Game::COLOR_WHITE);
  244. drawNumber(LINES_X, LINES_Y, mGame->stats().lines, LINES_LENGTH, Game::COLOR_WHITE);
  245. drawNumber(SCORE_X, SCORE_Y, mGame->stats().score, SCORE_LENGTH, Game::COLOR_WHITE);
  246. drawNumber(TETROMINO_X, TETROMINO_L_Y, mGame->stats().pieces[Game::TETROMINO_L], TETROMINO_LENGTH, Game::COLOR_ORANGE);
  247. drawNumber(TETROMINO_X, TETROMINO_I_Y, mGame->stats().pieces[Game::TETROMINO_I], TETROMINO_LENGTH, Game::COLOR_CYAN);
  248. drawNumber(TETROMINO_X, TETROMINO_T_Y, mGame->stats().pieces[Game::TETROMINO_T], TETROMINO_LENGTH, Game::COLOR_PURPLE);
  249. drawNumber(TETROMINO_X, TETROMINO_S_Y, mGame->stats().pieces[Game::TETROMINO_S], TETROMINO_LENGTH, Game::COLOR_GREEN);
  250. drawNumber(TETROMINO_X, TETROMINO_Z_Y, mGame->stats().pieces[Game::TETROMINO_Z], TETROMINO_LENGTH, Game::COLOR_RED);
  251. drawNumber(TETROMINO_X, TETROMINO_O_Y, mGame->stats().pieces[Game::TETROMINO_O], TETROMINO_LENGTH, Game::COLOR_YELLOW);
  252. drawNumber(TETROMINO_X, TETROMINO_J_Y, mGame->stats().pieces[Game::TETROMINO_J], TETROMINO_LENGTH, Game::COLOR_BLUE);
  253. drawNumber(PIECES_X, PIECES_Y, mGame->stats().totalPieces, PIECES_LENGTH, Game::COLOR_WHITE);
  254. }
  255. // Inform the game that we are done with the changed state
  256. mGame->onChangeProcessed();
  257. // Swap video buffers
  258. SDL_Flip(mScreen);
  259. }
  260. // Resting game
  261. SDL_Delay(SLEEP_TIME);
  262. }
  263. // Return a random positive integer number
  264. int PlatformSdl::random()
  265. {
  266. return rand();
  267. }
  268. // Release platform allocated resources
  269. void PlatformSdl::end()
  270. {
  271. // Free all the created surfaces
  272. SDL_FreeSurface(mBmpTiles);
  273. SDL_FreeSurface(mBmpBack);
  274. SDL_FreeSurface(mBmpNumbers);
  275. SDL_FreeSurface(mScreen);
  276. // Shut down SDL
  277. SDL_Quit();
  278. }
  279. }