ELEC2645 (2018/19) / Mbed 2 deprecated el17lw

Dependencies:   mbed

Engine/Engine.cpp

Committer:
lewisgw
Date:
2019-05-07
Revision:
29:bdc4138b5171
Parent:
28:be77ad6c0bda

File content as of revision 29:bdc4138b5171:

#include "Engine.h"

// Reference for the technique used to generate random numbers.
// [1] "rand" cplusplus. [Online] Available: 
// http://www.cplusplus.com/reference/cstdlib/rand/ [Accessed: 22 April 2019]. 

// Buffer to print updated score.
char buffer[14];   

// Constructor and destructor.
Engine::Engine() {} 

Engine::~Engine() {}

void Engine::init() {
  // Reset functions are used for init.
  reset_skater();
  reset_engine();
  srand(time(NULL));  // Set up for generating random numbers, [1].
}

void Engine::check_reset(N5110 &lcd, Gamepad &gamepad) {
  // If reset flag is true, end and restart the game.
  if (_skater.get_reset_flag()) {
    gamepad.leds_on();
    execute_dying_sequence(lcd, gamepad);
    wait(1);  // Short pause.
    reset_skater();
    reset_engine();
  }
}

void Engine::execute_dying_sequence(N5110 &lcd, Gamepad &gamepad) {
  // Player has died and game needs to restart, printing their score.
  wait(1);  // Short pause.
  gamepad.leds_off();
  lcd.clear();
  for (int i = 0; i < 40; i = i + 8) {  // Counter for y direction, sweeps top 
  // to bottom.
    for (int j = 0; j < 84; j++) {  // Counter for x direction, sweeps L to R. 
      lcd.setPixel(j,i,true);
      lcd.setPixel(i,j,true);
      lcd.printString("TRY AGAIN",30,5);  
      lcd.refresh();
      gamepad.tone(int(1099 - 15.4*j - 1.2*i), 0.05);  // Frequency of tone is 
      // dependent on counters.
      wait(0.001);  // Control speed of the sequence.
      sprintf(buffer,"%2d",_player_score); 
      lcd.printString(buffer,0,5);
    }  
  }
}

void Engine::reset_skater() {
  // Initial values for skater.
  _skater_direction = Left;
  _start_platform_flag = true;  // For printing start text in EngineController.
  _skater.set_reset_flag(false);
  _moving_counter = 0;
  _jump_counter = 20;  // Start game falling onto the platform. 
  _fall_flag = false;
}  

void Engine::reset_engine() {
  // Inital values for engine (and gamepad input).
  _input.coord.x = 0;
  _input.coord.y = 0;
  _input.A_flag = false;
  _lower_platforms.init(40);  // 40 is the platform y coord.
  _upper_platforms.init(22);  // 22 is the platform y coord.
  _coin.init();
  _fire.init();
  _coin_collision_flag = false;
  _player_score = 0;
  _fire_height = 0;
}  

void Engine::read_input(Gamepad &gamepad) {
  // Set up the input struct for use.
  _input.coord = gamepad.get_mapped_coord(); 
  _input.A_flag = gamepad.check_event(Gamepad::A_PRESSED);
} 

void Engine::process_y(Gamepad &gamepad) {
  // Sets the y coord by first checking if the skater should be falling.
  set_fall_flag();  // Update the fall flag dependent on skater position.
  if (_fall_flag) {
    _skater.fall(_fall_flag, gamepad);  // Fall if the skater should be.
  } else {
    _skater.set_y_position(_input.A_flag, _jump_counter, _level_condition, 
                           gamepad);
  }
  _fall_flag = _skater.get_fall_flag();  // Update fall flag.
  _skater_y = _skater.get_y_position();
  _jump_counter = _skater.get_jump_counter();  // Update jump counter.
}

void Engine::set_fall_flag() {
  // Set the fall flag to true if the skater is not on one of the lower 
  // platforms. The start platform condition has been offset by 6 to account for 
  // skater sprite size.
  // If the skater is between platform 1 and 2, and on the lower level. 
  if (((_lower_line_1.x_end < _skater_x) 
      && (_skater_x < (_lower_line_2.x_start - 6)))  
      && _skater_y == 23) {  
    _fall_flag = true; 
  // If the skater is between platforms 2 and 3, and on the lower level.  
  } else if (((_lower_line_2.x_end < _skater_x) 
             && (_skater_x < (_lower_line_3.x_start - 6))) 
             && _skater_y == 23) {
    _fall_flag = true;
  // If the skater is between platforms 3 and 1, and on the lower level.  
  } else if (((_lower_line_3.x_end < _skater_x) 
             && (_skater_x < (_lower_line_1.x_start - 6))) 
             && _skater_y == 23) {
    _fall_flag = true;
  }   
}
    
