A implementation of a simple bomberman game

Dependencies:   4DGL-uLCD-SE SDFileSystem mbed-rtos mbed wave_player

Fork of rtos_basic by mbed official

main.cpp

Committer:
apcastelein
Date:
2016-10-31
Revision:
7:12f7fd2b1a13
Parent:
3:c92e21f305d8

File content as of revision 7:12f7fd2b1a13:

#include "mbed.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "myBMP.h"
#include <string>
#include "navSwitch.h"
#include <mpr121.h>
#include "rtos.h"

float gameTime = 0.0;

string startText = "Loading";

bool gameRestart = false;

SDFileSystem sd(p11, p12, p13, p16, "sd"); //SD card

AnalogOut DACout(p18);

wave_player waver(&DACout);

uLCD_4DGL uLCD(p28,p27,p30);

int BACK = 0x2E0059;

char grid[7][7];

int o_p1I=0, o_p1J=0, o_p2I=6, o_p2J=6;
int p1I=0, p1J=0, p2I=6, p2J=6;

Nav_Switch myNav( p17, p6, p7, p5, p8);
bool navUpPress = false, navLeftPress = false,
     navRightPress = false, navDownPress = false, navFirePress = false;

I2C i2c(p9, p10);
Mpr121 pad(&i2c, Mpr121::ADD_VSS);

bool padUpPress = false, padLeftPress = false,
     padRightPress = false, padDownPress = false, padFirePress = false;

struct Bomb {
    float createTime;
    int playerId;
    int startI, startJ;
    int power;
    int minX, maxX, minY, maxY;
    bool exploded, soundPlayed;
    float cookTime, expTime;
    int decay,color;
    int o_decay;
};

Bomb bombs[10];
int bombIndex=0, numBombs = 0;
int bufferSize = 10;

int p1Bombs=1, p2Bombs=1;
int p1Power=1, p2Power=1;

void resetVars(){
    gameRestart = false;
    
    o_p1I=0;
    o_p1J=0;
    o_p2I=6; 
    o_p2J=6;
    p1I=0;
    p1J=0;
    p2I=6;
    p2J=6;
    
    numBombs = 0;
    bombIndex = 0;
    
    p1Bombs = 1;
    p1Power = 1;
    p2Bombs = 1;
    p2Power = 1;
}

bool outOfBounds(int i, int j){
   return i < 0 || i > 6 || j < 0 || j > 6;
}

bool valid(int i, int j)
{
    if(outOfBounds(i,j)){
        return false;    
    }

    return grid[i][j] != 'b' && grid[i][j] != 's';
}

int getX(int j)
{
    return 8+16*j;
}

int getY(int i)
{
    return 8+16*i;
}

void createBomb(int pId, int i, int j)
{
    Bomb *b = &bombs[(bombIndex + numBombs)%bufferSize];
    numBombs++;

    b->playerId = pId;
    b->createTime = gameTime;
    b->startI = i;
    b->startJ = j;
    b->power = (pId == 1)? p1Power : p2Power;
    if(pId == 1) {
        p1Bombs--;
    } else {
        p2Bombs--;
    }
    b->exploded = false;
    b->cookTime = 1.5;
    b->expTime = .8;
    b->decay = 0;
    b->o_decay = 0;
    b->soundPlayed = false;
}

void resetBlock(int i, int j){
    if(grid[i][j] == ' '){
        grid[i][j] = 'c';
        return;
    }
    int rr = (int)(gameTime * 1000) + 1023 * i + 523 * j;
    if(rr%3 == 0){
        grid[i][j] = 'k';
    }else if(rr%3 == 1){
        grid[i][j] = 'd';
    }else{
        grid[i][j] = 'c'; 
    }
}

