1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)pacman.c 5.00 2000/11/01 xlockmore";
10 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
12 * Permission to use, copy, modify, and distribute this software and its
13 * documentation for any purpose and without fee is hereby granted,
14 * provided that the above copyright notice appear in all copies and that
15 * both that copyright notice and this permission notice appear in
16 * supporting documentation.
18 * This file is provided AS IS with no warranties of any kind. The author
19 * shall have no liability with respect to the infringement of copyrights,
20 * trade secrets or any patents by this file or any part thereof. In no
21 * event will the author be liable for any lost revenue or profits or
22 * other special, indirect and consequential damages.
25 * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.
26 * splitted up code into several files. Retouched AI code, cleaned
28 * 3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
29 * 26-Nov-2001: Random level generator added
30 * 01-Nov-2000: Allocation checks
31 * 04-Jun-1997: Compatible with xscreensaver
37 2. make better ghost sprites (eyes, waving dress)
38 3. make a bit better pacman sprite (mouth should be larger)
39 4. think of a better level generation algorithm
44 # define PROGCLASS "Pacman"
45 # define HACK_INIT init_pacman
46 # define HACK_DRAW draw_pacman
47 # define pacman_opts xlockmore_opts
48 # define DEFAULTS "*delay: 10000 \n" \
51 "*trackmouse: False \n"
52 # define UNIFORM_COLORS
53 # define BRIGHT_COLORS
54 # include "xlockmore.h" /* in xscreensaver distribution */
55 #else /* STANDALONE */
56 # include "xlock.h" /* in xlockmore distribution */
57 #endif /* STANDALONE */
62 #include "pacman_ai.h"
63 #include "pacman_level.h"
65 #ifdef DISABLE_INTERACTIVE
66 ModeSpecOpt pacman_opts = {
68 (XrmOptionDescRec *) NULL,
74 static XrmOptionDescRec opts[] =
76 {(char *) "-trackmouse", (char *) ".pacman.trackmouse", XrmoptionNoArg,
78 {(char *) "+trackmouse", (char *) ".pacman.trackmouse", XrmoptionNoArg,
82 static argtype vars[] =
84 {(caddr_t *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse",
85 (char *) DEF_TRACKMOUSE, t_Bool}
88 static OptionStruct desc[] =
90 {(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the "
94 ModeSpecOpt pacman_opts =
95 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
99 ModStruct pacman_description = {
100 "pacman", /* *cmdline_arg; */
101 "init_pacman", /* *init_name; */
102 "draw_pacman", /* *callback_name; */
103 "release_pacman", /* *release_name; */
104 "refresh_pacman", /* *refresh_name; */
105 "change_pacman", /* *change_name; */
106 (char *) NULL, /* *unused_name; */
107 &pacman_opts, /* *msopts */
108 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
113 free_pacman(Display *display, pacmangamestruct *pp)
117 if (pp->ghosts != NULL) {
119 pp->ghosts = (ghoststruct *) NULL;
121 if (pp->stippledGC != None) {
122 XFreeGC(display, pp->stippledGC);
123 pp->stippledGC = None;
125 if (pp->ghostPixmap != None) {
126 XFreePixmap(display, pp->ghostPixmap);
127 pp->ghostPixmap = None;
129 for (dir = 0; dir < 4; dir++)
130 for (mouth = 0; mouth < MAXMOUTH; mouth++)
131 if (pp->pacmanPixmap[dir][mouth] != None) {
133 pp->pacmanPixmap[dir][mouth]);
134 pp->pacmanPixmap[dir][mouth] = None;
138 /* Checks for death of any ghosts/pacman and updates. It also makes a new
139 level if all ghosts are dead or all dots are eaten. */
141 check_death(ModeInfo * mi, pacmangamestruct *pp)
143 Display *display = MI_DISPLAY(mi);
144 Window window = MI_WINDOW(mi);
149 for (ghost = 0; ghost < pp->nghosts; ghost++) {
150 if (pp->ghosts[ghost].dead == True)
153 if ((pp->ghosts[ghost].nextrow == NOWHERE &&
154 pp->ghosts[ghost].nextcol == NOWHERE)) {
159 if (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
160 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
161 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
162 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
163 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
164 (pp->ghosts[ghost].col == pp->pacman.nextcol))) {
165 pp->ghosts[ghost].dead = 1;
166 XSetForeground(display,
169 XFillRectangle(display, window,
171 pp->ghosts[ghost].cf,
172 pp->ghosts[ghost].rf,
180 if (alldead == 1 || pp->dotsleft == 0)
184 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
186 repopulate(ModeInfo * mi)
188 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
190 int i = createnewlevel(mi);
195 pp->gamestate = GHOST_DANGER;
197 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT)/2 - i;
198 pp->pacman.col = (LEVWIDTH/2);
199 pp->pacman.nextrow = NOWHERE;
200 pp->pacman.nextcol = NOWHERE;
201 pp->pacman.cf = NOWHERE;
202 pp->pacman.rf = NOWHERE;
203 pp->pacman.oldcf = NOWHERE;
204 pp->pacman.oldrf = NOWHERE;
205 pp->pacman.oldlx = NOWHERE;
206 pp->pacman.oldly = NOWHERE;
207 pp->pacman.aistate = ps_eating;
208 pp->pacman.cur_trace = 0;
209 pp->pacman.roundscore = 0;
210 pp->pacman.speed = 4;
211 pp->pacman.lastturn = 0;
212 pp->pacman.delta.x = 0;
213 pp->pacman.delta.y = 0;
214 pac_clear_trace(&(pp->pacman));
216 for (ghost = 0; ghost < pp->nghosts; ghost++) {
217 pp->ghosts[ghost].col = (LEVWIDTH/2);
218 pp->ghosts[ghost].row = (LEVHEIGHT/2);
219 pp->ghosts[ghost].nextcol = NOWHERE;
220 pp->ghosts[ghost].nextrow = NOWHERE;
221 pp->ghosts[ghost].dead = 0;
222 pp->ghosts[ghost].lastbox = START;
223 pp->ghosts[ghost].cf = NOWHERE;
224 pp->ghosts[ghost].rf = NOWHERE;
225 pp->ghosts[ghost].oldcf = NOWHERE;
226 pp->ghosts[ghost].oldrf = NOWHERE;
227 pp->ghosts[ghost].aistate = inbox;
228 pp->ghosts[ghost].timeleft = ghost * 50;
229 pp->ghosts[ghost].speed = 3;
230 pp->ghosts[ghost].delta.x = 0;
231 pp->ghosts[ghost].delta.y = 0;
233 ghost_update(pp, &(pp->ghosts[ghost]));
236 pac_update(mi, pp, &(pp->pacman));
239 /* Sets the color to the color of a wall. */
241 setwallcolor(ModeInfo * mi)
243 Display *display = MI_DISPLAY(mi);
244 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
246 if (MI_NPIXELS(mi) > 2)
247 XSetForeground(display, pp->stippledGC,
250 XSetForeground(display, pp->stippledGC,
254 /* Sets the color to the color of a dot. */
256 setdotcolor(ModeInfo * mi)
258 Display *display = MI_DISPLAY(mi);
259 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
261 XSetForeground(display, pp->stippledGC,
265 /* Draws a block in the level at the specified x and y locations. */
267 drawlevelblock(ModeInfo * mi, pacmangamestruct *pp,
268 const unsigned x, const unsigned y)
270 Display *display = MI_DISPLAY(mi);
271 Window window = MI_WINDOW(mi);
274 if (pp->xs % 2 == 1) dx = -1;
275 if (pp->ys % 2 == 1) dy = -1;
277 XSetFillStyle(display, pp->stippledGC, FillSolid);
278 XSetLineAttributes(display, pp->stippledGC, pp->wallwidth,
279 LineSolid, CapRound, JoinMiter);
281 if (pp->xs < 2 || pp->ys < 2) {
282 switch(pp->level[y*LEVWIDTH + x]) {
288 (void)XDrawPoint(display, window,
291 y * pp->ys + pp->yb);
295 (void)XDrawPoint(display, window,
298 y * pp->ys + pp->yb);
304 switch (pp->level[y*LEVWIDTH + x]) {
311 if (pp->xs < 8 || pp->ys < 8) {
312 (void)XDrawPoint(display, window,
314 x * pp->xs + pp->xb +
316 y * pp->ys + pp->yb +
321 (void)XDrawArc(display, window, pp->stippledGC,
324 (pp->xs > 32 ? (pp->xs / 16) : 1) +
328 (pp->ys > 32 ? (pp->ys / 16) : 1) +
330 (pp->xs > 32 ? (pp->xs / 32) : 1),
331 (pp->ys > 32 ? (pp->ys / 32) : 1),
337 (void)XDrawLine(display, window, pp->stippledGC,
338 (pp->xs * x) + pp->xb,
339 (pp->ys * y) + (pp->ys / 2) + pp->yb,
340 (pp->xs * (x + 1)) + pp->xb,
341 (pp->ys * y) + (pp->ys / 2) + pp->yb);
346 (void)XDrawLine(display, window, pp->stippledGC,
347 (pp->xs * x) + (pp->xs / 2) + pp->xb,
348 (pp->ys * y) + pp->yb,
349 (pp->xs * x) + (pp->xs / 2) + pp->xb,
350 (pp->ys * (y + 1)) + pp->yb);
355 (void)XDrawArc(display, window, pp->stippledGC,
356 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
357 (pp->ys * y) + (pp->ys / 2) + pp->yb,
364 (void)XDrawArc(display, window, pp->stippledGC,
365 (pp->xs * x) + (pp->ys / 2) + pp->xb,
366 (pp->ys * y) + (pp->ys / 2) + pp->yb,
373 (void)XDrawArc(display, window, pp->stippledGC,
374 (pp->xs * x) + (pp->ys / 2) + pp->xb,
375 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
382 (void)XDrawArc(display, window, pp->stippledGC,
383 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
384 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
392 /* Draws a complete level. */
394 drawlevel(ModeInfo * mi)
396 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
399 for (y = 0; y < LEVHEIGHT; y++)
400 for (x = 0; x < LEVWIDTH; x++)
401 drawlevelblock(mi, pp, x, y);
404 /* There is some overlap so it can be made more efficient */
405 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
407 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
408 XFillRectangle(d,w,g,xl,yl,xs,ys); \
410 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
411 XFillRectangle(d,w,g,xl,yl,xs,ys); \
413 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
414 XFillRectangle(d,w,g,xl,yl,xs,ys); \
416 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
417 XFillRectangle(d,w,g,xl,yl,xs,ys)
420 /* Draws the pacman sprite, removing the previous location. */
422 draw_pacman_sprite(ModeInfo * mi)
424 Display *display = MI_DISPLAY(mi);
425 Window window = MI_WINDOW(mi);
426 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
429 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
430 pp->pacman.cfactor + pp->xb + pp->spritedx;
431 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
432 pp->pacman.rfactor + pp->yb + pp->spritedy;
434 dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
435 ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
437 XSetForeground(display, pp->stippledGC,
439 if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
441 XFillRectangle(display, window, pp->stippledGC,
442 pp->pacman.oldcf, pp->pacman.oldrf,
443 pp->spritexs, pp->spriteys);
445 ERASE_IMAGE(display, window, pp->stippledGC,
446 pp->pacman.cf, pp->pacman.rf,
447 pp->pacman.oldcf, pp->pacman.oldrf,
448 pp->spritexs, pp->spriteys);
452 XSetTSOrigin(display, pp->stippledGC,
453 pp->pacman.cf, pp->pacman.rf);
454 if (MI_NPIXELS(mi) > 2)
455 XSetForeground(display, pp->stippledGC,
456 MI_PIXEL(mi, YELLOW));
458 XSetForeground(display, pp->stippledGC,
461 XSetStipple(display, pp->stippledGC,
462 pp->pacmanPixmap[dir][pp->pacman.mouthstage]);
464 XSetFillStyle(display, pp->stippledGC, FillStippled);
466 XSetFillStyle(display, pp->stippledGC,
469 if (pp->xs < 2 || pp->ys < 2)
470 XDrawPoint(display, window, pp->stippledGC,
471 pp->pacman.cf, pp->pacman.rf);
473 XFillRectangle(display, window, pp->stippledGC,
474 pp->pacman.cf, pp->pacman.rf,
475 pp->spritexs, pp->spriteys);
476 pp->pacman.mouthstage += pp->pacman.mouthdirection;
477 if ((pp->pacman.mouthstage >= MAXMOUTH) ||
478 (pp->pacman.mouthstage < 0)) {
479 pp->pacman.mouthdirection *= -1;
480 pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
482 pp->pacman.oldcf = pp->pacman.cf;
483 pp->pacman.oldrf = pp->pacman.rf;
486 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
488 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost) {
489 Display *display = MI_DISPLAY(mi);
490 Window window = MI_WINDOW(mi);
491 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
493 pp->ghosts[ghost].cf =
494 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
495 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
496 pp->ghosts[ghost].rf =
497 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
498 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
500 XSetForeground(display,
504 if (pp->ghosts[ghost].oldcf != NOWHERE ||
505 pp->ghosts[ghost].oldrf != NOWHERE) {
507 XFillRectangle(display, window,
508 pp->stippledGC, pp->ghosts[ghost].oldcf,
509 pp->ghosts[ghost].oldrf,
510 pp->spritexs, pp->spriteys);
512 ERASE_IMAGE(display, window, pp->stippledGC,
513 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
514 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf,
515 pp->spritexs, pp->spriteys);
519 drawlevelblock(mi, pp,
520 (unsigned int)pp->ghosts[ghost].col,
521 (unsigned int)pp->ghosts[ghost].row);
523 XSetTSOrigin(display, pp->stippledGC,
524 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
526 if (MI_NPIXELS(mi) > 2)
527 XSetForeground(display,
529 MI_PIXEL(mi, GREEN));
531 XSetForeground(display,
535 XSetStipple(display, pp->stippledGC,
539 XSetFillStyle(display,
543 XSetFillStyle(display,
547 if (pp->xs < 2 || pp->ys < 2)
548 XDrawPoint(display, window, pp->stippledGC,
549 pp->ghosts[ghost].cf,
550 pp->ghosts[ghost].rf);
552 XFillRectangle(display,
555 pp->ghosts[ghost].cf,
556 pp->ghosts[ghost].rf,
557 pp->spritexs, pp->spriteys);
559 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
560 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
563 /* Does all drawing of moving sprites in the level. */
565 pacman_tick(ModeInfo * mi)
567 Display *display = MI_DISPLAY(mi);
568 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
571 for (ghost = 0; ghost < pp->nghosts; ghost++) {
572 if (pp->ghosts[ghost].dead == True)
574 draw_ghost_sprite(mi, ghost);
577 draw_pacman_sprite(mi);
579 (void)XFlush(display);
582 /* Hook function, sets state to initial position. */
584 init_pacman(ModeInfo * mi)
586 Display *display = MI_DISPLAY(mi);
587 Window window = MI_WINDOW(mi);
588 int size = MI_SIZE(mi);
589 pacmangamestruct *pp;
595 if (pacmangames == NULL) {
596 if ((pacmangames = (pacmangamestruct *)
597 calloc((size_t)MI_NUM_SCREENS(mi),
598 sizeof (pacmangamestruct))) == NULL)
601 pp = &pacmangames[MI_SCREEN(mi)];
603 pp->width = (unsigned short)MI_WIDTH(mi);
604 pp->height = (unsigned short)MI_HEIGHT(mi);
605 if (pp->ghostPixmap != None) {
606 XFreePixmap(display, pp->ghostPixmap);
607 pp->ghostPixmap = None;
608 pp->graphics_format = 0 /*IS_NONE*/;
612 MINGRIDSIZE * size > (int)pp->width ||
613 MINGRIDSIZE * size > (int)pp->height) {
615 pp->ys = pp->xs = MAX(MIN(pp->width/LEVWIDTH,
616 pp->height/LEVHEIGHT), 1);
619 pp->ys = (short)(NRAND( MIN( -size, MAX( MINSIZE,
620 MIN( pp->width, pp->height) / MINGRIDSIZE))
621 - MINSIZE + 1) + MINSIZE);
622 else if (size < MINSIZE)
625 pp->ys = (short)(MIN(size,
626 MAX(MINSIZE, MIN(pp->width, pp->height) /
631 pp->wallwidth = (unsigned int)(pp->xs + pp->ys) >> 4;
632 if (pp->wallwidth < 1) pp->wallwidth = 1;
633 pp->incx = (pp->xs >> 3) + 1;
634 pp->incy = (pp->ys >> 3) + 1;
635 pp->ncols = (unsigned short)MAX(LEVWIDTH, 2);
636 pp->nrows = (unsigned short)MAX(LEVHEIGHT, 2);
637 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
638 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
639 pp->spritexs = MAX(pp->xs + (pp->xs >> 1) - 1, 1);
640 pp->spriteys = MAX(pp->ys + (pp->ys >> 1) - 1, 1);
641 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
642 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
644 if ((pp->ghostPixmap = XCreatePixmap(display, window,
645 pp->spritexs, pp->spriteys, 1)) == None) {
646 free_pacman(display, pp);
652 if ((bg_gc = XCreateGC(display, pp->ghostPixmap,
653 GCForeground | GCBackground, &gcv)) == None) {
654 free_pacman(display, pp);
660 if ((fg_gc = XCreateGC(display, pp->ghostPixmap,
661 GCForeground | GCBackground, &gcv)) == None) {
662 XFreeGC(display, bg_gc);
663 free_pacman(display, pp);
667 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
669 /* draw the triangles on the bottom (scalable) */
670 SETPOINT(points[0], 1, pp->spriteys * 5 / 6);
671 SETPOINT(points[1], pp->spritexs / 6, pp->spriteys);
672 SETPOINT(points[2], pp->spritexs / 3, pp->spriteys * 5 / 6);
673 SETPOINT(points[3], pp->spritexs / 2, pp->spriteys);
674 SETPOINT(points[4], pp->spritexs * 2 / 3, pp->spriteys * 5 / 6);
675 SETPOINT(points[5], pp->spritexs * 5 / 6, pp->spriteys);
676 SETPOINT(points[6], pp->spritexs, pp->spriteys * 5 / 6);
677 SETPOINT(points[7], pp->spritexs, pp->spriteys / 2);
678 SETPOINT(points[8], 1, pp->spriteys / 2);
680 XFillRectangle(display, pp->ghostPixmap, bg_gc,
681 0, 0, pp->spritexs, pp->spriteys);
682 XFillArc(display, pp->ghostPixmap, fg_gc,
683 0, 0, pp->spritexs, pp->spriteys, 0, 11520);
684 XFillPolygon(display, pp->ghostPixmap, fg_gc,
685 points, 9, Nonconvex, CoordModeOrigin);
686 XFreeGC(display, bg_gc);
687 XFreeGC(display, fg_gc);
689 if (!pp->stippledGC) {
690 gcv.foreground = MI_BLACK_PIXEL(mi);
691 gcv.background = MI_BLACK_PIXEL(mi);
692 if ((pp->stippledGC = XCreateGC(display, window,
693 GCForeground | GCBackground, &gcv)) == None) {
694 free_pacman(display, pp);
698 if (pp->pacmanPixmap[0][0] != None)
699 for (dir = 0; dir < 4; dir++)
700 for (mouth = 0; mouth < MAXMOUTH; mouth++)
702 pp->pacmanPixmap[dir]
705 for (dir = 0; dir < 4; dir++)
706 for (mouth = 0; mouth < MAXMOUTH; mouth++) {
707 if ((pp->pacmanPixmap[dir][mouth] = XCreatePixmap(
708 display, MI_WINDOW(mi),
709 pp->spritexs, pp->spriteys, 1)) ==
711 free_pacman(display, pp);
716 if ((fg_gc = XCreateGC(display, pp->pacmanPixmap[dir][mouth],
717 GCForeground | GCBackground, &gcv)) == None) {
718 free_pacman(display, pp);
723 if ((bg_gc = XCreateGC(display,
724 pp->pacmanPixmap[dir][mouth],
726 GCBackground, &gcv)) ==
728 XFreeGC(display, fg_gc);
729 free_pacman(display, pp);
732 XFillRectangle(display,
733 pp->pacmanPixmap[dir][mouth], bg_gc,
734 0, 0, pp->spritexs, pp->spriteys);
735 if (pp->spritexs == 1 && pp->spriteys == 1)
736 XFillRectangle(display,
737 pp->pacmanPixmap[dir][mouth],
738 fg_gc, 0, 0, pp->spritexs,
742 pp->pacmanPixmap[dir][mouth],
744 0, 0, pp->spritexs, pp->spriteys,
745 ((90 - dir * 90) + mouth * 5) * 64,
746 (360 + (-2 * mouth * 5)) * 64);
747 XFreeGC(display, fg_gc);
748 XFreeGC(display, bg_gc);
751 pp->pacman.lastbox = START;
752 pp->pacman.mouthdirection = 1;
753 pp->pacman.nextcol = NOWHERE;
754 pp->pacman.nextrow = NOWHERE;
756 if (pp->ghosts != NULL) {
758 pp->ghosts = (ghoststruct *) NULL;
760 pp->nghosts = GHOSTS;
763 if ((pp->ghosts = (ghoststruct *) calloc((size_t)pp->nghosts,
764 sizeof (ghoststruct))) == NULL) {
765 free_pacman(display, pp);
769 pp->pacman.mouthstage = MAXMOUTH - 1;
775 /* Callback function called for each tick. This is the complete machinery of
776 everything that moves. */
778 draw_pacman(ModeInfo * mi)
781 pacmangamestruct *pp;
783 if (pacmangames == NULL)
785 pp = &pacmangames[MI_SCREEN(mi)];
786 if (pp->ghosts == NULL)
789 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
790 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
791 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
792 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
794 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
795 pac_update(mi, pp, &(pp->pacman));
797 pp->pacman.delta.x = pp->incx;
798 pp->pacman.delta.y = pp->incy;
801 if (pp->pacman.delta.x > pp->xs + pp->incx)
802 pp->pacman.delta.x = pp->xs + pp->incx;
803 if (pp->pacman.delta.y > pp->ys + pp->incy)
804 pp->pacman.delta.y = pp->ys + pp->incy;
806 for (g = 0; g < pp->nghosts; g++) {
807 if (pp->ghosts[g].dead == True) continue;
809 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) %
811 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) %
813 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ?
815 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ?
818 if (pp->ghosts[g].delta.x >= pp->xs &&
819 pp->ghosts[g].delta.y >= pp->ys) {
820 ghost_update(pp, &(pp->ghosts[g]));
821 pp->ghosts[g].delta.x = pp->incx;
822 pp->ghosts[g].delta.y = pp->incy;
825 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
826 pp->ghosts[g].delta.x = pp->xs + pp->incx;
827 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
828 pp->ghosts[g].delta.y = pp->ys + pp->incy;
834 /* Releases resources. */
836 release_pacman(ModeInfo * mi)
838 if (pacmangames != NULL) {
841 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
842 free_pacman(MI_DISPLAY(mi), &pacmangames[screen]);
844 pacmangames = (pacmangamestruct *) NULL;
848 /* Refresh current level. */
850 refresh_pacman(ModeInfo * mi)
856 /* Callback to change level. */
858 change_pacman(ModeInfo * mi)
864 #endif /* MODE_pacman */