A rouge-like rpg, heavily inspired on the binding of isaac. Running on a FRDM-K64F Mbed board. C++.

Dependencies:   mbed MotionSensor

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RoomEngine.cpp Source File

RoomEngine.cpp

00001 #include "RoomEngine.h"
00002 // Constructor
00003 RoomEngine::RoomEngine(float global_contrast)
00004 {
00005     _room_x = MAX_ROOMS_MAP_X / 2;
00006     _room_y = MAX_ROOMS_MAP_Y / 2;
00007     _global_contrast = global_contrast;
00008 }
00009 
00010 // Destructor
00011 RoomEngine::~RoomEngine()
00012 {
00013     room->unload();
00014 }
00015 
00016 // Public Functions
00017 void RoomEngine::load(Player *current_player, Room *current_room)
00018 {
00019     player = current_player;
00020     room = current_room;
00021     set_input(true,0,0,0,0,0,0,0);  // Displays health throughout entrance_scene
00022     update_player_position(check_player_room_position()); // Moving the player such that he enters opposite of his exit
00023     room->load();
00024 }
00025 
00026 void RoomEngine::update_player_position(int side)
00027 {
00028     switch(side) { // Depending on which side it exits from, player is repositioned
00029         case 0 :    // N
00030             player->set_position(39, 49);
00031             break;
00032         case 1 :    // E
00033             player->set_position(0 - player->get_hitbox_width(), 25);
00034             break;
00035         case 2 :    // S
00036             player->set_position(39, 0 - player->get_hitbox_height());
00037             break;
00038         case 3 :    // W
00039             player->set_position(85, 25);
00040             break;
00041         case 4 :    // Default where an exit does not exist (starting position)
00042             player->set_position(39, 30);
00043             break;
00044     }
00045 }
00046 
00047 void RoomEngine::entrance_scene(N5110 &lcd, Gamepad &gamepad)
00048 {
00049     int side = check_player_room_position();
00050     for(int i = 0; i<50; i++) {
00051         switch(side) {  // Setting movement of player as it enters the screen (velocity)
00052             case 0 :
00053                 set_mapped_coord(0, -((4 + player->get_sprite_height()) / (50 * player->get_velocity()))); break;
00054             case 1 :
00055                 set_mapped_coord(-((4 + player->get_hitbox_width()) / (50 * player->get_velocity())), 0); break;
00056             case 2 :
00057                 set_mapped_coord(0, ((4 + player->get_hitbox_height()) / (50 * player->get_velocity()))); break;
00058             case 3 :
00059                 set_mapped_coord(((4 + player->get_hitbox_width()) / (50 * player->get_velocity())), 0); break;
00060         }
00061         move_player();
00062         render(lcd, gamepad);
00063         if (0.75 - (i * 0.005) > _global_contrast) {    // Fade in
00064             lcd.setContrast(0.75 - (i * 0.005));
00065         } else { lcd.setContrast(_global_contrast);}
00066     }
00067 }
00068 
00069 int RoomEngine::check_player_room_position() // returns 0,1,2,3 if the player exits the respective directions, returns 4 if the player is in the room
00070 {
00071     if (player->get_pos_y() < 0) {
00072         return 0;
00073     }
00074     else if (player->get_pos_x() > WIDTH - (player->get_hitbox_width())) {
00075         return 1;
00076     }
00077     else if (player->get_pos_y() > HEIGHT - (player->get_hitbox_height())) {
00078         return 2;
00079     }
00080     else if (player->get_pos_x() < 0) {
00081         return 3;
00082     }
00083     else {
00084         return 4;
00085     }
00086 }
00087 
00088 void RoomEngine::read_input(Gamepad &gamepad)   // Updates member variables for easier access
00089 {
00090     _L = gamepad.check_event(Gamepad::L_PRESSED);
00091     _R = gamepad.check_event(Gamepad::R_PRESSED);
00092     _A = gamepad.check_event(Gamepad::A_PRESSED);
00093     _B = gamepad.check_event(Gamepad::B_PRESSED);
00094     _X = gamepad.check_event(Gamepad::X_PRESSED);
00095     _Y = gamepad.check_event(Gamepad::Y_PRESSED);
00096     mapped_coord = gamepad.get_mapped_coord();
00097 }
00098 
00099 void RoomEngine::update(int &number_of_enemies_killed)  // Updates all HP, deletes dead entities, increment score (kills),
00100 {                                                       // checks wall collisions (exclusive for entity:walls), movements for all entities and player action detection
00101     room->update_doorways();
00102     check_damage();
00103     check_enemies_death(number_of_enemies_killed);
00104     check_walls_collision();
00105     move();
00106     player->buttons(_A, _B, _Y, _X);
00107 }
00108 
00109 void RoomEngine::check_damage()
00110 {
00111     check_damage_player();  // damage player if collide with enemy / heal if collide with hearts
00112     check_damage_enemies(); // damage enemy if collide with player's bullets
00113 }
00114 
00115 
00116 void RoomEngine::check_enemies_death(int &number_of_enemies_killed) // Deletes dead enemies, spawn hearts based on chance(defined in each enemy) and increments kill score
00117 {
00118     // Enemy Death
00119     for (int i = 0; i < MAX_ENEMIES; i++) {
00120         if((room->valid_enemies[i]) && (room->enemies[i]->get_hp() <= 0)) {
00121             if ((rand() % 100) < room->enemies[i]->get_hp_drop_chance()){
00122                 for (int j = 0; j < MAX_ENEMIES; j++) {
00123                     if (!room->valid_collectibles[j]) {
00124                         room->collectibles[j] = new Health(room->enemies[i]->get_pos_x(), room->enemies[i]->get_pos_y()); // Spawn a health drop
00125                         room->valid_collectibles[j] = true;
00126                         break;
00127                     }
00128                 }
00129             }
00130             delete room->enemies[i];
00131             room->valid_enemies[i] = false;
00132             number_of_enemies_killed++;
00133         }
00134     }
00135 }
00136 
00137 void RoomEngine::check_walls_collision()    // Undo moves of player and enemy if colliding with entity:wall and kills bullets when colliding with entity:wall
00138 {                                           // Currently unused (room types 0 and 10 do not spawn entity:wall)
00139     // Enemy
00140     for (int i = 0; i < MAX_ENEMIES; i++) {
00141         if(room->valid_enemies[i]) {    // Undo move of every valid enemy upon collision with entity:wall
00142             room->enemies[i]->undo_move_x(entity_move_check_x(room->enemies[i], room->walls, 2, 10, room->valid_walls));
00143             room->enemies[i]->undo_move_y(entity_move_check_y(room->enemies[i], room->walls, 2, 10, room->valid_walls));
00144         }
00145     }
00146     // Player
00147     player->undo_move_x(entity_move_check_x(player, room->walls, 2, 10, room->valid_walls));
00148     player->undo_move_y(entity_move_check_y(player, room->walls, 2, 10, room->valid_walls));
00149     // Bullets
00150     for (int i = 0; i < bullets_max; i++) {
00151         for (int j = 0; j < 2; j++) {   // given that both walls and bullets are valid, check if they're colliding
00152             if ((player->valid_bullets[i]) && (room->valid_walls[j]) && (entity_collision(*player->bullets_array[i], *room->walls[j]))) {
00153                 delete player->bullets_array[i]; player->valid_bullets[i] = false;
00154             }
00155         }
00156     }
00157 }
00158 
00159 void RoomEngine::move()
00160 {
00161     move_player();
00162     move_enemies();
00163 }
00164 
00165 void RoomEngine::render(N5110 &lcd, Gamepad &gamepad)
00166 {
00167     pause_detection(lcd, gamepad);
00168     lcd.clear();
00169     draw(lcd);
00170     lcd.refresh();
00171     wait_ms(1000/40); // setting FPS
00172 }
00173 
00174 void RoomEngine::pause_detection(N5110 &lcd, Gamepad &gamepad)
00175 {
00176     if(gamepad.check_event(Gamepad::START_PRESSED)) {
00177         draw_health(lcd);
00178         char * paused_screen = lcd.readScreen();    // Saves current screen
00179         int pause_timer = 0;
00180         lcd.drawSpriteTransparent(20, 20, 9, 45, (char *)pause_sprite);
00181         // Ensures that Start button is toggled twice to exit loop
00182         while(gamepad.check_event(Gamepad::START_PRESSED)) {
00183             draw_pause_screen(lcd, paused_screen, pause_timer);
00184         }
00185         while(!gamepad.check_event(Gamepad::START_PRESSED)) {
00186             draw_pause_screen(lcd, paused_screen, pause_timer);
00187         }
00188         while(gamepad.check_event(Gamepad::START_PRESSED)) {
00189             draw_pause_screen(lcd, paused_screen, pause_timer);
00190         }
00191     }
00192 }
00193 
00194 void RoomEngine::draw_pause_screen(N5110 &lcd, char * paused_screen, int &pause_timer)
00195 {
00196     lcd.clear();
00197     lcd.drawSprite(0, 0, HEIGHT, WIDTH, paused_screen);
00198     if (pause_timer % 20 < 10) {    // Draws pause sprite only 10/20 frames of pause_timer, this way pause blinks
00199         lcd.drawSpriteTransparent(20, 20, 9, 45, (char *)pause_sprite);
00200     }
00201     lcd.refresh();
00202     pause_timer++;
00203     wait_ms(1000/40);
00204 }
00205 
00206 void RoomEngine::draw(N5110 &lcd)   // Draws everything onto the screen
00207 {   
00208     room->draw_room(lcd);
00209     for(int j = 0; j < HEIGHT; j++) {   // To create a 3D illusion, all entities are drawn at the order of it's hitbox height (highest to lowest)
00210         if (j == player->get_pos_y()) {
00211             player->draw(lcd);
00212         }
00213         player->draw_bullets(lcd, j);
00214         room->draw(lcd, j);
00215     }
00216     
00217     room->draw_room_overlay(lcd);   // Special overlay such as side doorways and boss doorways
00218     
00219     if (_L) {   // Displaying Hearts if L is pressed
00220         draw_health(lcd);
00221     }
00222 }
00223 
00224 void RoomEngine::draw_health(N5110 &lcd)
00225 {
00226     for (int i = 0; i < player->get_hp(); i++){ // For every health, draw a heart at x pos i*10
00227         lcd.drawSpriteTransparent(i*10,
00228                                   0,
00229                                   player->get_hearts_height(),
00230                                   player->get_hearts_width(),
00231                                   player->get_hearts_sprite());
00232     }
00233 }
00234 
00235 void RoomEngine::exit_scene(N5110 &lcd, Gamepad &gamepad)   // Plays an exit scene
00236 {
00237     int side = check_player_room_position();
00238     
00239    for(int i = 0; i<50; i++) {
00240         switch(side) {  // Setting movement of player as it exits the screen (velocity)
00241             case 0 :
00242                 set_mapped_coord(0, (player->get_velocity() / 2)); break;
00243             case 1 :
00244                 set_mapped_coord((player->get_velocity() / 2), 0); break;
00245             case 2 :
00246                 set_mapped_coord(0, -(player->get_velocity() / 2)); break;
00247             case 3 :
00248                 set_mapped_coord(-(player->get_velocity() / 2), 0); break;
00249         }
00250         move_player();
00251         render(lcd, gamepad);
00252         lcd.setContrast(_global_contrast + (i * 0.005)); // Fade out
00253     }
00254     lcd.setContrast(0.75);
00255 }
00256 
00257 void RoomEngine::update_current_room()  // Increments room coord depending on direction
00258 {
00259     switch(check_player_room_position()) {
00260         case 0 :
00261             _room_y--;
00262             break;
00263         case 1 :
00264             _room_x++;
00265             break;
00266         case 2 :
00267             _room_y++;
00268             break;
00269         case 3 :
00270             _room_x--;
00271             break;
00272         default :
00273         break;
00274     }
00275 }
00276 
00277 // Public Accessors
00278 
00279 int RoomEngine::get_room_x()
00280 {
00281     return _room_x;
00282 }
00283 int RoomEngine::get_room_y()
00284 {
00285     return _room_y;
00286 }
00287 
00288 // Private Mutators
00289 
00290 void RoomEngine::set_input(bool L, bool R, bool A, bool B, bool X, bool Y, float mapped_x, float mapped_y)
00291 {
00292     _L = L;
00293     _R = R;
00294     _A = A;
00295     _B = B;
00296     _X = X;
00297     _Y = Y;
00298     set_mapped_coord(mapped_x, mapped_y);
00299 }
00300 
00301 void RoomEngine::set_mapped_coord(float x, float y)
00302 {
00303     mapped_coord.x = x;
00304     mapped_coord.y = y;
00305 }
00306 
00307 // Methods
00308 
00309 bool RoomEngine::entity_collision(Entity &a, Entity &b)  // returns true if the two entity hitboxes collide
00310 {
00311     if (((b.get_pos_x() <= a.get_pos_x()) && (a.get_pos_x() <= b.get_pos_x() + b.get_hitbox_width() - 1)) ||    // if Entity A's x of left side of the hitbox is inside Entity B's hitbox x range
00312             ((b.get_pos_x() <= a.get_pos_x() + a.get_hitbox_width() - 1) && (a.get_pos_x() + a.get_hitbox_width() - 1 <= b.get_pos_x() + b.get_hitbox_width() - 1))) {  // if Entity A's x of right side of the hitbox is inside Entity B's hitbox x range
00313         if (((b.get_pos_y() <= a.get_pos_y()) && (a.get_pos_y() <= b.get_pos_y() + b.get_hitbox_height() - 1)) ||   // if Entity A's y of top side of the hitbox is inside Entity B's hitbox y range
00314                 ((b.get_pos_y() <= a.get_pos_y() + a.get_hitbox_height() - 1) && (a.get_pos_y() + a.get_hitbox_height() - 1 <= b.get_pos_y() + b.get_hitbox_height() - 1))) {   // if Entity A's y of bottom side of the hitbox is inside Entity B's hitbox y range
00315             return true;
00316         }
00317     }
00318     return 0;
00319 }
00320 
00321 // returns -1 or 1 if the hitbox of "entity a" collides with any hitboxes of entities within "array" as "entity a" moves on the x direction
00322 float RoomEngine::entity_move_check_x(Entity *a, Entity *array[], int no_of_enemies, int current_entity, bool valid_enemies[])
00323 {
00324     for (int i = 0; i < no_of_enemies; i++) { // For every enemy = Entity B
00325         if ((valid_enemies[i]) && (i != current_entity)) {  // only check if entity b exists and entity a is not the same as entity b
00326             if (((array[i]->get_prev_pos_x() <= a->get_pos_x()) && (a->get_pos_x() <= array[i]->get_prev_pos_x() + array[i]->get_hitbox_width() - 1)) ||    // Same as entity_collision, except that Entity B's x position is its previous x position
00327                     ((array[i]->get_prev_pos_x() <= a->get_pos_x() + a->get_hitbox_width() - 1) && (a->get_pos_x() + a->get_hitbox_width() - 1 <= array[i]->get_prev_pos_x() + array[i]->get_hitbox_width() - 1))) {
00328                 if (((array[i]->get_prev_pos_y() <= a->get_prev_pos_y()) && (a->get_prev_pos_y() <= array[i]->get_prev_pos_y() + array[i]->get_hitbox_height() - 1)) ||
00329                         ((array[i]->get_prev_pos_y() <= a->get_prev_pos_y() + a->get_hitbox_height() - 1) && (a->get_prev_pos_y() + a->get_hitbox_height() - 1 <= array[i]->get_prev_pos_y() + array[i]->get_hitbox_height() - 1))) {
00330                     return (2*((a->get_pos_x() > array[i]->get_prev_pos_x()) - 0.5));
00331                 }
00332             }
00333         }
00334     }
00335     return 0;
00336 }
00337 
00338 // returns -1 or 1 if the hitbox of "entity a" collides with any hitboxes of entities within "array" as "entity a" moves on the y direction
00339 float RoomEngine::entity_move_check_y(Entity *a, Entity *array[], int no_of_enemies, int current_entity, bool valid_enemies[])
00340 {
00341     for (int i = 0; i < no_of_enemies; i++) { // For every enemy = Entity B
00342         if ((valid_enemies[i]) && (i != current_entity)) {  // only check if entity b exists and entity a is not the same as entity b
00343             if (((array[i]->get_prev_pos_x() <= a->get_prev_pos_x()) && (a->get_prev_pos_x() <= array[i]->get_prev_pos_x() + array[i]->get_hitbox_width() - 1)) ||  // Same as entity_collision, except that Entity B's y position is its previous y position
00344                     ((array[i]->get_prev_pos_x() <= a->get_prev_pos_x() + a->get_hitbox_width() - 1) && (a->get_prev_pos_x() + a->get_hitbox_width() - 1 <= array[i]->get_prev_pos_x() + array[i]->get_hitbox_width() - 1))) {
00345                 if (((array[i]->get_prev_pos_y() <= a->get_pos_y()) && (a->get_pos_y() <= array[i]->get_prev_pos_y() + array[i]->get_hitbox_height() - 1)) ||
00346                         ((array[i]->get_prev_pos_y() <= a->get_pos_y() + a->get_hitbox_height() - 1) && (a->get_pos_y() + a->get_hitbox_height() - 1 <= array[i]->get_prev_pos_y() + array[i]->get_hitbox_height() - 1))) {
00347                     return (2*((a->get_pos_y() > array[i]->get_prev_pos_y()) - 0.5));
00348                 }
00349             }
00350         }
00351     }
00352     return 0;
00353 }
00354 
00355 void RoomEngine::check_damage_player()
00356 {
00357     for (int i = 0; i < MAX_ENEMIES; i++) {
00358         if (room->valid_enemies[i]) {   // Checking each valid enemy
00359             if(entity_collision(*player, *room->enemies[i])) {
00360                 player->take_damage(room->enemies[i]->get_attack());
00361                 break; // only let 1 enemy damage player at a time
00362             }
00363         }
00364         if (room->valid_collectibles[i]) {  // Checking each valid collectible (hearts, coins)
00365             if(entity_collision(*player, *room->collectibles[i])) {
00366                 player->take_damage(room->collectibles[i]->get_attack());
00367                 delete room->collectibles[i];
00368                 room->valid_collectibles[i] = false;
00369                 break; // only let 1 heart heal player at a time
00370             }
00371         }
00372     }
00373 }
00374 
00375 void RoomEngine::check_damage_enemies()
00376 {
00377     for (int i = 0; i < bullets_max; i++) {
00378         if (player->valid_bullets[i]) {
00379             if (!(player->delete_out_of_bounds_bullets(room->get_current_map_2d(), room->get_doorways()))) { // Delete any bullet that goes out the screen or hits walls, if none, then
00380                 for (int j = 0; j < MAX_ENEMIES; j++) {
00381                     if (room->valid_enemies[j] && (entity_collision(*player->bullets_array[i], *room->enemies[j]))) { // Delete bullets and damage enemy if player bullets collide with any enemy
00382                         room->enemies[j]->take_damage(player->get_attack());
00383                         delete player->bullets_array[i]; player->valid_bullets[i] = false;
00384                         break;
00385                     }
00386                 }
00387             }
00388         }
00389     }
00390 }
00391 
00392 void RoomEngine::move_player()
00393 {
00394     player->move(mapped_coord.x, mapped_coord.y, room->get_current_map_2d(), room->get_doorways());
00395 }
00396 
00397 void RoomEngine::move_enemies()
00398 {
00399     // Actual Movement of Enemies
00400     for (int i = 0; i < MAX_ENEMIES; i++) {
00401         if (room->valid_enemies[i]) {
00402             room->enemies[i]->update_prev_pos();
00403             room->enemies[i]->move(player->get_pos_x(), player->get_pos_y(), room->get_current_map_2d(), room->get_doorways());
00404         }
00405     }
00406     // Entity Collision Repulsion
00407     for (int i = 0; i < MAX_ENEMIES; i++) {
00408         if (room->valid_enemies[i]) {
00409             room->enemies[i]->position_add_x(entity_move_check_x(room->enemies[i], room->enemies, MAX_ENEMIES, i, room->valid_enemies));    // add 1 x position if collide
00410             room->enemies[i]->position_add_y(entity_move_check_y(room->enemies[i], room->enemies, MAX_ENEMIES, i, room->valid_enemies));    // add 1 y position if collide
00411         }
00412     }
00413 }