// $Id: spritemanager.cc,v 1.5 2006/08/07 12:50:08 matthew Exp $ // Fish Supper // Copyright (C) 2006 Matthew Clarke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. #include "spritemanager.h" // ******************* // *** CONSTRUCTOR *** // ******************* FS::SpriteManager::SpriteManager() { // FIXME - make these into constants? int temp_min_ys[] = { 87, 162, 237, 312, 387, 462 }; int temp_max_ys[] = { 136, 211, 286, 361, 436, 511 }; for (int i = 0; i < NUM_ROWS; ++i) { rows_min_ys[i] = temp_min_ys[i]; rows_max_ys[i] = temp_max_ys[i]; } // for my_map_sprites_allocated = false; } // FS::SpriteManager::SpriteManager() // ****************** // *** DESTRUCTOR *** // ****************** FS::SpriteManager::~SpriteManager() { // FIXME: this code appears twice in this file if (my_map_sprites_allocated) { logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { delete (* logs_iter); ++logs_iter; } // while collectables_iter = my_collectables.begin(); while ( collectables_iter != my_collectables.end() ) { delete (* collectables_iter); ++collectables_iter; } // while } // if } // FS::SpriteManager::~SpriteManager() // ************************ // *** MEMBER FUNCTIONS *** // ************************ // ************************************************** void FS::SpriteManager::reset() { the_cat.reset(); the_fish.reset(); is_first_pass = true; cat_crystal_collisions_disabled = false; disabled_crystal = 0; current_state = PLAYING_GAME; my_snd_engine->play_sound(SoundEngine::RIVER, -1, 30); } // FS::SpriteManager::reset() // ************************************************** void FS::SpriteManager::restart_level() { the_cat.reset(); // These two shouldn't need resetting, but won't do any // harm to include these lines anyway... cat_crystal_collisions_disabled = false; disabled_crystal = 0; logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { (*logs_iter)->reset(); ++logs_iter; } // while collectables_iter = my_collectables.begin(); while ( collectables_iter != my_collectables.end() ) { (*collectables_iter)->reset(); ++collectables_iter; } // while redraw_crystals = true; // forces crystals held to be re-drawn current_state = PLAYING_GAME; } // FS::SpriteManager::restart_level() // ************************************************** FS::SpriteManager::State FS::SpriteManager::update(int t, KeyboardEvent * events, int n, bool running, const int * current_start_pixels, const int * pixels_to_move, const int * old_pixels_to_move, const Direction * directions) { time_passed = t; // There won't be any collisions on first pass anyway, but // crystals need drawing now. if (is_first_pass) { my_player_data->draw_crystals_held(my_play_display); the_fish.draw(my_play_display); my_play_display->request_draw_score(my_player_data->get_score()); my_play_display->request_draw_lives(my_player_data->get_num_lives()); my_play_display->request_draw_level(my_player_data->get_level()); } // if logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { (* logs_iter)->update( current_start_pixels, pixels_to_move, old_pixels_to_move, directions, is_first_pass, my_play_display ); ++logs_iter; } // while collectables_iter = my_collectables.begin(); while ( collectables_iter != my_collectables.end() ) { if ( (*collectables_iter)->is_active() ) { (*collectables_iter)->update( current_start_pixels, pixels_to_move, old_pixels_to_move, directions, is_first_pass, my_play_display ); } // if ++collectables_iter; } // while the_cat.update(time_passed, events, n, my_play_display, running); bool short_circuit = false; int i = 0; switch (current_state) { case PLAYING_GAME: // If Albert is airborne or in water, we don't need // to check for any collisions. if ( the_cat.is_airborne() ) { // FIXME: only need to reset these once. cat_crystal_collisions_disabled = false; disabled_crystal = 0; break; } else if ( the_cat.is_in_water_or_being_zapped() ) { if ( the_cat.is_splashed_zapped_out() ) { int lives_left = my_player_data->dec_num_lives(); my_play_display->request_draw_lives(lives_left); if ( lives_left == 0 ) { the_cat.start_leave_in_a_huff(time_passed); current_state = RUNNING_GAME_OVER_ROUTINE; my_snd_engine->play_sound(SoundEngine::MIAOW); break; } else { current_state = REQUEST_LEVEL_RESTART; break; } // if ... else } // if break; // don't want collisions checking! } // if ... else check_collisions(); break; case RUNNING_LEVEL_COMPLETED_ROUTINE: while (i < n) { if (events[i] == JUMP_KEY_PRESSED) { short_circuit = true; break; } // if ++i; } // while if ( the_cat.has_left() || short_circuit ) { current_state = LEVEL_COMPLETED; my_snd_engine->stop_all_sounds(); } // if break; case RUNNING_GAME_OVER_ROUTINE: while (i < n) { if (events[i] == JUMP_KEY_PRESSED) { short_circuit = true; break; } // if ++i; } // while if ( the_cat.has_left() || short_circuit ) { current_state = GAME_OVER; my_snd_engine->stop_all_sounds(); } // if break; case REQUEST_LEVEL_RESTART: case LEVEL_COMPLETED: case GAME_OVER: break; } // switch if ( !is_first_pass ) { if ( the_cat.get_y() < UPPER_BANK_MAX_Y ) { my_play_display->check_redraw( the_cat.get_bounding_box(), my_player_data->get_score(), my_player_data->get_num_lives(), my_player_data->get_level() ); if ( redraw_fish || Collisions::intersects( the_cat.get_bounding_box(), the_fish.get_bounding_box() ) ) { the_fish.draw(my_play_display); } // if } // if if ( redraw_crystals || ( (the_cat.get_y() >= (LOWER_BANK_MIN_Y - ALBERT_HEIGHT)) && Collisions::intersects(the_cat.get_bounding_box(), my_player_data->get_crystals_held_bounding_box()) ) ) { my_player_data->draw_crystals_held(my_play_display); } // if } // if is_first_pass = false; redraw_crystals = false; redraw_fish = false; return current_state; } // FS::SpriteManager::update() // ************************************************** void FS::SpriteManager::check_collisions() { if ( the_cat.is_on_log() ) { check_cat_collectable_collision( the_cat.get_host_log_row() ); } else { RowDescription row_desc = calc_alberts_row(); switch (row_desc) { case ROW_0: case ROW_1: case ROW_2: case ROW_3: case ROW_4: case ROW_5: // check logs on given row // N.B. Check for crystals first and Albert would be able // to grab a crystal just before he landed on a log - might be // the crystal that'd keep him on that log! switch ( check_cat_log_collision((int) row_desc) ) { case ALLOWED: break; case DISALLOWED: the_cat.start_zap(time_passed); break; case MISSED: the_cat.start_splash(time_passed); my_snd_engine->play_sound(SoundEngine::SPLASH); break; } // switch break; case UPPER_BANK: // check for collision with fish if ( Collisions::intersects(the_cat.get_coll_box(), the_fish.get_coll_box()) ) { my_player_data->inc_score(PTS_FISH); my_play_display->request_draw_score(my_player_data->get_score()); the_cat.start_leave_with_fish(time_passed); the_fish.remove_fish(); redraw_fish = true; current_state = RUNNING_LEVEL_COMPLETED_ROUTINE; } // if break; case LOWER_BANK: break; case WATER: // SPLASH!!! the_cat.start_splash(time_passed); my_snd_engine->play_sound(SoundEngine::SPLASH); break; } // switch } // if ... else } // FS::SpriteManager::check_collisions() // ************************************************** FS::RowDescription FS::SpriteManager::calc_alberts_row() { if ( the_cat.upper_y() >= LOWER_BANK_MIN_Y ) { return LOWER_BANK; } else if ( the_cat.lower_y() <= UPPER_BANK_MAX_Y ) { return UPPER_BANK; } else { for (int i = 0; i < NUM_ROWS; ++i) { if ( (the_cat.upper_y() >= rows_min_ys[i]) && (the_cat.lower_y() <= rows_max_ys[i]) ) { return (RowDescription) i; } // if } // for } // if ... else return WATER; } // FS::SpriteManager::calc_alberts_row() // ************************************************** FS::SpriteManager::CatLogCollisionType FS::SpriteManager::check_cat_log_collision(int row) { logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { if ( ((*logs_iter)->get_row() == row) && Collisions::contains((*logs_iter)->get_coll_box(), the_cat.get_coll_box()) ) { the_cat.set_new_host_log(*logs_iter); if ( my_player_data->is_allowed_on_log((*logs_iter)->get_color(), my_play_display) ) { switch ( (*logs_iter)->get_color() ) { case L_RED: case L_YELLOW: case L_BLUE: my_player_data->inc_score(PTS_ONE_COLOR_LOG); redraw_crystals = true; break; case L_ORANGE: case L_GREEN: case L_PURPLE: my_player_data->inc_score(PTS_TWO_COLOR_LOG); redraw_crystals = true; break; case L_BROWN: my_player_data->inc_score(PTS_BROWN_LOG); break; case L_BACKGROUND: // should never happen break; } // switch my_play_display->request_draw_score(my_player_data->get_score()); return ALLOWED; } else { return DISALLOWED; } // if ... else } // if ++logs_iter; } // for return MISSED; // i.e. SPLASH!!! } // FS::SpriteManager::check_cat_log_collision() // ************************************************** void FS::SpriteManager::check_cat_collectable_collision(int row) { if (cat_crystal_collisions_disabled) { // Is Albert still 'over' the disabled crystal? if ( Collisions::intersects(the_cat.get_coll_box(), disabled_crystal->get_coll_box()) ) { return; } else { cat_crystal_collisions_disabled = false; disabled_crystal = 0; } // if ... else } // if collectables_iter = my_collectables.begin(); while ( collectables_iter != my_collectables.end() ) { if ( (*collectables_iter)->is_active() ) { if ( Collisions::intersects(the_cat.get_coll_box(), (*collectables_iter)->get_coll_box()) ) { // is this a crystal or a bonus? switch ( (*collectables_iter)->get_id() ) { case CollectableSprite::CRYSTAL: handle_crystal_collision( dynamic_cast (*collectables_iter) ); break; case CollectableSprite::BONUS: handle_bonus_collision( dynamic_cast (*collectables_iter) ); break; } // switch break; // only need to find one collision } // if } // if ++collectables_iter; } // while } // FS::SpriteManager::check_cat_collectable_collision() // ************************************************** void FS::SpriteManager::create_map_sprites(const int * row_lengths, const Block * const * my_blocks, const int * row_lengths_pixels, const Direction * directions) { // delete memory from my_logs and my_collectables if necessary if ( my_map_sprites_allocated ) { logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { delete (* logs_iter); ++logs_iter; } // while my_logs.clear(); collectables_iter = my_collectables.begin(); while ( collectables_iter != my_collectables.end() ) { delete (* collectables_iter); ++collectables_iter; } // while my_collectables.clear(); } // if // create logs using 'new' and push_back() into my_logs vector create_logs(row_lengths, my_blocks, row_lengths_pixels, directions); // create bonuses/crystals (with 'new') and push_back() into my_collectables vector create_collectables(row_lengths, my_blocks, row_lengths_pixels, directions); my_map_sprites_allocated = true; } // FS::SpriteManager::create_map_sprites() // ************************************************** void FS::SpriteManager::create_logs(const int * row_lengths, const Block * const * my_blocks, const int * row_lengths_pixels, const Direction * directions) { int start_index; int end_index; LogColor color = L_BACKGROUND; for (int r = 0; r < NUM_ROWS; ++r) { // reset indices start_index = end_index = -1; for (int c = 0; c < row_lengths[r]; ++c) { if (my_blocks[r][c].lc != L_BACKGROUND) { if (start_index < 0) { start_index = c; color = my_blocks[r][c].lc; } // if } else if (start_index >= 0) // AND current block is background { end_index = c; my_logs.push_back( new Log( rows_min_ys[r], (end_index - start_index) * BLOCK_WIDTH, BLOCK_HEIGHT, start_index * BLOCK_WIDTH, r, row_lengths_pixels[r], directions[r], color ) ); // get ready for another log on same row start_index = end_index = -1; } // if ... else } // for c } // for r } // FS::SpriteManager::create_logs() // ************************************************** void FS::SpriteManager::create_collectables(const int * row_lengths, const Block * const * my_blocks, const int * row_lengths_pixels, const Direction * directions) { for (int r = 0; r < NUM_ROWS; ++r) { for (int c = 0; c < row_lengths[r]; ++c) { // since there's a crystal here there won't be a bonus if ( my_blocks[r][c].cc != C_NONE ) { my_collectables.push_back( new Crystal( rows_min_ys[r], CRYSTAL_WIDTH, CRYSTAL_HEIGHT, c * BLOCK_WIDTH, r, row_lengths_pixels[r], my_blocks[r][c].cc ) ); } else if ( my_blocks[r][c].bt != B_NONE ) { my_collectables.push_back( new Bonus( rows_min_ys[r], BLOCK_WIDTH, BLOCK_HEIGHT, c * BLOCK_WIDTH, r, row_lengths_pixels[r], my_blocks[r][c].bt ) ); } // if ... else } // for } // for } // FS::SpriteManager::create_collectables() // ************************************************** void FS::SpriteManager::handle_crystal_collision(Crystal * c) { // update player data - i.e. crystals held CrystalColor cc = my_player_data->add_crystal(c->get_color(), my_play_display); redraw_crystals = true; my_snd_engine->play_sound(SoundEngine::PING); if (cc == C_NONE) { c->set_active(false); } else { // Have to stop Albert picking up this crystal // again straight afterwards... c->set_color(cc); cat_crystal_collisions_disabled = true; disabled_crystal = c; } // if ... else } // FS::SpriteManager::handle_crystal_collision() // ************************************************** void FS::SpriteManager::handle_bonus_collision(Bonus * b) { // What type of bonus are we dealing with? switch ( b->get_bonus_type() ) { case B_DOUBLE_JUMP: b->set_active(false); // tell Albert class that next jump's gonna be a 'double' the_cat.set_next_jump_double(); break; case B_LOGS_TO_BROWN: b->set_active(false); set_logs_to_brown( the_cat.get_host_log_row() ); break; case B_CHANGE_DIRECTION: b->set_active(false); my_map_engine->change_direction( the_cat.get_host_log_row(), time_passed ); break; case B_SLOW_DOWN: b->set_active(false); my_map_engine->slow_down( the_cat.get_host_log_row(), time_passed ); break; case B_SPEED_UP: b->set_active(false); my_map_engine->speed_up( the_cat.get_host_log_row(), time_passed ); case B_NONE: // should never happen - keeps compiler happy break; } // switch } // FS::SpriteManager::handle_bonus_collision() // ************************************************** void FS::SpriteManager::set_logs_to_brown(int row) { logs_iter = my_logs.begin(); while ( logs_iter != my_logs.end() ) { if ( (*logs_iter)->get_row() == row ) { (*logs_iter)->set_color(L_BROWN); } // if ++logs_iter; } // while } // FS::SpriteManager::set_logs_to_brown() // ************************************************** // ************************************************** // ************************************************** // ************************************************** // ************************************************** // **************************************************