void clearArea(Bomb *b)
{
    //find minX
    int p = b->power;
    int i = b->startI;
    int j = b->startJ;
    while(p>0) {
        if(outOfBounds(i,j-1) || grid[i][j-1] == 's') {
            break;
        }           
        resetBlock(i,j-1);
        j--;
        p--;
    }
    b->minX = getX(j) + ((p==0)?3:0);

    //find maxX
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i,j+1) || grid[i][j+1] == 's') {
            break;
        }
        resetBlock(i,j+1);
        j++;
        p--;
    }
    b->maxX = getX(j+1)+((p==0)?-3:0);

    //find minY
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i-1,j) || grid[i-1][j] == 's') {
            break;
        }
        resetBlock(i-1,j);
        i--;
        p--;
    }
    b->minY = getY(i) + ((p==0)?3:0);

    //find maxY
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i+1,j) || grid[i+1][j] == 's') {
            break;
        }
        resetBlock(i+1,j);
        i++;
        p--;
    }
    b->maxY = getY(i+1) + ((p==0)?-3:0);
}

void playExplosion()
{
    while(true){
        bool expFound = false;
        while(!expFound){
            for(int i = 0; i < numBombs; i++) {
                Bomb *b = &bombs[(bombIndex + i)%bufferSize]; 
                if(b->exploded && !b->soundPlayed){
                    expFound = true;
                    b->soundPlayed = true;
                }
            }
            Thread::wait(100);
        }
        FILE *explosion;
        explosion=fopen("/sd/explosion.wav","r");
        waver.play(explosion);
        fclose(explosion);
    }
}

void updateBombs()
{
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        if(!b->exploded && (gameTime - b->createTime) > b->cookTime) {
            b->exploded = true;
            clearArea(b);
            
        }
        if(!b->exploded) {
            int index = (int)((gameTime - b->createTime)/.15);
            if((index & 0x11) == 0) {
                b->color = WHITE;
            } else {
                b->color = BLACK;
            }
        } else {
            int index = (int)((gameTime - b->createTime - b->cookTime)/.2);
            if(index == 0) {
                b->color = WHITE;
            }
            if(index == 1) {
                b->color = 0xFFFF00;
            }
            if(index == 2) {
                b->color = 0xFF8800;
            }
            if(index >= 3) {
                b->color = RED;
            }
            index = (int)((gameTime - b->createTime - b->cookTime)/.25);
            b->decay = index;
        }
    }
}

void drawBombs()
{
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        int x = getX(b->startJ);
        int y = getY(b->startI);
        if(!b->exploded) {
            if(b->color == WHITE) {
                uLCD.filled_circle(x+8,y+8,5, WHITE);
                uLCD.filled_rectangle(x+7,y+1,x+9,y+3, WHITE);
            } else {
                uLCD.filled_circle(x+8,y+8,5, BLACK);
                uLCD.filled_rectangle(x+7,y+1,x+9,y+3, 0xFFFF00);
            }
        } else {
            if(b->o_decay != b->decay){
                b->o_decay = b->decay;
                uLCD.filled_rectangle(b->minX,y+3,b->maxX,y+13, BACK);
                uLCD.filled_rectangle(x+3,b->minY,x+13,b->maxY, BACK);
            }

            if(gameTime - b->createTime < (b->cookTime + b->expTime)) {
                uLCD.filled_rectangle(b->minX+b->decay,y+3+b->decay,b->maxX-b->decay,y+13-b->decay, b->color);
                uLCD.filled_rectangle(x+3+b->decay,b->minY+b->decay,x+13-b->decay,b->maxY-b->decay, b->color);
            } else {
                uLCD.filled_rectangle(b->minX,y+3,b->maxX,y+13, BACK);
                uLCD.filled_rectangle(x+3,b->minY,x+13,b->maxY, BACK);
                if(b->playerId == 1) {
                    p1Bombs++;
                } else {
                    p2Bombs++;
                }
                bombIndex=(bombIndex+1)%bufferSize;
                numBombs--;
            }
        }
    }
}

void generateGrid()
{
    //init empty
    for(int i = 0; i < 7; i++) {
        for(int j = 0; j < 7; j++) {
            grid[i][j] = ' ';
        }
    }
    //generate solids
    for(int i = 1; i < 7; i+=2) {
        for(int j = 1; j < 7; j+=2) {
            grid[i][j] = 's';
        }
    }
    //generate breakables
    for(int i = 0; i < 7; i++) {
        for(int j = 0; j < 7; j++) {
            if(grid[i][j] == 's') {
                continue;
            }
            bool hasBreakable = (i*17+j*349)%4 != 0;
            if(hasBreakable) {
                grid[i][j] = 'b';
            }
        }
    }
    //clear player regions
    grid[0][0] = ' ';
    grid[1][0] = ' ';
    grid[0][1] = ' ';
    grid[6][6] = ' ';
    grid[5][6] = ' ';
    grid[6][5] = ' ';
}

