Retro game that let's the player steer a ball through a hole filled maze. Has multiple levels of increasing difficulty.

Dependencies:   LCD_ST7735 MusicEngine RETRO_BallsAndThings mbed

Ball and Holes

In this game I attempted to create somewhat natural movement of the ball by implementing gravity and friction which combined over time determine the speed of the ball. Playing with the settings (aka. the magic numbers) that are spread out all over game.cpp, gives different effects, such as an icy, rough or liquid-like surface.

It took some time to figure out how to post my very first youtube video. Sorry for the shaky recording. Trying to record the video with my phone while playing the game in one hand was quite challenging, but here it is;

The left and right buttons are used to cheat: restart the current or go to the next level. Up and down control the game-tick. During game-play the robot-button shows the accelerator graph and the ship-button mutes the sound.

BTW. If your ball happens to get stuck, tilting the console in the opposite direction will set it free. For sake of argument: these magnetic wall-ends are in the words of Bob Ross "a happy accident". Since there is no specific code for it, others might call it a bug. As it results in more interesting game-play, I didn't attempt to fix it, but left a comment for those who dare to look at the mess I call code.

Committer:
maxint
Date:
Mon Mar 02 10:16:55 2015 +0000
Revision:
11:ef3cbc443f27
Parent:
10:1d75861242f7
more clean-up

Who changed what in which revision?

