Some random attempts at programming the retro console

Dependencies:   LCD_ST7735 mbed

Fork of RETRO_Pong_Mod by G. Andrew Duthie

Game.cpp

Committer:
loop
Date:
2015-02-27
Revision:
7:c0f12f624832
Parent:
6:20788dfdb7b8
Child:
8:c63981a45c95

File content as of revision 7:c0f12f624832:

// 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).
// Further mods buy loop (loop23)
// Changes:
// - Removed useless stuff
// - Read accelerometer in game

#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),
                disp(P0_19, P0_20, P0_7, P0_21, P0_22, P1_15, P0_2, LCD_ST7735::RGB) {
    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] = Color565::Red;
    this->colors[1] = Color565::Green;
    this->colors[2] = Color565::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::getXY(double& x, double& y) {
    char buffer[4];
    this->readRegisters(0x01, buffer, 4);
    x = this->convert(buffer);
    y = this->convert(buffer + 2);
}

void Game::getXYZ(double& x, double& y, double& z) {
    char buffer[6];
    this->readRegisters(0x01, buffer, 4);
    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(font_ibm, x, y, buffer);
}

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, Color565::White);
        this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, disp.getWidth(), i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, Color565::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 > disp.getWidth()) {
        this->graphX = 0;
        this->disp.clearScreen();
        this->drawAxes();
    }
}

void Game::initialize() {    
    this->initializeBall();
    this->pwmTicksLeft = 0;
    this->lives = 4;
    this->score = 0;
    this->muted = false;
    
    this->pwm.period(1);
    this->pwm.write(0.00);
    this->disp.setOrientation(LCD_ST7735::Rotate270, false);    
    this->disp.clearScreen();
}
    
void Game::initializeBall() {
    this->ballX = disp.getWidth() / 2 - Game::BALL_RADIUS;
    this->ballY = disp.getHeight() / 4 - Game::BALL_RADIUS;
    this->ballSpeedX = 0;
    this->ballSpeedY = 0;
    this->ballAccelX = 0;
    this->ballAccelY = 0;
}

void Game::readAccel() {
    double x, y;
    this->getXY(x, y);
    x = x * 8;
    y = y * 8;
    this->ballAccelX = (int)x;
    this->ballAccelY = (int)y;
//    this->printDouble(x, 20, 15);
//    this->printDouble(y, 20, 25);
}

void Game::tick() {
    int tcount = 0;
    this->checkButtons();
    
    if (this->mode) {
        if ((tcount++ % 10) == 0) {
          this->readAccel();
        };
        this->clearBall();
        this->updateBall();
        this->checkCollision();
        this->drawBall();
        // this->checkPwm();
        //this->checkLives(); 
    }
    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.clearScreen();
        
        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(font_ibm,
                          this->disp.getWidth() / 2 - (CHAR_WIDTH + CHAR_SPACING) * strlen(str) / 2,
                          y, str);
}
void Game::showSplashScreen() {
    this->drawString(Game::SPLASH_1, this->disp.getHeight() / 2 - CHAR_HEIGHT / 2);  
    this->drawString(Game::SPLASH_2, this->disp.getHeight() / 2 + CHAR_HEIGHT / 2); 
       
    while (this->circle.read())
        wait_ms(5);
        
    this->disp.clearScreen();
}

void Game::clearBall() {   
    this->disp.fillRect(ballX - BALL_RADIUS,
                        ballY - BALL_RADIUS,
                        ballX + BALL_RADIUS,
                        ballY + BALL_RADIUS,
                        Color565::Black);
}

void Game::drawBall() {
    this->disp.fillRect(ballX - BALL_RADIUS,
                        ballY - BALL_RADIUS,
                        ballX + BALL_RADIUS,
                        ballY + BALL_RADIUS,
                        Color565::Red);
}

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

void Game::checkCollision() {
    // Does bounds checking first..   
    if ((this->ballX - Game::BALL_RADIUS * 2 <= 0 && this->ballSpeedX < 0) || 
        (this->ballX + Game::BALL_RADIUS * 2 >= disp.getWidth() && 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 * 2 <= 0 && this->ballSpeedY < 0) ||
        (this->ballY + Game::BALL_RADIUS * 2 >= disp.getHeight() && 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(LCDST7735.getWidth() - (DisplayN18::CHAR_WIDTH * 12), 0, Game::SCORE, Color565::White, DisplayN18::BLACK);     
    this->disp.drawString(LCDST7735.getWidth() - (DisplayN18::CHAR_WIDTH * 4), 0, buf, Color565::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.clearScreen();
                
        this->drawString(Game::LOSE_1, disp.getHeight() / 2 - CHAR_HEIGHT); 
        this->drawString(Game::LOSE_2, disp.getHeight() / 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(font_ibm, 0, 0, Game::LIVES);
        //this->disp.drawCharacter(DisplayN18::CHAR_WIDTH * 8, 0, static_cast<char>(this->lives + '0'), Color565::White, DisplayN18::BLACK);   
    }
}