A rouge-like rpg, heavily inspired on the binding of isaac. Running on a FRDM-K64F Mbed board. C++.
Dependencies: mbed MotionSensor
RoomEngine/RoomEngine.cpp
- Committer:
- el17sm
- Date:
- 2019-05-09
- Revision:
- 50:2c5cb92a5361
- Parent:
- 49:3f83ed62d123
- Child:
- 55:fc618f82d1d0
File content as of revision 50:2c5cb92a5361:
#include "RoomEngine.h" // Constructor RoomEngine::RoomEngine(float global_contrast) { _room_x = MAX_ROOMS_MAP_X / 2; _room_y = MAX_ROOMS_MAP_Y / 2; _global_contrast = global_contrast; } // Destructor RoomEngine::~RoomEngine() { room->unload(); } // Public Functions void RoomEngine::load(Player *current_player, Room *current_room) { player = current_player; room = current_room; set_input(true,0,0,0,0,0,0,0); // Displays health throughout entrance_scene update_player_position(check_player_room_position()); // Moving the player such that he enters opposite of his exit room->load(); } void RoomEngine::update_player_position(int side) { switch(side) { // Depending on which side it exits from, player is repositioned case 0 : // N player->set_position(39, 49); break; case 1 : // E player->set_position(0 - player->get_hitbox_width(), 25); break; case 2 : // S player->set_position(39, 0 - player->get_hitbox_height()); break; case 3 : // W player->set_position(85, 25); break; case 4 : // Default where an exit does not exist (starting position) player->set_position(39, 30); break; } } void RoomEngine::entrance_scene(N5110 &lcd, Gamepad &gamepad) { int side = check_player_room_position(); for(int i = 0; i<50; i++) { switch(side) { // Setting movement of player as it enters the screen (velocity) case 0 : set_mapped_coord(0, -((4 + player->get_sprite_height()) / (50 * player->get_velocity()))); break; case 1 : set_mapped_coord(-((4 + player->get_hitbox_width()) / (50 * player->get_velocity())), 0); break; case 2 : set_mapped_coord(0, ((4 + player->get_hitbox_height()) / (50 * player->get_velocity()))); break; case 3 : set_mapped_coord(((4 + player->get_hitbox_width()) / (50 * player->get_velocity())), 0); break; } move_player(); render(lcd, gamepad); if (0.75 - (i * 0.005) > _global_contrast) { // Fade in lcd.setContrast(0.75 - (i * 0.005)); } else { lcd.setContrast(_global_contrast);} } } 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 { if (player->get_pos_y() < 0) { return 0; } else if (player->get_pos_x() > WIDTH - (player->get_hitbox_width())) { return 1; } else if (player->get_pos_y() > HEIGHT - (player->get_hitbox_height())) { return 2; } else if (player->get_pos_x() < 0) { return 3; } else { return 4; } } void RoomEngine::read_input(Gamepad &gamepad) // Updates member variables for easier access { _L = gamepad.check_event(Gamepad::L_PRESSED); _R = gamepad.check_event(Gamepad::R_PRESSED); _A = gamepad.check_event(Gamepad::A_PRESSED); _B = gamepad.check_event(Gamepad::B_PRESSED); _X = gamepad.check_event(Gamepad::X_PRESSED); _Y = gamepad.check_event(Gamepad::Y_PRESSED); mapped_coord = gamepad.get_mapped_coord(); } void RoomEngine::update(int &number_of_enemies_killed) // Updates all HP, deletes dead entities, increment score (kills), { // checks wall collisions (exclusive for entity:walls), movements for all entities and player action detection room->update_doorways(); check_damage(); check_enemies_death(number_of_enemies_killed); check_walls_collision(); move(); player->buttons(_A, _B, _Y, _X); } void RoomEngine::check_damage() { check_damage_player(); // damage player if collide with enemy / heal if collide with hearts check_damage_enemies(); // damage enemy if collide with player's bullets } 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 { // Enemy Death for (int i = 0; i < MAX_ENEMIES; i++) { if((room->valid_enemies[i]) && (room->enemies[i]->get_hp() <= 0)) { if ((rand() % 100) < room->enemies[i]->get_hp_drop_chance()){ for (int j = 0; j < MAX_ENEMIES; j++) { if (!room->valid_collectibles[j]) { room->collectibles[j] = new Health(room->enemies[i]->get_pos_x(), room->enemies[i]->get_pos_y()); // Spawn a health drop room->valid_collectibles[j] = true; break; } } } delete room->enemies[i]; room->valid_enemies[i] = false; number_of_enemies_killed++; } } } void RoomEngine::check_walls_collision() // Undo moves of player and enemy if colliding with entity:wall and kills bullets when colliding with entity:wall { // Currently unused (room types 0 and 10 do not spawn entity:wall) // Enemy for (int i = 0; i < MAX_ENEMIES; i++) { if(room->valid_enemies[i]) { // Undo move of every valid enemy upon collision with entity:wall room->enemies[i]->undo_move_x(entity_move_check_x(room->enemies[i], room->walls, 2, 10, room->valid_walls)); room->enemies[i]->undo_move_y(entity_move_check_y(room->enemies[i], room->walls, 2, 10, room->valid_walls)); } } // Player player->undo_move_x(entity_move_check_x(player, room->walls, 2, 10, room->valid_walls)); player->undo_move_y(entity_move_check_y(player, room->walls, 2, 10, room->valid_walls)); // Bullets for (int i = 0; i < bullets_max; i++) { for (int j = 0; j < 2; j++) { // given that both walls and bullets are valid, check if they're colliding if ((player->valid_bullets[i]) && (room->valid_walls[j]) && (entity_collision(*player->bullets_array[i], *room->walls[j]))) { delete player->bullets_array[i]; player->valid_bullets[i] = false; } } } } void RoomEngine::move() { move_player(); move_enemies(); } void RoomEngine::render(N5110 &lcd, Gamepad &gamepad) { pause_detection(lcd, gamepad); lcd.clear(); draw(lcd); lcd.refresh(); wait_ms(1000/40); // setting FPS } void RoomEngine::pause_detection(N5110 &lcd, Gamepad &gamepad) { if(gamepad.check_event(Gamepad::START_PRESSED)) { draw_health(lcd); char * paused_screen = lcd.readScreen(); // Saves current screen int pause_timer = 0; lcd.drawSpriteTransparent(20, 20, 9, 45, (char *)pause_sprite); // Ensures that Start button is toggled twice to exit loop while(gamepad.check_event(Gamepad::START_PRESSED)) { draw_pause_screen(lcd, paused_screen, pause_timer); } while(!gamepad.check_event(Gamepad::START_PRESSED)) { draw_pause_screen(lcd, paused_screen, pause_timer); } while(gamepad.check_event(Gamepad::START_PRESSED)) { draw_pause_screen(lcd, paused_screen, pause_timer); } } } void RoomEngine::draw_pause_screen(N5110 &lcd, char * paused_screen, int &pause_timer) { lcd.clear(); lcd.drawSprite(0, 0, HEIGHT, WIDTH, paused_screen); if (pause_timer % 20 < 10) { // Draws pause sprite only 10/20 frames of pause_timer, this way pause blinks lcd.drawSpriteTransparent(20, 20, 9, 45, (char *)pause_sprite); } lcd.refresh(); pause_timer++; wait_ms(1000/40); } void RoomEngine::draw(N5110 &lcd) // Draws everything onto the screen { room->draw_room(lcd); 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) if (j == player->get_pos_y()) { player->draw(lcd); } player->draw_bullets(lcd, j); room->draw(lcd, j); } room->draw_room_overlay(lcd); // Special overlay such as side doorways and boss doorways if (_L) { // Displaying Hearts if L is pressed draw_health(lcd); } } void RoomEngine::draw_health(N5110 &lcd) { for (int i = 0; i < player->get_hp(); i++){ // For every health, draw a heart at x pos i*10 lcd.drawSpriteTransparent(i*10, 0, player->get_hearts_height(), player->get_hearts_width(), player->get_hearts_sprite()); } } void RoomEngine::exit_scene(N5110 &lcd, Gamepad &gamepad) // Plays an exit scene { int side = check_player_room_position(); for(int i = 0; i<50; i++) { switch(side) { // Setting movement of player as it exits the screen (velocity) case 0 : set_mapped_coord(0, (player->get_velocity() / 2)); break; case 1 : set_mapped_coord((player->get_velocity() / 2), 0); break; case 2 : set_mapped_coord(0, -(player->get_velocity() / 2)); break; case 3 : set_mapped_coord(-(player->get_velocity() / 2), 0); break; } move_player(); render(lcd, gamepad); lcd.setContrast(_global_contrast + (i * 0.005)); // Fade out } lcd.setContrast(0.75); } void RoomEngine::update_current_room() // Increments room coord depending on direction { switch(check_player_room_position()) { case 0 : _room_y--; break; case 1 : _room_x++; break; case 2 : _room_y++; break; case 3 : _room_x--; break; default : break; } } // Public Accessors int RoomEngine::get_room_x() { return _room_x; } int RoomEngine::get_room_y() { return _room_y; } // Private Mutators void RoomEngine::set_input(bool L, bool R, bool A, bool B, bool X, bool Y, float mapped_x, float mapped_y) { _L = L; _R = R; _A = A; _B = B; _X = X; _Y = Y; set_mapped_coord(mapped_x, mapped_y); } void RoomEngine::set_mapped_coord(float x, float y) { mapped_coord.x = x; mapped_coord.y = y; } // Methods bool RoomEngine::entity_collision(Entity &a, Entity &b) // returns true if the two entity hitboxes collide { 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 ((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 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 ((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 return true; } } return 0; } // returns true if the hitbox of "entity a" collides with any hitboxes of entities within "array" as "entity a" moves on the x direction float RoomEngine::entity_move_check_x(Entity *a, Entity *array[], int no_of_enemies, int current_entity, bool valid_enemies[]) { for (int i = 0; i < no_of_enemies; i++) { // For every enemy = Entity B if ((valid_enemies[i]) && (i != current_entity)) { // only check if entity b exists and entity a is not the same as entity b 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 ((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))) { 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)) || ((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))) { return (2*((a->get_pos_x() > array[i]->get_prev_pos_x()) - 0.5)); } } } } return 0; } // returns true if the hitbox of "entity a" collides with any hitboxes of entities within "array" as "entity a" moves on the y direction float RoomEngine::entity_move_check_y(Entity *a, Entity *array[], int no_of_enemies, int current_entity, bool valid_enemies[]) { for (int i = 0; i < no_of_enemies; i++) { // For every enemy = Entity B if ((valid_enemies[i]) && (i != current_entity)) { // only check if entity b exists and entity a is not the same as entity b 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 ((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))) { 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)) || ((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))) { return (2*((a->get_pos_y() > array[i]->get_prev_pos_y()) - 0.5)); } } } } return 0; } void RoomEngine::check_damage_player() { for (int i = 0; i < MAX_ENEMIES; i++) { if (room->valid_enemies[i]) { // Checking each valid enemy if(entity_collision(*player, *room->enemies[i])) { player->take_damage(room->enemies[i]->get_attack()); break; // only let 1 enemy damage player at a time } } if (room->valid_collectibles[i]) { // Checking each valid collectible (hearts, coins) if(entity_collision(*player, *room->collectibles[i])) { player->take_damage(room->collectibles[i]->get_attack()); delete room->collectibles[i]; room->valid_collectibles[i] = false; break; // only let 1 heart heal player at a time } } } } void RoomEngine::check_damage_enemies() { for (int i = 0; i < bullets_max; i++) { if (player->valid_bullets[i]) { 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 for (int j = 0; j < MAX_ENEMIES; j++) { 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 room->enemies[j]->take_damage(player->get_attack()); delete player->bullets_array[i]; player->valid_bullets[i] = false; break; } } } } } } void RoomEngine::move_player() { player->move(mapped_coord.x, mapped_coord.y, room->get_current_map_2d(), room->get_doorways()); } void RoomEngine::move_enemies() { // Actual Movement of Enemies for (int i = 0; i < MAX_ENEMIES; i++) { if (room->valid_enemies[i]) { room->enemies[i]->update_prev_pos(); room->enemies[i]->move(player->get_pos_x(), player->get_pos_y(), room->get_current_map_2d(), room->get_doorways()); } } // Entity Collision Repulsion for (int i = 0; i < MAX_ENEMIES; i++) { if (room->valid_enemies[i]) { 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 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 } } }