My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brickout.cpp 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "../../../inc/MarlinConfigPre.h"
  23. #if ENABLED(MARLIN_BRICKOUT)
  24. #include "game.h"
  25. #define BRICK_H 5
  26. #define BRICK_TOP MENU_FONT_ASCENT
  27. #define PADDLE_H 2
  28. #define PADDLE_VEL 3
  29. #define PADDLE_W ((LCD_PIXEL_WIDTH) / 8)
  30. #define PADDLE_Y (LCD_PIXEL_HEIGHT - 1 - PADDLE_H)
  31. #define BRICK_W ((LCD_PIXEL_WIDTH) / (BRICK_COLS))
  32. #define BRICK_BOT (BRICK_TOP + BRICK_H * BRICK_ROWS - 1)
  33. #define BRICK_COL(X) ((X) / (BRICK_W))
  34. #define BRICK_ROW(Y) ((Y - (BRICK_TOP)) / (BRICK_H))
  35. brickout_data_t &bdat = marlin_game_data.brickout;
  36. inline void reset_bricks(const uint16_t v) {
  37. bdat.brick_count = (BRICK_COLS) * (BRICK_ROWS);
  38. LOOP_L_N(i, BRICK_ROWS) bdat.bricks[i] = v;
  39. }
  40. void reset_ball() {
  41. constexpr uint8_t ball_dist = 24;
  42. bdat.bally = BTOF(PADDLE_Y - ball_dist);
  43. bdat.ballv = FTOP(1.3f);
  44. bdat.ballh = -FTOP(1.25f);
  45. uint8_t bx = bdat.paddle_x + (PADDLE_W) / 2 + ball_dist;
  46. if (bx >= LCD_PIXEL_WIDTH - 10) { bx -= ball_dist * 2; bdat.ballh = -bdat.ballh; }
  47. bdat.ballx = BTOF(bx);
  48. bdat.hit_dir = -1;
  49. }
  50. void BrickoutGame::game_screen() {
  51. if (game_frame()) { // Run logic twice for finer resolution
  52. // Update Paddle Position
  53. bdat.paddle_x = constrain(int8_t(ui.encoderPosition), 0, (LCD_PIXEL_WIDTH - (PADDLE_W)) / (PADDLE_VEL));
  54. ui.encoderPosition = bdat.paddle_x;
  55. bdat.paddle_x *= (PADDLE_VEL);
  56. // Run the ball logic
  57. if (game_state) do {
  58. // Provisionally update the ball position
  59. const fixed_t newx = bdat.ballx + bdat.ballh, newy = bdat.bally + bdat.ballv; // current next position
  60. if (!WITHIN(newx, 0, BTOF(LCD_PIXEL_WIDTH - 1))) { // out in x?
  61. bdat.ballh = -bdat.ballh; _BUZZ(5, 220); // bounce x
  62. }
  63. if (newy < 0) { // out in y?
  64. bdat.ballv = -bdat.ballv; _BUZZ(5, 280); // bounce v
  65. bdat.hit_dir = 1;
  66. }
  67. // Did the ball go below the bottom?
  68. else if (newy > BTOF(LCD_PIXEL_HEIGHT)) {
  69. _BUZZ(500, 75);
  70. if (--bdat.balls_left) reset_ball(); else game_state = 0;
  71. break; // done
  72. }
  73. // Is the ball colliding with a brick?
  74. if (WITHIN(newy, BTOF(BRICK_TOP), BTOF(BRICK_BOT))) {
  75. const int8_t bit = BRICK_COL(FTOB(newx)), row = BRICK_ROW(FTOB(newy));
  76. const uint16_t mask = _BV(bit);
  77. if (bdat.bricks[row] & mask) {
  78. // Yes. Remove it!
  79. bdat.bricks[row] &= ~mask;
  80. // Score!
  81. score += BRICK_ROWS - row;
  82. // If bricks are gone, go to reset state
  83. if (!--bdat.brick_count) game_state = 2;
  84. // Bounce the ball cleverly
  85. if ((bdat.ballv < 0) == (bdat.hit_dir < 0)) { bdat.ballv = -bdat.ballv; bdat.ballh += fixed_t(random(-16, 16)); _BUZZ(5, 880); }
  86. else { bdat.ballh = -bdat.ballh; bdat.ballv += fixed_t(random(-16, 16)); _BUZZ(5, 640); }
  87. }
  88. }
  89. // Is the ball moving down and in paddle range?
  90. else if (bdat.ballv > 0 && WITHIN(newy, BTOF(PADDLE_Y), BTOF(PADDLE_Y + PADDLE_H))) {
  91. // Ball actually hitting paddle
  92. const int8_t diff = FTOB(newx) - bdat.paddle_x;
  93. if (WITHIN(diff, 0, PADDLE_W - 1)) {
  94. // Reverse Y direction
  95. bdat.ballv = -bdat.ballv; _BUZZ(3, 880);
  96. bdat.hit_dir = -1;
  97. // Near edges affects X velocity
  98. const bool is_left_edge = (diff <= 1);
  99. if (is_left_edge || diff >= PADDLE_W-1 - 1) {
  100. if ((bdat.ballh > 0) == is_left_edge) bdat.ballh = -bdat.ballh;
  101. }
  102. else if (diff <= 3) {
  103. bdat.ballh += fixed_t(random(-64, 0));
  104. NOLESS(bdat.ballh, BTOF(-2));
  105. NOMORE(bdat.ballh, BTOF(2));
  106. }
  107. else if (diff >= PADDLE_W-1 - 3) {
  108. bdat.ballh += fixed_t(random( 0, 64));
  109. NOLESS(bdat.ballh, BTOF(-2));
  110. NOMORE(bdat.ballh, BTOF(2));
  111. }
  112. // Paddle hit after clearing the board? Reset the board.
  113. if (game_state == 2) { reset_bricks(0xFFFF); game_state = 1; }
  114. }
  115. }
  116. bdat.ballx += bdat.ballh; bdat.bally += bdat.ballv; // update with new velocity
  117. } while (false);
  118. }
  119. u8g.setColorIndex(1);
  120. // Draw bricks
  121. if (PAGE_CONTAINS(BRICK_TOP, BRICK_BOT)) {
  122. LOOP_L_N(y, BRICK_ROWS) {
  123. const uint8_t yy = y * BRICK_H + BRICK_TOP;
  124. if (PAGE_CONTAINS(yy, yy + BRICK_H - 1)) {
  125. LOOP_L_N(x, BRICK_COLS) {
  126. if (TEST(bdat.bricks[y], x)) {
  127. const uint8_t xx = x * BRICK_W;
  128. LOOP_L_N(v, BRICK_H - 1)
  129. if (PAGE_CONTAINS(yy + v, yy + v))
  130. u8g.drawHLine(xx, yy + v, BRICK_W - 1);
  131. }
  132. }
  133. }
  134. }
  135. }
  136. // Draw paddle
  137. if (PAGE_CONTAINS(PADDLE_Y-1, PADDLE_Y)) {
  138. u8g.drawHLine(bdat.paddle_x, PADDLE_Y, PADDLE_W);
  139. #if PADDLE_H > 1
  140. u8g.drawHLine(bdat.paddle_x, PADDLE_Y-1, PADDLE_W);
  141. #if PADDLE_H > 2
  142. u8g.drawHLine(bdat.paddle_x, PADDLE_Y-2, PADDLE_W);
  143. #endif
  144. #endif
  145. }
  146. // Draw ball while game is running
  147. if (game_state) {
  148. const uint8_t by = FTOB(bdat.bally);
  149. if (PAGE_CONTAINS(by, by+1))
  150. u8g.drawFrame(FTOB(bdat.ballx), by, 2, 2);
  151. }
  152. // Or draw GAME OVER
  153. else
  154. draw_game_over();
  155. if (PAGE_UNDER(MENU_FONT_ASCENT)) {
  156. // Score Digits
  157. //const uint8_t sx = (LCD_PIXEL_WIDTH - (score >= 10 ? score >= 100 ? score >= 1000 ? 4 : 3 : 2 : 1) * MENU_FONT_WIDTH) / 2;
  158. constexpr uint8_t sx = 0;
  159. lcd_put_int(sx, MENU_FONT_ASCENT - 1, score);
  160. // Balls Left
  161. lcd_moveto(LCD_PIXEL_WIDTH - MENU_FONT_WIDTH * 3, MENU_FONT_ASCENT - 1);
  162. PGM_P const ohs = PSTR("ooo\0\0");
  163. lcd_put_u8str_P(ohs + 3 - bdat.balls_left);
  164. }
  165. // A click always exits this game
  166. if (ui.use_click()) exit_game();
  167. }
  168. #define SCREEN_M ((LCD_PIXEL_WIDTH) / 2)
  169. void BrickoutGame::enter_game() {
  170. init_game(2, game_screen); // 2 = reset bricks on paddle hit
  171. constexpr uint8_t paddle_start = SCREEN_M - (PADDLE_W) / 2;
  172. bdat.paddle_x = paddle_start;
  173. bdat.balls_left = 3;
  174. reset_bricks(0x0000);
  175. reset_ball();
  176. ui.encoderPosition = paddle_start / (PADDLE_VEL);
  177. }
  178. #endif // MARLIN_BRICKOUT