Initial publish

Dependencies:   mbed

Fork of el17dg by Dmitrijs Griskovs

game/game.cpp

Committer:
Noximilien
Date:
2019-05-07
Revision:
40:e3bbda7444fa
Parent:
39:ca77a6d574e6

File content as of revision 40:e3bbda7444fa:

#include "mbed.h"
#include "N5110.h"
#include "Gamepad.h"
#include "collision_lib.h"

#include "models.h"
#include "main.h"
#include "game.h"
#include "gameobject.h"

#include "boss.h"
#include "enemies.h"
#include "constants.h"
#include "stars.h"
#include "player.h"
#include "hud.h"
#include "gameovermanager.h"


const int increase_difficulty = 70;
int GameGlobals::game_score = 0;
int GameGlobals::score_count_for_difficulty = 0;
int GameGlobals::player_lifes = 3;
int GameGlobals::high_score = 0;
bool GameGlobals::is_shield_on = false;
int GameGlobals::score_count_for_boss_mode = 0;

Boss boss;
Enemies enemies;
Enemy enemy;
Stars stars;
Player player;
GameOverManager gameOverManager;

Hud hud;


void Game::updateAndDrawGameplay() {
    checkButtonToShoot();
    player.updateAndDraw();
    player.updateAndDrawBlasts();
    stars.updateAndDrawSmallStars();
    stars.updateAndDrawMediumStars();
    stars.starsSpawnDelay();
    increaseGameDifficultyAndEnemySpawnDelay();
    hud.displayLifes();
    hud.drawScore();
    collideEnemiesAndBlasts();
    collideEnemiesBlastsAndPlayer();
    collideEnemiesAndPlayer();
    enemies.updateAndDrawEnemies();
    enemies.updateAndDrawEnemyBlasts();
    boss.updateAndDrawBossBlasts();
    
    if (checkForGameOver()) { game_state = GameState_gameover;}
    if (is_boss_active) { game_state = GameState_boss_cutscene;}
}

void Game::updateAndDrawGameover() {
    gameOverManager.updateAndDraw();
    lcd.normalMode();
    if (gamepad.check_event(gamepad.Y_PRESSED) && !gameOverManager.isPlayingAnimation()) {
        gameOverManager.reset();
        game_state = GameState_newgame;
    }
}

void Game::updateAndDrawBossCutscene() {
    boss.updateCutscene();
    enemies.updateAndDrawEnemyBlasts();
    boss.draw();
    player.draw();
    stars.updateAndDrawSmallStars();
    stars.updateAndDrawMediumStars();
    gamepad.tone(60,2);
    for (int i = 0; i < enemies.max_enemies; ++i) {
        enemies.enemies[i].active = false;
    }
    for (int i = 0; i < enemies.max_enemy_blasts; ++i) {
        enemies.enemy_blasts[i].active = false;
    }
    if (boss.isFinishedCutscene()) {
        game_state = GameState_boss_gameplay;
        boss.resetCutscene(); 
    }
}

void Game::updateAndDrawBossGameplay() {
    checkButtonToShoot();
    enemies.updateAndDrawEnemyBlasts();
    player.updateAndDraw();
    player.updateAndDrawBlasts();
    stars.updateAndDrawSmallStars();
    stars.starsSpawnDelay();
    hud.displayLifes();
    hud.drawScore();
    boss.updateAndDrawBossBlasts();
    collideBossAndPlayerBlasts();
    collideBossBlastsAndPlayer();
    is_boss_active = boss.updateAndDrawBoss();
    if (checkForGameOver()) { game_state = GameState_gameover;}
    if (!is_boss_active) { game_state = GameState_gameplay;}
}

/**
  * This is the main function of game.cpp, where the actual gameplay happens.
  * Here all other functions are activeated, and when the player dies, it
  * returns back to main menu "main.cpp". 
  */
bool Game::updateAndDraw() {
    if (game_state == GameState_newgame) {
        DG_PRINTF("start game \n");
        startNewGame();
    }
    if (game_state == GameState_gameplay) { updateAndDrawGameplay(); }
    else if (game_state == GameState_boss_cutscene) { updateAndDrawBossCutscene(); }
    else if (game_state == GameState_boss_gameplay) { updateAndDrawBossGameplay(); }
    else if (game_state == GameState_gameover) { updateAndDrawGameover(); }
    
    return checkIfNeedsToReturnToMenu();
}