void drawImg(string imgName, int x, int y)
{
    RGBApixel *Colors = new RGBApixel [2];
    string file = "/sd/" + imgName;
    ReadBMPFromFile(x,y, file.c_str(), Colors, &uLCD);
}

void drawPlayer(int id)
{
    int col_prim, col_sec, x, y;
    if(id == 1) {
        col_prim = 0xFFD8B2;
        col_sec = 0xFFB2B2;
        x = getX(p1J);
        y = getY(p1I);
    }
    if(id == 2) {
        col_prim = 0xC6B2FF;
        col_sec = 0xFFB2FF;
        x = getX(p2J);
        y = getY(p2I);
    }
    uLCD.filled_rectangle(x+3,y+3,x+13,y+10, col_prim);
    uLCD.filled_rectangle(x+5,y+5,x+6,y+8, BLACK);
    uLCD.filled_rectangle(x+10,y+5,x+11,y+8, BLACK);
    uLCD.filled_rectangle(x+5,y+10,x+7,y+13, col_sec);
    uLCD.filled_rectangle(x+9,y+10,x+11,y+13, col_sec);
}

void initialDraw()
{
    uLCD.baudrate(3000000);
    uLCD.filled_rectangle(0,0,128,128, BACK);
    uLCD.text_width(2);
    uLCD.text_height(2);
    uLCD.color(WHITE);
    uLCD.locate(1,4);
    uLCD.printf("%s",startText);
    for(int i = -1; i < 8; i++){
        for(int j = -1; j < 8; j++){
            string img;
            int c = -1;
            if(i == -1 || j == -1 || i == 7 || j == 7){
               img = "wall.bmp";
               //c = BLACK;
            }else{
                if(grid[i][j] == ' '){
                    c = BACK;
                }
                if(grid[i][j] == 'b'){
                    img = "break.bmp";
                    //c = 0x888888;
                }
                if(grid[i][j] == 's'){
                    img = "wall.bmp";
                    //c = BLACK;
                }
            }
            int x = getX(j);
            int y = getY(i);
            if(c != -1){
                uLCD.filled_rectangle(x,y,x+16,y+16, c);
            }else{
                drawImg(img, x, y);
            }
        
        }
    }
    drawPlayer(1);
    drawPlayer(2);
}


void updateP1()
{
    if(!navUpPress && myNav.up()) {
        p1I++;
        navUpPress = true;
    }
    if(!navDownPress && myNav.down()) {
        p1I--;
        navDownPress = true;
    }
    if(!navLeftPress && myNav.left()) {
        p1J++;
        navLeftPress = true;
    }
    if(!navRightPress && myNav.right()) {
        p1J--;
        navRightPress = true;
    }
    if(!navFirePress && myNav.fire() && p1Bombs >0) {
        createBomb(1,p1I,p1J);
        navFirePress = true;
    }

    if(!valid(p1I,p1J)) {
        p1I = o_p1I;
        p1J = o_p1J;
    }
    
    if(grid[p1I][p1J] == 'f'){
        p1Power++;   
        grid[p1I][p1J] = 'c'; 
    }
    if(grid[p1I][p1J] == 'i'){
        p1Bombs++;    
        grid[p1I][p1J] = 'c';
    }

    if(!myNav.up()) {
        navUpPress = false;
    }
    if(!myNav.down()) {
        navDownPress = false;
    }
    if(!myNav.left()) {
        navLeftPress = false;
    }
    if(!myNav.right()) {
        navRightPress = false;
    }
    if(!myNav.fire()) {
        navFirePress = false;
    }
}

bool isOne(int val, int pos)
{
    return val >> pos & 0x01;
}

