A rouge-like rpg, heavily inspired on the binding of isaac. Running on a FRDM-K64F Mbed board. C++.
Dependencies: mbed MotionSensor
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 }
Generated on Tue Jul 19 2022 23:32:07 by 1.7.2