void Game::checkButtonToShoot(){
    if (gamepad.check_event(gamepad.X_PRESSED) && !GameGlobals::is_shield_on){
        // Checking the button second time to prevent double blast.
        gamepad.check_event(gamepad.X_PRESSED); 
        player.fireNewBlast();
        gamepad.tone(200,0.1);
    }
}

/**
  * This function checks whether the requirments for the collision of the two objects,
  * are met. When those requirments are met the collision of two objects function will
  * be checking wheter the boundaries of the objects colide. If they do, the blast
  * becomes inactive, in game score increases and enemy dies.
  */
void Game::collideEnemiesAndBlasts() {
    for (int i = 0; i < enemies.max_enemies; ++i) {
        for (int j = 0; j < player.max_player_blasts; ++j) {
            Enemy& enemy = enemies.enemies[i];
            GameObject& blast = player.blasts[j];
            if (enemy.active && !enemy.dead && blast.active) {
                bool collision = circleCollideTwoObjects(
                    enemy.pos, enemies.enemy_bounds, 
                    blast.pos, player.blast_bounds
                );
                if (collision) {
                    enemy.die();
                    DG_PRINTF("enemy got hit and dies \n");
                    GameGlobals::game_score += 30;
                    GameGlobals::score_count_for_difficulty +=30;
                    GameGlobals::score_count_for_boss_mode += 30;
                    blast.active = false;
                }
            }
        }
    }
}

/**
  * This code does the same work as the one before but with two other objects.
  * It checks whether the requirments for the collision of the two objects,
  * are met. When those requirments are met the collision of two objects function will
  * be checking wheter the boundaries of the objects colide. If they do, the blast
  * becomes inactive, in game score increases and enemy dies.
  */
void Game::collideEnemiesBlastsAndPlayer() {
    for (int i = 0; i < enemies.max_enemies; ++i) {
        GameObject& blast = enemies.enemy_blasts[i];
        if (blast.active) {
            bool collision = circleCollideTwoObjects(
                player.pos, player.player_bounds,
                blast.pos, enemies.enemy_blast_bounds
            );
            if (collision) {
                if (!GameGlobals::is_shield_on){
                    gamepad.tone(423,0.4); 
                    GameGlobals::player_lifes -= 1;
                    DG_PRINTF("lost a life from blast. left: %i \n", GameGlobals::player_lifes);
                    blast.active = false;
                }else{
                    blast.active = false;
                    gamepad.tone(700,0.6);
                }
            }
        }
    } 
}

/**
  * This code does the same work as the one before but with two other object.
  * of enemy ship and the player's ship
  */
void Game::collideEnemiesAndPlayer() {
    for (int i = 0; i < enemies.max_enemies; ++i) {
        Enemy& enemy = enemies.enemies[i];
        if (enemy.active && !enemy.dead) {
            bool collision = circleCollideTwoObjects(
                player.pos, player.player_bounds,
                enemy.pos, enemies.enemy_bounds
            );
            if (collision) {
                GameGlobals::player_lifes -= 1;
               
                enemy.die();
                DG_PRINTF("enemy got hit from collsion and dies");
                DG_PRINTF("lost a life from enemy col. left: %i \n", GameGlobals::player_lifes);
            }
        }
    } 
}

void Game::collideBossAndPlayerBlasts() {
    for (int i = 0; i < player.max_player_blasts; ++i) {
        GameObject& blast = player.blasts[i];
        if (blast.active) {
            bool collision = circleCollideTwoObjects(
                boss.pos, boss.boss_bounds,
                blast.pos, player.blast_bounds
            );
            if (collision) {
                boss.boss_lives -= 1;
                gamepad.tone(123,0.4);
                blast.active = false;
                DG_PRINTF("boss has. left: %i \n", boss.boss_lives);
            }
        }
    } 
}