void Engine::process_x(int game_counter) {
  // Sets the x coord from input.
  _skater.set_x_position_and_sprite(_input.coord.x, 
    _moving_counter, 
    _skater_direction,
    _input.coord.y);
  _skater_x = _skater.get_x_position();
  _moving_counter = _skater.get_moving_counter();  // Update moving counter.
  _speed_divider = int(-0.05*_player_score + 4);  // Speed divider is calculated
  // from player score.
  // Move the skater along with platforms at rate determined by speed divider if
  // the skater is not moving left.
  if ((game_counter % _speed_divider == 0) && (_input.coord.x > -0.1)) {  
    _moving_counter--;
  }
}
    
void Engine::process_sprite() {
  // Update the sprite and direction.
  _skater_sprite = _skater.get_sprite_value();
  _skater_direction = _skater.get_direction();
}


void Engine::set_level_condition() {
  // If the skater is under or on top of any of the upper platforms, set
  // level condition to 1. 
  // Offset of 6 is to account for sprite size.
  // If the skater is between start and end of upper platform 1.  
  if (((_upper_line_1.x_start - 6) <= _skater_x) 
      && (_skater_x <= _upper_line_1.x_end)) {
    _level_condition = 1; 
  // If the skater is between start and end of upper platform 2. 
  } else if (((_upper_line_2.x_start - 6) <= _skater_x) 
             && (_skater_x <= _upper_line_2.x_end)) {
    _level_condition = 1;
  // If the skater is between start and end of upper platform 3.   
  } else if (((_upper_line_3.x_start - 6) <= _skater_x) 
             && (_skater_x <= _upper_line_3.x_end)) {
    _level_condition = 1;
  } else {
    _level_condition = 0;
  }   
}
    
void Engine::generate_level(int game_counter) {
  // Generate properties for the level.
  generate_lower_lines();
  generate_upper_lines();
  _coin.generate_coin();
  generate_fire(game_counter);
}

void Engine::draw_screen_fire(int game_counter, N5110 &lcd) {
  // Prints the dynamic fire at the bottom of the screen.
  // Restricts the max fire height multiplier, which is dependent on player 
  // score. Fire will start when score = 3.
  if (_player_score < 15) {
    _fire_height = _player_score;
  } else {
    _fire_height = 14;
  }
  // i corresponds to horizontal positions of pixels.
  for (int i = 1; i < 84; i++) {
    if (i % 4 == 0) {
      // j corresponds to vertical height of pixels, and the max height is
      // dependent on game counter (0 to 99) so changes every loop, and is 
      // scaled.
      for (int j = 0; j < game_counter*0.01*_fire_height; j++) {
        lcd.setPixel(i + (rand() % 8 + 1) ,50 - j*1.3,true);  // Horizontal 
        // position of pixel is constrained random [1], multiplier on j varies 
        // local height. 
        lcd.setPixel(i - (rand() % 8 + 1) ,50 - j*1.2,true);  // [1].
        lcd.setPixel(i - (rand() % 8 + 1) ,50 - j*1.6,true);  // [1].
        lcd.setPixel(i + (rand() % 8 + 1) ,50 - j*1.1,true);  // [1].
        lcd.setPixel(i - (rand() % 8 + 1) ,50 - j*1.4,true);  // [1].
      }  
    }
  }    
}

void Engine::generate_lower_lines() {
  // Use a scaled random number to generate the length of the lower lines.
  _length_1 = (rand() %20) + 10;  // [1].   
  _lower_platforms.set_line_1(_length_1);
  _lower_line_1 = _lower_platforms.get_line_1(); 
  _length_2 = (rand() %20) + 10;  // [1].      
  _lower_platforms.set_line_2(_length_2);
  _lower_line_2 = _lower_platforms.get_line_2();
  _length_3 = (rand() %20) + 10;  // [1].      
  _lower_platforms.set_line_3(_length_3);
  _lower_line_3 = _lower_platforms.get_line_3();
}

