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