A complex 2D-dungeon game on LPC1768 in SWJTU-Leeds Joint School XJEL2645 project. Referenced from the framework contributed by https://os.mbed.com/users/Siriagus/code/SimplePlatformGame/

Dependencies:   mbed N5110 ShiftReg PinDetect

Game.cpp

Committer:
Siriagus
Date:
2015-05-10
Revision:
15:d5eb13c4c1c6
Parent:
14:b4fed570abaf
Child:
16:caf613d5b85e

File content as of revision 15:d5eb13c4c1c6:

#include "Game.h"

Serial pc(USBTX, USBRX); // TO DELETE - DEBUGGING ONLY

void Game::init()
{
    paused = false;
    
    // Set initial values for the player
    player.x = 40; // start x
    player.y = 5;  // start y
    player.width = player.height = 5; // important that this is correct, see size of Image::Player in Resources.h
    player.onGround = false;
    
    enemy = new Enemy(75, 3, 6, 5);
    enemy->vx = -2;
    enemy->facingLeft = true;
}

// Functions
void Game::update(float dt)
{
    // Pause button input
    if (input->read(Input::ButtonC))
    {
        if (releasedBtnC)
        {
            paused = !paused;
            releasedBtnC = false;
        }
    }
    else
        releasedBtnC = true;
        
    // Skip the rest if paused
    if (paused) return;
    
    
    
    
    // Handle input, should be its own function
    switch(input->joystick->getDirection())
    {
        case LEFT:
        case UP_LEFT:
        case DOWN_LEFT:
            player.vx = -2;
            player.facingLeft = true;
        break;
        
        case RIGHT:
        case UP_RIGHT:
        case DOWN_RIGHT:
            player.vx = 2;
            player.facingLeft = false;
        break;
        
        case CENTER:
            player.vx = 0;
        break;
    }
    
    // Random movement for enemies
    if (enemy->onGround && (rand() % 100) > 97) // 3 % chance
    {
        // jump
        enemy->vy = -3;
        enemy->onGround = false;
    }
    else if ((rand() % 100) > 98) // 2% chance
    {
        // switch direction
        enemy->vx *= -1; 
        enemy->facingLeft = (enemy->vx < 0);
    }
    
    // Gravity
    player.vy += 1;
    enemy->vy += 1;
    
    // Check if player is trying to jump. Player can only jump if it's on the ground
    if (input->read(Input::ButtonA) && player.onGround)
    {
        player.vy = -4;
        player.onGround = false;
    }
    
    // Terminal velocity 3 px/update
    if (player.vy > TERMINAL_VELOCITY) player.vy = TERMINAL_VELOCITY;
    
    moveWithCollisionTest(&player, map);
    
    if (!enemy->dead)
        moveWithCollisionTest(enemy, map);
    else
    {
        enemy->y += enemy->vy;
        enemy->x += enemy->vx;
    }
    // else if (enemy->y >= HEIGHT) ; // TODO : delete enemy
    
    // Enemy AI
    if (!enemy->dead)
    {
        int nextRight = enemy->getRight() + 1;   // Next position of right edge if enemy moves to the right
        for (int i = 0; i < enemy->height; ++i)  // Check for all heighs
        {
            // Check if crashing if moving right or left. Bounds should already be limited by moveWithCollisionTest!
            // will also try to climb a slope if it can find one
            if ((map[enemy->y + i][nextRight] && map[enemy->y+i-1][nextRight]) || (map[enemy->y + i][enemy->x - 1] && map[enemy->y + i-1][enemy->x - 1]))
            {
                enemy->vx *= -1; // move in opposite direction
                enemy->facingLeft = !enemy->facingLeft; // toggle direction
                break;          // no further testing required
            }
        }
    }
    
    // Check if bullet should be fired
    if (input->read(Input::ButtonB) && releasedBtnB)
    {
        // Create a new bullet and give it initial values
        Point* bullet = new Point;
        bullet->x = (int)(player.x + (player.width / 2)); //(player.facingLeft) ? (player.x-1) : (player.x + player.width);
        bullet->y = player.y + 2;
        bullet->vx = (player.facingLeft) ? -4 : 4;
        bullet->vy = 0;
        
        bullets.push_back(bullet);
        releasedBtnB = false;
    }
    else if (!input->read(Input::ButtonB))
        releasedBtnB = true;
    
    // Loop through bullets and move them + collision test
    for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end();)
    {
        Point* bullet = *it;
        
        int x0; // left border of collision rect
        int x1; // right border of collision rect
        
        int oldX = bullet->x;
        int newX = bullet->x + bullet->vx;
        
        x0 = min(oldX, newX);
        x1 = max(oldX, newX);
            
        // Collision rect for bullet in this time step
        Rectangle bulletColRect(x0, bullet->y, (x1-x0)+1, 1);
        
        bool col = false;
        // Delete if outside screen
        if (newX < 0 || newX > WIDTH || bulletHitMap(bulletColRect, map)) // if outside screen
        {
            col = true;
        }
        else
        {
            // loop through all enemies
            //bullet // point
            //enemy // entity
            
            if (!enemy->dead && bullet->x >= enemy->x && bullet->x <= enemy->getRight() && bullet->y >= enemy->y && bullet->y <= enemy->getBottom()) 
            {
                col = true;
                // break; // todo: make for loop - iterate through all enemies
                enemy->vx = bullet->vx / 2; // sends the dead enemy in the same direction as the incoming bullet
                enemy->vy = -3;             // sends the dead enemy upwards in the air, because of impact
                
                enemy->dead = true;
                // remove enemy - todo: make vector
            }
        }
        
        if (!col)
        {
            ++it;   // go to next element
            bullet->x += bullet->vx; // update position
        }
        else
        {
            delete bullet;
            it = bullets.erase(it); // go to next element
        }
        
        // TODO: Check for collisions
        // TODO: Go both ways
    }
}

