Game entry for the Game Programming Contest sponsored by OutrageousCircuits.com (RETRO Game Console)
Fork of Official_RETRO by
Diff: Game.cpp
- Revision:
- 4:2d41b942a823
- Parent:
- 3:7680307a02d7
--- a/Game.cpp Sat Feb 14 11:40:55 2015 +0000 +++ b/Game.cpp Sun Mar 01 09:34:29 2015 +0000 @@ -1,288 +1,332 @@ #include "Game.h" +#include <cstdarg> + +#define SOUNDWIN 1 -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."; - -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; - +unsigned short piececolor[5] = { 0, LGRAY, WHITE, WHITE, DGRAY }; + +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) +{ 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); +void Game::showNumber(int y, int val) +{ + char buf[8]; + int len = sprintf(buf, "%d", val); + + disp.fillRect( 128, y+9, 32, 8, BLACK ); + + if( val < 1000 ) + disp.drawString(145-len*3, y+9, buf, CYAN, BLACK); + else + disp.drawString(145-3*3, y+9, "...", CYAN, BLACK); } -int Game::writeRegister(char address, char value) { - char buffer[2] = { address, value }; - - return this->i2c.write(Game::I2C_ADDR, buffer, 2); +void Game::initialize() +{ + disp.clear(); + led1 = 0; led2 = 0; + pwm = 0; pwm.period_ms(1); + + tickCounter = 1000; + tickWin = 0; + bamTicksLeft = 0; + + level = 0; oldLevel = -1; + for( int lev=0; lev<L_LAST; lev++ ) + your[lev] = 10000; + + initTable(); } -double Game::convert(char* buffer) { - double val = ((buffer[0] << 2) | (buffer[1] >> 6)); - - if (val > 511.0) - val -= 1024.0; +void Game::tick() +{ + tickCounter++; + + playSound(); - return val / 512.0; + if( levelCompleted ) { + if( (tickCounter % 40) == 0 ) { led1 = 1; led2 = 0; } + if( (tickCounter % 40) == 20 ) { led1 = 0; led2 = 1; } + } + + checkButtons(); + if( level != L_INTRO) + showTable(); + + playBam(); + + wait_ms(25); } -void Game::getXYZ(double& x, double& y, double& z) { - char buffer[6]; +void Game::checkButtons() +{ + bool kSquare = false, kCircle = false, kUp = false, kDown = false, kLeft = false, kRight = false; - this->readRegisters(0x01, buffer, 6); + // + // Repeat key after 16*25ms = 400ms + // + char noKey = 0; + if( !square.read() ) kSquare = !lastKey; else noKey++; + if( !circle.read() ) kCircle = !lastKey; else noKey++; + if( !up.read() ) kUp = !lastKey; else noKey++; + if( !down.read() ) kDown = !lastKey; else noKey++; + if( !left.read() ) kLeft = !lastKey; else noKey++; + if( !right.read() ) kRight = !lastKey; else noKey++; + if( noKey < 6 ) { + if( lastKey == 0 ) lastKey = 16; else lastKey--; + } else + lastKey = 0; - x = this->convert(buffer); - y = this->convert(buffer + 2); - z = this->convert(buffer + 4); + // + // Select level + // + if( kSquare ) if( ++level > L_LAST ) level = L_INTRO; + if( kCircle ) if( --level < L_INTRO ) level = L_INTRO + 1; //L_LAST; + if( kSquare || kCircle || (!up.read() && !left.read()) ) + initTable(); + + // + // Arrow keys to move + // + if( kUp ) moveTable( -1, 0, 1, TABH, 0, TABW ); + if( kDown ) moveTable( 1, 0, TABH-2, -1, 0, TABW ); + if( kLeft ) moveTable( 0, -1, 0, TABH, 1, TABW ); + if( kRight ) moveTable( 0, 1, 0, TABH, TABW-2, -1 ); } -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::fillTable(char i0, char j0, char h, char w, unsigned char v) +{ + unsigned char nh = v & 0xF0; + unsigned char nl = v & 0x0F; + + for( int i=i0; i<i0+h; i++ ) + for( int j=j0; j<j0+w; j++ ) { + if( v <= BONG ) { + table[i][j] = v; + } else { + if( nh && table[i][j] <= BONG ) table[i][j] = FREE; + if( nh ) table[i][j] = (table[i][j] & 0x0F) | nh; + if( nl ) table[i][j] = (table[i][j] & 0xF0) | nl; + } + + if( nl > MARK ) nl += 0x01; + if( nh > BALL ) nh += 0x10; + + change[i] |= 1 << j; + } } -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::fillTableV( unsigned char v, ...) +{ + unsigned short pos; + + va_list arguments; + + va_start ( arguments, v ); + + while( (pos = (unsigned short)va_arg ( arguments, int)) != 0 ) { + unsigned char h = (pos & 0x00F0) >> 4; + unsigned char w = (pos & 0x000F) >> 0; + if( w == 0 ) w = 16; + + fillTable((pos&0xF000)>>12, (pos&0x0F00)>>8, h, w, v); + if( (v&0x0F) > MARK ) v += h*w; + if( v > BALL ) v += ((h*w) << 4); } + + va_end ( arguments ); } -void Game::drawPoint(int axis, double value) { - if (value < -1.0) - value = -1.0; +void Game::moveTable(int di, int dj, int i0, int i2, int j0, int j2 ) +{ + if( level == L_INTRO || levelCompleted ) return; + + int ai = 1, aj = 1, mov = 0; + + if( i2 < i0 ) ai = -1; + if( j2 < j0 ) aj = -1; - if (value > 1.0) - value = 1.0; + // + // Check BONG (RED) - If any ball hits BONG, no ball can move. + // + for( int i=i0; i!=i2; i+=ai ) + for( int j=j0; j!=j2; j+=aj ) + if( table[i][j] & 0xF0 ) + if( (table[i+di][j+dj] & 0x0F) == BONG ) { + showNumber( MOVY, ++moves ); + bamX = (j+dj)*8; bamY = (i+di)*8; + fillPieceV( BLACK, 0x0077, 0 ); + fillPieceV( YELLOW, 0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 ); + disp.draw((unsigned short *)piece, bamX, bamY, 7, 7); + bamTicksLeft = 5; + return; + } - value += 1.0; - value /= 2.0; - value = 1.0 - value; - value *= Game::GRAPH_HEIGHT; + // + // Move Balls + // + for( int i=i0; i!=i2; i+=ai ) + { + for( int j=j0; j!=j2; j+=aj ) + { + unsigned char ball = table[i][j] & 0xF0; + + if( !ball ) continue; + if( table[i+di][j+dj] & 0xF0 ) continue; + if( (table[i+di][j+dj] & 0x0F) == WALL ) continue; - 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(); + table[i+di][j+dj] |= ball; + table[i][j] &= 0x0F; + + change[i+di] |= 1 << (j+dj); + change[i] |= 1 << j; + + mov = 1; + } + } + moves += mov; + showNumber( MOVY, moves ); + + // + // Check if Level is Completed + // + int balls = 0, marks = 0; + for( int i=0; i<TABH; i++ ) { + for( int j=0; j<TABW; j++ ) + if( table[i][j] & 0xF0 ) { + balls++; + if( ((table[i][j] & 0x0F) - MARK + 1) == ((table[i][j] & 0xF0) >> 4) ) + marks++; + } + } + if( marks == balls ) { + levelCompleted = true; + if( moves < your[level] ) your[level] = moves; + showNumber( YOUY, your[level] ); + disp.drawString(8, 117, " LEVEL COMPLETED! ", RED, BLACK); + led1 = 1; led2 = 1; +#ifdef SOUNDWIN + tickWin = tickCounter; +#endif } } -void Game::initialize() { - this->initializeBall(); - - this->paddleX = DisplayN18::WIDTH / 2 - Game::PADDLE_WIDTH / 2; - this->pwmTicksLeft = 0; - this->lives = 10;//4; - - this->pwm.period_ms(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 = rand() % 2 ? 1 : -1; - this->ballSpeedY = rand() % 2 ? 1 : -1; +void Game::showTable() +{ + for( int i=0; i<TABH; i++ ) + for( int j=0; j<TABW; j++ ) + showPiece(i,j); } -void Game::tick() { - this->checkButtons(); +void Game::showPiece(int i, int j) +{ + if( !(change[i] & (1 << j)) ) return; + + change[i] &= ~(1 << j); + + unsigned char ball = table[i][j] >> 4; + unsigned char base = table[i][j] & 0x0F; + + unsigned char basec = base; + if( basec > MARK ) basec = MARK; + + fillPiece( 0, 0, 7, 7, piececolor[basec] ); - if (this->mode) { - this->clearPaddle(); - this->clearBall(); - - this->updatePaddle(); - this->updateBall(); + if( base == WALL ) { + fillPiece( 1, 1, 1, 1, 0 ); + fillPiece( 1, 5, 1, 1, 0 ); + fillPiece( 5, 1, 1, 1, 0 ); + fillPiece( 5, 5, 1, 1, 0 ); + } - this->checkCollision(); - - this->drawPaddle(); - this->drawBall(); + if( base == BONG ) { + fillPiece( 1, 1, 5, 5, RED ); + //fillPieceV( RED, 0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 ); + } + + if( base == MARK ) { + fillPiece( 3, 3, 1, 1, WHITE ); + } + + if( ball ) { + fillPieceV( BLUE, 0x1051,0x0175, 0x1651, 0 ); + //fillPieceV( WHITE 0x3311, 0 ); + } - this->checkPwm(); - this->checkLives(); - - wait_ms(25); - } - else { - double x, y, z; + disp.draw((unsigned short *)piece, j*8, i*8, 7, 7); + + if( base > MARK && !ball ) + disp.drawNumber( j*8+2, i*8+1, base-MARK, WHITE, piececolor[MARK] ); - this->getXYZ(x, y, z); - - this->checkGraphReset(); - this->drawPoint(0, x); - this->drawPoint(1, y); - this->drawPoint(2, z); - this->graphX++; - } + if( ball > 1 ) + disp.drawNumber( j*8+2, i*8+1, ball-1, WHITE, BLUE ); } -void Game::checkButtons() { - if (!this->square.read()) { - this->mode = !this->mode; - - this->disp.clear(); - - if (!this->mode) { - this->graphX = 0; - - this->drawAxes(); - } - - 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::fillPiece(int x0, int y0, int w, int h, unsigned short v) +{ + for( int y=y0; y<y0+h; y++ ) + for( int x=x0; x<x0+w; x++ ) + piece[y][x] = v; } -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::fillPieceV(unsigned short color, ...) +{ + va_list arguments; + va_start( arguments, color ); + + int pos; + while( (pos = va_arg ( arguments, int)) != 0 ) + fillPiece( (pos&0xF000)>>12, (pos&0x0F00)>>8, (pos & 0x00F0) >> 4, (pos & 0x000F) >> 0, color ); + + va_end( arguments ); } -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); +void Game::showImage( unsigned char x, unsigned char y, unsigned short back, unsigned short front, ... ) +{ + fillPieceV( back, 0x0077, 0 ); + + va_list args; + va_start( args, front ); + + int pos; + while( (pos = va_arg ( args, int)) != 0 ) + fillPiece( (pos&0xF000)>>12, (pos&0x0F00)>>8, (pos & 0x00F0) >> 4, (pos & 0x000F) >> 0, front ); + + va_end( args ); + + disp.draw( (unsigned short *)piece, x, y, 7, 7 ); } -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); -} - -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; +void Game::playBam() +{ + if( bamTicksLeft == 0 ) + return; - if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS >= DisplayN18::WIDTH && this->ballSpeedX > 0)) - this->ballSpeedX *= -1; - - if (this->ballY - Game::BALL_RADIUS < 0 && this->ballSpeedY < 0) - this->ballSpeedY *= -1; - - 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--; - } - else if (this->ballX > this->paddleX && this->ballX < this->paddleX + Game::PADDLE_WIDTH) { - this->ballSpeedY *= -1; - - this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS; - } + if( --bamTicksLeft % 2 ) { + led1 = 1; + led2 = 1; + } else { + led1 = 0; + led2 = 0; + } + + //pwm.write(0.5); + pwm.period_us(50); + pwm.pulsewidth_us(50); + + if (bamTicksLeft == 0) { + pwm.write(0.0); + fillPieceV( WHITE, 0x0077, 0 ); + fillPieceV( RED, 0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 ); + disp.draw((unsigned short *)piece, bamX, bamY, 7, 7); } } -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); - - while (this->circle.read()) - wait_ms(1); - - this->initialize(); - } - else { - this->disp.drawCharacter(0, 0, static_cast<char>(this->lives + '0'), DisplayN18::WHITE, DisplayN18::BLACK); - } +void Game::playSound() +{ + if( tickCounter == tickWin + 1 ) { pwm = 0.02; pwm.period( 1.0 / 330.0 ); } + if( tickCounter == tickWin + 9 ) { pwm = 0.02; pwm.period( 1.0 / 440.0 ); } + if( tickCounter == tickWin + 25 ) { pwm = 0; pwm.period_ms(1); } } \ No newline at end of file