Modified version of official firmware for Outrageous Circuits' RETRO. See Game.cpp for change history.

Dependencies:   mbed

Fork of Official_RETRO by GHI Electronics

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Game.cpp Source File

Game.cpp

00001 // Updated version of the official firmware for the Outrageous Circuits RETRO
00002 // Modified by G. Andrew Duthie (devhammer)
00003 // Changes:
00004 // - Added sounds for all ball bounces
00005 // - Changed ball from square to circle
00006 // - Adjusted collision detection to add ball speed every 10 paddle hits
00007 // - Added scoring
00008 // - Added mute function (not fully implemented...needs to be set up on a button).
00009 
00010 #include "Game.h"
00011 
00012 const char* Game::LOSE_1 = "You lose.";
00013 const char* Game::LOSE_2 = "Press ship to restart.";
00014 const char* Game::SPLASH_1 = "Press ship to start.";
00015 const char* Game::SPLASH_2 = "Press robot to switch.";
00016 const char* Game::LIVES = "Lives: ";
00017 const char* Game::SCORE = "Score: ";
00018     
00019 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), pwm(P0_18), ain(P0_15), i2c(P0_5, P0_4) {
00020     srand(this->ain.read_u16());
00021     
00022     this->lastUp = false;
00023     this->lastDown = false;
00024     this->mode = true;
00025     
00026     this->i2c.frequency(400);
00027     this->writeRegister(0x2A, 0x01); 
00028     
00029     this->colors[0] = DisplayN18::RED;
00030     this->colors[1] = DisplayN18::GREEN;
00031     this->colors[2] = DisplayN18::BLUE;
00032     
00033     this->initialize();
00034 }
00035 
00036 void Game::readRegisters(char address, char* buffer, int len) {
00037     this->i2c.write(Game::I2C_ADDR, &address, 1, true);
00038     this->i2c.read(Game::I2C_ADDR | 1, buffer, len);
00039 }
00040 
00041 int Game::writeRegister(char address, char value) {    
00042     char buffer[2] = { address, value };
00043     
00044     return this->i2c.write(Game::I2C_ADDR, buffer, 2);
00045 }
00046 
00047 double Game::convert(char* buffer) {
00048     double val = ((buffer[0] << 2) | (buffer[1] >> 6));
00049             
00050     if (val > 511.0) 
00051         val -= 1024.0;
00052     
00053     return val / 512.0;
00054 }
00055 
00056 void Game::getXYZ(double& x, double& y, double& z) {
00057     char buffer[6];
00058     
00059     this->readRegisters(0x01, buffer, 6);
00060     
00061     x = this->convert(buffer);
00062     y = this->convert(buffer + 2);
00063     z = this->convert(buffer + 4);
00064 }
00065 
00066 void Game::printDouble(double value, int x, int y) {
00067     char buffer[10];
00068     int len = sprintf(buffer, "%.1f", value);
00069     
00070     this->disp.drawString(x, y, buffer, DisplayN18::WHITE, DisplayN18::BLACK);
00071 }
00072 
00073 void Game::drawAxes() {
00074     for (int i = 0; i < 3; i++) {
00075         this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING), 0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT, DisplayN18::WHITE);
00076         this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, DisplayN18::WIDTH, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, DisplayN18::WHITE);
00077     }
00078 }
00079 
00080 void Game::drawPoint(int axis, double value) {
00081     if (value < -1.0)
00082         value = -1.0;
00083 
00084     if (value > 1.0)
00085         value = 1.0;
00086 
00087     value += 1.0;
00088     value /= 2.0;
00089     value = 1.0 - value;
00090     value *= Game::GRAPH_HEIGHT;
00091 
00092     this->disp.setPixel(this->graphX, axis * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + (int)value, this->colors[axis]);
00093 }
00094 
00095 void Game::checkGraphReset() {
00096     if (this->graphX > DisplayN18::WIDTH) {
00097         this->graphX = 0;
00098         this->disp.clear();
00099         this->drawAxes();
00100     }
00101 }
00102 
00103 void Game::initialize() {    
00104     this->initializeBall();
00105         
00106     this->paddleX = DisplayN18::WIDTH / 2 - Game::PADDLE_WIDTH / 2;
00107     this->pwmTicksLeft = 0;
00108     this->lives = 4;
00109     this->score = 0;
00110     this->muted = false;
00111     
00112     this->pwm.period(1);
00113     this->pwm.write(0.00);
00114     
00115     this->disp.clear();
00116 }
00117     
00118 void Game::initializeBall() {
00119     this->ballX = DisplayN18::WIDTH / 2 - Game::BALL_RADIUS;
00120     this->ballY = DisplayN18::HEIGHT / 4 - Game::BALL_RADIUS;
00121     
00122     this->ballSpeedX = Game::BALL_STARTING_SPEED;
00123     this->ballSpeedY = Game::BALL_STARTING_SPEED;
00124 
00125     this->ballSpeedX *= (rand() % 2 ? 1 : -1);
00126     this->ballSpeedY *= (rand() % 2 ? 1 : -1);
00127 }
00128 
00129 void Game::tick() {  
00130     this->checkButtons();
00131     
00132     if (this->mode) {
00133         this->clearPaddle();
00134         this->clearBall();
00135         
00136         this->updatePaddle();
00137         this->updateBall();
00138     
00139         this->checkCollision();
00140         
00141         this->drawPaddle();        
00142         this->drawBall();
00143         
00144         this->checkPwm();
00145         this->checkLives(); 
00146         
00147         wait_ms(25);
00148     }
00149     else {    
00150         double x, y, z;
00151         
00152         this->getXYZ(x, y, z);
00153         
00154         this->checkGraphReset();
00155         this->drawPoint(0, x);
00156         this->drawPoint(1, y);
00157         this->drawPoint(2, z);
00158         this->graphX++;
00159     } 
00160 }
00161 
00162 void Game::checkButtons() {
00163     if (!this->square.read()) {
00164         //this->muted = !this->muted;
00165         this->mode = !this->mode;
00166         
00167         this->disp.clear();
00168         
00169         if (!this->mode) {
00170             this->graphX = 0;
00171             
00172             this->drawAxes();
00173         }
00174         
00175         //this->led1.write(this->muted);
00176         //this->led2.write(!this->muted);
00177         this->led1.write(this->mode);
00178         this->led2.write(!this->mode);
00179     }  
00180     
00181     bool xDir = this->ballSpeedX > 0;
00182     bool yDir = this->ballSpeedY > 0;
00183     bool isUp = !this->up.read();
00184     bool isDown = !this->down.read();
00185     
00186     if (isUp && isDown) goto end;
00187     if (!isUp && !isDown) goto end;
00188     
00189     if (isUp && this->lastUp) goto end;
00190     if (isDown && this->lastDown) goto end;
00191     
00192     if (!xDir) this->ballSpeedX *= -1;
00193     if (!yDir) this->ballSpeedY *= -1;
00194     
00195     if (isUp) {
00196         if (++this->ballSpeedX > 5) this->ballSpeedX = 5;
00197         if (++this->ballSpeedY > 5) this->ballSpeedY = 5;
00198     }
00199     else if (isDown) {
00200         if (--this->ballSpeedX == 0) this->ballSpeedX = 1;
00201         if (--this->ballSpeedY == 0) this->ballSpeedY = 1;
00202     }
00203     
00204     if (!xDir) this->ballSpeedX *= -1;
00205     if (!yDir) this->ballSpeedY *= -1;
00206     
00207 end:
00208     this->lastUp = isUp;
00209     this->lastDown = isDown;    
00210 }
00211 
00212 void Game::drawString(const char* str, int y) {
00213     this->disp.drawString(DisplayN18::WIDTH / 2 - (DisplayN18::CHAR_WIDTH + DisplayN18::CHAR_SPACING) * strlen(str) / 2, y, str, DisplayN18::WHITE, DisplayN18::BLACK);         
00214 }
00215 
00216 void Game::showSplashScreen() {
00217     this->drawString(Game::SPLASH_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT / 2);  
00218     this->drawString(Game::SPLASH_2, DisplayN18::HEIGHT / 2 + DisplayN18::CHAR_HEIGHT / 2); 
00219        
00220     while (this->circle.read())
00221         wait_ms(1);
00222         
00223     this->disp.clear();
00224 }
00225 
00226 void Game::clearPaddle() {
00227     this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLACK);    
00228 }
00229 
00230 void Game::drawPaddle() {
00231     this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLUE);    
00232 }
00233 
00234 void Game::updatePaddle() {
00235     if (this->left.read())
00236         this->paddleX += Game::PADDLE_SPEED;
00237         
00238     if (this->right.read())
00239         this->paddleX -= Game::PADDLE_SPEED;
00240 }
00241 
00242 void Game::clearBall() {   
00243     //this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::BLACK);
00244     //this->disp.fillCircle(this->ballX - Game::BALL_RADIUS, this->ballY - Game::BALL_RADIUS, Game::BALL_RADIUS, DisplayN18::BLACK);
00245     this->disp.fillCircle(this->ballX, this->ballY, Game::BALL_RADIUS, DisplayN18::BLACK);
00246     this->disp.setPixel(this->ballX, this->ballY, DisplayN18::BLACK);
00247 }
00248 
00249 void Game::drawBall() {
00250     //this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::RED);
00251     //this->disp.fillCircle(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS, DisplayN18::RED);
00252     this->disp.fillCircle(this->ballX, ballY, Game::BALL_RADIUS, DisplayN18::RED);
00253     this->disp.setPixel(this->ballX, this->ballY, DisplayN18::GREEN);
00254 }
00255 
00256 void Game::updateBall() {
00257     this->ballX += this->ballSpeedX;
00258     this->ballY += this->ballSpeedY;
00259 }
00260 
00261 void Game::checkCollision() {    
00262     if (this->paddleX < 0)
00263         this->paddleX = 0;
00264         
00265     if (this->paddleX + Game::PADDLE_WIDTH > DisplayN18::WIDTH)
00266         this->paddleX = DisplayN18::WIDTH - Game::PADDLE_WIDTH;
00267         
00268     //if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS >= DisplayN18::WIDTH && this->ballSpeedX > 0)) {
00269     if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS * 2 >= DisplayN18::WIDTH && this->ballSpeedX > 0)) {
00270         this->ballSpeedX *= -1;
00271         if(!this->muted) {
00272             this->pwm.period_ms(2);
00273             this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
00274         }
00275     }
00276         
00277     if (this->ballY - Game::BALL_RADIUS < (0 + DisplayN18::CHAR_HEIGHT) && this->ballSpeedY < 0){
00278         this->ballSpeedY *= -1;
00279         if(!this->muted) {
00280             this->pwm.period_ms(2);
00281             this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
00282         }
00283     }
00284         
00285     if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT - Game::PADDLE_HEIGHT && this->ballSpeedY > 0) {
00286         if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT) {
00287             this->initializeBall();
00288             
00289             this->lives--;
00290 
00291             if(this->lives > 0) {
00292                 if(!this->muted) {
00293                     this->pwm.period(1.0/220);
00294                     this->pwm.write(0.5);
00295                     wait_ms(150);
00296                     this->pwm.write(0.0);
00297                 }
00298             }
00299 
00300         }
00301         else if (this->ballX > this->paddleX && this->ballX < this->paddleX + Game::PADDLE_WIDTH) {
00302             this->ballSpeedY *= -1;
00303             
00304             if(!this->muted){
00305                 this->pwm.period_ms(1);
00306                 this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
00307             }
00308             this->score = this->score + 10;
00309             if(this->score % 100 == 0) {
00310                 if(this->ballSpeedX < 0){
00311                     this->ballSpeedX -= 1;
00312                 }
00313                 else {
00314                     this->ballSpeedX += 1;
00315                 }
00316                 this->ballSpeedY -= 1;
00317             }
00318         }
00319     }
00320     char buf[10];
00321     int a = this->score;
00322     sprintf(buf, "%d", a);
00323     this->disp.drawString(DisplayN18::WIDTH - (DisplayN18::CHAR_WIDTH * 12), 0, Game::SCORE, DisplayN18::WHITE, DisplayN18::BLACK);     
00324     this->disp.drawString(DisplayN18::WIDTH - (DisplayN18::CHAR_WIDTH * 4), 0, buf, DisplayN18::WHITE, DisplayN18::BLACK);   
00325 }
00326 
00327 void Game::checkPwm() {
00328     if (this->pwmTicksLeft == 0) {
00329          this->pwm.write(0.0);
00330     }
00331     else {
00332         this->pwmTicksLeft--;
00333         this->pwm.write(0.5); 
00334     }
00335 }
00336 
00337 void Game::checkLives() {
00338     if (this->lives == 0) {
00339         this->disp.clear();
00340                 
00341         this->drawString(Game::LOSE_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT); 
00342         this->drawString(Game::LOSE_2, DisplayN18::HEIGHT / 2);  
00343         
00344         if(!this->muted) {
00345             this->pwm.period(1.0/220);
00346             this->pwm.write(0.5);
00347             wait_ms(150);
00348             this->pwm.write(0.0);
00349     
00350             this->pwm.period(1.0/196);
00351             this->pwm.write(0.5);
00352             wait_ms(150);
00353             this->pwm.write(0.0);
00354     
00355             this->pwm.period(1.0/164.81);
00356             this->pwm.write(0.5);
00357             wait_ms(150);
00358             this->pwm.write(0.0);
00359         }
00360         
00361         while (this->circle.read())
00362             wait_ms(1);
00363             
00364         this->initialize();
00365     }
00366     else {
00367         this->disp.drawString(0, 0, Game::LIVES, DisplayN18::WHITE, DisplayN18::BLACK);
00368         this->disp.drawCharacter(DisplayN18::CHAR_WIDTH * 8, 0, static_cast<char>(this->lives + '0'), DisplayN18::WHITE, DisplayN18::BLACK);   
00369     }
00370 }