/* * map.cc - game map and map sprites code for Bombermaze * written by Sydney Tang * * 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 * * For more details see the file COPYING. */ #include "map.hh" #include "game.hh" #include #include #include #include #include const int Direction_Increment_X[] = { 0, 1, 0, -1, 0, 0 }; const int Direction_Increment_Y[] = { -1, 0, 1, 0, 0, 0 }; /* const int Direction_Increment_X_Left[] = { -1, 1, 1, -1, 0, 0 }; const int Direction_Increment_Y_Left[] = { -1, -1, 1, 1, 0, 0 }; const int Direction_Increment_X_Right[] = { 1, 1, -1, -1, 0, 0 }; const int Direction_Increment_Y_Right[] = { -1, 1, 1, -1, 0, 0 }; */ void get_direction_increments(Direction dir, int &x, int &y) { x = Direction_Increment_X[dir]; y = Direction_Increment_Y[dir]; } /* void get_direction_increments_left(Direction dir, int &x, int &y) { x = Direction_Increment_X_Left[dir]; y = Direction_Increment_Y_Left[dir]; } void get_direction_increments_right(Direction dir, int &x, int &y) { x = Direction_Increment_X_Right[dir]; y = Direction_Increment_Y_Right[dir]; } */ int normalize(int n) { if (n != 0) return n/abs(n); else return 0; } gint compare_coordinates (gconstpointer a, gconstpointer b) { Coordinate p; p.x = ((Coordinate *)a)->x; p.y = ((Coordinate *)a)->y; Coordinate q; q.x = ((Coordinate *)a)->x; q.y = ((Coordinate *)a)->y; if (p.y < q.y) return -1; else if (p.y > q.y) return 1; else if (p.x < q.x) return -1; else if (p.x > q.x) return 1; else return 0; } /////////////////////////////////////////////////////////////////////////////// unsigned Entity::Steps_To_Middle_Of_Square = Entity::DEFAULT_STEPS_PER_SQUARE; Entity::Entity(GameMap *gm) { maze = gm; DeletePending = false; frame = 0; x_step = 0; y_step = 0; } Entity::~Entity() { } Entity::EntityType Entity::get_entity_type(void) { EntityType entity_type = ENTITY_UNDEFINED; assert(entity_type != ENTITY_UNDEFINED); return ENTITY_UNDEFINED; } bool Entity::check_for_barrier_entity(Entity *entity) { int entity_barrier = -1; assert(entity_barrier != -1); return false; } void Entity::animate(void) { return; int entity_animate = -1; assert(entity_animate != -1); } unsigned Entity::get_frame(void) { return frame; } unsigned Entity::get_x(void) { return (unsigned)x; } unsigned Entity::get_y(void) { return (unsigned)y; } void Entity::get_coordinates(int &x_coordinate, int &y_coordinate) { x_coordinate = x; y_coordinate = y; } int Entity::get_y_step(void) { return y_step; } bool Entity::check_if_delete_pending(void) { return DeletePending; } bool Entity::start_moving(Direction dir, unsigned delay) { moving_direction = dir; get_direction_increments(dir, dx, dy); flag_overlapped_squares_for_update(); maze->move_entity_to_top(this, x, y); if ((dx * x_step >= 0) && (dy * y_step >= 0)) { x_dest = x + dx; y_dest = y + dy; if (maze->check_if_square_is_clear(this, x_dest, y_dest) == false) { return false; } } else { x_dest = x; y_dest = y; } move_delay = delay; x_old = x; y_old = y; return true; } bool Entity::continue_moving(void) { if (move_delay > 1) { move_delay--; return true; } if (move_delay == 1) { move_delay = 0; finish_moving(); return false; } return false; } void Entity::finish_moving(void) { flag_overlapped_squares_for_update(); if ((dx * x_step == (int)Steps_To_Middle_Of_Square) || (dy * y_step == (int)Steps_To_Middle_Of_Square)) { if (maze->check_if_square_is_clear(this, x_dest, y_dest) == false) { flag_overlapped_squares_for_update(); maze->move_entity_to_top(this, x, y); return; } update_location(); x_step = -x_step; y_step = -y_step; } else { if ((x_step < 0) && (dy != 0)) x_step++; else if ((y_step < 0) && (dx != 0)) y_step++; else if ((x_step > 0) && (dy != 0)) x_step--; else if ((y_step > 0) && (dx != 0)) y_step--; else { x_step += dx; y_step += dy; } } flag_overlapped_squares_for_update(); maze->move_entity_to_top(this, x, y); } void Entity::update_location(void) { x = x_dest; y = y_dest; maze->remove_entity(this, x_old, y_old); return; } void Entity::flag_overlapped_squares_for_update(void) { x_step_normalized = normalize(x_step); y_step_normalized = normalize(y_step); maze->set_repaint_required(true, x, y); maze->set_repaint_required(true, x+x_step_normalized, y+y_step_normalized); maze->set_repaint_required(true, x+x_step_normalized, y); maze->set_repaint_required(true, x, y+y_step_normalized); } unsigned Entity::get_square_steps(void) { return Steps_To_Middle_Of_Square; } void Entity::set_steps_to_middle_of_square(unsigned steps) { unsigned square_width, square_height; unsigned maximum_steps; Sprite::get_standard_dimensions(square_width, square_height); if (square_width < square_height) { maximum_steps = square_width/2; } else { maximum_steps = square_height/2; } if (steps < STEPS_PER_SQUARE_MINIMUM) { Steps_To_Middle_Of_Square = STEPS_PER_SQUARE_MINIMUM; } else if (steps > maximum_steps) { Steps_To_Middle_Of_Square = maximum_steps; } else { Steps_To_Middle_Of_Square = steps; } } /////////////////////////////////////////////////////////////////////////////// Sprite Player::sprite[Player::MAX_NUMBER_OF_PLAYERS]; const unsigned Player::FrameSetStart[NUMBER_OF_PLAYER_FRAMESETS] = { NUMBER_OF_PLAYER_MOVE_FRAMES * PLAYER_FRAME_NORTH, NUMBER_OF_PLAYER_MOVE_FRAMES * PLAYER_FRAME_EAST, NUMBER_OF_PLAYER_MOVE_FRAMES * PLAYER_FRAME_SOUTH, NUMBER_OF_PLAYER_MOVE_FRAMES * PLAYER_FRAME_WEST, NUMBER_OF_PLAYER_MOVE_FRAMES * PLAYER_FRAME_TOASTED }; unsigned Player::Initial_Move_Delay = Player::DEFAULT_PLAYER_MOVE_DELAY; unsigned Player::Initial_Max_Bombs = Player::DEFAULT_BOMBS_PER_PLAYER; unsigned Player::Initial_Blast_Radius = Player::DEFAULT_BOMB_BLAST_RADIUS; Player::Player(): Entity(NULL) { id = 1; reset_fields(); Entity::sprite = &(sprite[id]); Entity::frame = FrameSetStart[PLAYER_FRAME_SOUTH] + PLAYER_STANDING_STILL; } Player::Player(int player_id): Entity(NULL) { if ((player_id >=1) && (player_id < MAX_NUMBER_OF_PLAYERS)) { id = player_id; } else { id = 1; } reset_fields(); Entity::sprite = &(sprite[id]); Entity::frame = FrameSetStart[PLAYER_FRAME_SOUTH] + PLAYER_STANDING_STILL; } Player::~Player() { } Entity::EntityType Player::get_entity_type(void) { return Entity::ENTITY_PLAYER; } bool Player::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_BOMB)) { return true; } else { return false; } } void Player::set_id(int new_id) { id = new_id; Entity::sprite = &(sprite[id]); } void Player::reset_fields(void) { status = Player::PLAYER_INACTIVE; x = -1; y = -1; player_move_delay = Initial_Move_Delay; max_bombs = Initial_Max_Bombs; bomb_blast_radius = Initial_Blast_Radius; can_trigger_bombs = false; can_kick_bombs = false; AnimationDelay = 0; return; } void Player::set_game_map(GameMap *gm) { maze = gm; } bool Player::activate(void) { if (status != Player::PLAYER_INACTIVE) { return false; } else { status = Player::PLAYER_IDLE; bombs_dropped = 0; x = x_old = x_initial; y = y_old = y_initial; x_step = 0; y_step = 0; facing_direction = DIR_SOUTH; pending_move_direction = DIR_NONE; frameset_index = PLAYER_STANDING_STILL; frame = FrameSetStart[facing_direction] + frameset_index; AnimationDelay = 0; return true; } } bool Player::move(Direction dir) { if ((status != PLAYER_MOVING_FINISHED) && (status != Player::PLAYER_IDLE)) { return false; } pending_move_direction = DIR_NONE; set_facing_direction(dir); if (start_moving(dir, player_move_delay) == true) { if (status == PLAYER_IDLE) { frameset_index = PLAYER_FRAME_MOVE_R_LIFT; } status = PLAYER_MOVING; return true; } else if (can_kick_bombs == true) { int x_target, y_target; x_target = x + dx; y_target = y + dy; if (maze->check_if_bomb_exists(x_target, y_target)) { maze->kick_bomb(dir, x_target, y_target); } } return false; } void Player::set_facing_direction(Direction dir) { facing_direction = dir; frame = FrameSetStart[dir] + frameset_index; maze->move_entity_to_top(this, x, y); } bool Player::drop_bomb(void) { if ((status == Player::PLAYER_TOASTED) || (status == PLAYER_INACTIVE)) { return false; } else if (maze->check_if_bomb_exists(x, y) == false) { if (bombs_dropped < max_bombs) { Bomb *bomb = maze->create_bomb(this, x, y); maze->move_entity_to_top(this, x, y); bombs_dropped++; if (can_trigger_bombs == true) { bomb->deactivate(); bomb->set_triggerable(true); } return true; } } return false; } void Player::decrement_bombs_dropped(void) { if (bombs_dropped > 0) { bombs_dropped--; } } void Player::special_action(void) { if (can_trigger_bombs == true) { trigger_bombs(); } } void Player::die(void) { if ((status != Player::PLAYER_INACTIVE) && (status != PLAYER_TOASTED)) { status = Player::PLAYER_TOASTED; AnimationDelay = DIE_FRAME_DELAY; frameset_index = PLAYER_FRAME_START_DYING; frame = FrameSetStart[PLAYER_FRAME_TOASTED] + frameset_index; flag_overlapped_squares_for_update(); } } void Player::perform_action(Player::PlayerAction action) { if ((status == PLAYER_TOASTED) || (status == PLAYER_INACTIVE)) return; if ((int)action < (int)NUMBER_OF_DIRECTIONS) { pending_move_direction = (Direction)action; if (status == PLAYER_IDLE) { move((Direction)action); } } else if (action == PLAYER_DROP_BOMB) { drop_bomb(); } else if (action == Player::PLAYER_SPECIAL) { special_action(); } return; } void Player::animate(void) { if (status == PLAYER_INACTIVE) return; maze->set_repaint_required(true, x, y); if (AnimationDelay > 1) { AnimationDelay--; return; } if (AnimationDelay == 1) { AnimationDelay = 0; if (status == PLAYER_MOVING_FINISHED) { if (pending_move_direction != DIR_NONE) { if (true == move(pending_move_direction)) return; } status = PLAYER_IDLE; frameset_index = PLAYER_STANDING_STILL; frame = FrameSetStart[facing_direction] + frameset_index; flag_overlapped_squares_for_update(); return; } else if (status == PLAYER_TOASTED) { animate_dying(); return; } } animate_movement(); return; } void Player::animate_movement(void) { if (status == PLAYER_MOVING) { if (continue_moving() == false) { if (maze->check_if_fire_exists(x, y)) { die(); return; } else { status = PLAYER_MOVING_FINISHED; frameset_index = (frameset_index+1) % (int)MOVEMENT_MODULUS; frame = FrameSetStart[facing_direction] + frameset_index; if (pending_move_direction != DIR_NONE) { if (true == move(pending_move_direction)) return; } AnimationDelay = player_move_delay; } } } } void Player::animate_idle(void) { flag_overlapped_squares_for_update(); } void Player::animate_dying(void) { flag_overlapped_squares_for_update(); frameset_index++; if (frameset_index >= NUMBER_OF_PLAYER_MOVE_FRAMES) { finish_dying(); return; } frame = FrameSetStart[PLAYER_FRAME_TOASTED] + frameset_index; AnimationDelay = DIE_FRAME_DELAY; } Player::PlayerStatus Player::get_status(void) { return status; } Direction Player::get_facing_direction(void) { return facing_direction; } void Player::set_starting_coordinates(unsigned starting_x, unsigned starting_y) { x_initial = (int)starting_x; y_initial = (int)starting_y; } unsigned Player::get_blast_radius(void) { return bomb_blast_radius; } unsigned Player::get_max_bombs(void) { return max_bombs; } void Player::increment_blast_radius(void) { bomb_blast_radius++; } void Player::increment_max_bombs(void) { max_bombs++; } void Player::increase_speed(void) { if (player_move_delay > PLAYER_MOVE_DELAY_MINIMUM) player_move_delay--; } void Player::set_trigger_bombs(bool ability) { can_trigger_bombs = ability; } void Player::set_kick_bombs(bool ability) { can_kick_bombs = ability; } void Player::set_initial_move_delay(unsigned delay) { if (delay < PLAYER_MOVE_DELAY_MINIMUM) { Initial_Move_Delay = PLAYER_MOVE_DELAY_MINIMUM; } else { Initial_Move_Delay = delay; } } void Player::set_initial_max_bombs(unsigned bombs) { Initial_Max_Bombs = bombs; } void Player::set_initial_blast_radius(unsigned radius) { Initial_Blast_Radius = radius; } void Player::trigger_bombs(void) { maze->explode_bombs_belonging_to_player(this); } void Player::finish_dying(void) { maze->remove_entity(this, x, y); status = PLAYER_INACTIVE; return; } /////////////////////////////////////////////////////////////////////////////// Sprite Bomb::sprite; unsigned Bomb::Bomb_Move_Delay = Bomb::DEFAULT_MOVE_DELAY; unsigned Bomb::Initial_Countdown = Bomb::DEFAULT_INITIAL_COUNTDOWN; unsigned Bomb::Chain_Reaction_Delay = Bomb::DEFAULT_CHAIN_REACTION_DELAY; const float Bomb::DEFAULT_COUNTDOWN_SECONDS = 2.0; Bomb::Bomb(GameMap *gm, Player *owner_of_bomb): Entity(gm) { owner = owner_of_bomb; status = Bomb::BOMB_INACTIVE; moving = false; triggerable = false; Entity::sprite = &sprite; frame = BOMB_FRAME_INACTIVE; } Bomb::~Bomb() { } Entity::EntityType Bomb::get_entity_type(void) { return Entity::ENTITY_BOMB; } bool Bomb::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_PLAYER) || (entity_type == ENTITY_POWERUP)) { return true; } else if (entity_type == ENTITY_BOMB) { if ((Bomb *)entity == (Bomb *)this) { return false; } else { return true; } } else { return false; } } void Bomb::drop(unsigned new_x, unsigned new_y) { x = (int)new_x; y = (int)new_y; status = Bomb::BOMB_ACTIVE; AnimationDelay = Initial_Countdown; frame = BOMB_FRAME_ACTIVE_START; } void Bomb::explode(void) { AnimationDelay = 0; status = Bomb::BOMB_EXPLODING; owner->decrement_bombs_dropped(); maze->remove_entity(this, x, y); maze->create_explosion(owner->get_blast_radius(), x, y); DeletePending = true; } void Bomb::chain_explode(void) { if ((status == BOMB_IMPENDING_EXPLOSION) || (status == BOMB_EXPLODING)) return; if (AnimationDelay > (int)Chain_Reaction_Delay) { AnimationDelay = Chain_Reaction_Delay; } status = BOMB_IMPENDING_EXPLOSION; } bool Bomb::move(Direction dir) { if (start_moving(dir, Bomb_Move_Delay) == true) { moving = true; } else { moving = false; } return moving; } bool Bomb::is_active(void) { if (status == Bomb::BOMB_ACTIVE) return true; else return false; } bool Bomb::is_exploded(void) { if (status == Bomb::BOMB_EXPLODING) return true; else return false; } bool Bomb::is_triggerable(void) { return triggerable; } bool Bomb::check_if_player_is_owner(Player *player) { if (owner == player) return true; else return false; } void Bomb::deactivate(void) { status = Bomb::BOMB_INACTIVE; frame = BOMB_FRAME_INACTIVE; } void Bomb::set_triggerable(bool setting) { triggerable = setting; frame = BOMB_FRAME_TRIGGERABLE; } unsigned Bomb::get_blast_radius(void) { return owner->get_blast_radius(); } void Bomb::set_initial_countdown(float count_seconds) { Bomb::Initial_Countdown = (unsigned)(count_seconds * 1000.0 / (float)InGameState::get_timeout_interval()); } void Bomb::set_chain_reaction_delay(unsigned frame_delay) { Chain_Reaction_Delay = frame_delay; } void Bomb::animate(void) { if (status != BOMB_IMPENDING_EXPLOSION) { if (AnimationDelay >= 1) { AnimationDelay--; } } else { status = BOMB_EXPLODING; } if (AnimationDelay <= 0) { if ((status == BOMB_ACTIVE) || (status == BOMB_EXPLODING)) { explode(); } return; } if (moving == true) { if (continue_moving() == false) { if (maze->check_if_fire_exists(x, y)) { chain_explode(); } move(moving_direction); } } if (status == BOMB_ACTIVE) { frame = (frame + 1) % BOMB_ACTIVE_ANIMATION_MODULUS; maze->set_repaint_required(true, x, y); } return; } /////////////////////////////////////////////////////////////////////////////// Sprite Fire::sprite; unsigned Fire::Duration = Fire::DEFAULT_FLAME_DURATION; const Fire::FlameDirection Fire::DirectionFlagsLeading[] = { FLAME_N, FLAME_E, FLAME_S, FLAME_W }; const Fire::FlameDirection Fire::DirectionFlagsLagging[] = { FLAME_S, FLAME_W, FLAME_N, FLAME_E }; Fire::Fire(GameMap *gm, int new_x, int new_y, FlameDirection dir): Entity(gm) { Entity::sprite = &sprite; frame = (unsigned)dir; directions = dir; x = new_x; y = new_y; AnimationDelay = Duration; for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++) { FadeDelay[i] = 0; } if (dir & FLAME_N) FadeDelay[DIR_NORTH] = Duration; if (dir & FLAME_E) FadeDelay[DIR_EAST] = Duration; if (dir & FLAME_S) FadeDelay[DIR_SOUTH] = Duration; if (dir & FLAME_W) FadeDelay[DIR_WEST] = Duration; } Fire::~Fire() { } Entity::EntityType Fire::get_entity_type(void) { return Entity::ENTITY_FIRE; } bool Fire::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_POWERUP) || (entity_type == ENTITY_BOMB)) { return true; } else { return false; } } void Fire::animate() { int dir; bool DirectionCleared = false; int ActiveDirections = 0; for (dir = DIR_NORTH; dir < NUMBER_OF_DIRECTIONS; dir++) { if (FadeDelay[dir] > 1) { FadeDelay[dir]--; ActiveDirections++; } else if (FadeDelay[dir] == 1) { FadeDelay[dir] = 0; directions=(FlameDirection)(directions&(~(DirectionFlagsLeading[dir]))); DirectionCleared = true; } } if (ActiveDirections == 0) { maze->remove_entity(this, x, y); maze->clear_fire(x, y); DeletePending = true; } frame = (unsigned)directions; if (DirectionCleared == true) { maze->set_repaint_required(true, x, y); } return; } void Fire::set_direction_flags(Fire::FlameDirection flags) { AnimationDelay = Duration; if (flags & FLAME_N) FadeDelay[DIR_NORTH] = Duration; if (flags & FLAME_E) FadeDelay[DIR_EAST] = Duration; if (flags & FLAME_S) FadeDelay[DIR_SOUTH] = Duration; if (flags & FLAME_W) FadeDelay[DIR_WEST] = Duration; directions = (FlameDirection)(directions | flags); frame = (unsigned)directions; } void Fire::set_flame_duration(unsigned frame_duration) { Duration = frame_duration; } /////////////////////////////////////////////////////////////////////////////// Sprite Brick::sprite; unsigned Brick::Shatter_Frame_Duration = Brick::DEFAULT_BRICK_SHATTER_DURATION; Brick::Brick(GameMap *gm, int new_x, int new_y): Entity(gm) { x = new_x; y = new_y; Entity::sprite = &sprite; frame = BRICK_FRAME_NORMAL; state = BRICK_NORMAL; AnimationDelay = 0; } Brick::~Brick() { } Entity::EntityType Brick::get_entity_type(void) { return Entity::ENTITY_BRICK; } bool Brick::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_PLAYER) || (entity_type == ENTITY_BOMB)) { return true; } else { return false; } } void Brick::animate() { if (state == BRICK_SHATTERED) { if (AnimationDelay > 1) { AnimationDelay--; return; } else if (AnimationDelay <= 1) { animate_shatter(); return; } } return; } void Brick::shatter(void) { if (state == BRICK_SHATTERED) return; state = BRICK_SHATTERED; frame = BRICK_FRAME_SHATTER_START; AnimationDelay = Shatter_Frame_Duration; } void Brick::animate_shatter(void) { if (frame == BRICK_FRAME_SHATTER_END) { maze->remove_entity(this, x, y); maze->destroy_brick(x, y); DeletePending = true; return; } frame++; AnimationDelay = Shatter_Frame_Duration; maze->set_repaint_required(this, x, y); } void Brick::set_shatter_duration(unsigned total_frame_duration) { Shatter_Frame_Duration = total_frame_duration/BRICK_SHATTER_FRAMES; } /////////////////////////////////////////////////////////////////////////////// Sprite Wall::sprite; Wall::Wall(GameMap *gm, int new_x, int new_y): Entity(gm) { x = new_x; y = new_y; Entity::sprite = &sprite; frame = WALL_FRAME; } Wall::~Wall() { } Entity::EntityType Wall::get_entity_type(void) { return Entity::ENTITY_WALL; } bool Wall::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_PLAYER) || (entity_type == ENTITY_BOMB)) { return true; } else { return false; } } void Wall::animate() { return; } /////////////////////////////////////////////////////////////////////////////// Sprite PowerUp::sprite; unsigned PowerUp::Shatter_Frame_Duration = PowerUp::DEFAULT_POWERUP_SHATTER_DURATION; PowerUp::PowerUp(GameMap *gm, int new_x, int new_y): Entity(gm) { x = new_x; y = new_y; Entity::sprite = &sprite; determine_powerup_type(); } PowerUp::~PowerUp() { } Entity::EntityType PowerUp::get_entity_type(void) { return Entity::ENTITY_POWERUP; } bool PowerUp::check_for_barrier_entity(Entity *entity) { Entity::EntityType entity_type = entity->get_entity_type(); if ((entity_type == ENTITY_WALL) || (entity_type == ENTITY_BRICK) || (entity_type == ENTITY_BOMB)) { return true; } else { return false; } } void PowerUp::animate() { if (frame >= POWERUP_DESTROYED_START) { if (AnimationDelay > 1) { AnimationDelay--; return; } else if (AnimationDelay <= 1) { animate_shatter(); return; } return; } GSList *EntityList; Entity *entity; for (EntityList = maze->get_entity_list(x, y); EntityList; EntityList = EntityList->next) { entity = (Entity *)(EntityList->data); if (entity->get_entity_type() == Entity::ENTITY_PLAYER) { switch (power) { case POWERUP_EXTRA_BOMB: ((Player *)entity)->increment_max_bombs(); break; case POWERUP_EXTRA_RANGE: ((Player *)entity)->increment_blast_radius(); break; case POWERUP_TRIGGER_BOMB: ((Player *)entity)->set_trigger_bombs(true); break; case POWERUP_KICK_BOMB: ((Player *)entity)->set_kick_bombs(true); break; case POWERUP_EXTRA_SPEED: ((Player *)entity)->increase_speed(); break; default: break; } maze->remove_entity(this, x, y); maze->destroy_powerup(x, y); DeletePending = true; break; } } return; } void PowerUp::destroy(void) { if (frame >= POWERUP_DESTROYED_START) return; frame = power = POWERUP_DESTROYED_START; AnimationDelay = DEFAULT_POWERUP_SHATTER_DURATION; } void PowerUp::animate_shatter(void) { if (frame == POWERUP_DESTROYED_END) { maze->remove_entity(this, x, y); maze->destroy_powerup(x, y); DeletePending = true; return; } frame++; AnimationDelay = DEFAULT_POWERUP_SHATTER_DURATION; maze->set_repaint_required(this, x, y); } float PowerUp::Probability_Of_PowerUp = (float)DEFAULT_PROBABILITY_OF_POWERUP/100.0; const float PowerUp::DefaultProbability[PowerUp::NUMBER_OF_CREATABLE_POWERUPS]= { 0.30, // POWERUP_EXTRA_BOMB 0.25, // POWERUP_EXTRA_RANGE 0.15, // POWERUP_TRIGGER_BOMB 0.15, // POWERUP_KICK_BOMB 0.15 // POWERUP_EXTRA_SPEED }; float PowerUp::Probability[PowerUp::NUMBER_OF_CREATABLE_POWERUPS] = { PowerUp::DefaultProbability[POWERUP_EXTRA_BOMB], PowerUp::DefaultProbability[POWERUP_EXTRA_RANGE], PowerUp::DefaultProbability[POWERUP_TRIGGER_BOMB], PowerUp::DefaultProbability[POWERUP_KICK_BOMB], PowerUp::DefaultProbability[POWERUP_EXTRA_SPEED] }; void PowerUp::set_probability_of_powerup(unsigned percent_probability) { Probability_Of_PowerUp = (float)percent_probability/100.0; } void PowerUp::set_probability_of_each_powerup_type (int percent_probability[PowerUp::NUMBER_OF_CREATABLE_POWERUPS]) { int cumulative_probability = 0; int i; for (i = 0; i < NUMBER_OF_CREATABLE_POWERUPS; i++) { if (percent_probability[i] < 0) percent_probability[i] = 0; else if (percent_probability[i] > 100) percent_probability[i] = 100; cumulative_probability += percent_probability[i]; if (cumulative_probability > 100) { Probability[i] = (float)(cumulative_probability-100)/100.0; for (++i; i < NUMBER_OF_CREATABLE_POWERUPS; i++) { Probability[i] = 0; } break; } else { Probability[i] = (float)percent_probability[i]/100.0; } } if (cumulative_probability < 100) { // this shouldn't happen, but it's not a fatal error. } } bool PowerUp::randomly_create_powerup(void) { unsigned r = rand(); if (r < RAND_MAX * Probability_Of_PowerUp) { return true; } else { return false; } } void PowerUp::determine_powerup_type(void) { int i; float cumulative_probability = 0; unsigned r = rand(); for (i = 0; i < NUMBER_OF_CREATABLE_POWERUPS; i++) { cumulative_probability += Probability[i]; if (r < RAND_MAX * cumulative_probability) { frame = (unsigned)(power = (PowerUpType)i); return; } } frame = (unsigned)(power = POWERUP_EXTRA_BOMB); } void PowerUp::set_shatter_duration(unsigned total_frame_duration) { Shatter_Frame_Duration = total_frame_duration/POWERUP_SHATTER_FRAMES; } /////////////////////////////////////////////////////////////////////////////// //Sprite MapSquare::FloorSprite; int MapSquare::Square_Width = 45; int MapSquare::Square_Height = 40; MapSquare::MapSquare() { status = SQUARE_EMPTY; PlayerWhoStartsHere = 0; EntityList = NULL; RepaintRequired = false; } MapSquare::~MapSquare() { g_slist_free(EntityList); } int MapSquare::get_square_width() { return Square_Width; } int MapSquare::get_square_height() { return Square_Height; } MapSquare::SquareStatus MapSquare::get_status(void) { return status; } int MapSquare::assign_status(char ch) { if (isdigit(ch)) { char StatusCharacterString[2]; StatusCharacterString[1] = '\0'; StatusCharacterString[0] = ch; PlayerWhoStartsHere = atoi(StatusCharacterString); if ((PlayerWhoStartsHere < 1) || (PlayerWhoStartsHere > Player::MAX_NUMBER_OF_PLAYERS)) { PlayerWhoStartsHere = 0; } status = SQUARE_EMPTY; return true; } int StatusHasBeenAssigned = true; int r; PlayerWhoStartsHere = 0; switch((int)ch) { case CHAR_SQUARE_EMPTY: status = SQUARE_EMPTY; break; case CHAR_SQUARE_WALL: status = SQUARE_WALL; break; case CHAR_SQUARE_BRICK: status = SQUARE_BRICK; break; case CHAR_SQUARE_RANDOM: r = rand(); if (r < RAND_MAX / 3) status = SQUARE_WALL; else if (r < RAND_MAX * 2/3) status = SQUARE_BRICK; else status = SQUARE_EMPTY; break; case CHAR_SQUARE_RANDOM_EMPTY_OR_BRICK: if (rand()%2) status = SQUARE_BRICK; else status = SQUARE_EMPTY; break; case CHAR_SQUARE_RANDOM_EMPTY_OR_WALL: if (rand()%2) status = SQUARE_WALL; else status = SQUARE_EMPTY; break; case CHAR_SQUARE_RANDOM_BRICK_OR_WALL: if (rand()%2) status = SQUARE_BRICK; else status = SQUARE_WALL; break; default: StatusHasBeenAssigned = false; break; } return StatusHasBeenAssigned; } int MapSquare::get_player_who_starts_here(void) { return PlayerWhoStartsHere; } bool MapSquare::check_if_square_is_clear(Entity *entity) { GSList *entity_list; for (entity_list = EntityList; entity_list; entity_list = entity_list->next) { if (entity->check_for_barrier_entity((Entity *)(EntityList->data)) == true) { return false; } } return true; } bool MapSquare::check_if_square_is_clear(void) { if ((status == SQUARE_WALL) || (status == SQUARE_BRICK) || (status == SQUARE_BOMB)) { return false; } else { return true; } } bool MapSquare::check_if_bomb_exists(void) { if (EntityList != NULL) { Entity *entity = (Entity *)(EntityList->data); if (entity->get_entity_type() == Entity::ENTITY_BOMB) { return true; } } return false; } void MapSquare::set_fire(void) { if (status != SQUARE_WALL) { status = SQUARE_FIRE; } } void MapSquare::clear_fire(void) { if (status == SQUARE_FIRE) { status = SQUARE_EMPTY; } } void MapSquare::destroy_brick(void) { if (status == SQUARE_BRICK) { status = SQUARE_EMPTY; } } void MapSquare::set_powerup(void) { if (status == SQUARE_EMPTY) { status = SQUARE_POWERUP; } } void MapSquare::destroy_powerup(void) { if (status == SQUARE_POWERUP) { status = SQUARE_EMPTY; } } void MapSquare::set_square_dimensions(int w, int h) { if ((w < 0) || (h < 0)) { Square_Width = DEFAULT_SQUARE_WIDTH; Square_Height = DEFAULT_SQUARE_WIDTH; } else { Square_Width = w; Square_Height = h; } return; } /////////////////////////////////////////////////////////////////////////////// ParsedMap::ParsedMap() { width = 0; height = 0; map = NULL; valid = false; } ParsedMap::~ParsedMap() { deallocate_map(); } void ParsedMap::allocate_map(int w, int h) { width = w; height = h; map = new char * [width]; unsigned i; for (i = 0; i < width; i++) { map[i] = new char[height]; } } void ParsedMap::deallocate_map(void) { unsigned i; if (map != NULL) { for (i = 0; i < width; i++) { delete[] map[i]; } delete[] map; map = NULL; } width = 0; height = 0; } unsigned ParsedMap::get_width() { return width; } unsigned ParsedMap::get_height() { return height; } bool ParsedMap::set_square(char square_type, unsigned x, unsigned y) { MapSquare square; map[x][y] = square_type; return square.assign_status(square_type); } char ParsedMap::get_square(unsigned x, unsigned y) { return map[x][y]; } void ParsedMap::set_valid(bool validity) { valid = validity; } bool ParsedMap::check_if_valid(void) { return valid; } /////////////////////////////////////////////////////////////////////////////// GameMap::GameMap(void) { map = NULL; width = -1; height = -1; int i; for(i = 0; i < Player::MAX_NUMBER_OF_PLAYERS; i++) { PlayerStartingCoordinateX[i] = -1; PlayerStartingCoordinateY[i] = -1; } AllowableNumberOfPlayers = -1; BombList = NULL; FireList = NULL; BrickList = NULL; WallList = NULL; PowerUpList = NULL; //SquaresToBeRepainted = g_tree_new(compare_coordinates); DisplayBuffer = NULL; DisplayWindow = NULL; DisplayContext = NULL; } GameMap::~GameMap() { //g_tree_destroy(SquaresToBeRepainted); depopulate_map(); } void GameMap::set_ui_target (UIDrawable *target, UIGraphicsContext *context, UIDrawable *buffer) { DisplayWindow = target; DisplayContext = context; DisplayBuffer = buffer; } UIDrawable *GameMap::get_ui_target(void) { return DisplayWindow; } void GameMap::populate_map(void) { int i, j; MapSquare::SquareStatus SquareType; for (j = 0; j < height; j++) { for(i = 0; i < width; i++) { map[i][j].RepaintRequired = true; SquareType = map[i][j].get_status(); if (SquareType == MapSquare::SQUARE_BRICK) { Brick *brick = new Brick(this, i, j); BrickList = g_slist_append(BrickList, brick); move_entity_to_bottom(brick, i, j); } else if (SquareType == MapSquare::SQUARE_WALL) { Wall *wall = new Wall(this, i, j); WallList = g_slist_append(WallList, wall); move_entity_to_bottom(wall, i, j); } else { } } } } void GameMap::allocate_map(MapSquare ***m) { *m = new MapSquare * [width]; int i; for (i = 0; i < width; i++) { (*m)[i] = new MapSquare[height]; } } void GameMap::deallocate_map(MapSquare ***m) { int i; if (*m != NULL) { for (i = 0; i < width; i++) { delete[] (*m)[i]; } delete[] (*m); *m = NULL; } } void GameMap::depopulate_map(void) { deallocate_map(&map); clear_entity_list(&BombList); clear_entity_list(&FireList); clear_entity_list(&BrickList); clear_entity_list(&WallList); clear_entity_list(&PowerUpList); } MapSquare::SquareStatus GameMap::get_square_status(int x, int y) { if (map == NULL) { return MapSquare::SQUARE_INVALID; } return map[x][y].get_status(); } bool GameMap::check_if_square_is_clear(Entity *entity, int x, int y) { if ( (x < 0) || (x >= width) || (y < 0) || (y >= height) || (map[x][y].check_if_square_is_clear() == false) ) { return false; } else { return map[x][y].check_if_square_is_clear(entity); } } int GameMap::get_allowable_number_of_players(void) { return AllowableNumberOfPlayers; } void GameMap::get_starting_coordinates_of_player(int player, int &x, int &y) { if ((player < 1) || (player > Player::MAX_NUMBER_OF_PLAYERS)) { x = -1; y = -1; return; } x = PlayerStartingCoordinateX[player-1]; y = PlayerStartingCoordinateY[player-1]; return; } int GameMap::get_width(void) { return width; } int GameMap::get_height(void) { return height; } bool GameMap::check_if_bomb_exists(int x, int y) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return false; return map[x][y].check_if_bomb_exists(); } bool GameMap::check_if_fire_exists(int x, int y) { if (map[x][y].get_status() == MapSquare::SQUARE_FIRE) return true; else return false; } int GameMap::check_if_repaint_required(int x, int y) { return map[x][y].RepaintRequired; } void GameMap::set_repaint_required(bool requirement, int x, int y) { map[x][y].RepaintRequired = requirement; if ((requirement == true) && (y > 0)) { map[x][y-1].RepaintRequired = true; /* g_tree_insert(SquaresToBeRepainted,&(map[x][y-1].location),&(map[x][y-1])); g_tree_insert (SquaresToBeRepainted, &(map[x][y].location), &(map[x][y])); */ } } GSList *GameMap::get_entity_list(int x, int y) { return map[x][y].EntityList; } void GameMap::remove_entity(Entity *entity, int x, int y) { map[x][y].EntityList = g_slist_remove(map[x][y].EntityList, entity); set_repaint_required(true, x, y); } void GameMap::move_entity_to_top(Entity *entity, int x, int y) { map[x][y].EntityList = g_slist_remove(map[x][y].EntityList, entity); GSList *elist; Entity *e; int y_step; int rank = 0; y_step = entity->get_y_step(); for (elist = map[x][y].EntityList; elist; elist = elist->next) { e = (Entity *)(elist->data); if ((y_step < e->get_y_step()) && (e->get_entity_type() == Entity::ENTITY_PLAYER)) { break; } rank++; } map[x][y].EntityList = g_slist_insert(map[x][y].EntityList, entity, rank); set_repaint_required(true, x, y); } void GameMap::move_entity_to_bottom(Entity *entity, int x, int y) { map[x][y].EntityList = g_slist_remove(map[x][y].EntityList, entity); map[x][y].EntityList = g_slist_prepend(map[x][y].EntityList, entity); set_repaint_required(true, x, y); } void GameMap::animate_map_entities(void) { GSList *EntityList; for (EntityList = BombList; EntityList; EntityList = EntityList->next) ((Bomb *)(EntityList->data))->animate(); trim_entity_list(&BombList); for (EntityList = FireList; EntityList; EntityList = EntityList->next) ((Fire *)(EntityList->data))->animate(); trim_entity_list(&FireList); for (EntityList = BrickList; EntityList; EntityList = EntityList->next) ((Brick *)(EntityList->data))->animate(); trim_entity_list(&BrickList); for (EntityList = PowerUpList; EntityList; EntityList = EntityList->next) ((PowerUp *)(EntityList->data))->animate(); trim_entity_list(&PowerUpList); //for (EntityList = WallList; EntityList; EntityList = EntityList->next) // ((Wall *)(EntityList->data))->animate(); //trim_entity_list(&WallList); } void GameMap::trim_entity_list(GSList **EntityList) { bool FoundClearedEntity; GSList *e; Entity *entity; do { FoundClearedEntity = false; for (e = *EntityList; e != NULL; e = e->next) { entity = (Entity *)(e->data); if (entity->check_if_delete_pending()) { FoundClearedEntity = true; *EntityList = g_slist_remove(*EntityList, entity); delete entity; break; } } } while (FoundClearedEntity == true); } void GameMap::clear_entity_list(GSList **EntityList) { GSList *e; Entity *entity; do { for (e = *EntityList; e != NULL; e = e->next) { entity = (Entity *)(e->data); *EntityList = g_slist_remove(*EntityList, entity); delete entity; break; } } while (*EntityList != NULL); } Bomb *GameMap::create_bomb(Player *player, int x, int y) { Bomb *bomb = new Bomb(this, player); bomb->drop((unsigned)x, (unsigned)y); BombList = g_slist_append(BombList, bomb); move_entity_to_bottom(bomb, x, y); return bomb; } void GameMap::explode_bombs_belonging_to_player(Player *player) { GSList *b; Bomb *bomb; for (b = BombList; b; b = b->next) { bomb = ((Bomb *)(b->data)); if (bomb->check_if_player_is_owner(player) == true) { if (bomb->is_triggerable() == true) { bomb->explode(); } } } } void GameMap::kick_bomb(Direction dir, int x, int y) { Bomb *bomb = (Bomb *)(map[x][y].EntityList->data); bomb->move(dir); } void GameMap::create_explosion(unsigned radius, int x, int y) { create_fire(x,y); int dir; for (dir = DIR_NORTH; dir <= DIR_WEST; dir++) { propagate_fire((Direction)dir, radius, x, y); } return; } void GameMap::propagate_fire(Direction dir, int radius, int cx, int cy) { int r; int x, y; int x0, y0; int dx, dy; bool hit_bomb; get_direction_increments(dir, dx, dy); x = cx; y = cy; for (r = 1; r <= radius; r++) { x0 = x; y0 = y; x += dx; y += dy; hit_bomb = false; set_flame_direction_flags(Fire::DirectionFlagsLeading[dir], x0, y0); if ( (x < 0) || (x >= width) || (y < 0) || (y >= height) ) { break; } else if (map[x][y].get_status() == MapSquare::SQUARE_WALL) { break; } else if (map[x][y].get_status() == MapSquare::SQUARE_BRICK) { Brick *brick = (Brick *)(map[x][y].EntityList->data); brick->shatter(); set_repaint_required(true, x, y); break; } else if (map[x][y].get_status() == MapSquare::SQUARE_POWERUP) { PowerUp *powerup = (PowerUp *)(map[x][y].EntityList->data); powerup->destroy(); set_repaint_required(true, x, y); break; } if (map[x][y].check_if_bomb_exists() == true) { Bomb *bomb = (Bomb *)(map[x][y].EntityList->data); bomb->chain_explode(); hit_bomb = true; } create_fire(x, y); set_flame_direction_flags(Fire::DirectionFlagsLagging[dir], x, y); if (hit_bomb == true) { break; } } } void GameMap::create_fire(int x, int y) { if (map[x][y].get_status() == MapSquare::SQUARE_FIRE) return; map[x][y].set_fire(); Fire *fire = new Fire(this, x, y); FireList = g_slist_append(FireList, fire); move_entity_to_bottom(fire, x, y); GSList *EntityList; Entity *entity; for (EntityList = map[x][y].EntityList; EntityList; EntityList = EntityList->next) { entity = (Entity *)(EntityList->data); if (entity->get_entity_type() == Entity::ENTITY_PLAYER) { ((Player *)entity)->die(); } } } void GameMap::set_flame_direction_flags( Fire::FlameDirection flags, int x, int y) { map[x][y].set_fire(); ((Fire *)(map[x][y].EntityList->data))->set_direction_flags(flags); set_repaint_required(true, x, y); } void GameMap::clear_fire(int x, int y) { map[x][y].clear_fire(); } void GameMap::destroy_brick(int x, int y) { map[x][y].destroy_brick(); create_powerup(x, y); } void GameMap::create_powerup(int x, int y) { if (true == PowerUp::randomly_create_powerup()) { map[x][y].set_powerup(); PowerUp *powerup = new PowerUp(this, x, y); PowerUpList = g_slist_append(PowerUpList, powerup); move_entity_to_bottom(powerup, x, y); } } void GameMap::destroy_powerup(int x, int y) { map[x][y].destroy_powerup(); } bool GameMap::WalledPerimeter = false; bool GameMap::WalledPerimeterTopOnly = true; void GameMap::set_walled_perimeter(bool walled, bool top_only) { WalledPerimeter = walled; WalledPerimeterTopOnly = top_only; } int GameMap::parse_map(const char *MapName, ParsedMap *TemplateMap) { TemplateMap->deallocate_map(); FILE *MapFile; int MapIsValid; MapFile = fopen(MapName,"r"); if (MapFile == NULL) { return false; } int w, h; MapIsValid = determine_map_dimensions(MapFile, &w, &h); if (MapIsValid == false) { fclose(MapFile); return false; } TemplateMap->allocate_map (w, h); rewind(MapFile); char *MapRow = new char[w+2]; int x, y; for (y = 0; y < h; y++) { fgets(MapRow, w+2, MapFile); for (x = 0; x < w; x++) { MapIsValid = TemplateMap->set_square(MapRow[x], x, y); if (MapIsValid == false) break; } if (MapIsValid == false) break; } fclose(MapFile); delete[] MapRow; if (MapIsValid == false) { return MapIsValid; } return MapIsValid; } void GameMap::set_square_coordinates(int x, int y) { map[x][y].location.x = x; map[x][y].PixelLocation.x = x*MapSquare::get_square_width(); map[x][y].location.y = y; map[x][y].PixelLocation.y = y*MapSquare::get_square_height(); } void GameMap::set_boundaries_by_perimeter( int &w, int &h, int &w_min, int &h_min, int &w_max, int &h_max, int &w_read_offset, int &h_read_offset ) { if (WalledPerimeter == true) { if (WalledPerimeterTopOnly == true) { w += 0; h += 1; w_min = 0; h_min = 1; w_max = w; h_max = h; w_read_offset = 0; h_read_offset = -1; } else { w += 2; h += 2; w_min = 1; h_min = 1; w_max = w-1; h_max = h-1; w_read_offset = -1; h_read_offset = -1; } } else { w_min = 0; h_min = 0; w_max = w; h_max = h; w_read_offset = 0; h_read_offset = 0; } } void GameMap::set_player_starting_coordinate(int w, int h) { int PlayerNumber = map[w][h].get_player_who_starts_here(); if (PlayerNumber != 0) { PlayerStartingCoordinateX[PlayerNumber-1] = w; PlayerStartingCoordinateY[PlayerNumber-1] = h; } } void GameMap::assign_perimeter_map_squares(void) { int w, h; if (WalledPerimeter == true) { if (WalledPerimeterTopOnly == true) { for (w = 0; w < width; w++) { map[w][0].assign_status (MapSquare::CHAR_SQUARE_WALL); set_square_coordinates(w, 0); } } else if (WalledPerimeterTopOnly == false) { for (w = 0; w < width; w++) { map[w][0].assign_status (MapSquare::CHAR_SQUARE_WALL); set_square_coordinates(w, 0); map[w][height-1].assign_status (MapSquare::CHAR_SQUARE_WALL); set_square_coordinates(w, height-1); } for (h = 0; h < height; h++) { map[0][h].assign_status (MapSquare::CHAR_SQUARE_WALL); set_square_coordinates(0, h); map[width-1][h].assign_status (MapSquare::CHAR_SQUARE_WALL); set_square_coordinates(width-1, h); } } } } void GameMap::assign_map_from_parsed_map(ParsedMap *TemplateMap) { int w, h; int w_min; int h_min; int w_max; int h_max; int dw; int dh; depopulate_map(); for(int i = 0; i < Player::MAX_NUMBER_OF_PLAYERS; i++) { PlayerStartingCoordinateX[i] = -1; PlayerStartingCoordinateY[i] = -1; } width = TemplateMap->get_width(); height = TemplateMap->get_height(); set_boundaries_by_perimeter(width, height, w_min, h_min, w_max, h_max, dw, dh); allocate_map(&map); for (h = h_min; h < h_max; h++) { for (w = w_min; w < w_max; w++) { map[w][h].assign_status(TemplateMap->get_square(w + dw, h + dh)); set_square_coordinates(w, h); set_player_starting_coordinate(w, h); } } determine_allowable_number_of_players(); assign_perimeter_map_squares(); } int GameMap::determine_map_dimensions(FILE *MapFile, int *w, int *h) { char ch; int x = 0; int y = 0; if (feof(MapFile)) return false; do { ch = getc(MapFile); x++; } while ( (ch != '\n') && !feof(MapFile) ); if (ch == '\n') x--; *w = x; *h = 1; if (feof(MapFile)) return true; for (y = 0; !feof(MapFile); y++) { for (x=0; (x < *w) && !feof(MapFile) ; x++) { ch = getc(MapFile); if (ch == '\n') return false; } if (!feof(MapFile)) { ch = getc(MapFile); if (ch != '\n') return false; } } *h = y; return true; } void GameMap::determine_allowable_number_of_players(void) { int i; for (i = 0; i < Player::MAX_NUMBER_OF_PLAYERS; i++) { if (PlayerStartingCoordinateX[i] == -1) { if (i > 0) { break; } else { AllowableNumberOfPlayers = -1; return; } } } AllowableNumberOfPlayers = i; } ///////////////////////////////////////////////////////////////////////////////