void Game::render()
{
    // Draw map
    drawImage(map);
    
    // Draw player
    drawImage(Image::Player, player.x, player.y, false, !player.facingLeft);
    
    // Draw enemies
    drawImage(Image::Enemy3, enemy->x, enemy->y, false, !enemy->facingLeft, enemy->dead);
    
    /*
    // Draw player - TODO: Make this a part of sprite class (so they can draw themselves)
    int x0, x1, y0, y1;
    x0 = (player.x < 0) ? 0 : player.x;                       // x0 = max(0,x);
    y0 = (player.y < 0) ? 0 : player.y;                       // y0 = max(0,y);
    x1 = (player.width + player.x > WIDTH) ? WIDTH : player.width + player.x;       //x1 = min(WIDTH, width);
    y1 = (player.height + player.y > HEIGHT) ? HEIGHT : player.height + player.y;   //y1 = min(HEIGHT, height);
    
    for (int i = y0; i < y1; ++i)
    {   
        for (int j = x0; j < x1; ++j)
        {
            // If player is going right, obtain data from sprite in reverse order => render in reverse
            int xIndex = (player.facingLeft) ? (j-x0) : (player.width - 1 - (j-x0));
            
            if (Image::Player[i-y0][xIndex])
                lcd->setPixel(j,i);
        }
    }*/
    
    // Render bullets
    for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
    {
        int x, y;
        x = (*it)->x;
        y = (*it)->y;
        
        if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) // Boundary check
            lcd->setPixel(x,y);
    }
    
    // Draw pause
    if (paused)
    {
        lcd->drawRect(24, 13, 40, 13, 0); // outline
        lcd->drawRect(25, 14, 38, 11, 2); // white fill
        lcd->printString("Paused", 27, 2); // text
    }
}

