ELEC2645 (2018/19) / Mbed 2 deprecated EL17MCD

Dependencies:   mbed

Committer:
el17mcd
Date:
Sat Apr 27 17:43:46 2019 +0000
Revision:
17:cb39d9fa08dc
Parent:
16:a2c945279b79
Child:
18:165e3d49daa8
!  Stylistic changes made to code so it conforms to google c++ guide. Beginning of inline commenting of source code.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
el17mcd 7:a3ccabdebe2e 1 /* TanksEngine.cpp
el17mcd 7:a3ccabdebe2e 2 Game Engine
el17mcd 7:a3ccabdebe2e 3 9.4.19
el17mcd 7:a3ccabdebe2e 4 */
el17mcd 7:a3ccabdebe2e 5 #include "TanksEngine.h"
el17mcd 11:4e2eb64031a0 6
el17mcd 7:a3ccabdebe2e 7 TanksEngine::TanksEngine()
el17mcd 11:4e2eb64031a0 8
el17mcd 7:a3ccabdebe2e 9 {
el17mcd 17:cb39d9fa08dc 10 _tankl.set_movement_limits(0, 21); // Prevent tanks from leaving the screen
el17mcd 17:cb39d9fa08dc 11 _tankr.set_movement_limits(54, 74); // or passing through map obstacle
el17mcd 17:cb39d9fa08dc 12 _grav = 0.035;
el17mcd 7:a3ccabdebe2e 13 }
el17mcd 7:a3ccabdebe2e 14
el17mcd 7:a3ccabdebe2e 15 TanksEngine::~TanksEngine()
el17mcd 7:a3ccabdebe2e 16 {
el17mcd 7:a3ccabdebe2e 17
el17mcd 7:a3ccabdebe2e 18 }
el17mcd 15:fa5282fcd134 19
el17mcd 17:cb39d9fa08dc 20 void TanksEngine::initgame(Menus &menus) // initialise a game based on preferences
el17mcd 17:cb39d9fa08dc 21 { // set in the menus, reset member variable's
el17mcd 17:cb39d9fa08dc 22 // Player Preferences: // values from the previous game.
el17mcd 17:cb39d9fa08dc 23 _initial_health = menus.get_health();
el17mcd 17:cb39d9fa08dc 24 _mute = menus.get_mute();
el17mcd 17:cb39d9fa08dc 25 _tankl.set_health(_initial_health);
el17mcd 17:cb39d9fa08dc 26 _tankr.set_health(_initial_health);
el17mcd 16:a2c945279b79 27 // Set up Game:
el17mcd 17:cb39d9fa08dc 28 _tankl.set_position(0, 0);
el17mcd 17:cb39d9fa08dc 29 _tankr.set_position(84-10, 0);
el17mcd 17:cb39d9fa08dc 30 _turn = 1;
el17mcd 17:cb39d9fa08dc 31 _wind = 0.00005 * (-100 + rand() % 201); // Determines "strength" of wind in horizontal
el17mcd 17:cb39d9fa08dc 32 _fire = false; // x direction.
el17mcd 17:cb39d9fa08dc 33 _move = 0;
el17mcd 17:cb39d9fa08dc 34 _cooldown_l = -1;
el17mcd 17:cb39d9fa08dc 35 _cooldown_r = -1;
el17mcd 17:cb39d9fa08dc 36 _turn_counter = 1;
el17mcd 14:fe2e16cdf219 37
el17mcd 12:9e6d5d0a0c82 38 }
el17mcd 12:9e6d5d0a0c82 39
el17mcd 17:cb39d9fa08dc 40 void TanksEngine::game_loop(Graphics &graphics, N5110 &lcd, Gamepad &pad, // The fundemental game loop,
el17mcd 17:cb39d9fa08dc 41 Menus &menus, Scores &scores) // the phase of the game is determined
el17mcd 17:cb39d9fa08dc 42 { // by _turn. Read's gamepad and writes
el17mcd 17:cb39d9fa08dc 43 while (_turn != 0) { // to lcd.
el17mcd 17:cb39d9fa08dc 44 lcd.clear();
el17mcd 17:cb39d9fa08dc 45 _read_input(pad);
el17mcd 17:cb39d9fa08dc 46 graphics.draw_parkinson_map(31, 17, lcd); // Draws map obstacle on the screen.
el17mcd 17:cb39d9fa08dc 47 if (_turn == 1) {
el17mcd 17:cb39d9fa08dc 48 _left_tank_turn(graphics, pad);
el17mcd 17:cb39d9fa08dc 49 } else if (_turn == 2 || _turn == 4) {
el17mcd 17:cb39d9fa08dc 50 _projectile_phase(lcd, pad);
el17mcd 17:cb39d9fa08dc 51 } else if (_turn == 3) {
el17mcd 17:cb39d9fa08dc 52 _right_tank_turn(graphics, pad);
el17mcd 17:cb39d9fa08dc 53 } else if (_turn == 5) {
el17mcd 17:cb39d9fa08dc 54 _end(graphics, lcd, pad, scores); // When either player drops to zero health, the
el17mcd 17:cb39d9fa08dc 55 } // game stops and an end screen is shown.
el17mcd 17:cb39d9fa08dc 56 _draw(graphics, lcd);
el17mcd 17:cb39d9fa08dc 57 lcd.refresh();
el17mcd 17:cb39d9fa08dc 58 wait_ms(1000/60);
el17mcd 17:cb39d9fa08dc 59 }
el17mcd 15:fa5282fcd134 60 }
el17mcd 17:cb39d9fa08dc 61
el17mcd 16:a2c945279b79 62 // Game Flow
el17mcd 17:cb39d9fa08dc 63
el17mcd 15:fa5282fcd134 64 void TanksEngine::_left_tank_turn(Graphics &graphics, Gamepad &pad)
el17mcd 12:9e6d5d0a0c82 65 {
el17mcd 17:cb39d9fa08dc 66 _tankl.move_position(_move);
el17mcd 17:cb39d9fa08dc 67 if (_angle >= 355 && _angle <= 360 || _angle >= 0 && _angle <= 90) { // turret must be at acceptable angle
el17mcd 17:cb39d9fa08dc 68 _tank_shoots(_tankl.get_position_x(), _tankl.get_position_y(), 2, pad);
el17mcd 17:cb39d9fa08dc 69 }
el17mcd 17:cb39d9fa08dc 70 graphics.show_health(_tankl.get_health(), pad); // Show player's health in LEDs
el17mcd 11:4e2eb64031a0 71 }
el17mcd 11:4e2eb64031a0 72
el17mcd 15:fa5282fcd134 73 void TanksEngine::_right_tank_turn(Graphics &graphics, Gamepad &pad)
el17mcd 11:4e2eb64031a0 74 {
el17mcd 17:cb39d9fa08dc 75 _tankr.move_position(_move);
el17mcd 17:cb39d9fa08dc 76 if (_angle >= 270 && _angle <= 360 || _angle >= 0 && _angle <= 5) { // turret must be at acceptable angle
el17mcd 17:cb39d9fa08dc 77 _tank_shoots(_tankr.get_position_x(), _tankr.get_position_y(), 4, pad);
el17mcd 17:cb39d9fa08dc 78 }
el17mcd 17:cb39d9fa08dc 79 graphics.show_health(_tankr.get_health(), pad); // Show player's health in LEDs
el17mcd 16:a2c945279b79 80 }
el17mcd 16:a2c945279b79 81
el17mcd 17:cb39d9fa08dc 82 void TanksEngine::_projectile_phase(N5110 &lcd, Gamepad &pad) // Projectile phase; where player's cannot impact game
el17mcd 17:cb39d9fa08dc 83 { // and await the projectile to leave boundaries or
el17mcd 17:cb39d9fa08dc 84 _proj.update_flight(); // collide with an object.
el17mcd 17:cb39d9fa08dc 85 _proj.generate_hitbox();
el17mcd 17:cb39d9fa08dc 86 if (_proj.check_boundaries() == true) { // change turn if projectile falls off left
el17mcd 17:cb39d9fa08dc 87 _change_turn(); // or right side of screen.
el17mcd 17:cb39d9fa08dc 88 }
el17mcd 17:cb39d9fa08dc 89 _object_hit(_proj.get_position_x(), _proj.get_position_y(), lcd, pad); // Check collision with other objects based on
el17mcd 17:cb39d9fa08dc 90 } // projectile's current x and y position.
el17mcd 16:a2c945279b79 91
el17mcd 17:cb39d9fa08dc 92 void TanksEngine::_change_turn() // Determines who's turn is next or if game should end.
el17mcd 16:a2c945279b79 93 {
el17mcd 17:cb39d9fa08dc 94 if (_tankl.get_health() == 0 || _tankr.get_health() == 0) {
el17mcd 17:cb39d9fa08dc 95 _turn = 5;
el17mcd 17:cb39d9fa08dc 96 } else if (_turn == 2) {
el17mcd 17:cb39d9fa08dc 97 _turn = 3;
el17mcd 17:cb39d9fa08dc 98 } else if (_turn == 4) {
el17mcd 17:cb39d9fa08dc 99 _turn = 1;
el17mcd 17:cb39d9fa08dc 100 _turn_counter++; // Increase turn counter for scoring purposes.
el17mcd 17:cb39d9fa08dc 101 }
el17mcd 17:cb39d9fa08dc 102 srand(time(0)); // Randomly change wind in either positive or negative
el17mcd 17:cb39d9fa08dc 103 _wind = 0.00005 * (-100 + rand() % 201); // horizontal x direction. srand seeds the rand function.
el17mcd 16:a2c945279b79 104 }
el17mcd 16:a2c945279b79 105
el17mcd 17:cb39d9fa08dc 106 void TanksEngine::_end(Graphics &graphics, N5110 &lcd, Gamepad &pad, // End screen that shows who won and their score.
el17mcd 17:cb39d9fa08dc 107 Scores &scores)
el17mcd 16:a2c945279b79 108 {
el17mcd 17:cb39d9fa08dc 109 _turn = 0; // This allows game loop to end and top-level loop to progress
el17mcd 17:cb39d9fa08dc 110 int score = scores.score_calculator(_turn_counter, _initial_health);
el17mcd 17:cb39d9fa08dc 111 pad.check_event(Gamepad::BACK_PRESSED); // Clears trigger so that the end screen is not skipped due to a
el17mcd 17:cb39d9fa08dc 112 // prevous button press.
el17mcd 17:cb39d9fa08dc 113 while(pad.check_event(Gamepad::BACK_PRESSED) == false) {
el17mcd 17:cb39d9fa08dc 114 lcd.clear();
el17mcd 17:cb39d9fa08dc 115 scores.display_score(score, lcd);
el17mcd 17:cb39d9fa08dc 116 pad.check_event(Gamepad::START_PRESSED); // Clear trigger so start screen is not skipped.
el17mcd 17:cb39d9fa08dc 117
el17mcd 17:cb39d9fa08dc 118 if (_tankl.get_health() == 0) {
el17mcd 17:cb39d9fa08dc 119 graphics.draw_right_victory(lcd);
el17mcd 17:cb39d9fa08dc 120 }
el17mcd 17:cb39d9fa08dc 121 else if (_tankr.get_health() == 0) {
el17mcd 17:cb39d9fa08dc 122 graphics.draw_left_victory(lcd);
el17mcd 17:cb39d9fa08dc 123 }
el17mcd 17:cb39d9fa08dc 124 lcd.refresh();
el17mcd 17:cb39d9fa08dc 125 }
el17mcd 11:4e2eb64031a0 126 }
el17mcd 11:4e2eb64031a0 127
el17mcd 17:cb39d9fa08dc 128 // Game Mechanics
el17mcd 17:cb39d9fa08dc 129
el17mcd 17:cb39d9fa08dc 130 void TanksEngine::_tank_shoots(int x, int y, int turn, Gamepad &pad) // Govern's the shooting mechanic for the tanks.
el17mcd 16:a2c945279b79 131 {
el17mcd 17:cb39d9fa08dc 132 if (_fire == true) { // _fire determined by A button; pressed = true.
el17mcd 17:cb39d9fa08dc 133 int angle = _angle; // changes float _angle to an int angle for use in modulo arithmetic below.
el17mcd 17:cb39d9fa08dc 134 angle = (-angle + 90) % 360; // gamepad's convention is N = 0 clockwise changed into E = 0 anticlockwise.
el17mcd 17:cb39d9fa08dc 135 x = x + 5;
el17mcd 17:cb39d9fa08dc 136 y = y + 5;
el17mcd 17:cb39d9fa08dc 137 _proj.set_launch_parameters(x, y, angle, _vel, _grav, _wind); // set launch parameters for projectile based on tank/game parameters.
el17mcd 17:cb39d9fa08dc 138 _tankl.generate_hitbox(); // generate hitboxes ready to determine collision during projectile phase.
el17mcd 17:cb39d9fa08dc 139 _tankr.generate_hitbox();
el17mcd 17:cb39d9fa08dc 140 _turn = turn; // change to projectile phase.
el17mcd 17:cb39d9fa08dc 141 _sounds(3, pad);
el17mcd 17:cb39d9fa08dc 142 }
el17mcd 17:cb39d9fa08dc 143 }
el17mcd 17:cb39d9fa08dc 144
el17mcd 17:cb39d9fa08dc 145 void TanksEngine::_object_hit(int x, int y, N5110 &lcd, Gamepad &pad) // Decides when to check for collsions and
el17mcd 17:cb39d9fa08dc 146 { // their impact.
el17mcd 17:cb39d9fa08dc 147 if (x >= 0 && x <= 30 && _collision_pl(_tankl, _proj) == true) {
el17mcd 17:cb39d9fa08dc 148 _tankl.lose_health();
el17mcd 17:cb39d9fa08dc 149 _change_turn();
el17mcd 17:cb39d9fa08dc 150 _sounds(1, pad);
el17mcd 17:cb39d9fa08dc 151 } else if (x >= 55 && x <= 83 && _collision_pr(_tankr, _proj) == true) {
el17mcd 17:cb39d9fa08dc 152 _tankr.lose_health();
el17mcd 17:cb39d9fa08dc 153 _change_turn();
el17mcd 17:cb39d9fa08dc 154 _sounds(1, pad);
el17mcd 17:cb39d9fa08dc 155 } else if (x >= 31 && x <= 54 && _collision_pm(x, y, lcd, _proj) == true) {
el17mcd 17:cb39d9fa08dc 156 _change_turn();
el17mcd 17:cb39d9fa08dc 157 _sounds(2, pad);
el17mcd 17:cb39d9fa08dc 158 }
el17mcd 17:cb39d9fa08dc 159 }
el17mcd 17:cb39d9fa08dc 160
el17mcd 17:cb39d9fa08dc 161 bool TanksEngine::_collision_pl(Tank _tankl, Projectile _proj) // Detects projectile hitting LEFT tank by comparing every
el17mcd 17:cb39d9fa08dc 162 { // element in the projectile's hitbox array to that of the
el17mcd 17:cb39d9fa08dc 163 for (int i0 = 0; i0 < 4; i0++) { // tank's hitbox array.
el17mcd 17:cb39d9fa08dc 164 for (int i1 = 0; i1 < 40; i1++) {
el17mcd 17:cb39d9fa08dc 165 if (_proj.get_hitbox(i0) == _tankl.get_hitbox(i1)) {
el17mcd 17:cb39d9fa08dc 166 return true;
el17mcd 17:cb39d9fa08dc 167 }
el17mcd 16:a2c945279b79 168 }
el17mcd 17:cb39d9fa08dc 169 }
el17mcd 17:cb39d9fa08dc 170 return false;
el17mcd 16:a2c945279b79 171 }
el17mcd 16:a2c945279b79 172
el17mcd 17:cb39d9fa08dc 173 bool TanksEngine::_collision_pr(Tank _tankr, Projectile _proj) // Detects projectile hitting RIGHT tank by comparing every
el17mcd 17:cb39d9fa08dc 174 { // element in the projectile's hitbox array to that of the
el17mcd 17:cb39d9fa08dc 175 for (int i0 = 0; i0 < 4; i0++) { // tank's hitbox array.
el17mcd 17:cb39d9fa08dc 176 for (int i1 = 0; i1 < 40; i1++) {
el17mcd 17:cb39d9fa08dc 177 if (_proj.get_hitbox(i0) == _tankr.get_hitbox(i1)) {
el17mcd 17:cb39d9fa08dc 178 return true;
el17mcd 17:cb39d9fa08dc 179 }
el17mcd 16:a2c945279b79 180 }
el17mcd 17:cb39d9fa08dc 181 }
el17mcd 17:cb39d9fa08dc 182 return false;
el17mcd 17:cb39d9fa08dc 183 }
el17mcd 17:cb39d9fa08dc 184
el17mcd 17:cb39d9fa08dc 185 bool TanksEngine::_collision_pm(int x, int y, N5110 &lcd, Projectile _proj) // Detects projectiles hitting the MAP by comparing the
el17mcd 17:cb39d9fa08dc 186 { // position of the projectile's bottom two pixels
el17mcd 17:cb39d9fa08dc 187 if (lcd.getPixel(x, 48 - y) == 1) { // to the positions on the screen. If they are occupied a
el17mcd 17:cb39d9fa08dc 188 return true; // pixel the map obstacle has been hit.
el17mcd 17:cb39d9fa08dc 189 } else if (lcd.getPixel(x + 1,48 - y) == 1) {
el17mcd 17:cb39d9fa08dc 190 return true;
el17mcd 17:cb39d9fa08dc 191 } else {
el17mcd 17:cb39d9fa08dc 192 return false ;
el17mcd 17:cb39d9fa08dc 193 }
el17mcd 16:a2c945279b79 194 }
el17mcd 16:a2c945279b79 195
el17mcd 17:cb39d9fa08dc 196 // Game Inputs
el17mcd 17:cb39d9fa08dc 197
el17mcd 17:cb39d9fa08dc 198 void TanksEngine::_read_input(Gamepad &pad) // Detects gamepad activity and it's bearing
el17mcd 17:cb39d9fa08dc 199 { // on the game.
el17mcd 17:cb39d9fa08dc 200 _vel = 1.15 + pad.read_pot();
el17mcd 17:cb39d9fa08dc 201 _angle = pad.get_angle();
el17mcd 17:cb39d9fa08dc 202 _mag = pad.get_mag();
el17mcd 17:cb39d9fa08dc 203 if (pad.check_event(Gamepad::L_PRESSED) == true && _cooldown_l < 0) { // toggle movement to the left.
el17mcd 17:cb39d9fa08dc 204 _move = -1 * !_move;
el17mcd 17:cb39d9fa08dc 205 _cooldown_l = 10; // Cooldown prevents multiple button presses being resistered.
el17mcd 17:cb39d9fa08dc 206 } else if (pad.check_event(Gamepad::R_PRESSED) == true && _cooldown_r < 0) { // toggle movement to the right.
el17mcd 17:cb39d9fa08dc 207 _move = 1 * !_move;
el17mcd 17:cb39d9fa08dc 208 _cooldown_r = 10;
el17mcd 17:cb39d9fa08dc 209 } else if (_turn == 2 || _turn == 4) {
el17mcd 17:cb39d9fa08dc 210 _move = 0; // movement cannot be triggered during projectile phase
el17mcd 17:cb39d9fa08dc 211 }
el17mcd 17:cb39d9fa08dc 212 if (pad.check_event(Gamepad::A_PRESSED) == true) { // Tank shoots if A button is pressed (assuming turret
el17mcd 17:cb39d9fa08dc 213 _fire = true; // is at an acceptable angle).
el17mcd 17:cb39d9fa08dc 214 } else {
el17mcd 17:cb39d9fa08dc 215 _fire = false;
el17mcd 17:cb39d9fa08dc 216 }
el17mcd 17:cb39d9fa08dc 217 _decrement_cooldowns();
el17mcd 16:a2c945279b79 218 }
el17mcd 16:a2c945279b79 219
el17mcd 17:cb39d9fa08dc 220 void TanksEngine::_decrement_cooldowns() // Prevents buttons being pressed multiple times in quick succession.
el17mcd 16:a2c945279b79 221 {
el17mcd 17:cb39d9fa08dc 222 _cooldown_l--;
el17mcd 17:cb39d9fa08dc 223 _cooldown_r--;
el17mcd 17:cb39d9fa08dc 224 if (_cooldown_l < -100) {
el17mcd 17:cb39d9fa08dc 225 _cooldown_l = -1;
el17mcd 17:cb39d9fa08dc 226 }
el17mcd 17:cb39d9fa08dc 227 if (_cooldown_r < -100) {
el17mcd 17:cb39d9fa08dc 228 _cooldown_r = -1;
el17mcd 17:cb39d9fa08dc 229 }
el17mcd 16:a2c945279b79 230 }
el17mcd 16:a2c945279b79 231
el17mcd 16:a2c945279b79 232 // Game Outputs
el17mcd 17:cb39d9fa08dc 233
el17mcd 17:cb39d9fa08dc 234 void TanksEngine::_draw(Graphics graphics, N5110 &lcd) // Draw graphics for all objects, turrets rotate
el17mcd 17:cb39d9fa08dc 235 { // depending on current turn
el17mcd 17:cb39d9fa08dc 236 graphics.draw_tank_l(_tankl.get_position_x(), _tankl.get_position_y(), lcd);
el17mcd 17:cb39d9fa08dc 237 if (_turn == 1) {
el17mcd 17:cb39d9fa08dc 238 graphics.draw_turret_l(_tankl.get_position_x(), _tankl.get_position_y(),
el17mcd 17:cb39d9fa08dc 239 _angle, lcd);
el17mcd 17:cb39d9fa08dc 240 graphics.draw_wind_bar(_wind, lcd);
el17mcd 17:cb39d9fa08dc 241 if (_mag >= 0.5) { // only draw the reticle if the joystick is not in
el17mcd 17:cb39d9fa08dc 242 graphics.draw_reticle(_tankl.get_position_x(), _tankl.get_position_y(), // a neutral position.
el17mcd 17:cb39d9fa08dc 243 _angle, lcd);
el17mcd 17:cb39d9fa08dc 244 }
el17mcd 17:cb39d9fa08dc 245 }
el17mcd 17:cb39d9fa08dc 246 if (_turn == 2 || _turn == 4) {
el17mcd 17:cb39d9fa08dc 247 graphics.draw_projectile(_proj.get_position_x(), _proj.get_position_y(),
el17mcd 17:cb39d9fa08dc 248 lcd);
el17mcd 17:cb39d9fa08dc 249 }
el17mcd 17:cb39d9fa08dc 250 graphics.draw_tank_r(_tankr.get_position_x(), _tankr.get_position_y(), lcd);
el17mcd 17:cb39d9fa08dc 251 if (_turn == 3) {
el17mcd 17:cb39d9fa08dc 252 graphics.draw_turret_r(_tankr.get_position_x(), _tankr.get_position_y(),
el17mcd 17:cb39d9fa08dc 253 _angle, lcd);
el17mcd 17:cb39d9fa08dc 254 graphics.draw_wind_bar(_wind, lcd);
el17mcd 17:cb39d9fa08dc 255 if (_mag >= 0.5) {
el17mcd 17:cb39d9fa08dc 256 graphics.draw_reticle(_tankr.get_position_x(), _tankr.get_position_y(),
el17mcd 17:cb39d9fa08dc 257 _angle, lcd);
el17mcd 17:cb39d9fa08dc 258 }
el17mcd 17:cb39d9fa08dc 259 }
el17mcd 11:4e2eb64031a0 260 }
el17mcd 11:4e2eb64031a0 261
el17mcd 17:cb39d9fa08dc 262 void TanksEngine::_sounds(int n, Gamepad &pad) // Play sound relating to game event.
el17mcd 15:fa5282fcd134 263 {
el17mcd 17:cb39d9fa08dc 264 if (_mute == false) {
el17mcd 17:cb39d9fa08dc 265 if (n == 1) { // tank is hit.
el17mcd 17:cb39d9fa08dc 266 pad.tone(300, 0.125);
el17mcd 17:cb39d9fa08dc 267 pad.tone(400, 0.125);
el17mcd 17:cb39d9fa08dc 268 pad.tone(500, 0.125);
el17mcd 17:cb39d9fa08dc 269 } else if (n == 2) { // building hit.
el17mcd 17:cb39d9fa08dc 270 pad.tone(500, 0.125);
el17mcd 17:cb39d9fa08dc 271 pad.tone(400, 0.125);
el17mcd 17:cb39d9fa08dc 272 pad.tone(300, 0.125);
el17mcd 17:cb39d9fa08dc 273 } else if (n == 3) { // tank shoots.
el17mcd 17:cb39d9fa08dc 274 pad.tone(200, 0.125);
el17mcd 13:feadff02d3f7 275 }
el17mcd 17:cb39d9fa08dc 276 }
el17mcd 13:feadff02d3f7 277 }
el17mcd 13:feadff02d3f7 278
el17mcd 12:9e6d5d0a0c82 279
el17mcd 12:9e6d5d0a0c82 280