UserRevisionLine numberNew contents of line
maxint 0:87ab172a74b4 1 #include "Game.h"
maxint 0:87ab172a74b4 2
maxint 3:ca8b21da67dc 3 const char* Game::LOSE_1 = "Game over.";
maxint 0:87ab172a74b4 4 const char* Game::LOSE_2 = "Press ship to restart.";
maxint 6:e2eead4e53fb 5 const char* Game::SPLASH_1 = "-*- Ball and holes -*-";
maxint 2:d4de5a5866fe 6 const char* Game::SPLASH_2 = "Press ship to start.";
maxint 10:1d75861242f7 7 const char* Game::SPLASH_3 = "Tilt console to steer ball.";
maxint 0:87ab172a74b4 8
maxint 0:87ab172a74b4 9
maxint 0:87ab172a74b4 10 #define WHITE Color565::White
maxint 0:87ab172a74b4 11 #define BLACK Color565::Black
maxint 0:87ab172a74b4 12 #define BLUE Color565::Blue
maxint 0:87ab172a74b4 13 #define RED Color565::Red
maxint 0:87ab172a74b4 14 #define YELLOW Color565::Yellow
maxint 0:87ab172a74b4 15
maxint 0:87ab172a74b4 16 #define CHAR_WIDTH 8
maxint 0:87ab172a74b4 17 #define CHAR_HEIGHT 8
maxint 0:87ab172a74b4 18 #define HEIGHT this->disp.getHeight()
maxint 0:87ab172a74b4 19 #define WIDTH this->disp.getWidth()
maxint 0:87ab172a74b4 20
maxint 0:87ab172a74b4 21
maxint 0:87ab172a74b4 22
maxint 0:87ab172a74b4 23 Game::Game() : left(P0_14, PullUp), right(P0_11, PullUp), down(P0_12, PullUp), up(P0_13, PullUp), square(P0_16, PullUp), circle(P0_1, PullUp), led1(P0_9), led2(P0_8),
maxint 8:b119501f4ef0 24 ain(P0_15), disp(P0_19, P0_20, P0_7, P0_21, P0_22, P1_15, P0_2, LCD_ST7735::RGB), accel(this->I2C_ADDR, &disp), vGravity(0, 0), vFriction(-0.005, -0.005), ball(&disp)
maxint 0:87ab172a74b4 25 {
maxint 1:c1ee4c699517 26 this->disp.setOrientation(LCD_ST7735::Rotate270, false);
maxint 1:c1ee4c699517 27 this->disp.setForegroundColor(WHITE);
maxint 1:c1ee4c699517 28 this->disp.setBackgroundColor(BLACK);
maxint 1:c1ee4c699517 29 this->disp.clearScreen();
maxint 1:c1ee4c699517 30
maxint 0:87ab172a74b4 31 srand(this->ain.read_u16());
maxint 0:87ab172a74b4 32
maxint 0:87ab172a74b4 33 this->lastUp = false;
maxint 0:87ab172a74b4 34 this->lastDown = false;
maxint 3:ca8b21da67dc 35 this->mode = true; // mode: true=game, false=graph
maxint 6:e2eead4e53fb 36
maxint 3:ca8b21da67dc 37 this->nGameTickDelay=25; // game tickdelay can be adjusted using up/down
maxint 2:d4de5a5866fe 38
maxint 11:ef3cbc443f27 39 nTopWall=8; // room for display of game stats such as the score
maxint 6:e2eead4e53fb 40 for(int i=0; i<NUM_WALLS; i++)
maxint 6:e2eead4e53fb 41 aWalls[i]=Wall(&(this->disp));
maxint 7:6e7b789f4060 42 for(int i=0; i<NUM_HOLES; i++)
maxint 7:6e7b789f4060 43 aHoles[i]=Hole(&(this->disp));
maxint 5:67e75a4f0b52 44
maxint 5:67e75a4f0b52 45 this->snd.reset();
maxint 0:87ab172a74b4 46 }
maxint 0:87ab172a74b4 47
maxint 2:d4de5a5866fe 48 void Game::printDouble(double value, int x, int y)
maxint 2:d4de5a5866fe 49 {
maxint 0:87ab172a74b4 50 char buffer[10];
maxint 0:87ab172a74b4 51 int len = sprintf(buffer, "%.1f ", value);
maxint 0:87ab172a74b4 52
maxint 1:c1ee4c699517 53 this->disp.drawString(font_oem, x, y, buffer);
maxint 0:87ab172a74b4 54 }
maxint 0:87ab172a74b4 55
maxint 1:c1ee4c699517 56 void Game::initialize()
maxint 1:c1ee4c699517 57 {
maxint 8:b119501f4ef0 58 nBalls = 4;
maxint 8:b119501f4ef0 59 nScore = 0;
maxint 0:87ab172a74b4 60
maxint 8:b119501f4ef0 61 tWait.start(); // start the timer
maxint 5:67e75a4f0b52 62
maxint 6:e2eead4e53fb 63 for(int i=0; i<NUM_WALLS; i++)
maxint 8:b119501f4ef0 64 aWalls[i]=Wall(&(disp));
maxint 8:b119501f4ef0 65
maxint 8:b119501f4ef0 66 initLevel();
maxint 8:b119501f4ef0 67
maxint 8:b119501f4ef0 68 newBall(); // start first ball
maxint 8:b119501f4ef0 69 snd.play("T240 L16 O5 D E F");
maxint 8:b119501f4ef0 70 }
maxint 8:b119501f4ef0 71
maxint 8:b119501f4ef0 72 void Game::initLevel()
maxint 8:b119501f4ef0 73 {
maxint 8:b119501f4ef0 74 char szLevel0[]="01:1 0262 2787-2227 8187 ";
maxint 8:b119501f4ef0 75 char szLevel1[]="01:1 0292 0343 63:3 2484 0545 65:5 1797-5257 1617 2526 3637 7677 8586 9697 ";
maxint 8:b119501f4ef0 76 char szLevel2[]="01:1 0232 82:2 1343 6393 0484 1696-4143 6163 5355 9396 3435 7475 1617 4547 5758 6567 ";
maxint 8:b119501f4ef0 77 char szLevel3[]="01:1 0232 82:2 3444 1545 6696-4147 6166 2223 8284 1315 5358 9397 ";
maxint 8:b119501f4ef0 78
maxint 8:b119501f4ef0 79 disp.clearScreen();
maxint 8:b119501f4ef0 80 snd.reset();
maxint 8:b119501f4ef0 81
maxint 8:b119501f4ef0 82 // reset current walls and holes
maxint 8:b119501f4ef0 83 for(int i=0; i<NUM_WALLS; i++)
maxint 8:b119501f4ef0 84 aWalls[i].fActive=false;
maxint 8:b119501f4ef0 85 for(int i=0; i<NUM_HOLES; i++)
maxint 8:b119501f4ef0 86 aHoles[i].fActive=false;
maxint 8:b119501f4ef0 87
maxint 8:b119501f4ef0 88 // add walls/holes depending on level
maxint 8:b119501f4ef0 89 switch(nScore)
maxint 8:b119501f4ef0 90 {
maxint 8:b119501f4ef0 91 case 0:
maxint 8:b119501f4ef0 92 addWalls(szLevel0);
maxint 8:b119501f4ef0 93 addHoles("4411 6412 ");
maxint 8:b119501f4ef0 94 break;
maxint 8:b119501f4ef0 95 case 1:
maxint 8:b119501f4ef0 96 addWalls(szLevel1);
maxint 11:ef3cbc443f27 97 addHoles("6411 0312 9412 4612 5612 ");
maxint 8:b119501f4ef0 98 break;
maxint 8:b119501f4ef0 99 case 2:
maxint 8:b119501f4ef0 100 addWalls(szLevel1);
maxint 8:b119501f4ef0 101 addHoles("5711 0312 9412 4612 5612 ");
maxint 8:b119501f4ef0 102 break;
maxint 8:b119501f4ef0 103 case 3:
maxint 8:b119501f4ef0 104 addWalls(szLevel1);
maxint 8:b119501f4ef0 105 addHoles("0211 0312 9412 4612 5612 ");
maxint 8:b119501f4ef0 106 break;
maxint 8:b119501f4ef0 107
maxint 8:b119501f4ef0 108 case 4:
maxint 8:b119501f4ef0 109 addWalls(szLevel2);
maxint 8:b119501f4ef0 110 addHoles("4411 6112 5112 1512 8512 2712 7712 8612 ");
maxint 8:b119501f4ef0 111 break;
maxint 8:b119501f4ef0 112 case 5:
maxint 8:b119501f4ef0 113 addWalls(szLevel2);
maxint 8:b119501f4ef0 114 addHoles("5611 6112 5112 1512 8512 2712 7712 8612 ");
maxint 8:b119501f4ef0 115 break;
maxint 8:b119501f4ef0 116 case 6:
maxint 8:b119501f4ef0 117 addWalls(szLevel2);
maxint 8:b119501f4ef0 118 addHoles("9111 6112 5112 1512 8512 2712 7712 8612 ");
maxint 8:b119501f4ef0 119 break;
maxint 8:b119501f4ef0 120
maxint 8:b119501f4ef0 121 case 7:
maxint 8:b119501f4ef0 122 addWalls(szLevel3);
maxint 8:b119501f4ef0 123 addHoles("3411 4112 5112 6112 2212 3312 6312 1412 7412 8512 0612 2612 6612 8612 5712 7712 ");
maxint 8:b119501f4ef0 124 break;
maxint 8:b119501f4ef0 125 case 8:
maxint 8:b119501f4ef0 126 addWalls(szLevel3);
maxint 8:b119501f4ef0 127 addHoles("5211 4112 5112 6112 2212 3312 6312 1412 7412 8512 0612 2612 6612 8612 5712 7712 ");
maxint 8:b119501f4ef0 128 break;
maxint 8:b119501f4ef0 129 case 9:
maxint 8:b119501f4ef0 130 addWalls(szLevel3);
maxint 8:b119501f4ef0 131 addHoles("9711 4112 5112 6112 2212 3312 6312 1412 7412 8512 0612 2612 6612 8612 5712 7712 ");
maxint 8:b119501f4ef0 132 break;
maxint 8:b119501f4ef0 133 case 10:
maxint 8:b119501f4ef0 134 addWalls(szLevel3);
maxint 8:b119501f4ef0 135 addHoles("9111 4112 5112 6112 2212 3312 6312 1412 7412 8512 0612 2612 6612 8612 5712 7712 ");
maxint 8:b119501f4ef0 136 break;
maxint 8:b119501f4ef0 137 case 11:
maxint 8:b119501f4ef0 138 default:
maxint 8:b119501f4ef0 139 addWalls(szLevel3);
maxint 8:b119501f4ef0 140 addHoles("9111 4112 5112 6112 2212 3312 6312 1412 7452 8512 0612 2652 6612 8612 5712 7712 ");
maxint 8:b119501f4ef0 141 break;
maxint 6:e2eead4e53fb 142 }
maxint 6:e2eead4e53fb 143
maxint 6:e2eead4e53fb 144
maxint 8:b119501f4ef0 145 drawWalls();
maxint 8:b119501f4ef0 146 drawHoles();
maxint 8:b119501f4ef0 147 checkNumBalls();
maxint 8:b119501f4ef0 148 }
maxint 7:6e7b789f4060 149
maxint 7:6e7b789f4060 150
maxint 2:d4de5a5866fe 151
maxint 6:e2eead4e53fb 152 Point Game::getGridPos(char *sPos)
maxint 6:e2eead4e53fb 153 { // get item pos based on a grid definition of 16x16 pixel squares, layed out in a 10x8 grid
maxint 6:e2eead4e53fb 154 // sPos is a 2 character string containing the coordinates in decimal notation: xy
maxint 6:e2eead4e53fb 155 int x=(sPos[0]-'0')*16+8;
maxint 6:e2eead4e53fb 156 int y=(sPos[1]-'0')*16+8;
maxint 6:e2eead4e53fb 157 return(Point(x,y));
maxint 2:d4de5a5866fe 158 }
maxint 2:d4de5a5866fe 159
maxint 6:e2eead4e53fb 160 void Game::addWall(char *sWall)
maxint 6:e2eead4e53fb 161 { // add a wall based on a grid definition of 16x16 pixel squares, layed out in a 10x8 grid
maxint 6:e2eead4e53fb 162 // sWall is a 4 character string containing the edge coordinates in decimal notation: xyXY
maxint 6:e2eead4e53fb 163 // grid axis range from x: '0'-'9', ':'=10 - y: '0'-'8'
maxint 6:e2eead4e53fb 164 for(int i=0; i<NUM_WALLS; i++)
maxint 6:e2eead4e53fb 165 {
maxint 6:e2eead4e53fb 166 if(!aWalls[i].fActive)
maxint 6:e2eead4e53fb 167 {
maxint 6:e2eead4e53fb 168 int x1=sWall[0]-'0';
maxint 6:e2eead4e53fb 169 int y1=sWall[1]-'0';
maxint 6:e2eead4e53fb 170 int x2=sWall[2]-'0';
maxint 6:e2eead4e53fb 171 int y2=sWall[3]-'0';
maxint 7:6e7b789f4060 172 aWalls[i].setRect(Rectangle(x1*16,y1*16,x2*16+1,y2*16+1));
maxint 6:e2eead4e53fb 173 aWalls[i].fActive=true;
maxint 6:e2eead4e53fb 174 break;
maxint 2:d4de5a5866fe 175 }
maxint 2:d4de5a5866fe 176 }
maxint 0:87ab172a74b4 177 }
maxint 0:87ab172a74b4 178
maxint 8:b119501f4ef0 179 void Game::addWalls(char *sWalls)
maxint 8:b119501f4ef0 180 {
maxint 8:b119501f4ef0 181 char sWall[]="0000";
maxint 8:b119501f4ef0 182 for(int i=0; i<strlen(sWalls); i+=5)
maxint 8:b119501f4ef0 183 {
maxint 8:b119501f4ef0 184 strncpy(sWall, sWalls+i, 4);
maxint 8:b119501f4ef0 185 addWall(sWall);
maxint 8:b119501f4ef0 186 }
maxint 8:b119501f4ef0 187 }
maxint 8:b119501f4ef0 188
maxint 7:6e7b789f4060 189
maxint 6:e2eead4e53fb 190 void Game::drawWalls()
maxint 2:d4de5a5866fe 191 {
maxint 6:e2eead4e53fb 192 for(int i=0; i<NUM_WALLS; i++)
maxint 6:e2eead4e53fb 193 if(aWalls[i].fActive)
maxint 6:e2eead4e53fb 194 {
maxint 6:e2eead4e53fb 195 aWalls[i].draw();
maxint 6:e2eead4e53fb 196 }
maxint 6:e2eead4e53fb 197 }
maxint 2:d4de5a5866fe 198
maxint 7:6e7b789f4060 199 void Game::addHole(char *sHole)
maxint 7:6e7b789f4060 200 { // add a wall based on a grid definition of 16x16 pixel squares, layed out in a 10x8 grid
maxint 7:6e7b789f4060 201 // sWall is a 4 character string containing the edge coordinates in decimal notation: xyXY
maxint 7:6e7b789f4060 202 // grid axis range from x: '0'-'9', ':'=10 - y: '0'-'8'
maxint 7:6e7b789f4060 203 for(int i=0; i<NUM_WALLS; i++)
maxint 7:6e7b789f4060 204 {
maxint 7:6e7b789f4060 205 if(!aHoles[i].fActive)
maxint 7:6e7b789f4060 206 {
maxint 8:b119501f4ef0 207 int x=(sHole[0]-'0')*16+8;
maxint 8:b119501f4ef0 208 int y=(sHole[1]-'0')*16+8;
maxint 7:6e7b789f4060 209 int r=sHole[2]-'0';
maxint 7:6e7b789f4060 210 int c=sHole[3]-'0';
maxint 8:b119501f4ef0 211 int rnd1=0, rnd2=0;
maxint 8:b119501f4ef0 212 if(r==2) rnd1=rand() % r - r/2;
maxint 8:b119501f4ef0 213 if(r==2) rnd2=rand() % r - r/2;
maxint 8:b119501f4ef0 214 aHoles[i].setCirc(Circle(x+rnd1,y+rnd2, Game::HOLE_RADIUS));
maxint 7:6e7b789f4060 215 if(c==1) aHoles[i].setColor(Color565::Green);
maxint 7:6e7b789f4060 216 if(c==2) aHoles[i].setColor(Color565::Gray);
maxint 7:6e7b789f4060 217 if(c==3) aHoles[i].setColor(Color565::Red);
maxint 7:6e7b789f4060 218 aHoles[i].fActive=true;
maxint 7:6e7b789f4060 219 break;
maxint 7:6e7b789f4060 220 }
maxint 7:6e7b789f4060 221 }
maxint 7:6e7b789f4060 222 }
maxint 7:6e7b789f4060 223
maxint 8:b119501f4ef0 224 void Game::addHoles(char *sHoles)
maxint 8:b119501f4ef0 225 {
maxint 8:b119501f4ef0 226 char sHole[]="0000";
maxint 8:b119501f4ef0 227 for(int i=0; i<strlen(sHoles); i+=5)
maxint 8:b119501f4ef0 228 {
maxint 8:b119501f4ef0 229 strncpy(sHole, sHoles+i, 4);
maxint 8:b119501f4ef0 230 addHole(sHole);
maxint 8:b119501f4ef0 231 }
maxint 8:b119501f4ef0 232 }
maxint 8:b119501f4ef0 233
maxint 8:b119501f4ef0 234
maxint 7:6e7b789f4060 235 void Game::drawHoles()
maxint 7:6e7b789f4060 236 {
maxint 7:6e7b789f4060 237 for(int i=0; i<NUM_HOLES; i++)
maxint 7:6e7b789f4060 238 if(aHoles[i].fActive)
maxint 7:6e7b789f4060 239 {
maxint 7:6e7b789f4060 240 aHoles[i].draw();
maxint 7:6e7b789f4060 241 }
maxint 7:6e7b789f4060 242 }
maxint 7:6e7b789f4060 243
maxint 6:e2eead4e53fb 244 void Game::newBall()
maxint 6:e2eead4e53fb 245 { // add a ball to the game
maxint 6:e2eead4e53fb 246 Point ptBall=getGridPos("01");
maxint 6:e2eead4e53fb 247 ball.initialize(ptBall.getX(), ptBall.getY(), Game::BALL_RADIUS, Color565::White);
maxint 2:d4de5a5866fe 248 }
maxint 2:d4de5a5866fe 249
maxint 6:e2eead4e53fb 250 void Game::updateBall()
maxint 6:e2eead4e53fb 251 {
maxint 6:e2eead4e53fb 252 ball.update(); // update the ball position
maxint 6:e2eead4e53fb 253
maxint 6:e2eead4e53fb 254 // increase speed based on gravity
maxint 6:e2eead4e53fb 255 checkTilt();
maxint 8:b119501f4ef0 256 if(ball.vSpeed.getSize()<2.0)
maxint 6:e2eead4e53fb 257 ball.vSpeed.add(this->vGravity); // add some gravity
maxint 6:e2eead4e53fb 258
maxint 6:e2eead4e53fb 259 // decrease speed based on friction
maxint 6:e2eead4e53fb 260 Vector vDecel=ball.vSpeed.getNormalized();
maxint 6:e2eead4e53fb 261 vDecel.multiply(vFriction);
maxint 6:e2eead4e53fb 262 ball.vSpeed.add(vDecel);
maxint 6:e2eead4e53fb 263 }
maxint 6:e2eead4e53fb 264
maxint 6:e2eead4e53fb 265 void Game::redrawBall()
maxint 6:e2eead4e53fb 266 {
maxint 6:e2eead4e53fb 267 ball.redraw(); // update the ball position
maxint 6:e2eead4e53fb 268 }
maxint 6:e2eead4e53fb 269
maxint 2:d4de5a5866fe 270
maxint 2:d4de5a5866fe 271 void Game::tick()
maxint 2:d4de5a5866fe 272 {
maxint 6:e2eead4e53fb 273 checkButtons();
maxint 0:87ab172a74b4 274
maxint 6:e2eead4e53fb 275 if(mode)
maxint 6:e2eead4e53fb 276 {
maxint 0:87ab172a74b4 277 /*
maxint 0:87ab172a74b4 278 if(this->tWait.read_ms()>100)
maxint 0:87ab172a74b4 279 {
maxint 0:87ab172a74b4 280 this->tWait.reset();
maxint 0:87ab172a74b4 281 }
maxint 0:87ab172a74b4 282 */
maxint 2:d4de5a5866fe 283
maxint 6:e2eead4e53fb 284 updateBall(); // update the ball positions
maxint 0:87ab172a74b4 285
maxint 6:e2eead4e53fb 286 checkBallCollision();
maxint 5:67e75a4f0b52 287
maxint 6:e2eead4e53fb 288 redrawBall();
maxint 6:e2eead4e53fb 289
maxint 7:6e7b789f4060 290 if(this->tWait.read_ms()>100)
maxint 7:6e7b789f4060 291 { // redraw walls and holes every tenth second
maxint 7:6e7b789f4060 292 drawWalls();
maxint 7:6e7b789f4060 293 drawHoles();
maxint 7:6e7b789f4060 294
maxint 7:6e7b789f4060 295 checkNumBalls();
maxint 7:6e7b789f4060 296
maxint 7:6e7b789f4060 297 this->tWait.reset();
maxint 7:6e7b789f4060 298 }
maxint 0:87ab172a74b4 299
maxint 5:67e75a4f0b52 300 // this->snd.checkPwm();
maxint 1:c1ee4c699517 301 //this->checkScore();
maxint 6:e2eead4e53fb 302 //this->checkBall();
maxint 0:87ab172a74b4 303
maxint 6:e2eead4e53fb 304 wait_ms(nGameTickDelay); // can be adjusted using up/down
maxint 0:87ab172a74b4 305 }
maxint 6:e2eead4e53fb 306 else
maxint 6:e2eead4e53fb 307 {
maxint 6:e2eead4e53fb 308 accel.updateGraph();
maxint 0:87ab172a74b4 309 wait_ms(100);
maxint 0:87ab172a74b4 310 }
maxint 0:87ab172a74b4 311 }
maxint 0:87ab172a74b4 312
maxint 6:e2eead4e53fb 313 void Game::checkTilt()
maxint 6:e2eead4e53fb 314 { // move the gravity direction and weight by tilting the board
maxint 0:87ab172a74b4 315 double x, y, z;
maxint 0:87ab172a74b4 316 this->accel.getXYZ(x, y, z);
maxint 0:87ab172a74b4 317
maxint 6:e2eead4e53fb 318 vGravity=Vector(x,y);
maxint 11:ef3cbc443f27 319
maxint 11:ef3cbc443f27 320 // don't let the tilt generate too much gravity
maxint 9:7bd5def2115d 321 while(vGravity.getSize()>0.5)
maxint 9:7bd5def2115d 322 vGravity.multiply(Vector(0.9, 0.9));
maxint 0:87ab172a74b4 323 }
maxint 0:87ab172a74b4 324
maxint 0:87ab172a74b4 325 void Game::checkButtons()
maxint 0:87ab172a74b4 326 {
maxint 3:ca8b21da67dc 327 if(!this->square.read()) // note: button.read() is false (LOW/0) when pressed
maxint 0:87ab172a74b4 328 {
maxint 3:ca8b21da67dc 329 wait_ms(250); // el-cheapo deboounce
maxint 0:87ab172a74b4 330 this->mode = !this->mode;
maxint 0:87ab172a74b4 331
maxint 0:87ab172a74b4 332 this->disp.clearScreen();
maxint 0:87ab172a74b4 333
maxint 0:87ab172a74b4 334 if (!this->mode)
maxint 0:87ab172a74b4 335 {
maxint 1:c1ee4c699517 336 this->accel.resetGraph();
maxint 0:87ab172a74b4 337 }
maxint 0:87ab172a74b4 338
maxint 0:87ab172a74b4 339 this->led1.write(this->mode);
maxint 0:87ab172a74b4 340 this->led2.write(!this->mode);
maxint 0:87ab172a74b4 341 }
maxint 3:ca8b21da67dc 342 else if(!this->circle.read() && this->mode) // note: button.read() is false (LOW/0) when pressed
maxint 3:ca8b21da67dc 343 {
maxint 3:ca8b21da67dc 344 bool fMute=this->snd.getMute();
maxint 3:ca8b21da67dc 345 fMute=!fMute;
maxint 3:ca8b21da67dc 346 this->snd.setMute(fMute);
maxint 3:ca8b21da67dc 347 this->led2.write(fMute);
maxint 3:ca8b21da67dc 348 wait_ms(250); // el-cheapo deboounce
maxint 3:ca8b21da67dc 349 }
maxint 0:87ab172a74b4 350 else
maxint 0:87ab172a74b4 351 {
maxint 0:87ab172a74b4 352 bool isUp = !this->up.read();
maxint 0:87ab172a74b4 353 bool isDown = !this->down.read();
maxint 9:7bd5def2115d 354
maxint 9:7bd5def2115d 355 if(!this->left.read())
maxint 9:7bd5def2115d 356 { // cheat: restart level
maxint 10:1d75861242f7 357 snd.play("T240 L128 O6 CEF");
maxint 9:7bd5def2115d 358 ball.fActive=false;
maxint 9:7bd5def2115d 359 initLevel();
maxint 9:7bd5def2115d 360 wait_ms(500);
maxint 9:7bd5def2115d 361 newBall(); // start a new ball
maxint 9:7bd5def2115d 362 return;
maxint 9:7bd5def2115d 363 }
maxint 9:7bd5def2115d 364 else if(!this->right.read())
maxint 9:7bd5def2115d 365 { // cheat: next level
maxint 10:1d75861242f7 366 snd.play("T240 L128 O5 FEC");
maxint 9:7bd5def2115d 367 nScore++;
maxint 9:7bd5def2115d 368 ball.fActive=false;
maxint 9:7bd5def2115d 369 initLevel();
maxint 9:7bd5def2115d 370 wait_ms(500);
maxint 9:7bd5def2115d 371 newBall(); // start a new ball
maxint 9:7bd5def2115d 372 return;
maxint 9:7bd5def2115d 373 }
maxint 9:7bd5def2115d 374
maxint 0:87ab172a74b4 375
maxint 0:87ab172a74b4 376 if (isUp && isDown) goto end;
maxint 0:87ab172a74b4 377 if (!isUp && !isDown) goto end;
maxint 0:87ab172a74b4 378
maxint 0:87ab172a74b4 379 if (isUp && this->lastUp) goto end;
maxint 0:87ab172a74b4 380 if (isDown && this->lastDown) goto end;
maxint 0:87ab172a74b4 381
maxint 0:87ab172a74b4 382 if (isUp)
maxint 0:87ab172a74b4 383 {
maxint 3:ca8b21da67dc 384 if(this->nGameTickDelay<1000) this->nGameTickDelay=(float)this->nGameTickDelay*1.20;
maxint 3:ca8b21da67dc 385 this->printf(100, 0, "Speed: %d ", this->nGameTickDelay);
maxint 0:87ab172a74b4 386 }
maxint 0:87ab172a74b4 387 else if (isDown)
maxint 0:87ab172a74b4 388 {
maxint 3:ca8b21da67dc 389 if(this->nGameTickDelay>5) this->nGameTickDelay=(float)this->nGameTickDelay/1.20;
maxint 3:ca8b21da67dc 390 this->printf(100, 0, "Speed: %d ", this->nGameTickDelay);
maxint 0:87ab172a74b4 391 }
maxint 0:87ab172a74b4 392
maxint 0:87ab172a74b4 393 end:
maxint 0:87ab172a74b4 394 this->lastUp = isUp;
maxint 0:87ab172a74b4 395 this->lastDown = isDown;
maxint 0:87ab172a74b4 396 }
maxint 0:87ab172a74b4 397 }
maxint 0:87ab172a74b4 398
maxint 1:c1ee4c699517 399 void Game::drawString(const char* str, int y)
maxint 1:c1ee4c699517 400 {
maxint 1:c1ee4c699517 401 uint8_t width;
maxint 1:c1ee4c699517 402 uint8_t height;
maxint 1:c1ee4c699517 403
maxint 1:c1ee4c699517 404 this->disp.measureString(font_oem, str, width, height);
maxint 1:c1ee4c699517 405 this->disp.drawString(font_oem, WIDTH / 2 - width / 2, y, str);
maxint 1:c1ee4c699517 406
maxint 0:87ab172a74b4 407 }
maxint 0:87ab172a74b4 408
maxint 6:e2eead4e53fb 409 void Game::showSplashScreen()
maxint 6:e2eead4e53fb 410 {
maxint 0:87ab172a74b4 411 this->drawString(Game::SPLASH_1, HEIGHT / 2 - CHAR_HEIGHT / 2);
maxint 0:87ab172a74b4 412 this->drawString(Game::SPLASH_2, HEIGHT / 2 + CHAR_HEIGHT / 2);
maxint 5:67e75a4f0b52 413 this->drawString(Game::SPLASH_3, HEIGHT / 2 + CHAR_HEIGHT / 2 + 2*CHAR_HEIGHT);
maxint 6:e2eead4e53fb 414
maxint 0:87ab172a74b4 415 while (this->circle.read())
maxint 0:87ab172a74b4 416 wait_ms(1);
maxint 3:ca8b21da67dc 417 wait_ms(250); // el-cheapo deboounce
maxint 3:ca8b21da67dc 418
maxint 2:d4de5a5866fe 419 this->initialize(); // start a new game
maxint 0:87ab172a74b4 420 }
maxint 0:87ab172a74b4 421
maxint 6:e2eead4e53fb 422 void Game::checkBallCollision()
maxint 2:d4de5a5866fe 423 {
maxint 11:ef3cbc443f27 424 Rectangle rTop=Rectangle(0, -10, WIDTH, this->nTopWall); // top wall
maxint 11:ef3cbc443f27 425 Rectangle rBottom=Rectangle(0, HEIGHT, WIDTH, HEIGHT+10); // bottom wall
maxint 11:ef3cbc443f27 426 Rectangle rLeft=Rectangle(-10, 0, 0, HEIGHT); // left wall
maxint 11:ef3cbc443f27 427 Rectangle rRight=Rectangle(WIDTH, 0, WIDTH+10, HEIGHT); // right wall
maxint 2:d4de5a5866fe 428
maxint 11:ef3cbc443f27 429 if(ball.collides(rTop) && ball.vSpeed.isUp()) // top wall
maxint 6:e2eead4e53fb 430 ball.vSpeed.y=0;
maxint 11:ef3cbc443f27 431 if(ball.collides(rRight) && ball.vSpeed.isRight()) // right wall
maxint 6:e2eead4e53fb 432 ball.vSpeed.x=0;
maxint 11:ef3cbc443f27 433 if(ball.collides(rLeft) && ball.vSpeed.isLeft()) // left wall
maxint 6:e2eead4e53fb 434 ball.vSpeed.x=0;
maxint 11:ef3cbc443f27 435 if(ball.collides(rBottom) && ball.vSpeed.isDown()) // bottom wall
maxint 6:e2eead4e53fb 436 ball.vSpeed.y=0;
maxint 6:e2eead4e53fb 437
maxint 9:7bd5def2115d 438 for(int i=0; i<NUM_WALLS; i++) // check maze walls
maxint 6:e2eead4e53fb 439 {
maxint 6:e2eead4e53fb 440 if(aWalls[i].fActive)
maxint 2:d4de5a5866fe 441 {
maxint 6:e2eead4e53fb 442 Rectangle rWall=aWalls[i].getRect();
maxint 6:e2eead4e53fb 443 if(ball.collides(rWall))
maxint 2:d4de5a5866fe 444 {
maxint 7:6e7b789f4060 445 //printf(30, 0, "b: %.2f", ball.vSpeed.getSize());
maxint 6:e2eead4e53fb 446 if(rWall.isHorizontal())
maxint 7:6e7b789f4060 447 {
maxint 7:6e7b789f4060 448 if(fabs(ball.vSpeed.y)>0.8)
maxint 7:6e7b789f4060 449 snd.play("T240 L128 O4 A");
maxint 7:6e7b789f4060 450 ball.Bounce(Vector(1,-0.1));
maxint 7:6e7b789f4060 451 }
maxint 6:e2eead4e53fb 452 else
maxint 7:6e7b789f4060 453 {
maxint 7:6e7b789f4060 454 if(fabs(ball.vSpeed.x)>0.8)
maxint 7:6e7b789f4060 455 snd.play("T240 L128 O4 A");
maxint 7:6e7b789f4060 456 ball.Bounce(Vector(-0.1,1));
maxint 7:6e7b789f4060 457 }
maxint 11:ef3cbc443f27 458 // TODO: the ball can stick to the end of wall.
maxint 11:ef3cbc443f27 459 // To fix, the above code should compare the position of the ball relative to the wall, not just the speed and wall-orientation
maxint 11:ef3cbc443f27 460 // Although this is a bug, its causes some interesting game-play, so leave it in for now
maxint 11:ef3cbc443f27 461 // Magnetic walls are fun!
maxint 9:7bd5def2115d 462
maxint 6:e2eead4e53fb 463 aWalls[i].draw();
maxint 3:ca8b21da67dc 464 }
maxint 6:e2eead4e53fb 465 //printf(0, 100, "W: %d,%d - %d,%d", rWall.getX1(), rWall.getY1(), rWall.getX2(), rWall.getY2());
maxint 2:d4de5a5866fe 466 }
maxint 0:87ab172a74b4 467 }
maxint 7:6e7b789f4060 468
maxint 9:7bd5def2115d 469 for(int i=0; i<NUM_HOLES; i++) // check holes
maxint 7:6e7b789f4060 470 {
maxint 7:6e7b789f4060 471 if(aHoles[i].fActive)
maxint 7:6e7b789f4060 472 {
maxint 7:6e7b789f4060 473 Circle cHole=aHoles[i].getCirc();
maxint 7:6e7b789f4060 474 if(ball.collides(cHole))
maxint 7:6e7b789f4060 475 {
maxint 7:6e7b789f4060 476 if(aHoles[i].hasGoneIn(ball.getBoundingCircle()))
maxint 7:6e7b789f4060 477 {
maxint 8:b119501f4ef0 478 ball.fActive=false;
maxint 7:6e7b789f4060 479 if(i==0)
maxint 11:ef3cbc443f27 480 { // TODO: for now first hole array is assumed to be the target hole
maxint 8:b119501f4ef0 481 nScore++;
maxint 8:b119501f4ef0 482 snd.play("T240 L16 O5 CDEFG");
maxint 7:6e7b789f4060 483 }
maxint 7:6e7b789f4060 484 else
maxint 7:6e7b789f4060 485 {
maxint 8:b119501f4ef0 486 nBalls--;
maxint 8:b119501f4ef0 487 snd.beepLow();
maxint 7:6e7b789f4060 488 }
maxint 8:b119501f4ef0 489
maxint 8:b119501f4ef0 490 initLevel();
maxint 9:7bd5def2115d 491 wait_ms(500);
maxint 8:b119501f4ef0 492
maxint 8:b119501f4ef0 493 newBall(); // start a new ball
maxint 7:6e7b789f4060 494 }
maxint 7:6e7b789f4060 495 }
maxint 7:6e7b789f4060 496 }
maxint 7:6e7b789f4060 497 }
maxint 2:d4de5a5866fe 498 }
maxint 2:d4de5a5866fe 499
maxint 0:87ab172a74b4 500
maxint 1:c1ee4c699517 501 void Game::printf(int x, int y, const char *szFormat, ...)
maxint 0:87ab172a74b4 502 {
maxint 0:87ab172a74b4 503 char szBuffer[256];
maxint 0:87ab172a74b4 504 va_list args;
maxint 0:87ab172a74b4 505
maxint 0:87ab172a74b4 506 va_start(args, szFormat);
maxint 0:87ab172a74b4 507 vsprintf(szBuffer, szFormat, args);
maxint 0:87ab172a74b4 508 va_end(args);
maxint 1:c1ee4c699517 509 this->disp.drawString(font_oem, x, y, szBuffer);
maxint 0:87ab172a74b4 510 }
maxint 7:6e7b789f4060 511
maxint 7:6e7b789f4060 512 void Game::checkNumBalls()
maxint 7:6e7b789f4060 513 {
maxint 7:6e7b789f4060 514 if (this->nBalls == 0)
maxint 7:6e7b789f4060 515 { // game over
maxint 7:6e7b789f4060 516 char buf[256];
maxint 7:6e7b789f4060 517 this->disp.clearScreen();
maxint 7:6e7b789f4060 518
maxint 7:6e7b789f4060 519 this->drawString(Game::LOSE_1, HEIGHT / 2 - CHAR_HEIGHT);
maxint 7:6e7b789f4060 520 this->drawString(Game::LOSE_2, HEIGHT / 2);
maxint 7:6e7b789f4060 521 sprintf(buf,"Your score: %d ", this->nScore);
maxint 7:6e7b789f4060 522 this->drawString(buf, HEIGHT / 2 + CHAR_HEIGHT / 2 + CHAR_HEIGHT );
maxint 7:6e7b789f4060 523
maxint 7:6e7b789f4060 524 this->snd.play("T120 O3 L4 R4 F C F2 C");
maxint 7:6e7b789f4060 525 while (this->circle.read())
maxint 7:6e7b789f4060 526 wait_ms(1);
maxint 7:6e7b789f4060 527 wait_ms(250); // el-cheapo deboounce
maxint 7:6e7b789f4060 528 this->initialize();
maxint 7:6e7b789f4060 529 }
maxint 7:6e7b789f4060 530 else
maxint 7:6e7b789f4060 531 {
maxint 7:6e7b789f4060 532 printf(0, 0, "Balls: %d ", this->nBalls);
maxint 7:6e7b789f4060 533 printf(100, 0, "Score: %d ", this->nScore);
maxint 7:6e7b789f4060 534 }
maxint 7:6e7b789f4060 535 }