http://www.jwz.org/xscreensaver/xscreensaver-5.12.tar.gz
[xscreensaver] / hacks / pacman.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)pacman.c      5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
11  *
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.
17  *
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.
23  *
24  * Revision History:
25  * 25-Feb-2005: Added bonus dots. I am using a recursive back track algorithm
26  *              to help the ghost find there way home. This also means that
27  *              they do not know the shorts path.
28  *              Jeremy English jhe@jeremyenglish.org
29  * 15-Aug-2004: Added support for pixmap pacman. 
30  *              Jeremy English jhe@jeremyenglish.org
31  * 11-Aug-2004: Added support for pixmap ghost.
32  *              Jeremy English jhe@jeremyenglish.org
33  * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.  
34  *              splitted up code into several files.  Retouched AI code, cleaned
35  *              up code.
36  *  3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
37  * 26-Nov-2001: Random level generator added
38  * 01-Nov-2000: Allocation checks
39  * 04-Jun-1997: Compatible with xscreensaver
40  *
41  */
42
43 /* TODO:
44    1. think of a better level generation algorithm
45 */
46
47 #define DEF_TRACKMOUSE "False"
48
49 #ifdef STANDALONE
50 #       define MODE_pacman
51 #       define DEFAULTS "*delay:   10000 \n" \
52                                         "*size:    0     \n" \
53                                         "*ncolors: 6     \n" \
54                                         "*fpsTop: true   \n" \
55                                         "*fpsSolid: true \n" \
56
57 #       define UNIFORM_COLORS
58 #       define BRIGHT_COLORS
59 #   define reshape_pacman 0
60 #   define pacman_handle_event 0
61 #       include "xlockmore.h"   /* in xscreensaver distribution */
62 #   include <assert.h>
63 #else /* STANDALONE */
64 #       include "xlock.h"       /* in xlockmore distribution */
65 #endif /* STANDALONE */
66
67 #ifdef MODE_pacman
68
69 #include "pacman.h"
70 #include "pacman_ai.h"
71 #include "pacman_level.h"
72
73 #if defined(USE_PIXMAP) /* computed in pacman.h */
74 # include "images/pacman/ghost-u1.xpm"
75 # include "images/pacman/ghost-u2.xpm"
76 # include "images/pacman/ghost-r1.xpm"
77 # include "images/pacman/ghost-r2.xpm"
78 # include "images/pacman/ghost-l1.xpm"
79 # include "images/pacman/ghost-l2.xpm"
80 # include "images/pacman/ghost-d1.xpm"
81 # include "images/pacman/ghost-d2.xpm"
82 /* Used to clean up the dust left by wag. */
83 # include "images/pacman/ghost-mask.xpm"
84 # include "images/pacman/pacman-u1.xpm"
85 # include "images/pacman/pacman-u2.xpm"
86 # include "images/pacman/pacman-r1.xpm"
87 # include "images/pacman/pacman-r2.xpm"
88 # include "images/pacman/pacman-l1.xpm"
89 # include "images/pacman/pacman-l2.xpm"
90 # include "images/pacman/pacman-d1.xpm"
91 # include "images/pacman/pacman-d2.xpm"
92 # include "images/pacman/pacman-0.xpm"
93 # include "images/pacman/ghost-s1.xpm"
94 # include "images/pacman/ghost-s2.xpm"
95 # include "images/pacman/ghost-sf1.xpm"
96 # include "images/pacman/ghost-sf2.xpm"
97 # include "images/pacman/eyes-l.xpm"
98 # include "images/pacman/eyes-r.xpm"
99 # include "images/pacman/eyes-u.xpm"
100 # include "images/pacman/eyes-d.xpm"
101 # include "images/pacman/pacman-ds1.xpm"
102 # include "images/pacman/pacman-ds2.xpm"
103 # include "images/pacman/pacman-ds3.xpm"
104 # include "images/pacman/pacman-ds4.xpm"
105 # include "images/pacman/pacman-ds5.xpm"
106 # include "images/pacman/pacman-ds6.xpm"
107 # include "images/pacman/pacman-ds7.xpm"
108 # include "images/pacman/pacman-ds8.xpm"
109 #endif
110
111 static const struct
112 {
113     int dx, dy;
114 } dirvecs[DIRVECS] = { { -1, 0},
115                        {  0, 1},
116                        {  1, 0},
117                        {  0, -1}};
118
119 #ifdef DISABLE_INTERACTIVE
120 ENTRYPOINT ModeSpecOpt pacman_opts = {
121     0,
122     (XrmOptionDescRec *) NULL,
123     0,
124     (argtype *) NULL,
125     (OptionStruct *) NULL
126 };
127 #else
128 static XrmOptionDescRec opts[] = {
129     {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
130     {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
131 };
132
133 static argtype vars[] = {
134     {&trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
135 };
136
137 static OptionStruct desc[] = {
138     {"-/+trackmouse", "turn on/off the tracking of the mouse"}
139 };
140
141 ENTRYPOINT ModeSpecOpt pacman_opts =
142     { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars,
143 desc };
144 #endif
145
146 #ifdef USE_MODULES
147 ModStruct pacman_description = {
148     "pacman",                   /* *cmdline_arg; */
149     "init_pacman",              /* *init_name; */
150     "draw_pacman",              /* *callback_name; */
151     "release_pacman",           /* *release_name; */
152     "refresh_pacman",           /* *refresh_name; */
153     "change_pacman",            /* *change_name; */
154     (char *) NULL,              /* *unused_name; */
155     &pacman_opts,               /* *msopts */
156     10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
157 };
158
159 #endif
160
161 Bool trackmouse;
162 pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;
163
164 static void repopulate (ModeInfo * mi);
165 static void drawlevel (ModeInfo * mi);
166
167
168 static void
169 free_pacman (Display * display, pacmangamestruct * pp)
170 {
171     int dir, mouth, i, j, k;
172
173     if (pp->ghosts != NULL) {
174         free (pp->ghosts);
175         pp->ghosts = (ghoststruct *) NULL;
176     }
177     if (pp->stippledGC != None) {
178         XFreeGC (display, pp->stippledGC);
179         pp->stippledGC = None;
180     }
181     for (i = 0; i < 4; i++) {
182         for (j = 0; j < MAXGDIR; j++) {
183             for (k = 0; k < MAXGWAG; k++) {
184                 if (pp->ghostPixmap[i][j][k] != None) {
185                     XFreePixmap (display, pp->ghostPixmap[i][j][k]);
186                     pp->ghostPixmap[i][j][k] = None;
187                 }
188             }
189         }
190     }
191     for (dir = 0; dir < 4; dir++)
192         for (mouth = 0; mouth < MAXMOUTH; mouth++)
193             if (pp->pacmanPixmap[dir][mouth] != None) {
194                 XFreePixmap (display, pp->pacmanPixmap[dir][mouth]);
195                 pp->pacmanPixmap[dir][mouth] = None;
196             }
197 }
198
199 /* set pacman and the ghost in there starting positions, but don't draw a new
200  level */
201 static void
202 reset_level (ModeInfo * mi, int n, int pac_init)
203 {
204     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
205     unsigned int ghost;
206
207     MI_CLEARWINDOW (mi);
208     drawlevel (mi);
209
210     pp->gamestate = GHOST_DANGER;
211
212     if ( pac_init ){
213         pp->pacman.row = (LEVHEIGHT + JAILHEIGHT) / 2 - n;
214         pp->pacman.init_row = pp->pacman.row;
215     }
216     else{
217         pp->pacman.row = pp->pacman.init_row;
218     }
219     pp->pacman.col = (LEVWIDTH / 2);
220     pp->pacman.nextrow = NOWHERE;
221     pp->pacman.nextcol = NOWHERE;
222     pp->pacman.cf = NOWHERE;
223     pp->pacman.rf = NOWHERE;
224     pp->pacman.oldcf = NOWHERE;
225     pp->pacman.oldrf = NOWHERE;
226     pp->pacman.oldlx = NOWHERE;
227     pp->pacman.oldly = NOWHERE;
228     pp->pacman.aistate = ps_eating;
229     pp->pacman.cur_trace = 0;
230     pp->pacman.roundscore = 0;
231     pp->pacman.speed = 4;
232     pp->pacman.lastturn = 0;
233     pp->pacman.delta.x = 0;
234     pp->pacman.delta.y = 0;
235
236     for (ghost = 0; ghost < pp->nghosts; ghost++) {
237         pp->ghosts[ghost].col = (LEVWIDTH / 2);
238         pp->ghosts[ghost].row = (LEVHEIGHT / 2);
239         pp->ghosts[ghost].nextcol = NOWHERE;
240         pp->ghosts[ghost].nextrow = NOWHERE;
241         pp->ghosts[ghost].dead = 0;
242         pp->ghosts[ghost].lastbox = START;
243         pp->ghosts[ghost].cf = NOWHERE;
244         pp->ghosts[ghost].rf = NOWHERE;
245         pp->ghosts[ghost].oldcf = NOWHERE;
246         pp->ghosts[ghost].oldrf = NOWHERE;
247         pp->ghosts[ghost].aistate = inbox;
248         pp->ghosts[ghost].timeleft = ghost * 20;
249         pp->ghosts[ghost].speed = 3;
250         pp->ghosts[ghost].delta.x = 0;
251         pp->ghosts[ghost].delta.y = 0;
252         pp->ghosts[ghost].flash_scared = False;
253         pp->ghosts[ghost].wait_pos = False;
254         ghost_update (pp, &(pp->ghosts[ghost]));
255     }
256     pac_update (mi, pp, &(pp->pacman));
257 }
258
259 static int
260 pacman_ghost_collision(unsigned int ghost, pacmangamestruct * pp)
261 {
262     return (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
263              (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
264             ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
265              (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
266              (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
267              (pp->ghosts[ghost].col == pp->pacman.nextcol)));
268 }
269
270
271 /* Checks for death of any ghosts/pacman and updates.  It also makes a new
272    level if all ghosts are dead or all dots are eaten. */
273 static void
274 check_death (ModeInfo * mi, pacmangamestruct * pp)
275 {
276     Display *display = MI_DISPLAY (mi);
277     Window window = MI_WINDOW (mi);
278     unsigned int ghost;
279
280     if (pp->pacman.aistate == ps_dieing) return;
281
282     for (ghost = 0; ghost < pp->nghosts; ghost++) {
283
284         /* The ghost have to be scared before you can kill them */
285         if ( pacman_ghost_collision ( ghost, pp ) ) {
286             if (pp->ghosts[ghost].aistate == goingin) continue;
287
288             if (pp->ghosts[ghost].aistate == hiding) {
289                 pp->ghosts[ghost].dead = 1;
290                 pp->ghosts[ghost].aistate = goingin;
291                 pp->ghosts[ghost].wait_pos = True;
292                 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
293                 XFillRectangle (display, window,
294                                 pp->stippledGC,
295                                 pp->ghosts[ghost].cf,
296                                 pp->ghosts[ghost].rf,
297                                 pp->spritexs, pp->spriteys);
298             }
299             /* DIE PACMAN... */
300             else {
301                 pp->pacman.deaths++;
302                 pp->pacman.aistate = ps_dieing;
303
304             }
305             continue;
306         }
307     }
308 }
309
310 /* Resets state of ghosts + pacman.  Creates a new level, draws that level. */
311 static void
312 repopulate (ModeInfo * mi)
313 {
314     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
315     pp->pacman.deaths = 0;
316     reset_level (mi, createnewlevel (pp), True);
317     check_death (mi, pp);
318 }
319
320 /* Sets the color to the color of a wall. */
321 static void
322 setwallcolor (ModeInfo * mi)
323 {
324     Display *display = MI_DISPLAY (mi);
325     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
326
327     if (MI_NPIXELS (mi) > 2)
328         XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, BLUE));
329     else
330         XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
331 }
332
333 /* Sets the color to the color of a dot. */
334 static void
335 setdotcolor (ModeInfo * mi)
336 {
337     Display *display = MI_DISPLAY (mi);
338     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
339
340     XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
341 }
342
343 static void
344 cleardotcolor (ModeInfo * mi)
345 {
346     Display *display = MI_DISPLAY (mi);
347     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
348
349     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
350 }
351
352 #if 0
353 static void
354 draw_position (ModeInfo * mi, int x, int y, int color)
355 {
356     Display *display = MI_DISPLAY (mi);
357     Window window = MI_WINDOW (mi);
358     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
359     XFontStruct *font = NULL;
360     char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
361     char *s = NULL;
362
363     font = XLoadQueryFont (display, f_name);
364     assert (font != NULL);
365
366     s = (char *) malloc (256);
367     assert (s != NULL);
368     sprintf (s, "(%d,%d)", x, y);
369     XSetForeground (display, pp->stippledGC, color);
370     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
371     free (s);
372     free (font);
373 }
374 #endif
375 #if 0
376 static void
377 draw_number (ModeInfo * mi, int x, int y, int num, int color)
378 {
379     Display *display = MI_DISPLAY (mi);
380     Window window = MI_WINDOW (mi);
381     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
382     XFontStruct *font = NULL;
383     char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
384     char *s = NULL;
385
386     font = XLoadQueryFont (display, f_name);
387     assert (font != NULL);
388
389     s = (char *) malloc (256);
390     assert (s != NULL);
391     sprintf (s, "%d", num);
392     XSetForeground (display, pp->stippledGC, color);
393     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
394     free (s);
395     free (font);
396 }
397 #endif
398
399 #if 0
400 /* draw_grid - draws a grid on top of the playing field.
401  * Used so that I can determine if I'm converting from rows and columns to x and y 
402  * coordinates correctly.
403  */
404 static void
405 draw_grid (ModeInfo * mi)
406 {
407     Display *display = MI_DISPLAY (mi);
408     Window window = MI_WINDOW (mi);
409     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
410     int h = MI_HEIGHT (mi);
411     int w = MI_WIDTH (mi);
412     int y = 0;
413     int x = 0;
414     XSetForeground (display, pp->stippledGC, 0xff0000);
415     while (y < h) {
416         while (x < w) {
417             XDrawLine (display, window, pp->stippledGC, x, 0, x, h);
418             x += 10;
419         }
420         x = 0;
421         XDrawLine (display, window, pp->stippledGC, 0, y, w, y);
422         y += 10;
423     }
424 }
425 #endif
426
427 #if 0
428 static void
429 draw_string (ModeInfo * mi, int x, int y, char *s, int color)
430 {
431     Display *display = MI_DISPLAY (mi);
432     Window window = MI_WINDOW (mi);
433     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
434     XFontStruct *font = NULL;
435     char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
436
437     font = XLoadQueryFont (display, f_name);
438     assert (font != NULL);
439
440     assert (s != NULL);
441     XSetForeground (display, pp->stippledGC, color);
442     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
443     free (font);
444 }
445
446 /* I think this function has a memory leak. Be careful if you enable it. */
447 /* I only used it briefly to help me debug the ghost's aistate. It prints */
448 /* the state of each ghost on the left hand side of the screen */
449 static void
450 print_ghost_stats (ModeInfo *mi, ghoststruct *g , int ghost_num)
451 {
452     char s[1024];
453
454     sprintf (s, "GHOST: %d", ghost_num ); 
455     switch (g->aistate){
456     case inbox: 
457         sprintf (s, "%s inbox", s);
458         break;
459     case goingout:
460         sprintf (s, "%s goingout", s);
461         break;
462     case randdir:
463         sprintf (s, "%s randdir", s);
464         break;
465     case chasing:
466         sprintf (s, "%s chasing", s);
467         break;
468     case hiding:
469         sprintf (s, "%s hiding", s);
470         break;
471     case goingin:
472         sprintf (s, "%s goingin",s);
473         break;
474     }
475     draw_string (mi, 0, (ghost_num *3) *10+50, g->last_stat, 0x000000);
476     draw_string (mi, 0, (ghost_num *3) *10+50, s, 0xff0000);
477     strcpy(g->last_stat,s);
478 }
479
480 /* prints the number of times pacman has died and his aistate on the left hand */
481 /* side of the screen */
482 static void
483 print_pac_stats ( ModeInfo *mi, pacmanstruct *pac )
484 {
485     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
486     char s[1024];
487     sprintf (s, "Pacman, Deaths: %d", pac->deaths );
488     switch ( pac->aistate ){
489     case ps_eating:
490         sprintf(s, "%s ps_eating",s );
491         break;
492     case ps_chasing:
493         sprintf(s, "%s ps_chasing",s );
494         break;
495     case ps_hiding:
496         sprintf(s, "%s ps_hiding",s );
497         break;
498     case ps_random:
499         sprintf(s, "%s ps_random",s );
500         break;
501     case ps_dieing:
502         sprintf(s, "%s ps_dieing",s );
503         break;
504     }
505     draw_string ( mi, 0, 200, pp->last_pac_stat, 0x000000);
506     draw_string ( mi, 0, 200, s, 0xff0000);
507     strcpy(pp->last_pac_stat, s );
508 }
509
510 #endif
511
512 /*Ok, yeah whatever?*/
513 /*dot_rc_to_pixel - magic that converts row and columns into
514  *the x and y coordinates of the screen.
515  */
516 static void
517 dot_rc_to_pixel (ModeInfo * mi, int *x, int *y)
518 {
519     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
520     *x = (pp->xs * *x) +
521         (pp->xs / 2) - (pp->xs > 32 ? (pp->xs / 16) : 1) + pp->xb;
522     *y = (pp->ys * *y) +
523         (pp->ys / 2) - (pp->ys > 32 ? (pp->ys / 16) : 1) + pp->yb;
524 }
525
526 /* dot_width_height - magic used to get the width and height of
527  * a dot. This dot can also be scaled by a value.
528  */
529 static void
530 dot_width_height (ModeInfo *mi, int *w, int *h)
531 {
532     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
533     if (pp->xs > 32){
534         *w = *h = (pp->xs / 32 );
535     }else {
536         *w = *h = 1;
537     }
538 }
539
540 static void
541 bonus_dot_width_height (ModeInfo *mi, int *w, int *h )
542 {
543     *w = *h = MI_HEIGHT (mi) / 65;
544 }
545
546 \f
547
548 static void
549 draw_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y,
550           void (*width_height)(ModeInfo * mi, int *w, int *h), 
551           int (*arc_func) (Display * display, Drawable d, GC gc,
552                                       int x, int y, unsigned int width,
553                                       unsigned int height, int angle1,
554                                       int angle2))
555 {
556     Display *display = MI_DISPLAY (mi);
557     Window window = MI_WINDOW (mi);
558     int w, h;
559     dot_rc_to_pixel (mi, &x, &y);
560     width_height(mi, &w, &h);
561     (void) arc_func (display, window, pp->stippledGC,
562                      x, y, w, h, 0, 23040);
563 }
564
565 static void
566 draw_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
567 {
568     int x2 = x;
569     int y2 = y;
570     setdotcolor (mi);
571     draw_dot (mi, pp, x, y, bonus_dot_width_height,  XFillArc);
572     dot_rc_to_pixel (mi, &x2, &y2);
573 #if 0
574     draw_position (mi, x2, y2, 0xff0000);
575 #endif
576 }
577
578 static void
579 clear_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
580 {
581     cleardotcolor (mi);
582     draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
583 }
584
585 static void
586 draw_regular_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
587 {
588     setdotcolor (mi);
589     draw_dot (mi, pp, x, y, dot_width_height, XDrawArc);
590 }
591
592 /* Draws a block in the level at the specified x and y locations. */
593 static void
594 drawlevelblock (ModeInfo * mi, pacmangamestruct * pp,
595                 const unsigned x, const unsigned y)
596 {
597     Display *display = MI_DISPLAY (mi);
598     Window window = MI_WINDOW (mi);
599     int dx = 0, dy = 0;
600
601     if (pp->xs % 2 == 1)
602         dx = -1;
603     if (pp->ys % 2 == 1)
604         dy = -1;
605
606 #ifndef HAVE_COCOA
607     XSetFillStyle (display, pp->stippledGC, FillSolid);
608 #endif /* !HAVE_COCOA */
609     XSetLineAttributes (display, pp->stippledGC, pp->wallwidth,
610                         LineSolid, CapRound, JoinMiter);
611
612     if (pp->xs < 2 || pp->ys < 2) {
613         switch (pp->level[y * LEVWIDTH + x]) {
614         case ' ':
615         case '=':
616             break;
617         case '.':
618             setdotcolor (mi);
619             (void) XDrawPoint (display, window,
620                                pp->stippledGC,
621                                x * pp->xs + pp->xb, y * pp->ys + pp->yb);
622             break;
623         default:
624             setwallcolor (mi);
625             (void) XDrawPoint (display, window,
626                                pp->stippledGC,
627                                x * pp->xs + pp->xb, y * pp->ys + pp->yb);
628         }
629
630         return;
631     }
632
633     switch (pp->level[y * LEVWIDTH + x]) {
634     case ' ':
635     case '=':
636         break;
637
638     case '.':
639         setdotcolor (mi);
640         if (pp->xs < 8 || pp->ys < 8) {
641             (void) XDrawPoint (display, window,
642                                pp->stippledGC,
643                                x * pp->xs + pp->xb +
644                                pp->xs / 2, y * pp->ys + pp->yb + pp->ys / 2);
645             break;
646         }
647
648         draw_regular_dot (mi, pp, x, y);
649         break;
650         /* What we will probably want to do here is have the pp->level store a 'o' for
651          * the bonus dots. The we can use the drawing routine above just with a bigger
652          * radius.
653          */
654     case 'o':
655         draw_bonus_dot (mi, pp, x, y);
656         break;
657
658
659     case '-':
660         setwallcolor (mi);
661         (void) XDrawLine (display, window, pp->stippledGC,
662                           (pp->xs * x) + pp->xb,
663                           (pp->ys * y) + (pp->ys / 2) + pp->yb,
664                           (pp->xs * (x + 1)) + pp->xb,
665                           (pp->ys * y) + (pp->ys / 2) + pp->yb);
666         break;
667
668     case '|':
669         setwallcolor (mi);
670         (void) XDrawLine (display, window, pp->stippledGC,
671                           (pp->xs * x) + (pp->xs / 2) + pp->xb,
672                           (pp->ys * y) + pp->yb,
673                           (pp->xs * x) + (pp->xs / 2) + pp->xb,
674                           (pp->ys * (y + 1)) + pp->yb);
675         break;
676
677     case '_':
678         setwallcolor (mi);
679         (void) XDrawArc (display, window, pp->stippledGC,
680                          (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
681                          (pp->ys * y) + (pp->ys / 2) + pp->yb,
682                          pp->xs, pp->ys, 0 * 64, 90 * 64);
683         break;
684
685     case ',':
686         setwallcolor (mi);
687         (void) XDrawArc (display, window, pp->stippledGC,
688                          (pp->xs * x) + (pp->ys / 2) + pp->xb,
689                          (pp->ys * y) + (pp->ys / 2) + pp->yb,
690                          pp->xs, pp->ys, 90 * 64, 90 * 64);
691         break;
692
693     case '`':
694         setwallcolor (mi);
695         (void) XDrawArc (display, window, pp->stippledGC,
696                          (pp->xs * x) + (pp->ys / 2) + pp->xb,
697                          (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
698                          pp->xs, pp->ys, 180 * 64, 90 * 64);
699         break;
700
701     case '\'':
702         setwallcolor (mi);
703         (void) XDrawArc (display, window, pp->stippledGC,
704                          (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
705                          (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
706                          pp->xs, pp->ys, 270 * 64, 90 * 64);
707         break;
708
709     }
710 }
711
712 /* Draws a complete level. */
713 static void
714 drawlevel (ModeInfo * mi)
715 {
716     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
717     unsigned int x, y;
718
719     for (y = 0; y < LEVHEIGHT; y++)
720         for (x = 0; x < LEVWIDTH; x++)
721             drawlevelblock (mi, pp, x, y);
722 }
723
724 /* There is some overlap so it can be made more efficient */
725 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
726  if (yl<y) \
727   (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
728   XFillRectangle(d,w,g,xl,yl,xs,ys); \
729  else if (yl>y) \
730   (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
731   XFillRectangle(d,w,g,xl,yl,xs,ys); \
732  if (xl<x) \
733   (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
734   XFillRectangle(d,w,g,xl,yl,xs,ys); \
735  else if (xl>x) \
736   (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
737   XFillRectangle(d,w,g,xl,yl,xs,ys)
738
739
740 /* Draws the pacman sprite, removing the previous location. */
741 #if defined(USE_PIXMAP)
742
743 static void
744 draw_pacman_sprite (ModeInfo * mi)
745 {
746     Display *display = MI_DISPLAY (mi);
747     Window window = MI_WINDOW (mi);
748     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
749     unsigned int dir = 0;
750     int old_mask_dir = 0;
751     int old_mask_mouth = 0;
752     Pixmap old_mask, new_mask;
753     Pixmap pacman;
754
755 #define MAX_MOUTH_DELAY 2
756 #define MAX_DEATH_DELAY 20
757
758     if (pp->pacman.aistate == ps_dieing){
759         pp->pacman.cf = pp->pacman.oldcf;
760         pp->pacman.rf = pp->pacman.oldrf;
761     }
762     else {
763         pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
764             pp->pacman.cfactor + pp->xb + pp->spritedx;
765         pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
766             pp->pacman.rfactor + pp->yb + pp->spritedy;
767     }
768
769     dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
770            ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
771
772     if (pp->pm_mouth_delay == MAX_MOUTH_DELAY) {
773         if (pp->pm_mouth == (MAXMOUTH - 1) || pp->pm_mouth == 0) {
774             pp->pm_open_mouth = !pp->pm_open_mouth;
775         }
776         pp->pm_open_mouth ? pp->pm_mouth++ : pp->pm_mouth--;
777         pp->pm_mouth_delay = 0;
778     }
779     else {
780         pp->pm_mouth_delay++;
781     }
782     
783     if (pp->pacman.aistate == ps_dieing){
784         if (pp->pm_death_frame >= PAC_DEATH_FRAMES) {
785             pp->pacman.aistate = ps_eating;
786             pp->pm_death_frame = 0;
787             pp->pm_death_delay = 0;
788             reset_level (mi, 0, False);
789             return;
790         }
791         else {
792             old_mask   = pp->pacmanMask[0][0];
793             new_mask   = pp->pacmanMask[0][0];
794             pacman     = pp->pacman_ds[pp->pm_death_frame];
795             if (pp->pm_death_delay == MAX_DEATH_DELAY){
796                 pp->pm_death_frame++;
797                 pp->pm_death_delay = 0;
798             }
799             else{
800                 pp->pm_death_delay++;
801             }
802         }
803     }
804     else{
805         old_mask = pp->pacmanMask[old_mask_dir][old_mask_mouth];
806         new_mask = pp->pacmanMask[dir][pp->pm_mouth];
807         pacman   = pp->pacmanPixmap[dir][pp->pm_mouth];
808     }
809
810     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
811
812     XSetClipMask (display, pp->stippledGC, old_mask);
813                   
814     XSetClipOrigin (display, pp->stippledGC, pp->pacman.oldcf,
815                     pp->pacman.oldrf);
816     XFillRectangle (display, window, pp->stippledGC, pp->pacman.oldcf,
817                     pp->pacman.oldrf, pp->spritexs, pp->spriteys);
818     XSetClipMask (display, pp->stippledGC, new_mask);
819     XSetClipOrigin (display, pp->stippledGC, pp->pacman.cf, pp->pacman.rf);
820     XCopyArea (display, pacman, window,
821                pp->stippledGC, 0, 0, pp->spritexs, pp->spriteys,
822                pp->pacman.cf, pp->pacman.rf);
823     XSetClipMask (display, pp->stippledGC, None);
824     if (pp->pacman.aistate != ps_dieing){
825         pp->pacman.oldcf = pp->pacman.cf;
826         pp->pacman.oldrf = pp->pacman.rf;
827     }
828 }
829
830 #if 0
831 static void
832 draw_ghost_position (ModeInfo * mi, ghoststruct * ghost)
833 {
834     draw_position (mi, ghost->oldcf, ghost->oldrf, MI_BLACK_PIXEL (mi));
835     draw_position (mi, ghost->cf, ghost->rf, 0x00ff00);
836 }
837 #endif
838 #if 0
839 static void
840 draw_ghost_ndirs ( ModeInfo *mi, ghoststruct * ghost)
841 {
842     draw_number (mi, ghost->oldcf, ghost->oldrf, ghost->oldndirs, MI_BLACK_PIXEL (mi));
843     ghost->oldndirs = ghost->ndirs;
844     draw_number (mi, ghost->cf, ghost->rf, ghost->ndirs, 0x00ff00);
845 }
846
847 #endif
848
849 static void
850 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
851 {
852     Display *display = MI_DISPLAY (mi);
853     Window window = MI_WINDOW (mi);
854     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
855 #define MAX_WAG_COUNT 50
856     unsigned int dir = 0;
857     unsigned int fs  = 0; /*flash scared*/
858     Pixmap g_pix; /*ghost pixmap*/
859
860     
861     dir = (ABS (pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
862            ABS (pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
863                                              
864
865     fs = pp->ghosts[ghost].flash_scared;
866     assert (fs == 0 || fs == 1);
867
868     /* Choose the pixmap */
869     switch (pp->ghosts[ghost].aistate){
870     case hiding:
871         g_pix = pp->s_ghostPixmap[fs][pp->gh_wag];
872         break;
873     case goingin:
874         g_pix = pp->ghostEyes[dir];
875 #if 1
876         {
877             int i = 0;
878             while ( i < pp->ghosts[ghost].trace_idx ){
879                 XFillRectangle (display,
880                                 window,
881                                 pp->stippledGC,
882                                 pp->ghosts[ghost].trace[i].vx,
883                                 pp->ghosts[ghost].trace[i].vy, 
884                                 pp->spritexs, pp->spriteys);
885
886                 i++;
887             }
888         }
889 #endif
890                 
891         break;
892     default:
893         g_pix = pp->ghostPixmap[ghost][dir][pp->gh_wag];
894     }
895
896     pp->ghosts[ghost].cf =
897         pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
898         pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
899     pp->ghosts[ghost].rf =
900         pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
901         pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
902
903     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
904
905     XSetClipMask (display, pp->stippledGC, pp->ghostMask);
906     XSetClipOrigin (display, pp->stippledGC,
907                     pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
908     XFillRectangle (display,
909                     window,
910                     pp->stippledGC,
911                     pp->ghosts[ghost].oldcf,
912                     pp->ghosts[ghost].oldrf, pp->spritexs, pp->spriteys);
913
914     
915     if (pp->pacman.aistate != ps_dieing) {
916         drawlevelblock (mi, pp,
917                         (unsigned int) pp->ghosts[ghost].col,
918                         (unsigned int) pp->ghosts[ghost].row);
919
920
921
922         XSetClipOrigin (display, pp->stippledGC,
923                         pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
924
925         XCopyArea (display, g_pix, window, pp->stippledGC, 0, 0,
926                    pp->spritexs, pp->spriteys, pp->ghosts[ghost].cf,
927                    pp->ghosts[ghost].rf);
928     }
929     XSetClipMask (display, pp->stippledGC, None);
930
931 #if 0
932     draw_ghost_position (mi, &(pp->ghosts[ghost]));
933 #endif
934
935 #if 0
936     draw_ghost_ndirs ( mi, &(pp->ghosts[ghost]));
937 #endif
938     
939     if (pp->pacman.aistate != ps_dieing) {
940         pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
941         pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
942         if (pp->gh_wag_count++ == MAX_WAG_COUNT) {
943             pp->gh_wag = !pp->gh_wag;
944             pp->gh_wag_count = 0;
945         }
946     }
947 }
948
949 #else /* USE_PIXMAP */
950
951 /* Draws the pacman sprite, removing the previous location. */
952 static void
953 draw_pacman_sprite (ModeInfo * mi)
954 {
955     Display *display = MI_DISPLAY (mi);
956     Window window = MI_WINDOW (mi);
957     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
958     unsigned int dir;
959
960     pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
961         pp->pacman.cfactor + pp->xb + pp->spritedx;
962     pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
963         pp->pacman.rfactor + pp->yb + pp->spritedy;
964
965     dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
966            ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
967
968     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
969     if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
970
971         ERASE_IMAGE (display, window, pp->stippledGC,
972                      pp->pacman.cf, pp->pacman.rf,
973                      pp->pacman.oldcf, pp->pacman.oldrf,
974                      pp->spritexs, pp->spriteys);
975     }
976
977     if (MI_NPIXELS (mi) > 2)
978         XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, YELLOW));
979     else
980         XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
981
982     XSetFillStyle (display, pp->stippledGC, FillOpaqueStippled);
983
984     if (pp->xs < 2 || pp->ys < 2)
985         XDrawPoint (display, window, pp->stippledGC,
986                     pp->pacman.cf, pp->pacman.rf);
987     else
988         XFillRectangle (display, window, pp->stippledGC,
989                         pp->pacman.cf, pp->pacman.rf,
990                         pp->spritexs, pp->spriteys);
991     pp->pacman.mouthstage += pp->pacman.mouthdirection;
992     if ((pp->pacman.mouthstage >= MAXMOUTH) || (pp->pacman.mouthstage < 0)) {
993         pp->pacman.mouthdirection *= -1;
994         pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
995     }
996     pp->pacman.oldcf = pp->pacman.cf;
997     pp->pacman.oldrf = pp->pacman.rf;
998 }
999
1000 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
1001 static void
1002 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
1003 {
1004     Display *display = MI_DISPLAY (mi);
1005     Window window = MI_WINDOW (mi);
1006     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1007
1008     pp->ghosts[ghost].cf =
1009         pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
1010         pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
1011     pp->ghosts[ghost].rf =
1012         pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
1013         pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
1014
1015     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
1016     XFillRectangle (display,
1017                     window,
1018                     pp->stippledGC,
1019                     pp->ghosts[ghost].cf,
1020                     pp->ghosts[ghost].rf, pp->spritexs, pp->spriteys);
1021
1022     if (pp->ghosts[ghost].oldcf != NOWHERE ||
1023         pp->ghosts[ghost].oldrf != NOWHERE) {
1024
1025         ERASE_IMAGE (display, window, pp->stippledGC,
1026                      pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
1027                      pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf,
1028                      pp->spritexs, pp->spriteys);
1029     }
1030
1031     drawlevelblock (mi, pp,
1032                     (unsigned int) pp->ghosts[ghost].col,
1033                     (unsigned int) pp->ghosts[ghost].row);
1034
1035     if (MI_NPIXELS (mi) > 2)
1036         XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, GREEN));
1037     else
1038         XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
1039
1040     XSetFillStyle (display, pp->stippledGC, FillOpaqueStippled);
1041
1042     if (pp->xs < 2 || pp->ys < 2)
1043         XDrawPoint (display, window, pp->stippledGC,
1044                     pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
1045     else
1046         XFillRectangle (display,
1047                         window,
1048                         pp->stippledGC,
1049                         pp->ghosts[ghost].cf,
1050                         pp->ghosts[ghost].rf, pp->spritexs, pp->spriteys);
1051
1052     pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
1053     pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
1054 }
1055 #endif /* USE_PIXMAP */
1056
1057 static int
1058 ghost_over (ModeInfo * mi, int x, int y)
1059 {
1060     int ghost = 0;
1061     int ret = False;
1062     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1063     dot_rc_to_pixel (mi, &x, &y);
1064     for (ghost = 0; ghost < pp->nghosts; ghost++) {
1065         if ((pp->ghosts[ghost].cf <= x
1066              && x <= pp->ghosts[ghost].cf + pp->spritexs)
1067             && (pp->ghosts[ghost].rf <= y
1068                 && y <= pp->ghosts[ghost].rf + pp->spriteys)) {
1069             ret = True;
1070             break;
1071         }
1072     }
1073     return ret;
1074 }
1075 \f
1076
1077 static void
1078 flash_bonus_dots (ModeInfo * mi)
1079 {
1080 #define MAX_FLASH_COUNT 25
1081     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1082     int i, x, y;
1083     for (i = 0; i < NUM_BONUS_DOTS; i++) {
1084         if (!bonus_dot_eaten (pp, i)) {
1085             bonus_dot_pos (pp, i, &x, &y);
1086             if (ghost_over (mi, x, y))
1087                 continue;
1088             if (pp->bd_on)
1089                 draw_bonus_dot (mi, pp, x, y);
1090             else
1091                 clear_bonus_dot (mi, pp, x, y);
1092         }
1093     }
1094     if (pp->bd_flash_count-- == 0) {
1095         pp->bd_flash_count = MAX_FLASH_COUNT;
1096         pp->bd_on = !pp->bd_on;
1097     }
1098 }
1099
1100 static unsigned int
1101 ate_bonus_dot (ModeInfo * mi)
1102 {
1103     /*Check pacman's position. If it is over a bonus dot and that dot
1104      *has not been eaten, then return true
1105      */
1106     unsigned int ret = 0;
1107     int idx = 0;
1108     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1109     if (is_bonus_dot (pp, pp->pacman.col, pp->pacman.row, &idx)) {
1110         ret = !bonus_dot_eaten (pp, idx);
1111         eat_bonus_dot (pp, idx);
1112     }
1113     return ret;
1114 }
1115
1116 static void
1117 ghost_scared (ModeInfo * mi)
1118 {
1119     unsigned int ghost;
1120     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1121     for (ghost = 0; ghost < pp->nghosts; ghost++) {
1122         if (pp->ghosts[ghost].aistate == goingin || 
1123             pp->ghosts[ghost].aistate == goingout ||
1124             pp->ghosts[ghost].aistate == inbox ) continue;
1125         pp->ghosts[ghost].aistate = hiding;
1126         pp->ghosts[ghost].flash_scared = 0;
1127         if (pp->pacman.aistate != ps_dieing)
1128             pp->pacman.aistate = ps_chasing;
1129     }
1130 }
1131
1132 static void
1133 ghost_not_scared (ModeInfo * mi)
1134 {
1135     unsigned int ghost;
1136     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1137     for (ghost = 0; ghost < pp->nghosts; ghost++){
1138         if (pp->ghosts[ghost].aistate == goingin ||
1139             pp->ghosts[ghost].aistate == goingout ||
1140             pp->ghosts[ghost].aistate == inbox ) continue;
1141         pp->ghosts[ghost].aistate = chasing;
1142     }
1143     if (pp->pacman.aistate != ps_dieing)
1144         pp->pacman.aistate = ps_eating;
1145     
1146 }
1147
1148 static void
1149 ghost_flash_scared (ModeInfo * mi)
1150 {
1151     unsigned int ghost;
1152     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1153     for (ghost = 0; ghost < pp->nghosts; ghost++)
1154         pp->ghosts[ghost].flash_scared = !pp->ghosts[ghost].flash_scared;
1155 }
1156
1157 /* Does all drawing of moving sprites in the level. */
1158 static void
1159 pacman_tick (ModeInfo * mi)
1160 {
1161 #define DEFAULT_SCARED_TIME 500
1162 #define START_FLASH 200
1163 #define FLASH_COUNT 25
1164
1165     pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1166     unsigned int ghost;
1167 #if 0
1168     draw_grid (mi);
1169 #endif
1170     for (ghost = 0; ghost < pp->nghosts; ghost++) {
1171         draw_ghost_sprite (mi, ghost);
1172 #if 0
1173         print_ghost_stats (mi, &(pp->ghosts[ghost]), ghost);
1174 #endif
1175     }
1176 #if 0    
1177     print_pac_stats (mi, &(pp->pacman));
1178 #endif
1179     draw_pacman_sprite (mi);
1180     flash_bonus_dots (mi);
1181     if (ate_bonus_dot (mi)) {
1182         pp->ghost_scared_timer = (random () % 100) + DEFAULT_SCARED_TIME;
1183         ghost_scared (mi);
1184     }
1185
1186     if (pp->ghost_scared_timer > 0) {
1187         if (--pp->ghost_scared_timer == 0)
1188             ghost_not_scared (mi);
1189         else if (pp->ghost_scared_timer <= START_FLASH) {
1190             if (pp->flash_timer <= 0) {
1191                 pp->flash_timer = FLASH_COUNT;
1192                 ghost_flash_scared (mi);
1193             }
1194             pp->flash_timer--;
1195         }
1196     }
1197
1198     /*
1199       We don't want to miss the last death sequence. So if pacman has died three times
1200       we wait for his state to change from dieing to something else before we repopulate
1201       the level. If pacman ate all of the dots then we just repopulate.
1202     */
1203
1204     if (pp->dotsleft == 0 )
1205         repopulate (mi);
1206     else if (pp->pacman.deaths >= 3){
1207         if (pp->old_pac_state == ps_dieing && pp->pacman.aistate != ps_dieing)
1208             repopulate (mi);
1209     }
1210
1211     pp->old_pac_state = pp->pacman.aistate;
1212 }
1213 \f
1214
1215 /* CODE TO LOAD AND SCALE THE PIXMAPS
1216  */
1217
1218 #if defined(USE_PIXMAP)
1219 /*  Grabbed the scaling routine off of usenet. 
1220  *  Changed it so that the information specific 
1221  *  to the source pixmap does not have to be a parameter.
1222  *
1223  *  There is probably a better way to scale pixmaps.
1224  *  From: Chris Fiddyment (cxf@itd.dsto.gov.au)
1225  *  Subject: Scaling Pixmap Algorithm.
1226  *  Newsgroups: comp.graphics.algorithms
1227  *  Date: 1994-07-06 18:51:38 PST 
1228  *  -jeremy
1229  */
1230
1231 static Pixmap
1232 scale_pixmap (Display ** dpy, GC gc, Pixmap source, int dwidth, int dheight)
1233 {
1234     Pixmap temp, dest;
1235     int j, end;
1236     float i;
1237     float xscale, yscale;
1238     unsigned int swidth, sheight;
1239     Window window;
1240     int x, y;
1241     unsigned border_width_return, depth;
1242     XGetGeometry (*dpy, source, &window, &x, &y, &swidth, &sheight,
1243                   &border_width_return, &depth);
1244
1245     xscale = (float) swidth / (float) dwidth;   /* Scaling factors */
1246     yscale = (float) sheight / (float) dheight;
1247
1248     dest = XCreatePixmap (*dpy, window, dwidth, dheight, depth);
1249     if (!dest) {
1250         fprintf (stderr, "%s Could not scale image", progname);
1251     }
1252     temp = XCreatePixmap (*dpy, window, dwidth, sheight, depth);
1253     if (!temp) {
1254         fprintf (stderr, "%s Could not scale image", progname);
1255     }
1256
1257     j = 0;
1258     end = dwidth * xscale;
1259     /* Scale width of source into temp pixmap */
1260     for (i = 0; i <= end; i += xscale)
1261         XCopyArea (*dpy, source, temp, gc, i, 0, 1, sheight, j++, 0);
1262
1263     j = 0;
1264     end = dheight * yscale;
1265     /* Scale height of temp into dest pixmap */
1266     for (i = 0; i <= end; i += yscale)
1267         XCopyArea (*dpy, temp, dest, gc, 0, i, swidth, 1, 0, j++);
1268
1269     XFreePixmap (*dpy, temp);
1270     return (Pixmap) dest;
1271 }
1272
1273 static void
1274 pacman_fail (char *s)
1275 {
1276     fprintf (stderr, "%s: %s\n", progname, s);
1277     exit (1);
1278 }
1279
1280 /* Load the ghost pixmaps and their mask. */
1281 static void
1282 load_ghost_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1283 {
1284     pacmangamestruct *pp = *ps;
1285     Display *display = *dpy;
1286     char *colors[] = {
1287         ".      c #FF0000",         /*Red */
1288         ".  c #00FFDE",         /*Blue */
1289         ".  c #FFB847",         /*Orange */
1290         ".  c #FFB8DE",         /*Pink */
1291     };
1292
1293     char **bits[] = {
1294         ghost_u1_xpm, ghost_u2_xpm, ghost_r1_xpm, ghost_r2_xpm,
1295         ghost_d1_xpm, ghost_d2_xpm, ghost_l1_xpm, ghost_l2_xpm
1296     };
1297     char * const *s_bits[] = {
1298         ghost_s1_xpm, ghost_s2_xpm,
1299         ghost_sf1_xpm, ghost_sf2_xpm
1300     };
1301     char * const *e_bits[] = {
1302         eyes_u_xpm, eyes_r_xpm, eyes_d_xpm, eyes_l_xpm
1303     };
1304         
1305     int i, j, k, m;
1306     int w = pp->spritexs;
1307     int h = pp->spriteys;
1308     GC gc = 0;
1309     Pixmap temp;
1310
1311     for (i = 0; i < 4; i++) {
1312         m = 0;
1313         for (j = 0; j < MAXGDIR; j++) {
1314             for (k = 0; k < MAXGWAG; k++) {
1315                 bits[m][2] = colors[i];
1316                 pp->ghostPixmap[i][j][k] =
1317                     xpm_data_to_pixmap (display, window, bits[m], &w, &h,
1318                                         &pp->ghostMask);
1319
1320                 if (!pp->ghostPixmap[i][j][k])
1321                     pacman_fail ("Cannot load ghost images");
1322
1323                 pp->ghostPixmap[i][j][k] =
1324                     scale_pixmap (&display, pp->stippledGC,
1325                                   pp->ghostPixmap[i][j][k], pp->spritexs,
1326                                   pp->spriteys);
1327
1328                 if (!pp->ghostPixmap[i][j][k])
1329                     pacman_fail ("Cannot scale ghost images");
1330                 m++;
1331             }
1332         }
1333     }
1334     /* load the scared ghost */
1335     m = 0;
1336     for (i = 0; i < MAXGFLASH; i++) {
1337         for (j = 0; j < MAXGWAG; j++) {
1338             pp->s_ghostPixmap[i][j] =
1339                 xpm_data_to_pixmap (display, window, s_bits[m++], &w, &h,
1340                                     &pp->ghostMask);
1341
1342             if (!pp->s_ghostPixmap[i][j])
1343                 pacman_fail ("Cannot Scare Ghost images");
1344             pp->s_ghostPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1345                                                     pp->s_ghostPixmap[i][j],
1346                                                     pp->spritexs,
1347                                                     pp->spriteys);
1348
1349             if (!pp->s_ghostPixmap[i][j])
1350                 pacman_fail ("Cannot scale Scared Ghost images");
1351         }
1352     }
1353     /* load the ghost eyes */
1354     for (i = 0; i < MAXGDIR; i++) {
1355         pp->ghostEyes[i] =
1356             xpm_data_to_pixmap (display, window, e_bits[i], &w, &h,
1357                                 &pp->ghostMask);
1358
1359         if (!pp->ghostEyes[i])
1360             pacman_fail ("Cannot open ghost eye images");
1361
1362         pp->ghostEyes[i] = scale_pixmap (&display, pp->stippledGC,
1363                                          pp->ghostEyes[i],
1364                                          pp->spritexs,
1365                                          pp->spriteys);
1366             
1367         if (!pp->ghostEyes[i])
1368             pacman_fail ("Cannot open ghost eye images");
1369     }
1370
1371
1372
1373     /* We really only need a single mask. This saves the headache of getting the
1374      * bottom of the ghost to clip just right. What we'll do is mask
1375      * the top portion of the ghost, but the bottom of the ghost will be solid.
1376      * I did this by setting the pixels between the fringe of their sheets
1377      * to black instead of none. -jeremy
1378      */
1379     temp = xpm_data_to_pixmap (display, window, ghost_mask_xpm,
1380                                &w, &h, &pp->ghostMask);
1381
1382     if (!temp)
1383         pacman_fail ("Cannot load temporary ghost image");
1384
1385     temp = scale_pixmap (&display, pp->stippledGC,
1386                          temp, pp->spritexs, pp->spriteys);
1387
1388     if (!temp)
1389         pacman_fail ("Cannot scale temporary ghost image");
1390
1391     gc = XCreateGC (display, pp->ghostMask, 0, 0);
1392
1393     pp->ghostMask = scale_pixmap (&display, gc, pp->ghostMask,
1394                                   pp->spritexs, pp->spriteys);
1395     XFreePixmap (display, temp);
1396 }
1397
1398 /* Load the pacman pixmaps and their mask. */
1399 static void
1400 load_pacman_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1401 {
1402     pacmangamestruct *pp = *ps;
1403     Display *display = *dpy;
1404
1405     char **bits[] = {
1406         pacman_0_xpm, pacman_u1_xpm, pacman_u2_xpm,
1407         pacman_0_xpm, pacman_r1_xpm, pacman_r2_xpm,
1408         pacman_0_xpm, pacman_d1_xpm, pacman_d2_xpm,
1409         pacman_0_xpm, pacman_l1_xpm, pacman_l2_xpm
1410     };
1411  
1412     char * const *ds_bits[] = {
1413         pacman_ds1_xpm, pacman_ds2_xpm, pacman_ds3_xpm, pacman_ds4_xpm,
1414         pacman_ds5_xpm, pacman_ds6_xpm, pacman_ds7_xpm, pacman_ds8_xpm
1415     };
1416
1417     int i, j, m;
1418     int w = pp->spritexs;
1419     int h = pp->spriteys;
1420     GC gc = 0;
1421
1422     m = 0;
1423     for (i = 0; i < 4; i++) {
1424         for (j = 0; j < MAXMOUTH; j++) {
1425             pp->pacmanPixmap[i][j] =
1426                 xpm_data_to_pixmap (display, window, bits[m++], &w, &h,
1427                                     &pp->pacmanMask[i][j]);
1428
1429             if (!pp->pacmanPixmap[i][j])
1430                 pacman_fail ("Cannot load pacman pixmap.");
1431
1432             pp->pacmanPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1433                                                    pp->pacmanPixmap[i][j],
1434                                                    pp->spritexs,
1435                                                    pp->spriteys);
1436
1437             if (!pp->pacmanPixmap[i][j])
1438                 pacman_fail ("Cannot scale pacman pixmap.");
1439
1440             if (!gc)
1441                 gc = XCreateGC (display, pp->pacmanMask[i][j], 0, 0);
1442
1443             pp->pacmanMask[i][j] =
1444                 scale_pixmap (&display, gc, pp->pacmanMask[i][j],
1445                               pp->spritexs, pp->spriteys);
1446         }
1447     }
1448     
1449     /* Load pacman death sequence */
1450     for ( i = 0; i < PAC_DEATH_FRAMES; i++ ){
1451         pp->pacman_ds[i] = 
1452             xpm_data_to_pixmap (display, window, ds_bits[i], &w, &h,
1453                                 &pp->pacman_ds_mask[i]);
1454
1455         if (!pp->pacman_ds[i])
1456             pacman_fail ("Cannot load pacman death frame.");
1457         
1458         pp->pacman_ds[i] = scale_pixmap ( &display, pp->stippledGC,
1459                                           pp->pacman_ds[i],
1460                                           pp->spritexs,
1461                                           pp->spriteys);
1462         
1463         if (!pp->pacman_ds[i])
1464             pacman_fail ("Cannot scale pixmap.");
1465
1466         if (!gc)
1467             gc = XCreateGC (display, pp->pacman_ds_mask[i], 0, 0);
1468
1469         pp->pacman_ds_mask[i] = 
1470             scale_pixmap (&display, gc, pp->pacman_ds_mask[i],
1471                           pp->spritexs, pp->spriteys);
1472     }
1473
1474 }
1475 #endif /* USE_PIXMAP */
1476
1477 /* Hook function, sets state to initial position. */
1478 ENTRYPOINT void
1479 init_pacman (ModeInfo * mi)
1480 {
1481     Display *display = MI_DISPLAY (mi);
1482     Window window = MI_WINDOW (mi);
1483     int size = MI_SIZE (mi);
1484     pacmangamestruct *pp;
1485     XGCValues gcv;
1486     int i, j, k;
1487
1488 #if (! defined( USE_PIXMAP ))
1489     GC fg_gc, bg_gc;
1490     XPoint points[9];
1491     int dir, mouth;
1492 #endif
1493
1494     if (pacmangames == NULL) {
1495         if ((pacmangames = (pacmangamestruct *)
1496              calloc ((size_t) MI_NUM_SCREENS (mi),
1497                      sizeof (pacmangamestruct))) == NULL)
1498             return;
1499     }
1500     pp = &pacmangames[MI_SCREEN (mi)];
1501
1502     pp->width = (unsigned short) MI_WIDTH (mi);
1503     pp->height = (unsigned short) MI_HEIGHT (mi);
1504     for (i = 0; i < 4; i++) {
1505         for (j = 0; j < MAXGDIR; j++) {
1506             for (k = 0; k < MAXGWAG; k++) {
1507                 if (pp->ghostPixmap[i][j][k] != None) {
1508                     XFreePixmap (display, pp->ghostPixmap[i][j][k]);
1509                     pp->ghostPixmap[i][j][k] = None;
1510                     pp->graphics_format = 0 /*IS_NONE */ ;
1511                 }
1512             }
1513         }
1514     }
1515
1516     for (i = 0; i < MAXGFLASH; i++) {
1517         for (j = 0; j < MAXGWAG; j++) {
1518             if (pp->s_ghostPixmap[i][j] != None) {
1519                 XFreePixmap (display, pp->s_ghostPixmap[i][j]);
1520                 pp->s_ghostPixmap[i][j] = None;
1521             }
1522         }
1523     }
1524
1525     if (size == 0 ||
1526         MINGRIDSIZE * size > (int) pp->width ||
1527         MINGRIDSIZE * size > (int) pp->height) {
1528
1529         pp->ys = pp->xs = MAX (MIN (pp->width / LEVWIDTH,
1530                                     pp->height / LEVHEIGHT), 1);
1531     }
1532     else {
1533         if (size < -MINSIZE)
1534             pp->ys = (short) (NRAND (MIN (-size, MAX (MINSIZE,
1535                                                       MIN (pp->width,
1536                                                            pp->height) /
1537                                                       MINGRIDSIZE))
1538                                      - MINSIZE + 1) + MINSIZE);
1539         else if (size < MINSIZE)
1540             pp->ys = MINSIZE;
1541         else
1542             pp->ys = (short) (MIN (size,
1543                                    MAX (MINSIZE, MIN (pp->width, pp->height) /
1544                                         MINGRIDSIZE)));
1545         pp->xs = pp->ys;
1546     }
1547
1548     pp->wallwidth = (unsigned int) (pp->xs + pp->ys) >> 4;
1549     if (pp->wallwidth < 1)
1550         pp->wallwidth = 1;
1551     pp->incx = (pp->xs >> 3) + 1;
1552     pp->incy = (pp->ys >> 3) + 1;
1553     pp->ncols = (unsigned short) MAX (LEVWIDTH, 2);
1554     pp->nrows = (unsigned short) MAX (LEVHEIGHT, 2);
1555     pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1556     pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1557     pp->spritexs = MAX (pp->xs + (pp->xs >> 1) - 1, 1);
1558     pp->spriteys = MAX (pp->ys + (pp->ys >> 1) - 1, 1);
1559     pp->spritedx = (pp->xs - pp->spritexs) >> 1;
1560     pp->spritedy = (pp->ys - pp->spriteys) >> 1;
1561     pp->old_pac_state = chasing;
1562
1563     if (!pp->stippledGC) {
1564         gcv.foreground = MI_BLACK_PIXEL (mi);
1565         gcv.background = MI_BLACK_PIXEL (mi);
1566         if ((pp->stippledGC = XCreateGC (display, window,
1567                                          GCForeground | GCBackground,
1568                                          &gcv)) == None) {
1569             free_pacman (display, pp);
1570             return;
1571         }
1572     }
1573
1574 #ifdef HAVE_COCOA
1575     jwxyz_XSetAntiAliasing (display, pp->stippledGC, False);
1576 #endif
1577
1578 #if defined(USE_PIXMAP)
1579     load_ghost_pixmaps (&display, window, &pp);
1580     load_pacman_pixmaps (&display, window, &pp);
1581 #else
1582     if ((pp->ghostPixmap[0][0][0] = XCreatePixmap (display, window,
1583                                                    pp->spritexs, pp->spriteys,
1584                                                    1)) == None) {
1585         free_pacman (display, pp);
1586         return;
1587     }
1588
1589     gcv.foreground = 0;
1590     gcv.background = 1;
1591     if ((bg_gc = XCreateGC (display, pp->ghostPixmap[0][0][0],
1592                             GCForeground | GCBackground, &gcv)) == None) {
1593         free_pacman (display, pp);
1594         return;
1595     }
1596
1597     gcv.foreground = 1;
1598     gcv.background = 0;
1599     if ((fg_gc = XCreateGC (display, pp->ghostPixmap[0][0][0],
1600                             GCForeground | GCBackground, &gcv)) == None) {
1601         XFreeGC (display, bg_gc);
1602         free_pacman (display, pp);
1603         return;
1604     }
1605
1606 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
1607
1608     /* draw the triangles on the bottom (scalable) */
1609     SETPOINT (points[0], 1, pp->spriteys * 5 / 6);
1610     SETPOINT (points[1], pp->spritexs / 6, pp->spriteys);
1611     SETPOINT (points[2], pp->spritexs / 3, pp->spriteys * 5 / 6);
1612     SETPOINT (points[3], pp->spritexs / 2, pp->spriteys);
1613     SETPOINT (points[4], pp->spritexs * 2 / 3, pp->spriteys * 5 / 6);
1614     SETPOINT (points[5], pp->spritexs * 5 / 6, pp->spriteys);
1615     SETPOINT (points[6], pp->spritexs, pp->spriteys * 5 / 6);
1616     SETPOINT (points[7], pp->spritexs, pp->spriteys / 2);
1617     SETPOINT (points[8], 1, pp->spriteys / 2);
1618
1619     XFillRectangle (display, pp->ghostPixmap[0][0][0], bg_gc,
1620                     0, 0, pp->spritexs, pp->spriteys);
1621     XFillArc (display, pp->ghostPixmap[0][0][0], fg_gc,
1622               0, 0, pp->spritexs, pp->spriteys, 0, 11520);
1623     XFillPolygon (display, pp->ghostPixmap[0][0][0], fg_gc,
1624                   points, 9, Nonconvex, CoordModeOrigin);
1625     XFreeGC (display, bg_gc);
1626     XFreeGC (display, fg_gc);
1627
1628
1629     if (pp->pacmanPixmap[0][0] != None)
1630         for (dir = 0; dir < 4; dir++)
1631             for (mouth = 0; mouth < MAXMOUTH; mouth++)
1632                 XFreePixmap (display, pp->pacmanPixmap[dir]
1633                              [mouth]);
1634
1635     for (dir = 0; dir < 4; dir++)
1636         for (mouth = 0; mouth < MAXMOUTH; mouth++) {
1637             if ((pp->pacmanPixmap[dir][mouth] =
1638                  XCreatePixmap (display, MI_WINDOW (mi), pp->spritexs,
1639                                 pp->spriteys, 1)) == None) {
1640                 free_pacman (display, pp);
1641                 return;
1642             }
1643             gcv.foreground = 1;
1644             gcv.background = 0;
1645             if ((fg_gc = XCreateGC (display, pp->pacmanPixmap[dir][mouth],
1646                                     GCForeground | GCBackground,
1647                                     &gcv)) == None) {
1648                 free_pacman (display, pp);
1649                 return;
1650             }
1651             gcv.foreground = 0;
1652             gcv.background = 0;
1653             if ((bg_gc = XCreateGC (display,
1654                                     pp->pacmanPixmap[dir][mouth],
1655                                     GCForeground |
1656                                     GCBackground, &gcv)) == None) {
1657                 XFreeGC (display, fg_gc);
1658                 free_pacman (display, pp);
1659                 return;
1660             }
1661             XFillRectangle (display,
1662                             pp->pacmanPixmap[dir][mouth], bg_gc,
1663                             0, 0, pp->spritexs, pp->spriteys);
1664             if (pp->spritexs == 1 && pp->spriteys == 1)
1665                 XFillRectangle (display,
1666                                 pp->pacmanPixmap[dir][mouth],
1667                                 fg_gc, 0, 0, pp->spritexs, pp->spriteys);
1668             else
1669                 XFillArc (display,
1670                           pp->pacmanPixmap[dir][mouth],
1671                           fg_gc,
1672                           0, 0, pp->spritexs, pp->spriteys,
1673                           ((90 - dir * 90) + mouth * 5) * 64,
1674                           (360 + (-2 * mouth * 5)) * 64);
1675             XFreeGC (display, fg_gc);
1676             XFreeGC (display, bg_gc);
1677         }
1678 #endif /* USE_PIXMAP */
1679
1680     pp->pacman.lastbox = START;
1681     pp->pacman.mouthdirection = 1;
1682     pp->pacman.nextcol = NOWHERE;
1683     pp->pacman.nextrow = NOWHERE;
1684
1685     if (pp->ghosts != NULL) {
1686         free (pp->ghosts);
1687         pp->ghosts = (ghoststruct *) NULL;
1688     }
1689     pp->nghosts = GHOSTS;
1690
1691     if (!pp->ghosts)
1692         if ((pp->ghosts = (ghoststruct *) calloc ((size_t) pp->nghosts,
1693                                                   sizeof (ghoststruct))) ==
1694             NULL) {
1695             free_pacman (display, pp);
1696             return;
1697         }
1698
1699     pp->pacman.mouthstage = MAXMOUTH - 1;
1700
1701     MI_CLEARWINDOW (mi);
1702     repopulate (mi);
1703 }
1704
1705 /* Callback function called for each tick.  This is the complete machinery of
1706    everything that moves. */
1707 ENTRYPOINT void
1708 draw_pacman (ModeInfo * mi)
1709 {
1710     unsigned int g;
1711     pacmangamestruct *pp;
1712
1713     if (pacmangames == NULL)
1714         return;
1715     pp = &pacmangames[MI_SCREEN (mi)];
1716     if (pp->ghosts == NULL)
1717         return;
1718
1719     pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1720     pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1721     pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1722     pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1723
1724     if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1725         pac_update (mi, pp, &(pp->pacman));
1726         check_death (mi, pp);
1727         pp->pacman.delta.x = pp->incx;
1728         pp->pacman.delta.y = pp->incy;
1729     }
1730
1731     if (pp->pacman.delta.x > pp->xs + pp->incx)
1732         pp->pacman.delta.x = pp->xs + pp->incx;
1733     if (pp->pacman.delta.y > pp->ys + pp->incy)
1734         pp->pacman.delta.y = pp->ys + pp->incy;
1735
1736     for (g = 0; g < pp->nghosts; g++) {
1737         pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % pp->ghosts[g].speed;
1738         pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % pp->ghosts[g].speed;
1739         pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? pp->incx : 0;
1740         pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? pp->incy : 0; 
1741         
1742         if (pp->ghosts[g].delta.x >= pp->xs &&
1743             pp->ghosts[g].delta.y >= pp->ys) {
1744             ghost_update (pp, &(pp->ghosts[g]));
1745             pp->ghosts[g].delta.x = pp->incx;
1746             pp->ghosts[g].delta.y = pp->incy;
1747         }
1748
1749         if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1750             pp->ghosts[g].delta.x = pp->xs + pp->incx;
1751         if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1752             pp->ghosts[g].delta.y = pp->ys + pp->incy;
1753     }
1754     pacman_tick (mi);
1755 }
1756
1757 /* Releases resources. */
1758 ENTRYPOINT void
1759 release_pacman (ModeInfo * mi)
1760 {
1761     if (pacmangames != NULL) {
1762         int screen;
1763
1764         for (screen = 0; screen < MI_NUM_SCREENS (mi); screen++)
1765             free_pacman (MI_DISPLAY (mi), &pacmangames[screen]);
1766         free (pacmangames);
1767         pacmangames = (pacmangamestruct *) NULL;
1768     }
1769 }
1770
1771 /* Refresh current level. */
1772 ENTRYPOINT void
1773 refresh_pacman (ModeInfo * mi)
1774 {
1775     drawlevel (mi);
1776     pacman_tick (mi);
1777 }
1778
1779 #ifndef STANDALONE
1780 /* Callback to change level. */
1781 ENTRYPOINT void
1782 change_pacman (ModeInfo * mi)
1783 {
1784     MI_CLEARWINDOW (mi);
1785     repopulate (mi);
1786 }
1787 #endif /* !STANDALONE */
1788
1789
1790 XSCREENSAVER_MODULE ("Pacman", pacman)
1791
1792 #endif /* MODE_pacman */