Game entry for the Game Programming Contest sponsored by OutrageousCircuits.com (RETRO Game Console)

Dependencies:   mbed

Fork of Official_RETRO by GHI Electronics

Game.cpp

Committer:
Rogerup
Date:
2015-03-01
Revision:
4:2d41b942a823
Parent:
3:7680307a02d7

File content as of revision 4:2d41b942a823:

#include "Game.h"
#include <cstdarg>

#define SOUNDWIN    1

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::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);
}

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();
}

void Game::tick()
{
    tickCounter++;
    
    playSound();
    
    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::checkButtons()
{
    bool kSquare = false, kCircle = false, kUp = false, kDown = false, kLeft = false, kRight = false;
    
    //
    // 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;
    
    //
    // 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::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::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::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;

    //
    // 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;
                }

    //
    // 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;

            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::showTable()
{
    for( int i=0; i<TABH; i++ )
        for( int j=0; j<TABW; j++ )
            showPiece(i,j);
}

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( 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 );
    }
    
    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 );
    }
        
    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] );
        
    if( ball > 1 )
        disp.drawNumber( j*8+2, i*8+1, ball-1, WHITE, BLUE );
}

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::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::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::playBam()
{
    if( bamTicksLeft == 0 )
        return;
        
    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::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); }
}