/******************************************************* * pman game * * stay away from the Zs and eat dots!! (500) * program: pman * by.....: yetimach * date...: not sure, sometime in 2023 * * As it stands, the terminal size is a big * factor in the difficulty of the game. * * ***************************************************/ #include #include #include #include #include #define DIRECTIONS 4 #define UP 'w' #define DOWN 's' #define LEFT 'a' #define RIGHT 'd' #define DASH '-' #define PMANCH 'P' #define EZCH 'Z' #define PMANSTART 4 #define PMANGRIDMIN 4 #define WINNING_POINTS 500 #define TOTAL_ZS 4 #define Z_ATTACK_RANGE 4 #define MAX_SCREEN 200 #define DELAY 50 char direction; struct pman { int y; int x; char ch; int dir; int prev_dir; }; struct pman pm; struct evilz { int y; int x; char ch; int dir; int prev_dir; }; struct evilz ez[TOTAL_ZS]; int points = 0; bool dead = FALSE; struct evilz evilz_movement(struct evilz *pez, struct pman *ppm, int my, int mx, int peaten[MAX_SCREEN][MAX_SCREEN]){ bool fail = FALSE; int distance_y = abs(pez->y - ppm->y); int distance_x = abs(pez->x - ppm->x); if (distance_x > Z_ATTACK_RANGE || distance_y > Z_ATTACK_RANGE){ if (distance_x < distance_y){ if (ppm->y - pez->y < 0) pez->dir = 0; else pez->dir = 1; }else{ if (ppm->x - pez->x < 0) pez->dir = 2; else pez->dir = 3; } }else pez->dir = rand() % DIRECTIONS; switch (pez->dir) { case 0: --pez->y; if (pez->y < PMANGRIDMIN){ ++pez->y; fail = TRUE; break; } mvaddch(pez->y, pez->x, pez->ch); if (peaten[pez->y][pez->x] == FALSE){ peaten[pez->y][pez->x] = TRUE; } mvaddch(pez->y + 1, pez->x, ' '); if ( pez->y == ppm->y && pez->x == ppm->x) dead = TRUE; break; case 1: ++pez->y; if (pez->y > my - 1){ --pez->y; fail = TRUE; break; } mvaddch(pez->y, pez->x, pez->ch); if (peaten[pez->y][pez->x] == FALSE){ peaten[pez->y][pez->x] = TRUE; } mvaddch(pez->y - 1, pez->x, ' '); if ( pez->y == ppm->y && pez->x == ppm->x) dead = TRUE; break; case 2: --pez->x; if (pez->x < PMANGRIDMIN){ ++pez->x; fail = TRUE; break; } mvaddch(pez->y, pez->x, pez->ch); if (peaten[pez->y][pez->x] == FALSE){ peaten[pez->y][pez->x] = TRUE; } mvaddch(pez->y, pez->x + 1, ' '); if ( pez->y == ppm->y && pez->x == ppm->x) dead = TRUE; break; case 3: ++pez->x; if (pez->x > mx){ --pez->x; fail = TRUE; break; } mvaddch(pez->y, pez->x, pez->ch); if (peaten[pez->y][pez->x] == FALSE){ peaten[pez->y][pez->x] = TRUE; } mvaddch(pez->y, pez->x - 1, ' '); if ( pez->y == ppm->y && pez->x == ppm->x) dead = TRUE; break; } refresh(); if (fail){ fail = FALSE; // This is something to think about for later // but I don't want to get into that rabbit hole now... } return *pez; } // pointer pman, pointer evilz, max_y, max_x, parameter eaten struct pman pman_movement(struct pman *ppm, int my, int mx, int peaten[MAX_SCREEN][MAX_SCREEN]){ ppm->dir = tolower(getch()); if (ppm->dir == ERR) ppm->dir = ppm->prev_dir; switch (ppm->dir) { case UP: --ppm->y; if (ppm->y < PMANGRIDMIN){ ++ppm->y; break; } mvaddch(ppm->y, ppm->x, ppm->ch); if (peaten[ppm->y][ppm->x] == FALSE){ ++points; peaten[ppm->y][ppm->x] = TRUE; } mvaddch(ppm->y + 1, ppm->x, ' '); break; case DOWN: ++ppm->y; if (ppm->y > my - 1){ --ppm->y; break; } mvaddch(ppm->y, ppm->x, ppm->ch); if (peaten[ppm->y][ppm->x] == FALSE){ ++points; peaten[ppm->y][ppm->x] = TRUE; } mvaddch(ppm->y - 1, ppm->x, ' '); break; case LEFT: --ppm->x; if (ppm->x < PMANGRIDMIN){ ++ppm->x; break; } mvaddch(ppm->y, ppm->x, ppm->ch); if (peaten[ppm->y][ppm->x] == FALSE){ ++points; peaten[ppm->y][ppm->x] = TRUE; } mvaddch(ppm->y, ppm->x + 1, ' '); break; case RIGHT: ++ppm->x; if (ppm->x > mx - 1){ --ppm->x; break; } mvaddch(ppm->y, ppm->x, ppm->ch); if (peaten[ppm->y][ppm->x] == FALSE){ ++points; peaten[ppm->y][ppm->x] = TRUE; } mvaddch(ppm->y, ppm->x - 1, ' '); break; } ppm->prev_dir = ppm->dir; return *ppm; } void flash_colors(int flashes){ for (int i = 0; i <= flashes; i++){ bkgd(COLOR_PAIR(2)); refresh(); napms(300); bkgd(COLOR_PAIR(3)); refresh(); napms(300); bkgd(COLOR_PAIR(2)); refresh(); napms(300); bkgd(COLOR_PAIR(3)); refresh(); napms(300); } } int main() { srand(time(NULL)); initscr(); pm.ch = PMANCH; for (int i = 0; i < TOTAL_ZS; i++) ez[i].ch = EZCH; int eaten[MAX_SCREEN][MAX_SCREEN]; int height, width, i, j; getmaxyx(stdscr, height, width); height -= PMANGRIDMIN; width -= PMANGRIDMIN; int EZSTART_Y = height / 2; int EZSTART_X = width / 2; char *title = "Pman & Evil Z's"; mvprintw(2, ((width - strlen(title) ) / 2), title); noecho(); curs_set(FALSE); nodelay(stdscr, TRUE); if (has_colors()){ if (start_color() == 0){ init_pair(1, COLOR_BLACK, COLOR_WHITE); init_pair(2, COLOR_YELLOW, COLOR_GREEN); init_pair(3, COLOR_RED, COLOR_BLUE); bkgd(COLOR_PAIR(1)); } } // fill srceen with DASH for (i = PMANGRIDMIN; i < height; i++){ for (j = PMANGRIDMIN; j < width; j++){ mvaddch(i, j, DASH); eaten[i][j] = FALSE; } } // place first pman mvaddch(PMANSTART, PMANSTART, pm.ch); pm.y = PMANSTART; pm.x = PMANSTART; eaten[PMANSTART][PMANSTART] = TRUE; ++points; // place evilzs for (i = 0; i < TOTAL_ZS; i++){ mvaddch(EZSTART_Y, EZSTART_X, ez[i].ch); ez[i].y = EZSTART_Y; ez[i].x = EZSTART_X; } eaten[EZSTART_Y][EZSTART_X] = TRUE; refresh(); pm.prev_dir = RIGHT; while (TRUE){ mvprintw(2, width - 30, "Points: %d (reach %d to win)", points, WINNING_POINTS); pm = pman_movement(&pm, height, width, eaten); for (i = 0; i < TOTAL_ZS; i++){ ez[i] = evilz_movement(&ez[i], &pm, height, width, eaten); } napms(DELAY); refresh(); if (points == WINNING_POINTS) break; if (dead) break; } refresh(); attron(A_BOLD); (dead) ? addstr("\n\tYou lose!") : addstr("\n\tYou win!"); // flash effect on win flash_colors(4); endwin(); return 0; }