new mods upon mods by devhammer: - Added paddle control using tilting of the console - Finished mute function - Reduced flickering See game.cpp for full info.

Dependencies:   mbed

Fork of RETRO_Pong_Mod by G. Andrew Duthie

This is a mod of the official Pong game released with the RETRO game console.

Game.cpp

Committer:
devhammer
Date:
2015-01-15
Revision:
3:2f09c90a732d
Parent:
2:6ab46f2e851a
Child:
4:9ad3bc45b6ce

File content as of revision 3:2f09c90a732d:

// Updated version of the official firmware for the Outrageous Circuits RETRO
// Modified by G. Andrew Duthie (devhammer)
// Changes:
// - Added sounds for all ball bounces
// - Changed ball from square to circle
// - Adjusted collision detection to add ball speed every 10 paddle hits
// - Added scoring
// - Added mute function (not fully implemented...needs to be set up on a button).

#include "Game.h"

const char* Game::LOSE_1 = "You lose.";
const char* Game::LOSE_2 = "Press ship to restart.";
const char* Game::SPLASH_1 = "Press ship to start.";
const char* Game::SPLASH_2 = "Press robot to switch.";
const char* Game::LIVES = "Lives: ";
const char* Game::SCORE = "Score: ";
    
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) {
    srand(this->ain.read_u16());
    
    this->lastUp = false;
    this->lastDown = false;
    this->mode = true;
    
    this->i2c.frequency(400);
    this->writeRegister(0x2A, 0x01); 
    
    this->colors[0] = DisplayN18::RED;
    this->colors[1] = DisplayN18::GREEN;
    this->colors[2] = DisplayN18::BLUE;
    
    this->initialize();
}

void Game::readRegisters(char address, char* buffer, int len) {
    this->i2c.write(Game::I2C_ADDR, &address, 1, true);
    this->i2c.read(Game::I2C_ADDR | 1, buffer, len);
}

int Game::writeRegister(char address, char value) {    
    char buffer[2] = { address, value };
    
    return this->i2c.write(Game::I2C_ADDR, buffer, 2);
}

double Game::convert(char* buffer) {
    double val = ((buffer[0] << 2) | (buffer[1] >> 6));
            
    if (val > 511.0) 
        val -= 1024.0;
    
    return val / 512.0;
}

void Game::getXYZ(double& x, double& y, double& z) {
    char buffer[6];
    
    this->readRegisters(0x01, buffer, 6);
    
    x = this->convert(buffer);
    y = this->convert(buffer + 2);
    z = this->convert(buffer + 4);
}

void Game::printDouble(double value, int x, int y) {
    char buffer[10];
    int len = sprintf(buffer, "%.1f", value);
    
    this->disp.drawString(x, y, buffer, DisplayN18::WHITE, DisplayN18::BLACK);
}

void Game::drawAxes() {
    for (int i = 0; i < 3; i++) {
        this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING), 0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT, DisplayN18::WHITE);
        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);
    }
}

void Game::drawPoint(int axis, double value) {
    if (value < -1.0)
        value = -1.0;

    if (value > 1.0)
        value = 1.0;

    value += 1.0;
    value /= 2.0;
    value = 1.0 - value;
    value *= Game::GRAPH_HEIGHT;

    this->disp.setPixel(this->graphX, axis * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + (int)value, this->colors[axis]);
}

void Game::checkGraphReset() {
    if (this->graphX > DisplayN18::WIDTH) {
        this->graphX = 0;
        this->disp.clear();
        this->drawAxes();
    }
}

void Game::initialize() {    
    this->initializeBall();
        
    this->paddleX = DisplayN18::WIDTH / 2 - Game::PADDLE_WIDTH / 2;
    this->pwmTicksLeft = 0;
    this->lives = 4;
    this->score = 0;
    this->muted = false;
    
    this->pwm.period(1);
    this->pwm.write(0.00);
    
    this->disp.clear();
}
    