void Engine::generate_upper_lines() {   
  // Set the length of the upper lines to be proportionally smaller to
  // the length of the lower lines.
  _upper_platforms.set_line_1(_length_1 / 2);
  _upper_line_1 = _upper_platforms.get_line_1();       
  _upper_platforms.set_line_2(_length_2 / 2);
  _upper_line_2 = _upper_platforms.get_line_2();  
  _upper_platforms.set_line_3(_length_3 / 2);
  _upper_line_3 = _upper_platforms.get_line_3();
}

void Engine::generate_fire(int game_counter) {
  // Generates the x and y coordinate of the fire. Y oscillates from 5 to 23.
  _fire.generate_fire(); // Generates X coord of fire.
  // Y is calculated from parabolic relation to game counter.
  _fire_y = int(-0.0073*game_counter*game_counter + 0.73*game_counter + 5);  
}

void Engine::update_lcd(N5110 &lcd, int game_counter){
  // Draw all sprites, screen fire, platforms and player score. 
  lcd.drawSprite(_skater_x,_skater_y,17,10,
                (int *)_skater.get_sprite(_skater_sprite));
  lcd.drawSprite(_coin.get_coin_x(),_coin.get_coin_y(),5,5,
                 (int*)_coin.get_coin_sprite());
  lcd.drawSprite(_fire.get_fire_x(),_fire_y,5,8,(int*)_fire.get_fire_sprite()); 
  lcd.drawLine(_lower_line_2.x_start,_lower_line_2.y,_lower_line_2.x_end,
               _lower_line_2.y,FILL_BLACK);
  lcd.drawLine(_lower_line_1.x_start,_lower_line_1.y,_lower_line_1.x_end,
               _lower_line_1.y,FILL_BLACK);
  lcd.drawLine(_lower_line_3.x_start,_lower_line_3.y,_lower_line_3.x_end,
               _lower_line_3.y,FILL_BLACK);
  lcd.drawLine(_upper_line_2.x_start,_upper_line_2.y,_upper_line_2.x_end,
               _upper_line_2.y,FILL_BLACK);
  lcd.drawLine(_upper_line_1.x_start,_upper_line_1.y,_upper_line_1.x_end,
               _upper_line_1.y,FILL_BLACK);
  lcd.drawLine(_upper_line_3.x_start,_upper_line_3.y,_upper_line_3.x_end,
               _upper_line_3.y,FILL_BLACK);
  // Print the score.
  sprintf(buffer,"%2d",_player_score);
  lcd.printString(buffer,0,0);
  draw_screen_fire(game_counter, lcd);
}

bool Engine::get_start_platform_flag() {
  if (_input.A_flag) _start_platform_flag = false;  // Makes starting platform 
  // vanish after first jump of the game.
  return _start_platform_flag;
}

void Engine::check_coin_collision(Gamepad &gamepad) {
  // If skater x and y coord matches the coin's. Small adjustment for Y coord
  // compensates for skater sprite size.
  if (_skater_x == _coin.get_coin_x() 
      && (_skater_y == _coin.get_coin_y() - 10)) {  
    _coin_collision_flag = true;
    _player_score++;
    _coin.set_coin((rand() % 100),(abs(rand() % 100 - 20)));  // Place coin 
    // on a constrained random position, [1].
    gamepad.tone(1500, 0.05);  // Make collection noise on buzzer.
    wait(0.05);
    gamepad.tone(3000, 0.05);   
  }
}

void Engine::check_fire_collision(Gamepad &gamepad, int game_counter) {
  // If skater is not ducking, has same x coord and within a range of y coord 
  // as fire.
  if (_input.coord.y > -0.1 
      && _skater_x == _fire.get_fire_x() 
      && _skater_y > _fire_y - 10 
      && _skater_y < _fire_y + 10
      ) {  // A range of Y coords to make collision 
  // more frequent.
    _skater.set_reset_flag(true);
    gamepad.tone(400, 0.25); // Make collision noise on buzzer.
    wait(0.05);
    gamepad.tone(200, 0.25);
  } else if ( _skater_x < -10 || _skater_x > 84 ) { // If skater goes off the 
  // screen.
    _skater.set_reset_flag(true);
    gamepad.tone(200, 0.5);
  }
  // If the skater collides with the scaled peak of the background fire. 
  if (_skater_y > (50 - game_counter*_fire_height*0.023)) {
    _fall_flag = true;
    wait(0.05);  // Slow down falling sequence. 
  } 
}

int Engine::get_player_score() {
  return _player_score;
}