Kostadin Chakarov / Mbed 2 deprecated el17kec

Dependencies:   mbed

Map/Map.cpp

Committer:
kocemax
Date:
2019-05-08
Revision:
8:9b77eea95088
Parent:
7:cd3cafda3dd4
Child:
9:f720f5d87420

File content as of revision 8:9b77eea95088:

#include "Map.h"
#include "Ball.h"

// Levels

void Level1::initBricks(Map & map)
{
    int w = 41;
    int h = 2;
    int gap = 1;
    int y = 0;
    for (int j = 0; j < 9; ++j) {
        int x = 0;
        for (int i = 0; i < 2; ++i) {
            Brick b;
            b.x = x;
            b.y = y;
            b.w = w;
            b.h = h;
            map.addBrick(b);

            x += w + gap;
        }
        y += h + gap;
    }
}

void Level2::initBricks(Map & map)
{
    int w = 20;
    int h = 2;
    int gap = 1;
    int y = 0;

    for (int j = 0; j < 9; ++j) {
        int x = 0;
        for (int i = 0; i < 4; ++i) {
            Brick b;
            b.x = x;
            b.y = y;
            b.w = w;
            b.h = h;
            map.addBrick(b);

            x += w + gap;
        }
        y += h + gap;
    }
}


// Power-Ups

  
//                      rows,cols
int powerUpPaddleSizeSprite[PowerUpH][PowerUpW] =   {
    { 0,1,0 },
    { 1,1,1 },
    { 0,1,0 },
    { 1,1,1 }
};

//                      rows,cols
int powerUpPaddleSpeedSprite[PowerUpH][PowerUpW] =   {
    { 0,1,0 },
    { 0,1,0 },
    { 1,1,1 },
    { 1,1,1 }
};

//                      rows,cols
int powerUpBallSpeedSprite[PowerUpH][PowerUpW] =   {
    { 0,1,0 },
    { 1,1,1 },
    { 1,1,1 },
    { 0,1,0 }
};

void PowerUpType::draw(N5110 &lcd, PowerUp &pUp) {
    //printf("%f, %f; %d,%d - %d\n", pUp.getPos().x,pUp.getPos().y,pUp.getH(),pUp.getW(), (int)((int*)sprite));
    lcd.drawSprite(pUp.getPos().x,pUp.getPos().y,pUp.getH(),pUp.getW(), (int*)sprite);
}

PowerUpType* PowerUpTypes[3] = {
    new PaddleSizePUpType(0, *powerUpPaddleSizeSprite),
    new PaddleSpeedPUpType(1, *powerUpPaddleSpeedSprite),
    new BallSpeedPUpType(2, *powerUpBallSpeedSprite)
};

PowerUp::PowerUp(float x, float y, PowerUpType& pUpType) : _pUpType(&pUpType)
 {
    pos.x = x;
    pos.y = y;
    velocity.x = 0;
    velocity.y = 0.5f;
    w = PowerUpW;
    h = PowerUpH;
}


void PowerUp::draw(N5110 &lcd) {
    //printf("powerup %.02f , %.02f, %d\n", pos.x, pos.y, powerUpSprite[0][2]);
    _pUpType->draw(lcd, *this);
}

void PaddleSizePUpType::giveBonus(Paddle &paddle, Ball &ball) {
    int add = 2;
    paddle.setW(paddle.getW()+add);
}

void PaddleSpeedPUpType::giveBonus(Paddle &paddle, Ball &ball) {
    float add = 0.2f;
    paddle.setSpeed(paddle.getSpeed() + add);
}

void BallSpeedPUpType::giveBonus(Paddle &paddle, Ball &ball) {
    float multiply = 0.9f;
    Vector2D& v = ball.getVelocity();
    
    // 1. create variable speed = magnitude (length) of v
    // 2. change speed by ...
    // 3. Then normalize v and multiply by speed
    float oldSpeed = sqrt(v.x*v.x + v.y*v.y);
    float speed = oldSpeed * multiply;
    
    v.x = (v.x / oldSpeed) * speed;
    v.y = (v.y / oldSpeed) * speed;
    
}

// Map

/** Constructor */
Map::Map()
{
    levels.push_back(new Level1());
    levels.push_back(new Level2());
    reset();
}


/** Destructor */
Map::~Map()
{

}

void Map::initBricks()
{
    bricks.clear();

    Level *level = levels[currentLevel];
    level->initBricks(*this);
}