void Game::initializeBall() {
    this->ballX = DisplayN18::WIDTH / 2 - Game::BALL_RADIUS;
    this->ballY = DisplayN18::HEIGHT / 4 - Game::BALL_RADIUS;
    
    this->ballSpeedX = Game::BALL_STARTING_SPEED;
    this->ballSpeedY = Game::BALL_STARTING_SPEED;

    this->ballSpeedX *= (rand() % 2 ? 1 : -1);
    this->ballSpeedY *= (rand() % 2 ? 1 : -1);
}

void Game::tick() {  
    this->checkButtons();
    
    if (this->mode) {
        this->clearPaddle();
        this->clearBall();
        
        this->updatePaddle();
        this->updateBall();
    
        this->checkCollision();
        
        this->drawPaddle();        
        this->drawBall();
        
        this->checkPwm();
        this->checkLives(); 
        
        wait_ms(25);
    }
    else {    
        double x, y, z;
        
        this->getXYZ(x, y, z);
        
        this->checkGraphReset();
        this->drawPoint(0, x);
        this->drawPoint(1, y);
        this->drawPoint(2, z);
        this->graphX++;
    } 
}

void Game::checkButtons() {
    if (!this->square.read()) {
        //this->muted = !this->muted;
        this->mode = !this->mode;
        
        this->disp.clear();
        
        if (!this->mode) {
            this->graphX = 0;
            
            this->drawAxes();
        }
        
        //this->led1.write(this->muted);
        //this->led2.write(!this->muted);
        this->led1.write(this->mode);
        this->led2.write(!this->mode);
    }  
    
    bool xDir = this->ballSpeedX > 0;
    bool yDir = this->ballSpeedY > 0;
    bool isUp = !this->up.read();
    bool isDown = !this->down.read();
    
    if (isUp && isDown) goto end;
    if (!isUp && !isDown) goto end;
    
    if (isUp && this->lastUp) goto end;
    if (isDown && this->lastDown) goto end;
    
    if (!xDir) this->ballSpeedX *= -1;
    if (!yDir) this->ballSpeedY *= -1;
    
    if (isUp) {
        if (++this->ballSpeedX > 5) this->ballSpeedX = 5;
        if (++this->ballSpeedY > 5) this->ballSpeedY = 5;
    }
    else if (isDown) {
        if (--this->ballSpeedX == 0) this->ballSpeedX = 1;
        if (--this->ballSpeedY == 0) this->ballSpeedY = 1;
    }
    
    if (!xDir) this->ballSpeedX *= -1;
    if (!yDir) this->ballSpeedY *= -1;
    
end:
    this->lastUp = isUp;
    this->lastDown = isDown;    
}

void Game::drawString(const char* str, int y) {
    this->disp.drawString(DisplayN18::WIDTH / 2 - (DisplayN18::CHAR_WIDTH + DisplayN18::CHAR_SPACING) * strlen(str) / 2, y, str, DisplayN18::WHITE, DisplayN18::BLACK);         
}

void Game::showSplashScreen() {
    this->drawString(Game::SPLASH_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT / 2);  
    this->drawString(Game::SPLASH_2, DisplayN18::HEIGHT / 2 + DisplayN18::CHAR_HEIGHT / 2); 
       
    while (this->circle.read())
        wait_ms(1);
        
    this->disp.clear();
}

void Game::clearPaddle() {
    this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLACK);    
}

void Game::drawPaddle() {
    this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLUE);    
}

void Game::updatePaddle() {
    if (this->left.read())
        this->paddleX += Game::PADDLE_SPEED;
        
    if (this->right.read())
        this->paddleX -= Game::PADDLE_SPEED;
}

void Game::clearBall() {   
    //this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::BLACK);
    //this->disp.fillCircle(this->ballX - Game::BALL_RADIUS, this->ballY - Game::BALL_RADIUS, Game::BALL_RADIUS, DisplayN18::BLACK);
    this->disp.fillCircle(this->ballX, this->ballY, Game::BALL_RADIUS, DisplayN18::BLACK);
    this->disp.setPixel(this->ballX, this->ballY, DisplayN18::BLACK);
}

