Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed
TanksEngine/TanksEngine.cpp
- Committer:
- el17mcd
- Date:
- 2019-04-30
- Revision:
- 18:165e3d49daa8
- Parent:
- 17:cb39d9fa08dc
- Child:
- 21:44e87d88afe2
File content as of revision 18:165e3d49daa8:
/* TanksEngine.cpp
Game Engine
9.4.19
*/
#include "TanksEngine.h"
TanksEngine::TanksEngine()
{
_tankl.set_movement_limits(0, 21); // Prevent tanks from leaving the screen
_tankr.set_movement_limits(54, 74); // or passing through map obstacle
_grav = 0.035;
}
TanksEngine::~TanksEngine()
{
}
void TanksEngine::initgame(Menus &menus) // initialise a game based on preferences
{ // set in the menus, reset member variable's
// Player Preferences: // values from the previous game.
_initial_health = menus.get_health();
_mute = menus.get_mute();
_tankl.set_health(_initial_health);
_tankr.set_health(_initial_health);
// Set up Game:
_tankl.set_position(0, 0);
_tankr.set_position(84-10, 0);
_turn = 1;
_wind = 0.00005 * (-100 + rand() % 201); // Determines "strength" of wind in horizontal
_fire = false; // x direction.
_move = 0;
_cooldown_l = -1;
_cooldown_r = -1;
_turn_counter = 1;
}
void TanksEngine::game_loop(Graphics &graphics, N5110 &lcd, Gamepad &pad, // The fundemental game loop,
Menus &menus, Scores &scores) // the phase of the game is determined
{ // by _turn. Read's gamepad and writes
while (_turn != 0) { // to lcd.
lcd.clear();
_read_input(pad);
graphics.draw_parkinson_map(31, 17, lcd); // Draws map obstacle on the screen.
if (_turn == 1) {
_left_tank_turn(graphics, pad);
} else if (_turn == 2 || _turn == 4) {
_projectile_phase(lcd, pad);
} else if (_turn == 3) {
_right_tank_turn(graphics, pad);
} else if (_turn == 5) {
_end(graphics, lcd, pad, scores); // When either player drops to zero health, the
} // game stops and an end screen is shown.
_draw(graphics, lcd);
lcd.refresh();
wait_ms(1000/60);
}
}
// Game Flow
void TanksEngine::_left_tank_turn(Graphics &graphics, Gamepad &pad)
{
_tankl.move_position(_move);
if (_angle >= 355 && _angle <= 360 || _angle >= 0 && _angle <= 90) { // turret must be at acceptable angle
_tank_shoots(_tankl.get_position_x(), _tankl.get_position_y(), 2, pad);
}
graphics.show_health(_tankl.get_health(), pad); // Show player's health in LEDs
}
void TanksEngine::_right_tank_turn(Graphics &graphics, Gamepad &pad)
{
_tankr.move_position(_move);
if (_angle >= 270 && _angle <= 360 || _angle >= 0 && _angle <= 5) { // turret must be at acceptable angle
_tank_shoots(_tankr.get_position_x(), _tankr.get_position_y(), 4, pad);
}
graphics.show_health(_tankr.get_health(), pad); // Show player's health in LEDs
}
void TanksEngine::_projectile_phase(N5110 &lcd, Gamepad &pad) // Projectile phase; where player's cannot impact game
{ // and await the projectile to leave boundaries or
_proj.update_flight(); // collide with an object.
_proj.generate_hitbox();
if (_proj.check_boundaries() == true) { // change turn if projectile falls off left
_change_turn(); // or right side of screen.
}
_object_hit(_proj.get_position_x(), _proj.get_position_y(), lcd, pad); // Check collision with other objects based on
} // projectile's current x and y position.
void TanksEngine::_change_turn() // Determines who's turn is next or if game should end.
{
if (_tankl.get_health() == 0 || _tankr.get_health() == 0) {
_turn = 5;
} else if (_turn == 2) {
_turn = 3;
} else if (_turn == 4) {
_turn = 1;
_turn_counter++; // Increase turn counter for scoring purposes.
}
srand(time(0)); // Randomly change wind in either positive or negative
_wind = 0.00005 * (-100 + rand() % 201); // horizontal x direction. srand seeds the rand function.
}
void TanksEngine::_end(Graphics &graphics, N5110 &lcd, Gamepad &pad, // End screen that shows who won and their score.
Scores &scores)
{
_turn = 0; // This allows game loop to end and top-level loop to progress
int score = scores.score_calculator(_turn_counter, _initial_health);
pad.check_event(Gamepad::BACK_PRESSED); // Clears trigger so that the end screen is not skipped due to a
// prevous BACK button press.
while(pad.check_event(Gamepad::BACK_PRESSED) == false) {
lcd.clear();
scores.display_score(score, lcd);
pad.check_event(Gamepad::START_PRESSED); // Clear trigger so start screen is not skipped.
if (_tankl.get_health() == 0) {
graphics.draw_right_victory(lcd);
}
else if (_tankr.get_health() == 0) {
graphics.draw_left_victory(lcd);
}
lcd.refresh();
}
}
// Game Mechanics
void TanksEngine::_tank_shoots(int x, int y, int turn, Gamepad &pad) // Govern's the shooting mechanic for the tanks.
{
if (_fire == true) { // _fire determined by A button; pressed = true.
int angle = _angle; // changes float _angle to an int angle for use in modulo arithmetic below.
angle = (-angle + 90) % 360; // gamepad's convention is N = 0 clockwise changed into E = 0 anticlockwise.
x = x + 5;
y = y + 5;
_proj.set_launch_parameters(x, y, angle, _vel, _grav, _wind); // set launch parameters for projectile based on tank/game parameters.
_tankl.generate_hitbox(); // generate hitboxes ready to determine collision during projectile phase.
_tankr.generate_hitbox();
_turn = turn; // change to projectile phase.
_sounds(3, pad);
}
}
void TanksEngine::_object_hit(int x, int y, N5110 &lcd, Gamepad &pad) // Decides when to check for collsions and
{ // their impact.
if (x >= 0 && x <= 30 && _collision_pl(_tankl, _proj) == true) {
_tankl.lose_health();
_change_turn();
_sounds(1, pad);
} else if (x >= 55 && x <= 83 && _collision_pr(_tankr, _proj) == true) {
_tankr.lose_health();
_change_turn();
_sounds(1, pad);
} else if (x >= 31 && x <= 54 && _collision_pm(x, y, lcd, _proj) == true) {
_change_turn();
_sounds(2, pad);
}
}
bool TanksEngine::_collision_pl(Tank _tankl, Projectile _proj) // Detects projectile hitting LEFT tank by comparing every
{ // element in the projectile's hitbox array to that of the
for (int i0 = 0; i0 < 4; i0++) { // tank's hitbox array.
for (int i1 = 0; i1 < 40; i1++) {
if (_proj.get_hitbox(i0) == _tankl.get_hitbox(i1)) {
return true;
}
}
}
return false;
}
bool TanksEngine::_collision_pr(Tank _tankr, Projectile _proj) // Detects projectile hitting RIGHT tank by comparing every
{ // element in the projectile's hitbox array to that of the
for (int i0 = 0; i0 < 4; i0++) { // tank's hitbox array.
for (int i1 = 0; i1 < 40; i1++) {
if (_proj.get_hitbox(i0) == _tankr.get_hitbox(i1)) {
return true;
}
}
}
return false;
}
bool TanksEngine::_collision_pm(int x, int y, N5110 &lcd, Projectile _proj) // Detects projectiles hitting the MAP by comparing the
{ // position of the projectile's bottom two pixels
if (lcd.getPixel(x, 48 - y) == 1) { // to the positions on the screen. If they are occupied by a
return true; // pixel the map obstacle has been hit.
} else if (lcd.getPixel(x + 1,48 - y) == 1) {
return true;
} else {
return false ;
}
}
// Game Inputs
void TanksEngine::_read_input(Gamepad &pad) // Detects gamepad activity and it's bearing
{ // on the game.
_vel = 1.15 + pad.read_pot();
_angle = pad.get_angle();
_mag = pad.get_mag();
if (pad.check_event(Gamepad::L_PRESSED) == true && _cooldown_l < 0) { // toggle movement to the left.
_move = -1 * !_move;
_cooldown_l = 10; // Cooldown prevents multiple button presses being registered.
} else if (pad.check_event(Gamepad::R_PRESSED) == true && _cooldown_r < 0) { // toggle movement to the right.
_move = 1 * !_move;
_cooldown_r = 10;
} else if (_turn == 2 || _turn == 4) {
_move = 0; // movement cannot be triggered during projectile phase
}
if (pad.check_event(Gamepad::A_PRESSED) == true) { // Tank shoots if A button is pressed (assuming turret
_fire = true; // is at an acceptable angle).
} else {
_fire = false;
}
_decrement_cooldowns();
}
void TanksEngine::_decrement_cooldowns() // Prevents buttons being pressed multiple times in quick succession.
{
_cooldown_l--;
_cooldown_r--;
if (_cooldown_l < -100) {
_cooldown_l = -1;
}
if (_cooldown_r < -100) {
_cooldown_r = -1;
}
}
// Game Outputs
void TanksEngine::_draw(Graphics graphics, N5110 &lcd) // Draw graphics for all objects, turrets rotate
{ // for the current tank's turn.
graphics.draw_tank_l(_tankl.get_position_x(), _tankl.get_position_y(), lcd);
if (_turn == 1) {
graphics.draw_turret_l(_tankl.get_position_x(), _tankl.get_position_y(),
_angle, lcd);
graphics.draw_wind_bar(_wind, lcd);
if (_mag >= 0.5) { // only draw the reticle if the joystick is not in
graphics.draw_reticle(_tankl.get_position_x(), _tankl.get_position_y(), // a neutral position.
_angle, lcd);
}
}
if (_turn == 2 || _turn == 4) {
graphics.draw_projectile(_proj.get_position_x(), _proj.get_position_y(),
lcd);
}
graphics.draw_tank_r(_tankr.get_position_x(), _tankr.get_position_y(), lcd);
if (_turn == 3) {
graphics.draw_turret_r(_tankr.get_position_x(), _tankr.get_position_y(),
_angle, lcd);
graphics.draw_wind_bar(_wind, lcd);
if (_mag >= 0.5) {
graphics.draw_reticle(_tankr.get_position_x(), _tankr.get_position_y(),
_angle, lcd);
}
}
}
void TanksEngine::_sounds(int n, Gamepad &pad) // Play sound relating to game event.
{
if (_mute == false) {
if (n == 1) { // tank is hit.
pad.tone(300, 0.125);
pad.tone(400, 0.125);
pad.tone(500, 0.125);
} else if (n == 2) { // building hit.
pad.tone(500, 0.125);
pad.tone(400, 0.125);
pad.tone(300, 0.125);
} else if (n == 3) { // tank shoots.
pad.tone(200, 0.125);
}
}
}