#include "field.h" #include "ghosts.h" #include "pac.h" #include "queue.h" #include "color_palette.h" #include #include #include #include #include enum { timeout_duration = 0, sleep_duration = 120000, key_escape = 27, whitespace = 32, max_get_out_stage = 6, chase_limit = 70, scatter_limit = 35, fright_limit = 50, phase_limit = 4, prison_limit = 30, max_score = 243 }; struct mode_type { enum behavior_mode current_mode; int chase_counter; int scatter_counter; int fright_counter; int phase_number; int reverse_direction; int get_out_stage; }; struct game_params_type { int exit; int pause; }; static void initialize_ghosts(struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { initialize_ghost(red_ghost, red); initialize_ghost(pink_ghost, pink); initialize_ghost(blue_ghost, blue); initialize_ghost(orange_ghost, orange); } static void initialize_modes(struct mode_type *mode_params) { mode_params->current_mode = scatter; mode_params->chase_counter = 0; mode_params->scatter_counter = 0; mode_params->fright_counter = 0; mode_params->phase_number = 0; mode_params->reverse_direction = 0; mode_params->get_out_stage = max_get_out_stage; } static void initialize_params(game_space *field, struct queue *eaten_coins, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { timeout(timeout_duration); *field = get_new_field(); print_field(*field); queue_init(eaten_coins); initialize_pac(pac); initialize_ghosts(red_ghost, pink_ghost, blue_ghost, orange_ghost); initialize_modes(mode_params); display_ghosts_on_field(red_ghost, pink_ghost, blue_ghost, orange_ghost, mode_params->fright_counter); display_character(pac->position.y, pac->position.x, pac_char, 0); } static int is_up_move_blocked(const struct ghost_type *ghost, enum intersection_type intersection) { return intersection == yellow_block && (ghost->direction == left || ghost->direction == right); } static void pathfinder_stage(game_space field, struct mode_type *mode_params, const struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { void (*search_method)(game_space, struct ghost_type*, struct coordinates); enum intersection_type intersection; int color_priority; struct ghost_type *current_ghost; struct coordinates new_target_point; for(color_priority = 0; color_priority <= orange; ++color_priority) { switch(color_priority) { case red: if(red_ghost->prison_params.active) continue; current_ghost = red_ghost; new_target_point = mode_params->current_mode == scatter ? current_ghost->home_position : pac->position; search_method = mode_params->current_mode == scatter ? compute_distance_between_points : breadth_first_search; break; case pink: if(pink_ghost->prison_params.active) continue; current_ghost = pink_ghost; new_target_point = mode_params->current_mode == scatter ? current_ghost->home_position : identify_target_in_front(*pac, pink_shift); search_method = compute_distance_between_points; break; case blue: if(blue_ghost->prison_params.active) continue; current_ghost = blue_ghost; new_target_point = mode_params->current_mode == scatter ? current_ghost->home_position : identify_blue_target(*pac, red_ghost); search_method = compute_distance_between_points; break; case orange: if(orange_ghost->prison_params.active) continue; current_ghost = orange_ghost; new_target_point = mode_params->current_mode == scatter ? current_ghost->home_position : pac->position; search_method = mode_params->current_mode == scatter ? compute_distance_between_points : breadth_first_search; } intersection = get_intersection(field, current_ghost); if(is_up_move_blocked(current_ghost, intersection)) continue; if(intersection != direct_path) redirect(field, intersection, current_ghost, new_target_point, search_method); } } static void random_pathfinder_stage(game_space field, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { enum intersection_type intersection; int color_priority; struct ghost_type *current_ghost; for(color_priority = 0; color_priority <= orange; ++color_priority) { switch(color_priority) { case red: if(red_ghost->prison_params.active) continue; current_ghost = red_ghost; break; case pink: if(pink_ghost->prison_params.active) continue; current_ghost = pink_ghost; break; case blue: if(blue_ghost->prison_params.active) continue; current_ghost = blue_ghost; break; case orange: if(orange_ghost->prison_params.active) continue; current_ghost = orange_ghost; break; } intersection = get_intersection(field, current_ghost); if(intersection != direct_path) random_redirect(field, current_ghost); } } static void change_mode(struct mode_type *mode_params, struct pacman *pac) { if(pac->is_energizer_eaten) { mode_params->current_mode = frightened; mode_params->reverse_direction = 1; pac->is_energizer_eaten = 0; /* if frightened mode is activated already */ mode_params->fright_counter = 0; return; } /* * After all phases are completed, you can only change the mode here from * frightened to chase. */ if(!(mode_params->current_mode == frightened) && mode_params->phase_number == phase_limit) return; switch(mode_params->current_mode) { case chase: if(mode_params->chase_counter > chase_limit) { mode_params->current_mode = scatter; mode_params->chase_counter = 0; ++mode_params->phase_number; if(mode_params->phase_number == phase_limit) mode_params->current_mode = chase; if(mode_params->phase_number != phase_limit) ++mode_params->reverse_direction; } break; case scatter: if(mode_params->scatter_counter > scatter_limit) { mode_params->current_mode = chase; mode_params->scatter_counter = 0; ++mode_params->reverse_direction; } break; case frightened: if(mode_params->fright_counter > fright_limit) { mode_params->current_mode = mode_params->chase_counter || (mode_params->phase_number == phase_limit) ? chase : scatter; mode_params->fright_counter = 0; ++mode_params->reverse_direction; } } } static void chase_mode(game_space field, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { pathfinder_stage(field, mode_params, pac, red_ghost, pink_ghost, blue_ghost, orange_ghost); if(mode_params->phase_number < phase_limit) { ++mode_params->chase_counter; change_mode(mode_params, pac); } } static void scatter_mode(game_space field, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { pathfinder_stage(field, mode_params, NULL, red_ghost, pink_ghost, blue_ghost, orange_ghost); if(mode_params->phase_number < phase_limit) { ++mode_params->scatter_counter; change_mode(mode_params, pac); } } static void frightened_mode(game_space field, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { random_pathfinder_stage(field, red_ghost, pink_ghost, blue_ghost, orange_ghost); ++mode_params->fright_counter; change_mode(mode_params, pac); } static int is_outside_prison(struct ghost_type ghost) { return ghost.prison_params.active && ghost.direction == up; } static int is_ghost_prisoner(struct ghost_type ghost) { return ghost.prison_params.active && ghost.direction == none; } static void prison_leaving_stage(game_space field, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { struct ghost_type *current_ghost; int color_priority; for(color_priority = 0; color_priority <= orange; ++color_priority) { switch(color_priority) { case red: current_ghost = red_ghost; break; case pink: current_ghost = pink_ghost; break; case blue: current_ghost = blue_ghost; break; case orange: current_ghost = orange_ghost; break; } if(is_ghost_prisoner(*current_ghost)) { ++current_ghost->prison_params.prison_counter; /* * liberation */ if(current_ghost->prison_params.prison_counter == prison_limit) { current_ghost->prison_params.prison_counter = 0; current_ghost->direction = up; } } else if(is_outside_prison(*current_ghost)) current_ghost->prison_params.active = current_ghost->position.y == liberation_y ? 0 : 1; } } static int is_castling(struct pacman pac, struct ghost_type ghost) { return ghost.capture_info.status && pac.position.x == ghost.capture_info.previous_ghost_position.x && pac.position.y == ghost.capture_info.previous_ghost_position.y && ghost.position.x == ghost.capture_info.previous_pac_position.x && ghost.position.y == ghost.capture_info.previous_pac_position.y; } static void catching_stage(game_space field, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { struct ghost_type *current_ghost; current_ghost = red_ghost; int color_priority; for(color_priority = 0; color_priority <= orange; ++color_priority) { switch(color_priority) { case red: if(is_pac_nearby(*pac, *red_ghost)) { set_capture_info(red_ghost, pac); continue; } current_ghost = red_ghost; break; case pink: if(is_pac_nearby(*pac, *pink_ghost)) { set_capture_info(pink_ghost, pac); continue; } current_ghost = pink_ghost; break; case blue: if(is_pac_nearby(*pac, *blue_ghost)) { set_capture_info(blue_ghost, pac); continue; } current_ghost = blue_ghost; break; case orange: if(is_pac_nearby(*pac, *orange_ghost)) { set_capture_info(orange_ghost, pac); continue; } current_ghost = orange_ghost; break; } if(is_equal_points(pac->position, current_ghost->position) || is_castling(*pac, *current_ghost)) { if(mode_params->current_mode == frightened) catch_ghost(field, current_ghost); else { current_ghost->reached_pacman = 1; catch_pac(pac); return; } } if(current_ghost->capture_info.status) current_ghost->capture_info.status = 0; } } /* * to recognize a hit and also to start a new attempt */ static int ghost_caught_pacman(struct ghost_type red_ghost, struct ghost_type pink_ghost, struct ghost_type blue_ghost, struct ghost_type orange_ghost) { return red_ghost.reached_pacman || pink_ghost.reached_pacman || blue_ghost.reached_pacman || orange_ghost.reached_pacman; } static void final_stage(struct game_params_type *game_options, int win) { int key; reset_attr(); if(win) { move(17, 10); printw("YOU WIN"); } else { move(17, 9); printw("GAME OVER"); } move(14, 11); printw("RETRY?"); move(15, 12); printw("Y/N"); refresh(); timeout(-1); while((key = getch()) != 'y' && key != 'n') {} game_options->exit = (key == 'y') ? 0 : 1; } /* * restart after winning or losing */ static void clear_field_and_queue(game_space *field, struct queue *eaten_coins) { clear_field(field); queue_clear(eaten_coins); } static void new_game(game_space *field, struct queue *eaten_coins, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { clear_field_and_queue(field, eaten_coins); initialize_params(field, eaten_coins, mode_params, pac, red_ghost, pink_ghost, blue_ghost, orange_ghost); } static void new_attempt(game_space *field, struct queue *eaten_coins, struct mode_type *mode_params, struct pacman *pac, struct ghost_type *red_ghost, struct ghost_type *pink_ghost, struct ghost_type *blue_ghost, struct ghost_type *orange_ghost) { erase_hit(pac->position); clear_ghost_positions(*field, eaten_coins, red_ghost, pink_ghost, blue_ghost, orange_ghost); pac->position.x = pac_x; pac->position.y = pac_y; initialize_ghosts(red_ghost, pink_ghost, blue_ghost, orange_ghost); initialize_modes(mode_params); display_ghosts_on_field(red_ghost, pink_ghost, blue_ghost, orange_ghost, mode_params->fright_counter); display_character(pac->position.y, pac->position.x, pac_char, 0); } static void waiting_for_readiness() { display_ready(); usleep(sleep_duration*5); clear_ready(); } static void set_pause(struct game_params_type *game_options) { int key; timeout(-1); while((key = getch()) != whitespace && key != key_escape) ; if(key == whitespace) game_options->pause = 1; else game_options->exit = 1; timeout(timeout_duration); } static void help_message() { fprintf(stderr, "Sorry.\n" "To play Pacman for Console, your console window must be\n" "at least 28x30. Please resize your window/resolution and\n" "re-run the game.\n" ); } static int check_screen_resolution() { initscr(); int row, col; getmaxyx(stdscr, row, col); endwin(); return row >= field_height && col >= field_width; } int main() { if(!check_screen_resolution()) { help_message(); return 1; } game_space field = NULL; struct ghost_type red_ghost, pink_ghost, blue_ghost, orange_ghost; struct pacman pac; struct queue eaten_coins; struct mode_type mode_params; struct game_params_type game_options; int key; enum movement_direction stored_direction; srand(time(NULL)); initscr(); start_color(); set_pairs(); cbreak(); noecho(); curs_set(0); keypad(stdscr, 1); stored_direction = none; game_options.exit = 0; game_options.pause = 0; initialize_params(&field, &eaten_coins, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); waiting_for_readiness(); while((key = getch()) != key_escape) { /* * pause */ if(key == whitespace) { set_pause(&game_options); if(game_options.pause) { game_options.pause = 0; continue; } else break; } /* * pacman */ if(key != ERR) change_pac_direction(field, &pac, key, &stored_direction); else check_remaining_direction(field, &pac, &stored_direction); make_pac_move(field, &pac, &eaten_coins); if(pac.is_energizer_eaten) change_mode(&mode_params, &pac); /* * ghosts */ if(mode_params.get_out_stage) pull_out_ghosts(&mode_params.get_out_stage, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); else if(mode_params.reverse_direction) { reverse_all_ghosts(&red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); mode_params.reverse_direction = 0; } else if(mode_params.current_mode == chase) chase_mode(field, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); else if(mode_params.current_mode == scatter) scatter_mode(field, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); else if(mode_params.current_mode == frightened) frightened_mode(field, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); make_ghost_moves(field, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost, &eaten_coins); /* * catching */ catching_stage(field, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); /* * displaying characters and score */ display_score(pac.score); display_ghosts_on_field(&red_ghost, &pink_ghost, &blue_ghost, &orange_ghost, mode_params.fright_counter); if(ghost_caught_pacman(red_ghost, pink_ghost, blue_ghost, orange_ghost)) { display_hit(pac.position, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); usleep(sleep_duration*5); } else display_character(pac.position.y, pac.position.x, pac_char, 0); usleep(sleep_duration); prison_leaving_stage(field, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); #ifdef DEBUG move(0, 50); reset_attr(); if(mode_params.current_mode == chase) printw("CHASE %d ", mode_params.chase_counter); else if(mode_params.current_mode == scatter) printw("SCATTER %d ", mode_params.scatter_counter); else if(mode_params.current_mode == frightened) printw("FRIGHTENED %d ", mode_params.fright_counter); move(1, 50); printw("LIVES: %d ", pac.lives); refresh(); #endif /* * result check */ if(pac.lives < 0 || pac.score == max_score ) { final_stage(&game_options, pac.lives < 0 ? 0 : 1); if(game_options.exit) break; else { stored_direction = none; new_game(&field, &eaten_coins, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); waiting_for_readiness(); } continue; } /* a new attempt if pacman has any life */ if(ghost_caught_pacman(red_ghost, pink_ghost, blue_ghost, orange_ghost)) { stored_direction = none; new_attempt(&field, &eaten_coins, &mode_params, &pac, &red_ghost, &pink_ghost, &blue_ghost, &orange_ghost); waiting_for_readiness(); } } clear_field_and_queue(&field, &eaten_coins); endwin(); return 0; }