void Game::drawBall() {
    //this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::RED);
    //this->disp.fillCircle(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS, DisplayN18::RED);
    this->disp.fillCircle(this->ballX, ballY, Game::BALL_RADIUS, DisplayN18::RED);
    this->disp.setPixel(this->ballX, this->ballY, DisplayN18::GREEN);
}

void Game::updateBall() {
    this->ballX += this->ballSpeedX;
    this->ballY += this->ballSpeedY;
}

void Game::checkCollision() {    
    if (this->paddleX < 0)
        this->paddleX = 0;
        
    if (this->paddleX + Game::PADDLE_WIDTH > DisplayN18::WIDTH)
        this->paddleX = DisplayN18::WIDTH - Game::PADDLE_WIDTH;
        
    //if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS >= DisplayN18::WIDTH && this->ballSpeedX > 0)) {
    if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS * 2 >= DisplayN18::WIDTH && this->ballSpeedX > 0)) {
        this->ballSpeedX *= -1;
        if(!this->muted) {
            this->pwm.period_ms(2);
            this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
        }
    }
        
    if (this->ballY - Game::BALL_RADIUS < (0 + DisplayN18::CHAR_HEIGHT) && this->ballSpeedY < 0){
        this->ballSpeedY *= -1;
        if(!this->muted) {
            this->pwm.period_ms(2);
            this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
        }
    }
        
    if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT - Game::PADDLE_HEIGHT && this->ballSpeedY > 0) {
        if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT) {
            this->initializeBall();
            
            this->lives--;

            if(this->lives > 0) {
                if(!this->muted) {
                    this->pwm.period(1.0/220);
                    this->pwm.write(0.5);
                    wait_ms(150);
                    this->pwm.write(0.0);
                }
            }

        }
        else if (this->ballX > this->paddleX && this->ballX < this->paddleX + Game::PADDLE_WIDTH) {
            this->ballSpeedY *= -1;
            
            if(!this->muted){
                this->pwm.period_ms(1);
                this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;
            }
            this->score = this->score + 10;
            if(this->score % 100 == 0) {
                if(this->ballSpeedX < 0){
                    this->ballSpeedX -= 1;
                }
                else {
                    this->ballSpeedX += 1;
                }
                this->ballSpeedY -= 1;
            }
        }
    }
    char buf[10];
    int a = this->score;
    sprintf(buf, "%d", a);
    this->disp.drawString(DisplayN18::WIDTH - (DisplayN18::CHAR_WIDTH * 12), 0, Game::SCORE, DisplayN18::WHITE, DisplayN18::BLACK);     
    this->disp.drawString(DisplayN18::WIDTH - (DisplayN18::CHAR_WIDTH * 4), 0, buf, DisplayN18::WHITE, DisplayN18::BLACK);   
}

void Game::checkPwm() {
    if (this->pwmTicksLeft == 0) {
         this->pwm.write(0.0);
    }
    else {
        this->pwmTicksLeft--;
        this->pwm.write(0.5); 
    }
}

void Game::checkLives() {
    if (this->lives == 0) {
        this->disp.clear();
                
        this->drawString(Game::LOSE_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT); 
        this->drawString(Game::LOSE_2, DisplayN18::HEIGHT / 2);  
        
        if(!this->muted) {
            this->pwm.period(1.0/220);
            this->pwm.write(0.5);
            wait_ms(150);
            this->pwm.write(0.0);
    
            this->pwm.period(1.0/196);
            this->pwm.write(0.5);
            wait_ms(150);
            this->pwm.write(0.0);
    
            this->pwm.period(1.0/164.81);
            this->pwm.write(0.5);
            wait_ms(150);
            this->pwm.write(0.0);
        }
        
        while (this->circle.read())
            wait_ms(1);
            
        this->initialize();
    }
    else {
        this->disp.drawString(0, 0, Game::LIVES, DisplayN18::WHITE, DisplayN18::BLACK);
        this->disp.drawCharacter(DisplayN18::CHAR_WIDTH * 8, 0, static_cast<char>(this->lives + '0'), DisplayN18::WHITE, DisplayN18::BLACK);   
    }
}