// Collision test between entites and map
void Game::moveWithCollisionTest(Entity* entity, const int map[HEIGHT][WIDTH])
{   
    int x = entity->x;
    int y = entity->y;
    int steps = abs(entity->vx); // how many units (pixels) the entity should move in said direction
    bool collision; // true if colliding
    
    // Check x-axis
    if (entity->vx > 0) // moving right
    {
        int entityRight = x + entity->width - 1; // Need to check right border of entity, since it is moving right
        
        while(steps--) // While it still have more movement left
        {
            collision = false;
            for (int i = 0; i < entity->height; ++i) // Loop through all vertical points on the right hand side of the entity (y+i)
            {
                if (map[y+i][entityRight+1]) // If moving to the right leads to collision for given y+i
                {
                    // Slope + allows player to climb to top of platform by going right if it hits close to top of wall.
                    if (!map[y+i-1][entityRight+1] && entity->onGround)
                    {
                        entity->vy = -1;
                    }
                    else
                    {
                        collision = true; // Then collision is true
                        break;            // Skip the for loop, no need for further testing
                    }
                        
                }
            }
            
            if (collision) // If collision
                break;     // skip the while loop, entity can not move further, even though its velocity is higher
            else
                ++entityRight;  // Move entity one px to the right
        }
        
        entity->x = entityRight - (entity->width - 1); // Update entity's position. Need to set upper-left pixel.
    }
    else // moving left
    {
        while(steps--) // While still movement left
        {
            collision = false;
            
            
            for (int i = 0; i < entity->height; ++i) // Check for all y-positions
            {
                if (map[y+i][x-1])                  // If solid block
                {
                    if (!map[y+i-1][x-1] && entity->onGround) // If slope or close to top of wall (=> can climb by going left).
                    {
                        entity->vy = -1;   
                    }
                    else
                    {
                        collision = true;
                        break;                          // Collision detected, no further testing required
                    }
                }
            }
            
            if (collision)
                break;
            else
                --x;    // Move to the left if no collision is detected
        }
        
        entity->x = x;
    }
    
    // Check collision with map in y-direction - works the same way as the x-axis, except for other axis
    
    x = entity->x;
    y = entity->y;
    steps = abs(entity->vy);
    
    if (entity->vy > 0) // downwards
    {
        int entityBottom = y + entity->height - 1; // Need to check if bottom part collides
        while(steps--)  // Still movement left
        {
            collision = false;
            for (int i = 0; i < entity->width; ++i)  // Loop through all x-position on lower part of entity
            {
                if (map[entityBottom+1][x+i])       // If moving the entity one step down for a given (x+i)-position gives a collision
                {
                    collision = true;
                    break;                          // No further testing required
                }
            }
            
            if (collision)                          // If collision
            {
                entity->vy = 0;                      // Set vertical velocity to 0 (playe
                entity->onGround = true;             // entity has hit ground
                break;                               // Skip the while loop as the entity can not move further downwards
            }
            else                // Can safely move entity without collision
            {
                ++entityBottom; // Move entity one step down
                entity->onGround = false;
            }
        }
        
        entity->y = entityBottom - (entity->height - 1);      // Update position when done moving, remember that entity.y refers to upper part of the entity
    }
    else // moving up, check collision from top
    {
        while(steps--)  // Still movement left
        {
            collision = false;
            
            for (int i = 0; i < entity->width; ++i) // Check for all x-positions
            {
                if (map[y-1][x+i])                  // If moving upwards gives collision for a given x+i
                {
                    collision = true;               // Then we have a collision
                    break;                          // No further testing needed, skip for loop
                }
            }
            
            if (collision)  // If collision was detected
            {
                entity->vy = 0;  // Set vertical velocity to zero
                break;          // Skip while loop as entity can not move further up
            }
            else            // If safe to move for all x-values
                --y;        // Move entity one step up
        }
        
        entity->y = y;       // Update vertical position of entity
    }    
}

bool Game::hitTestRect(Rectangle r1, Rectangle r2)
{
    return ((r1.x + r1.width > r2.x)       // r1's right edge to the right of r2's left edge
        &&  (r1.x < r2.x + r2.width)       // r1's left edge to the left of r2's right edge
        &&  (r1.y + r2.height > r2.y)      // r1's bottom lower than r2's top
        &&  (r1.y < r2.y + r2.height));    // r1's top higher than r2's bottom
        
    // Note: Right border: r1.x + r1.width - 1, but we don't need to subtract 1 as we use > instead of >=
}

bool Game::bulletHitMap(Rectangle &bulletColRect, const int map[HEIGHT][WIDTH])
{
    for (int j = 0; j < bulletColRect.width; ++j)
    {
        if (map[bulletColRect.y][bulletColRect.x + j])
            return true;
    }
    
    return false;
}