-/* this file is a part of pacman.c, and included via pacman.h. It handles the
- AI of the ghosts and the pacman. */
-
-
-/* fills array of DIRVECS size with possible directions, returns number of
- directions. 'posdirs' points to a possibly undefined array of four
- integers. The vector will contain booleans wether the direction is
- a possible direction or not. Reverse directions are deleted. */
-static int
-ghost_get_posdirs(pacmangamestruct *pp, int *posdirs, ghoststruct *g)
-{
- unsigned int i, nrdirs = 0;
-
- /* bit of black magic here */
- for (i = 0; i < DIRVECS; i++) {
- /* remove opposite */
- if (g->lastbox != NOWHERE && i == (g->lastbox + 2) % DIRVECS
- && g->aistate != goingout) {
- posdirs[i] = 0;
- continue;
- }
- if (g->aistate == goingout && i == 1) {
- posdirs[i] = 0;
- continue;
- }
- /* check if possible direction */
- if ((posdirs[i] =
- check_pos(pp, g->row + dirvecs[i].dy,
- g->col + dirvecs[i].dx,
- g->aistate == goingout ? True : False)) ==
- True) {
- nrdirs++;
- }
- }
-
- return nrdirs;
-}
-
-/* Directs ghost to a random direction, exluding opposite (except in the
- impossible situation that there is only one valid direction). */
-static void
-ghost_random(pacmangamestruct *pp, ghoststruct *g)
-{
- int posdirs[DIRVECS], nrdirs = 0, i, dir = 0;
-
- nrdirs = ghost_get_posdirs(pp, posdirs, g);
- for (i = 0; i < DIRVECS; i++)
- if (posdirs[i] == True) dir = i;
-
- if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
- else if (nrdirs > 1)
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == True && NRAND(nrdirs) == 0) {
- dir = i;
- break;
- }
- }
-
- g->nextrow = g->row + dirvecs[dir].dy;
- g->nextcol = g->col + dirvecs[dir].dx;
- g->lastbox = dir;
-}
-
-/* Determines best direction to chase the pacman and goes that direction. */
-static void
-ghost_chasing(pacmangamestruct *pp, ghoststruct *g)
-{
- int posdirs[DIRVECS], nrdirs = 0, i, dir = 0, highest = -100000,
- thisvecx, thisvecy, thisvec;
-
- nrdirs = ghost_get_posdirs(pp, posdirs, g);
- for (i = 0; i < DIRVECS; i++)
- if (posdirs[i] == True) dir = i;
-
- if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
- else if (nrdirs > 1)
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == False) continue;
- thisvecx = (pp->pacman.col - g->col) * dirvecs[i].dx;
- thisvecy = (pp->pacman.row - g->row) * dirvecs[i].dy;
- thisvec = thisvecx + thisvecy;
- if (thisvec >= highest) {
- dir = i;
- highest = thisvec;
- }
- }
-
- g->nextrow = g->row + dirvecs[dir].dy;
- g->nextcol = g->col + dirvecs[dir].dx;
- g->lastbox = dir;
-}
-
-/* Determines the best direction to go away from the pacman, and goes that
- direction. */
-static void
-ghost_hiding(pacmangamestruct *pp, ghoststruct *g)
-{
- int posdirs[DIRVECS], nrdirs = 0, i, dir = 0, highest = -100000,
- thisvecx, thisvecy, thisvec;
-
- nrdirs = ghost_get_posdirs(pp, posdirs, g);
- for (i = 0; i < DIRVECS; i++)
- if (posdirs[i] == True) dir = i;
-
- if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
- else if (nrdirs > 1)
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == False) continue;
- thisvecx = (g->col - pp->pacman.col) * dirvecs[i].dx;
- thisvecy = (g->row - pp->pacman.row) * dirvecs[i].dy;
- thisvec = thisvecx + thisvecy;
- if (thisvec >= highest)
- dir = i;
- }
-
- g->nextrow = g->row + dirvecs[dir].dy;
- g->nextcol = g->col + dirvecs[dir].dx;
- g->lastbox = dir;
-}
-
-/* Determines a vector from the pacman position, towards all dots. The vector
- is inversely proportional to the square of the distance of each dot.
- (so, close dots attract more than far away dots). */
-static void
-pac_dot_vec(pacmangamestruct *pp, pacmanstruct *p, long *vx, long *vy)
-{
- int x, y, bx = 0, by = 0, ex = LEVWIDTH, ey = LEVHEIGHT;
- long dx, dy, dist, top = 0;
-
-#if 0
- int rnr = NRAND(50);
- /* determine begin and end vectors */
-
- switch (rnr) {
- case 0: ex = LEVHEIGHT/2; break;
- case 1: bx = LEVHEIGHT/2; break;
- case 2: ey = LEVHEIGHT/2; break;
- case 3: by = LEVHEIGHT/2; break;
- }
-#endif
- *vx = *vy = 0;
-
- for (y = by; y < ey; y++)
- for (x = bx; x < ex; x++)
- if (check_dot(pp, x, y) == 1) {
- dx = (long)x - (long)(p->col);
- dy = (long)y - (long)(p->row);
- dist = dx * dx + dy * dy;
- if (dist > top)
- top = dist;
- *vx += (dx * ((long)LEVWIDTH * (long)LEVHEIGHT))
- / dist;
- *vy += (dy * ((long)LEVWIDTH * (long)LEVHEIGHT))
- / dist;
- }
-}
-
-/* Determine a vector towards the closest ghost (in one loop, so we spare a
- couple of cycles which we can spoil somewhere else just as fast). */
-static int
-pac_ghost_prox_and_vector(pacmangamestruct *pp, pacmanstruct *p,
- int *vx, int *vy)
-{
- int dx, dy, dist, closest = 100000;
- unsigned int g;
-
- if (vx != NULL)
- *vx = *vy = 0;
-
- for (g = 0; g < pp->nghosts; g++) {
- if (pp->ghosts[g].dead == True ||
- pp->ghosts[g].aistate == inbox ||
- pp->ghosts[g].aistate == goingout)
- continue;
- dx = pp->ghosts[g].col + /*dirvecs[pp->ghosts[g].lastbox].dx*/ -
- p->col;
- dy = pp->ghosts[g].row + /*dirvecs[pp->ghosts[g].lastbox].dy*/ -
- p->row;
- dist = dx * dx + dy * dy;
- if (dist < closest) {
- closest = dist;
- if (vx != NULL) {
- *vx = dx; *vy = dy;
- }
- }
- }
- return closest;
-}
-
-/* fills array of DIRVECS size with possible directions, returns number of
- directions. posdirs should point to an array of 4 integers. */
-static int
-pac_get_posdirs(pacmangamestruct *pp, pacmanstruct *p, int *posdirs)
-{
- int nrdirs = 0;
- unsigned int i;
-
- for (i = 0; i < DIRVECS; i++) {
- /* if we just ate, or we are in a statechange, it is allowed
- to go the opposite direction */
- if (p->justate == 0 &&
- p->state_change == 0 &&
- p->lastbox != NOWHERE &&
- i == (p->lastbox + 2) % DIRVECS) {
- posdirs[i] = 0;
- } else if ((posdirs[i] =
- check_pos(pp, p->row + dirvecs[i].dy,
- p->col + dirvecs[i].dx, 0)) == 1)
- nrdirs++;
- }
- p->state_change = 0;
-
- return nrdirs;
-}
-
-/* Clears the trace of vectors. */
-static void
-pac_clear_trace(pacmanstruct *p)
-{
- int i;
-
- for(i = 0; i < TRACEVECS; i++) {
- p->trace[i].vx = NOWHERE; p->trace[i].vy = NOWHERE;
- }
- p->cur_trace = 0;
-}
-
-/* Adds a new vector to the trace. */
-static void
-pac_save_trace(pacmanstruct *p, const int vx, const int vy)
-{
- if (!(vx == NOWHERE && vy == NOWHERE)) {
- p->trace[p->cur_trace].vx = vx;
- p->trace[p->cur_trace].vy = vy;
- p->cur_trace = (p->cur_trace + 1) % TRACEVECS;
- }
-}
-
-/* Check if a vector can be found in the trace. */
-static int
-pac_check_trace(const pacmanstruct *p, const int vx, const int vy)
-{
- int i, curel;
-
- for (i = 1; i < TRACEVECS; i++) {
- curel = (p->cur_trace - i + TRACEVECS) % TRACEVECS;
- if (p->trace[curel].vx == NOWHERE &&
- p->trace[curel].vy == NOWHERE)
- continue;
- if (p->trace[curel].vx == vx &&
- p->trace[curel].vy == vy)
- return 1;
- }
-
-
- return 0;
-}
-
-/* AI mode "Eating" for pacman. Tries to eat as many dots as possible, goes
- to "hiding" if too close to a ghost. If in state "hiding" the vectors
- of the ghosts are also taken into account (thus not running towards them
- is the general idea). */
-static void
-pac_eating(pacmangamestruct *pp, pacmanstruct *p)
-{
- int posdirs[DIRVECS], nrdirs, i, highest = -(1 << 16),
- score, dir = 0, dotfound = 0, prox, worst = 0;
- long vx, vy;
-
- if ((prox = pac_ghost_prox_and_vector(pp, p, (int *)&vx, (int *)&vy)) <
- 4 * 4 && p->aistate == ps_eating) {
- p->aistate = ps_hiding;
- p->state_change = 1;
- pac_clear_trace(p);
- }
-
- if (prox > 6 * 6 && p->aistate == ps_hiding) {
- p->aistate = ps_eating;
- if (p->justate == 0) p->state_change = 1;
- pac_clear_trace(p);
- }
-
- if (prox < 3 * 3) p->state_change = 1;
-
- nrdirs = pac_get_posdirs(pp, p, posdirs);
-
- /* remove directions which lead to ghosts */
- if (p->aistate == ps_hiding) {
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == 0) continue;
- score = vx * dirvecs[i].dx + vy * dirvecs[i].dy;
- if (score > highest) {
- worst = i;
- highest = score;
- }
- dir = i;
- }
- nrdirs--;
- posdirs[worst] = 0;
- highest = -(1 << 16);
- }
-
- /* get last possible direction if all else fails */
- for (i = 0; i < DIRVECS; i++)
- if (posdirs[i] != 0) dir = i;
-
- pac_dot_vec(pp, p, &vx, &vy);
-
- if (vx != NOWHERE && vy != NOWHERE && pac_check_trace(p, vx, vy) > 0) {
- p->roundscore++;
- if (p->roundscore >= 12) {
- p->roundscore = 0;
- p->aistate = ps_random;
- pac_clear_trace(p);
- }
- } else
- p->roundscore = 0;
-
- if (p->justate == 0) pac_save_trace(p, vx, vy);
-
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == 0) continue;
- score = dirvecs[i].dx * vx + dirvecs[i].dy * vy;
- if (check_dot(pp, p->col + dirvecs[i].dx,
- p->row + dirvecs[i].dy) == 1) {
- if (dotfound == 0) {
- highest = score;
- dir = i;
- dotfound = 1;
- } else if (score > highest) {
- highest = score;
- dir = i;
- }
- } else if (score > highest && dotfound == 0) {
- dir = i;
- highest = score;
- }
- }
-
- p->nextrow = p->row + dirvecs[dir].dy;
- p->nextcol = p->col + dirvecs[dir].dx;
- p->lastbox = dir;
-}
-
-#if 0
-/* Tries to catch the ghosts. */
-static void
-pac_chasing(pacmangamestruct *pp, pacmanstruct *p)
-{
-}
-#endif
-
-/* Goes completely random, but not in the opposite direction. Used when a
- loop is detected. */
-static void
-pac_random(pacmangamestruct *pp, pacmanstruct *p)
-{
- int posdirs[DIRVECS], nrdirs, i, dir = -1, lastdir = 0;
-
- if (pac_ghost_prox_and_vector(pp, p, NULL, NULL) < 5 * 5) {
- p->aistate = ps_hiding;
- p->state_change = 1;
- }
- if (NRAND(20) == 0) {
- p->aistate = ps_eating;
- p->state_change = 1;
- pac_clear_trace(p);
- }
-
- nrdirs = pac_get_posdirs(pp, p, posdirs);
-
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == 0) continue;
- lastdir = i;
- if (check_dot(pp, p->col + dirvecs[i].dx,
- p->row + dirvecs[i].dy) == 1) {
- dir = i;
- p->aistate = ps_eating;
- p->state_change = 1;
- pac_clear_trace(p);
- break;
- } else if (NRAND(nrdirs) == 0)
- dir = i;
- }
-
- if (dir == -1) dir = lastdir;
-
- p->nextrow = p->row + dirvecs[dir].dy;
- p->nextcol = p->col + dirvecs[dir].dx;
- p->lastbox = dir;
-}
-
-static int
-pac_get_vector_screen(pacmangamestruct *pp, pacmanstruct *p,
- const int x, const int y, int *vx, int *vy)
-{
- int lx, ly;
-
- lx = (x - pp->xb) / pp->xs;
- ly = (y - pp->yb) / pp->ys;
-
- if (lx < 0) lx = 0;
- else if ((unsigned int) lx > LEVWIDTH) lx = LEVWIDTH - 1;
-
- if (ly < 0) ly = 0;
- else if ((unsigned int) ly > LEVHEIGHT) ly = LEVHEIGHT - 1;
-
- *vx = lx - p->col;
- *vy = ly - p->row;
-
- if (lx == p->oldlx && ly == p->oldly) return 0;
- p->oldlx = lx; p->oldly = ly;
- return 1;
-}
-
-static int
-pac_trackmouse(ModeInfo * mi, pacmangamestruct *pp, pacmanstruct *p)
-{
- int dx, dy, cx, cy, vx, vy;
- unsigned int m;
- int posdirs[DIRVECS], i, dir = -1, highest = -(2 << 16), score;
- Window r, c;
-
- (void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
- &r, &c, &dx, &dy, &cx, &cy, &m);
-
- if (cx <= 0 || cy <= 0 ||
- cx >= MI_WIDTH(mi) - 1 ||
- cy >= MI_HEIGHT(mi) - 1)
- return 0;
- /* get vector */
- p->state_change = pac_get_vector_screen(pp, p, cx, cy, &vx, &vy);
-
- (void) pac_get_posdirs(pp, p, posdirs);
-
- for (i = 0; i < DIRVECS; i++) {
- if (posdirs[i] == 0) continue;
- score = dirvecs[i].dx * vx + dirvecs[i].dy * vy;
- if (score > highest) {
- highest = score;
- dir = i;
- }
- }
-
- p->nextrow = p->row + dirvecs[dir].dy;
- p->nextcol = p->col + dirvecs[dir].dx;
- p->lastbox = dir;
- return 1;
-}
-
-/* Calls correct state function, and changes between states. */
-static void
-ghost_update(pacmangamestruct *pp, ghoststruct *g)
-{
-
- if (!(g->nextrow == NOWHERE && g->nextcol == NOWHERE)) {
- g->row = g->nextrow;
- g->col = g->nextcol;
- }
-
- /* update ghost */
- if (g->dead == True) /* dead ghosts are dead,
- so they don't run around */
- return;
-
- if ((g->aistate == randdir || g->aistate == chasing) &&
- NRAND(10) == 0) {
- switch (NRAND(3)) {
- case 0:
- g->aistate = randdir; break;
- case 1:
- g->aistate = chasing; break;
- case 2:
- g->aistate = chasing; break;
- }
-
- } else if (g->aistate == inbox) {
- if (g->timeleft < 0)
- g->aistate = goingout;
- else
- g->timeleft--;
-
- } else if (g->aistate == goingout) {
- if (g->col < LEVWIDTH/2 - JAILWIDTH/2 ||
- g->col > LEVWIDTH/2 + JAILWIDTH/2 ||
- g->row < LEVHEIGHT/2 - JAILHEIGHT/2 ||
- g->row > LEVHEIGHT/2 + JAILHEIGHT/2 )
- g->aistate = randdir;
- }
-
- switch (g->aistate) {
- case inbox:
- case goingout:
- case randdir:
- ghost_random(pp, g);
- break;
- case chasing:
- ghost_chasing(pp, g);
- break;
- case hiding:
- ghost_hiding(pp, g);
- break;
- }
-
- g->cfactor = GETFACTOR(g->nextcol, g->col);
- g->rfactor = GETFACTOR(g->nextrow, g->row);
-}
-
-/* Calls correct pacman state function. */
-static void
-pac_update(ModeInfo *mi, pacmangamestruct *pp, pacmanstruct *p)
-{
- if (!(p->nextrow == NOWHERE && p->nextcol == NOWHERE)) {
- p->row = p->nextrow;
- p->col = p->nextcol;
- }
-
- if (pp->level[p->row * LEVWIDTH + p->col] == '.') {
- pp->level[p->row * LEVWIDTH + p->col] = ' ';
- if (!trackmouse)
- p->justate = 1;
- pp->dotsleft--;
- } else if (!trackmouse) {
- p->justate = 0;
- }