void updateP2()
{
    int value=pad.read(0x00);
    value +=pad.read(0x01)<<8;

    bool up =  isOne(value,5);
    bool down =  isOne(value,7);
    bool left = isOne(value,10);
    bool right = isOne(value,2);
    bool fire = isOne(value,6);

    if(!padUpPress && up) {
        p2I--;
        padUpPress = true;
    }
    if(!padDownPress && down) {
        p2I++;
        padDownPress = true;
    }
    if(!padLeftPress && left) {
        p2J--;
        padLeftPress = true;
    }
    if(!padRightPress && right) {
        p2J++;
        padRightPress = true;
    }
    if(!padFirePress && fire && p2Bombs > 0) {
        createBomb(2,p2I,p2J);
        padFirePress = true;
    }

    if(!valid(p2I,p2J)) {
        p2I = o_p2I;
        p2J = o_p2J;
    }
    
    if(grid[p2I][p2J] == 'f'){
        p2Power++;   
        grid[p2I][p2J] = 'c'; 
    }
    if(grid[p2I][p2J] == 'i'){
        p2Bombs++;    
        grid[p2I][p2J] = 'c';
    }
    
    if(!up) {
        padUpPress = false;
    }
    if(!down) {
        padDownPress = false;
    }
    if(!left) {
        padLeftPress = false;
    }
    if(!right) {
        padRightPress = false;
    }
    if(!fire) {
        padFirePress = false;
    }
}

void draw()
{
    for(int i = 0; i < 7; i++){
        for(int j = 0; j < 7; j++){
            if(grid[i][j] == 'c' || grid[i][j] == 'd' || grid[i][j] == 'k'){
                if(grid[i][j] == 'c') grid[i][j] = ' ';
                if(grid[i][j] == 'd') grid[i][j] = 'f';
                if(grid[i][j] == 'k') grid[i][j] = 'i';
                uLCD.filled_rectangle(getX(j),getY(i),getX(j+1),getY(i+1), BACK);
            }
            if(grid[i][j] == 'f'){
                uLCD.filled_circle(getX(j)+8,getY(i)+8,3,RED);
            }
            if(grid[i][j] == 'i'){
                uLCD.filled_circle(getX(j)+8,getY(i)+8,3,0xFFFF00);
            }
        }    
    }
    if(p1I != o_p1I || p1J != o_p1J) {
        int o_x = getX(o_p1J);
        int o_y = getY(o_p1I);
        uLCD.filled_rectangle(o_x,o_y,o_x+16,o_y+16, BACK);
        o_p1I = p1I;
        o_p1J = p1J;
    }
    if(p2I != o_p2I || p2J != o_p2J) {
        int o_x = getX(o_p2J);
        int o_y = getY(o_p2I);
        uLCD.filled_rectangle(o_x,o_y,o_x+16,o_y+16, BACK);
        o_p2I = p2I;
        o_p2J = p2J;
    }
    drawBombs();
    drawPlayer(1);
    drawPlayer(2);
}

void checkPlayerDeath(int pid){
    if(gameRestart) return;
    int centerX = getX((pid == 1)?(p1J):(p2J))+8;
    int centerY = getY((pid == 1)?(p1I):(p2I))+8;
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        if(b->exploded){
            int bx = getX(b->startJ);
            int by = getY(b->startI);
            if(b->minX < centerX && centerX < b->maxX && by < centerY && centerY < by+16){
                gameRestart = true;
            }
            if(b->minY < centerY && centerY < b->maxY && bx < centerX && centerX < bx+16){
                gameRestart = true;
            }
        }
    }
    if(gameRestart){
        if(pid == 1){
            startText = "P1 Died";
        }else{
            startText = "P2 Died";
        }
    }
}

int main()
{
    Thread thr(playExplosion);
    while(true){
        resetVars();
        generateGrid();
        initialDraw();
        while(!gameRestart) {
            updateP1();
            updateP2();
            updateBombs();
            checkPlayerDeath(1);
            checkPlayerDeath(2);
            draw();
            gameTime+=.02;
            wait(.02);
        }
        wait(3);
    }
}