void Game::collideBossBlastsAndPlayer() {
    for (int i = 0; i < boss.max_boss_blasts; ++i) {
        GameObject& blast = boss.boss_blasts[i];
        if (blast.active) {
            bool collision = circleCollideTwoObjects(
                player.pos, player.player_bounds,
                blast.pos, boss.boss_blast_bounds
            );
            DG_PRINTF("player pos:%i,%i; offset:%i,%i; radius:%f.3; \n", player.pos.x, player.pos.y, player.player_bounds.center.x, player.player_bounds.center.y, player.player_bounds.radius);
            DG_PRINTF("blast pos:%i,%i; offset:%i,%i; radius:%f.3; \n", blast.pos.x, blast.pos.y, boss.boss_blast_bounds.center.x, boss.boss_blast_bounds.center.y, boss.boss_blast_bounds.radius);
            if (collision) {
                if (!GameGlobals::is_shield_on){
                    
                    gamepad.tone(423,0.4); 
                    GameGlobals::player_lifes -= 1;
                    blast.active = false;
                    DG_PRINTF("collision happened;\n");
                    DG_PRINTF("lost a life from blast. left: %i \n", GameGlobals::player_lifes);
                }else{
                    blast.active = false;
                    gamepad.tone(700,0.6);
                }
            }
        }
    } 
}

/**
    * This function resets all the values to their intial states when the game is
    * first began when the player dies and wants to restart the game.
    * It does not reset the values when the game is paused.
    */
void Game::startNewGame() {
    is_boss_active = false;
    game_state = GameState_gameplay;
    player.pos.x = 0; //player was defined in player.h
    player.pos.y = 24;
    stars.stars_delay = 0;
    enemy_ship_delay_max = 40;
    enemy_ship_delay_counter = enemy_ship_delay_max;
    GameGlobals::is_shield_on = false;
    GameGlobals::game_score = 0;
    GameGlobals::score_count_for_difficulty = 0;
    GameGlobals::player_lifes = 3;
    GameGlobals::score_count_for_boss_mode = 0;
    hud.resetRedLed();
    enemies.enemy_blast_speed = 3;
    enemy.enemy_speed = 1;
    gamepad.check_event(gamepad.START_PRESSED); 
    for (int i = 0; i < enemies.max_enemies; ++i) {
        enemies.enemies[i].active = false;
    }
    for (int i = 0; i < player.max_player_blasts; ++i) {
        player.blasts[i].active = false;
    }
    for (int i = 0; i < enemies.max_enemy_blasts; ++i) {
        enemies.enemy_blasts[i].active = false;
    }
     for (int i = 0; i < boss.max_boss_blasts; ++i) {
        boss.boss_blasts[i].active = false;
    }
    gamepad.check_event(gamepad.START_PRESSED);
}

/**
    * A function tbat delays enemy spawn on low game score.
    * It decreases the enemy spawn delay as in game score increases.
    * The enemy spawn delay is located in game.cpp because the game difficulty
    * depends on the on how fast enemy appears.
    */
void Game::increaseGameDifficultyAndEnemySpawnDelay(){
    if  (enemy_ship_delay_counter <= 0){ 
        enemies.spawnNewEnemy();
        enemy_ship_delay_counter = enemy_ship_delay_max;
    }
    else { enemy_ship_delay_counter -= 1;}
    
    if (enemy_ship_delay_max >= 4 && GameGlobals::score_count_for_difficulty >= increase_difficulty){
        //decrease enemy delay spawn.
        enemy_ship_delay_max -= 3;
        if (enemy_ship_delay_max <= 20 && enemy_ship_delay_max >= 15){
            enemies.enemy_blast_speed += 1;
            enemy.enemy_speed += 1;   
        }
        GameGlobals::score_count_for_difficulty = 0;   
    }
    if (GameGlobals::score_count_for_boss_mode >= 400){
        is_boss_active = true; 
        lcd.inverseMode();
    }
}

bool Game::forceShildActivate(){
    if (gamepad.check_event(gamepad.R_PRESSED)){
       GameGlobals::is_shield_on = !GameGlobals::is_shield_on; 
    }   
    return GameGlobals::is_shield_on;
}

/**
    * This is a single line function to set the player lifes to 0 when the.
    * game is over. 
    */
bool Game::checkForGameOver() {
    return GameGlobals::player_lifes <= 0;
}

/**  This small statment checks whether the pause button was pressed or not.
  * If it was pressed, then the game will go back to main menu and will save 
  * the current status of the object until the game is continued.
  */
bool Game::checkIfNeedsToReturnToMenu(){
    bool want_to_pause = false;

    if (gamepad.check_event(gamepad.START_PRESSED)){
        DG_PRINTF("game paused\n");
        gamepad.check_event(gamepad.START_PRESSED);
        gamepad.check_event(gamepad.A_PRESSED);
        want_to_pause = true;
    }
    return want_to_pause;
}