void Map::drawMap(N5110 &lcd)
{   
    vector<Brick>::size_type end = bricks.size();
    for (int i = 0; i < end; i++) {
        const Brick& b = bricks[i];
        lcd.drawRect(b.x,b.y,b.w,b.h,FILL_BLACK);
    }
    
    
    for (int i = 0; i < powerUps.size(); i++) {
        powerUps[i].draw(lcd);
    }
}
//See: https://stackoverflow.com/questions/31022269/collision-detection-between-two-rectangles-in-java
bool rectIntersect(
    int Ax, int Ay, int Aw, int Ah,
    float Bx, float By, float Bw, float Bh)
{
    return
        Bx + Bw > Ax &&
        By + Bh > Ay &&
        Ax + Aw > Bx &&
        Ay + Ah > By;
}

//Normals to each side of the rectangle
const Vector2D Up = {0, -1};
const Vector2D Down = {0, 1};
const Vector2D Left = {-1, 0};
const Vector2D Right = {1, 0};

//See: https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector
// r = d - 2(d dot n)n
void reflect(Vector2D &d, const Vector2D &n)
{
    float dotProd = d.x * n.x + d.y * n.y;
    float s = 2 * dotProd;

    d.x = d.x - s * n.x;
    d.y = d.y - s * n.y;
}

void Map::resolveCollision(const Brick &b, GameObject &obj)
{
    // take velocity of the ball to determine direction and previous coords
    float vx = obj.getVelocity().x;
    float vy = obj.getVelocity().y;
    // take the previous x and y coords
    float x = obj.getPos().x - vx;
    float y = obj.getPos().y - vy;
    // sides of the bricks
    float leftX = b.x;
    float rightX = b.x + b.w;
    //float topY = b.y;
    //float bottomY = b.y + b.h;

    // mid of x and y coords
    //float cx = b.x + b.w/2.0f;
    float cy = b.y + b.h/2.0f;

    Vector2D &vel = obj.getVelocity();
    float thresh = 0.1;
    if (x > rightX - thresh) {
        // hit right
        reflect(vel, Right);
    } else if (x < leftX + thresh) {
        // hit left
        reflect(vel, Left);
    } else if (y < cy) {
        // hit top
        reflect(vel, Up);
    } else if (y > cy) {
        // hit bottom
        reflect(vel, Down);
    } else {
        if (vy > 0) {
            // hit top
            reflect(vel, Up);
        } else {
            // hit bottom
            reflect(vel, Down);
        }
    }
}

void Map::checkCollision(Ball &ball, Paddle &paddle)
{
    // check bricks
    vector<Brick>::size_type end = bricks.size();
    for (int i = 0; i < end; i++) {
        const Brick& b = bricks[i];
        if (rectIntersect(b.x, b.y, b.w, b.h,
                          ball.getPos().x, ball.getPos().y, ball.getW(), ball.getH())) {
            // brick collision event!
            
            // bounce the ball
            resolveCollision(b, ball);
            
            // (maybe) spawn power-up
            onBrickHit(b);
            
            // remove brick
            bricks.erase(bricks.begin() + i);
            
            break;
        
        }
    }
    
    /** check power ups */
    for (int i = 0; i < powerUps.size(); i++) 
    {
        PowerUp& p = powerUps[i];
        /** use rectIntersect to check for collisions between power-up and pad */
        if (rectIntersect(p.getPos().x, p.getPos().y, p.getW(), p.getH(),
                          paddle.getPos().x, paddle.getPos().y, paddle.getW(), paddle.getH())) {
            
            p.giveBonus(paddle, ball);
                            
            powerUps.erase(&p);
            break;             
        }
                          
    }
}

void Map::onBrickHit(const Brick& brick) {
    if ((rand() % 100) < PowerUpDropChancePct) {
        // spawn power up
        int type = rand() % 3;  // each power up has the same rarity
        
        //printf("Power up - t=%d", type);
        PowerUpType& pUpType = *PowerUpTypes[type];
        
        //printf(", pUpType=%d\n", pUpType.type);
        
        powerUps.push_back( 
            PowerUp(brick.x+(brick.w/2)-PowerUpW/2, brick.y, pUpType)
        );
       }
}

void Map::update() {
    for (int i = 0; i < powerUps.size(); i++) {
        powerUps[i].move();
    }
}

bool Map::checkLevel()
{
    if (bricks.size() == 0) {
        // cleared level
        ++currentLevel;
        // printf("cleared level! %d %d\n", currentLevel, hasWon());
        if (!hasWon()) {
            // initialize next level
            initBricks();
        }
        return true;
    }
    return false;
}

void Map::reset()
{
    currentLevel = 0;
    initBricks();
    powerUps.clear();
}