]> git.hungrycats.org Git - xscreensaver/blob - hacks/pacman.c
From https://www.jwz.org/xscreensaver/xscreensaver-6.09.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 0
5 static const char sccsid[] = "@(#)pacman.c      5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 25-Feb-2005: Added bonus dots. I am using a recursive back track algorithm
25  *              to help the ghost find there way home. This also means that
26  *              they do not know the shorts path.
27  *              Jeremy English jhe@jeremyenglish.org
28  * 15-Aug-2004: Added support for pixmap pacman. 
29  *              Jeremy English jhe@jeremyenglish.org
30  * 11-Aug-2004: Added support for pixmap ghost.
31  *              Jeremy English jhe@jeremyenglish.org
32  * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.  
33  *              splitted up code into several files.  Retouched AI code, cleaned
34  *              up code.
35  *  3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
36  * 26-Nov-2001: Random level generator added
37  * 01-Nov-2000: Allocation checks
38  * 04-Jun-1997: Compatible with xscreensaver
39  *
40  */
41
42 /* TODO:
43    1. think of a better level generation algorithm
44 */
45
46 #define DEF_TRACKMOUSE "False"
47
48 #ifdef STANDALONE
49 #       define MODE_pacman
50 #       define DEFAULTS "*delay:   10000 \n" \
51                                         "*size:    0     \n" \
52                                         "*ncolors: 6     \n" \
53                                         "*fpsTop: true   \n" \
54                                         "*fpsSolid: true \n" \
55
56 #       define UNIFORM_COLORS
57 #       define BRIGHT_COLORS
58 #       define release_pacman 0
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 #include "ximage-loader.h"
72 #include "images/gen/pacman_png.h"
73
74 #ifdef DISABLE_INTERACTIVE
75 ENTRYPOINT ModeSpecOpt pacman_opts = {
76     0,
77     (XrmOptionDescRec *) NULL,
78     0,
79     (argtype *) NULL,
80     (OptionStruct *) NULL
81 };
82 #else
83 static XrmOptionDescRec opts[] = {
84     {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
85     {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
86 };
87
88 static argtype vars[] = {
89     {&pacman_trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
90 };
91
92 static OptionStruct desc[] = {
93     {"-/+trackmouse", "turn on/off the tracking of the mouse"}
94 };
95
96 ENTRYPOINT ModeSpecOpt pacman_opts =
97     { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars,
98 desc };
99 #endif
100
101 #ifdef USE_MODULES
102 ModStruct pacman_description = {
103     "pacman",                   /* *cmdline_arg; */
104     "init_pacman",              /* *init_name; */
105     "draw_pacman",              /* *callback_name; */
106     (char *) NULL,              /* *release_name; */
107     "refresh_pacman",           /* *refresh_name; */
108     "change_pacman",            /* *change_name; */
109     "free_pacman",              /* *free_name; */
110     &pacman_opts,               /* *msopts */
111     10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
112 };
113
114 #endif
115
116 Bool pacman_trackmouse;
117 pacmangamestruct *pacman_games = (pacmangamestruct *) NULL;
118
119 static void repopulate (ModeInfo * mi);
120 static void drawlevel (ModeInfo * mi);
121
122
123 ENTRYPOINT void
124 free_pacman (ModeInfo * mi)
125 {
126     Display * display = MI_DISPLAY (mi);
127     pacmangamestruct * pp = &pacman_games[MI_SCREEN (mi)];
128     int dir, mouth, i, j, k;
129
130     if (pp->tiles) free (pp->tiles);
131
132     if (pp->ghosts != NULL) {
133         free (pp->ghosts);
134         pp->ghosts = (ghoststruct *) NULL;
135     }
136     if (pp->stippledGC != None) {
137         XFreeGC (display, pp->stippledGC);
138         pp->stippledGC = None;
139     }
140     for (i = 0; i < 4; i++) {
141         for (j = 0; j < MAXGDIR; j++) {
142             for (k = 0; k < MAXGWAG; k++) {
143                 if (pp->ghostPixmap[i][j][k] != None) {
144                     XFreePixmap (display, pp->ghostPixmap[i][j][k]);
145                     pp->ghostPixmap[i][j][k] = None;
146                 }
147             }
148         }
149     }
150     for (dir = 0; dir < 4; dir++)
151         for (mouth = 0; mouth < MAXMOUTH; mouth++)
152             if (pp->pacmanPixmap[dir][mouth] != None) {
153                 XFreePixmap (display, pp->pacmanPixmap[dir][mouth]);
154                 pp->pacmanPixmap[dir][mouth] = None;
155             }
156 }
157
158 /* set pacman and the ghost in there starting positions, but don't draw a new
159  level */
160 static void
161 reset_level (ModeInfo * mi, int n, int pac_init)
162 {
163     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
164     unsigned int ghost;
165
166     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
167     drawlevel (mi);
168
169     pp->gamestate = GHOST_DANGER;
170
171     if ( pac_init ){
172         pp->pacman.row = (LEVHEIGHT + JAILHEIGHT) / 2 - n;
173         pp->pacman.init_row = pp->pacman.row;
174     }
175     else{
176         pp->pacman.row = pp->pacman.init_row;
177     }
178     pp->pacman.col = (LEVWIDTH / 2);
179     pp->pacman.nextrow = NOWHERE;
180     pp->pacman.nextcol = NOWHERE;
181     pp->pacman.cf = NOWHERE;
182     pp->pacman.rf = NOWHERE;
183     pp->pacman.oldcf = NOWHERE;
184     pp->pacman.oldrf = NOWHERE;
185     pp->pacman.oldlx = NOWHERE;
186     pp->pacman.oldly = NOWHERE;
187     pp->pacman.aistate = ps_eating;
188     pp->pacman.cur_trace = 0;
189     pp->pacman.roundscore = 0;
190     pp->pacman.speed = 4;
191     pp->pacman.lastturn = 0;
192     pp->pacman.delta.x = 0;
193     pp->pacman.delta.y = 0;
194
195     for (ghost = 0; ghost < pp->nghosts; ghost++) {
196         pp->ghosts[ghost].col = (LEVWIDTH / 2);
197         pp->ghosts[ghost].row = (LEVHEIGHT / 2);
198         pp->ghosts[ghost].nextcol = NOWHERE;
199         pp->ghosts[ghost].nextrow = NOWHERE;
200         pp->ghosts[ghost].dead = 0;
201         pp->ghosts[ghost].lastbox = START;
202         pp->ghosts[ghost].cf = NOWHERE;
203         pp->ghosts[ghost].rf = NOWHERE;
204         pp->ghosts[ghost].oldcf = NOWHERE;
205         pp->ghosts[ghost].oldrf = NOWHERE;
206         pp->ghosts[ghost].aistate = inbox;
207         pp->ghosts[ghost].timeleft = ghost * 20;
208         pp->ghosts[ghost].speed = 3;
209         pp->ghosts[ghost].delta.x = 0;
210         pp->ghosts[ghost].delta.y = 0;
211         pp->ghosts[ghost].flash_scared = False;
212         pp->ghosts[ghost].wait_pos = False;
213         pacman_ghost_update (pp, &(pp->ghosts[ghost]));
214     }
215     pacman_update (mi, pp, &(pp->pacman));
216 }
217
218 static int
219 pacman_ghost_collision(unsigned int ghost, pacmangamestruct * pp)
220 {
221     return (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
222              (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
223             ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
224              (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
225              (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
226              (pp->ghosts[ghost].col == pp->pacman.nextcol)));
227 }
228
229
230 /* Checks for death of any ghosts/pacman and updates.  It also makes a new
231    level if all ghosts are dead or all dots are eaten. */
232 static void
233 check_death (ModeInfo * mi, pacmangamestruct * pp)
234 {
235     Display *display = MI_DISPLAY (mi);
236     Window window = MI_WINDOW (mi);
237     unsigned int ghost;
238
239     if (pp->pacman.aistate == ps_dieing) return;
240
241     for (ghost = 0; ghost < pp->nghosts; ghost++) {
242
243         /* The ghost have to be scared before you can kill them */
244         if ( pacman_ghost_collision ( ghost, pp ) ) {
245             if (pp->ghosts[ghost].aistate == goingin) continue;
246
247             if (pp->ghosts[ghost].aistate == hiding) {
248                 pp->ghosts[ghost].dead = 1;
249                 pp->ghosts[ghost].aistate = goingin;
250                 pp->ghosts[ghost].wait_pos = True;
251                 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
252                 XFillRectangle (display, window,
253                                 pp->stippledGC,
254                                 pp->ghosts[ghost].cf,
255                                 pp->ghosts[ghost].rf,
256                                 pp->spritexs, pp->spriteys);
257             }
258             /* DIE PACMAN... */
259             else {
260                 pp->pacman.deaths++;
261                 pp->pacman.aistate = ps_dieing;
262
263             }
264             continue;
265         }
266     }
267 }
268
269 /* Resets state of ghosts + pacman.  Creates a new level, draws that level. */
270 static void
271 repopulate (ModeInfo * mi)
272 {
273     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
274     pp->pacman.deaths = 0;
275     reset_level (mi, pacman_createnewlevel (pp), True);
276     check_death (mi, pp);
277 }
278
279 /* Sets the color to the color of a wall. */
280 static void
281 setwallcolor (ModeInfo * mi)
282 {
283     Display *display = MI_DISPLAY (mi);
284     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
285
286     if (MI_NPIXELS (mi) > 2)
287         XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, BLUE));
288     else
289         XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
290 }
291
292 /* Sets the color to the color of a dot. */
293 static void
294 setdotcolor (ModeInfo * mi)
295 {
296     Display *display = MI_DISPLAY (mi);
297     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
298
299     XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
300 }
301
302 static void
303 cleardotcolor (ModeInfo * mi)
304 {
305     Display *display = MI_DISPLAY (mi);
306     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
307
308     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
309 }
310
311 #if 0
312 static void
313 draw_position (ModeInfo * mi, int x, int y, int color)
314 {
315     Display *display = MI_DISPLAY (mi);
316     Window window = MI_WINDOW (mi);
317     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
318     XFontStruct *font = NULL;
319     char *f_name = "Utopia 60, Helvetica 60";
320     char *s = NULL;
321
322     font = load_font_retry (display, f_name);
323     assert (font != NULL);
324
325     s = (char *) malloc (256);
326     assert (s != NULL);
327     sprintf (s, "(%d,%d)", x, y);
328     XSetForeground (display, pp->stippledGC, color);
329     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
330     free (s);
331     free (font);
332 }
333 #endif
334 #if 0
335 static void
336 draw_number (ModeInfo * mi, int x, int y, int num, int color)
337 {
338     Display *display = MI_DISPLAY (mi);
339     Window window = MI_WINDOW (mi);
340     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
341     XFontStruct *font = NULL;
342     char *f_name = "Utopia 60, Helvetica 60";
343     char *s = NULL;
344
345     font = load_font_retry (display, f_name);
346     assert (font != NULL);
347
348     s = (char *) malloc (256);
349     assert (s != NULL);
350     sprintf (s, "%d", num);
351     XSetForeground (display, pp->stippledGC, color);
352     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
353     free (s);
354     free (font);
355 }
356 #endif
357
358 #if 0
359 /* draw_grid - draws a grid on top of the playing field.
360  * Used so that I can determine if I'm converting from rows and columns to x and y 
361  * coordinates correctly.
362  */
363 static void
364 draw_grid (ModeInfo * mi)
365 {
366     Display *display = MI_DISPLAY (mi);
367     Window window = MI_WINDOW (mi);
368     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
369     int h = MI_HEIGHT (mi);
370     int w = MI_WIDTH (mi);
371     int y = 0;
372     int x = 0;
373     XSetForeground (display, pp->stippledGC, 0xff0000);
374     while (y < h) {
375         while (x < w) {
376             XDrawLine (display, window, pp->stippledGC, x, 0, x, h);
377             x += 10;
378         }
379         x = 0;
380         XDrawLine (display, window, pp->stippledGC, 0, y, w, y);
381         y += 10;
382     }
383 }
384 #endif
385
386 #if 0
387 static void
388 draw_string (ModeInfo * mi, int x, int y, char *s, int color)
389 {
390     Display *display = MI_DISPLAY (mi);
391     Window window = MI_WINDOW (mi);
392     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
393     XFontStruct *font = NULL;
394     char *f_name = "Utopia 60, Helvetica 60";
395
396     font = load_font_retry (display, f_name);
397     assert (font != NULL);
398
399     assert (s != NULL);
400     XSetForeground (display, pp->stippledGC, color);
401     XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
402     free (font);
403 }
404
405 /* I think this function has a memory leak. Be careful if you enable it. */
406 /* I only used it briefly to help me debug the ghost's aistate. It prints */
407 /* the state of each ghost on the left hand side of the screen */
408 static void
409 print_ghost_stats (ModeInfo *mi, ghoststruct *g , int ghost_num)
410 {
411     char s[1024];
412
413     sprintf (s, "GHOST: %d", ghost_num ); 
414     switch (g->aistate){
415     case inbox: 
416         sprintf (s, "%s inbox", s);
417         break;
418     case goingout:
419         sprintf (s, "%s goingout", s);
420         break;
421     case randdir:
422         sprintf (s, "%s randdir", s);
423         break;
424     case chasing:
425         sprintf (s, "%s chasing", s);
426         break;
427     case hiding:
428         sprintf (s, "%s hiding", s);
429         break;
430     case goingin:
431         sprintf (s, "%s goingin",s);
432         break;
433     }
434     draw_string (mi, 0, (ghost_num *3) *10+50, g->last_stat, 0x000000);
435     draw_string (mi, 0, (ghost_num *3) *10+50, s, 0xff0000);
436     strcpy(g->last_stat,s);
437 }
438
439 /* prints the number of times pacman has died and his aistate on the left hand */
440 /* side of the screen */
441 static void
442 print_pac_stats ( ModeInfo *mi, pacmanstruct *pac )
443 {
444     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
445     char s[1024];
446     sprintf (s, "Pacman, Deaths: %d", pac->deaths );
447     switch ( pac->aistate ){
448     case ps_eating:
449         sprintf(s, "%s ps_eating",s );
450         break;
451     case ps_chasing:
452         sprintf(s, "%s ps_chasing",s );
453         break;
454     case ps_hiding:
455         sprintf(s, "%s ps_hiding",s );
456         break;
457     case ps_random:
458         sprintf(s, "%s ps_random",s );
459         break;
460     case ps_dieing:
461         sprintf(s, "%s ps_dieing",s );
462         break;
463     }
464     draw_string ( mi, 0, 200, pp->last_pac_stat, 0x000000);
465     draw_string ( mi, 0, 200, s, 0xff0000);
466     strcpy(pp->last_pac_stat, s );
467 }
468
469 #endif
470
471 /*Ok, yeah whatever?*/
472 /*dot_rc_to_pixel - magic that converts row and columns into
473  *the x and y coordinates of the screen.
474  */
475 static void
476 dot_rc_to_pixel (ModeInfo * mi, int *x, int *y)
477 {
478     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
479     *x = (pp->xs * *x) +
480         (pp->xs / 2) - (pp->xs > 32 ? (pp->xs / 16) : 1) + pp->xb;
481     *y = (pp->ys * *y) +
482         (pp->ys / 2) - (pp->ys > 32 ? (pp->ys / 16) : 1) + pp->yb;
483 }
484
485 /* dot_width_height - magic used to get the width and height of
486  * a dot. This dot can also be scaled by a value.
487  */
488 static void
489 dot_width_height (ModeInfo *mi, int *w, int *h)
490 {
491     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
492     if (pp->xs > 32){
493         *w = *h = (pp->xs / 16 );
494     }else {
495         *w = *h = 1;
496     }
497 }
498
499 static void
500 bonus_dot_width_height (ModeInfo *mi, int *w, int *h )
501 {
502     *w = *h = MI_HEIGHT (mi) / 65;
503 }
504
505 \f
506
507 static void
508 draw_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y,
509           void (*width_height)(ModeInfo * mi, int *w, int *h), 
510           int (*arc_func) (Display * display, Drawable d, GC gc,
511                                       int x, int y, unsigned int width,
512                                       unsigned int height, int angle1,
513                                       int angle2))
514 {
515     Display *display = MI_DISPLAY (mi);
516     Window window = MI_WINDOW (mi);
517     int w, h;
518     dot_rc_to_pixel (mi, &x, &y);
519     width_height(mi, &w, &h);
520     (void) arc_func (display, window, pp->stippledGC,
521                      x, y, w, h, 0, 23040);
522 }
523
524 static void
525 draw_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
526 {
527     int x2 = x;
528     int y2 = y;
529     setdotcolor (mi);
530     draw_dot (mi, pp, x, y, bonus_dot_width_height,  XFillArc);
531     dot_rc_to_pixel (mi, &x2, &y2);
532 #if 0
533     draw_position (mi, x2, y2, 0xff0000);
534 #endif
535 }
536
537 static void
538 clear_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
539 {
540     cleardotcolor (mi);
541     draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
542 }
543
544 static void
545 draw_regular_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
546 {
547     setdotcolor (mi);
548     draw_dot (mi, pp, x, y, dot_width_height, XDrawArc);
549 }
550
551 /* Draws a block in the level at the specified x and y locations. */
552 static void
553 drawlevelblock (ModeInfo * mi, pacmangamestruct * pp,
554                 const unsigned x, const unsigned y)
555 {
556     Display *display = MI_DISPLAY (mi);
557     Window window = MI_WINDOW (mi);
558     int dx = 0, dy = 0;
559
560     if (pp->xs % 2 == 1)
561         dx = -1;
562     if (pp->ys % 2 == 1)
563         dy = -1;
564
565 #ifndef HAVE_JWXYZ
566     XSetFillStyle (display, pp->stippledGC, FillSolid);
567 #endif /* !HAVE_JWXYZ */
568     XSetLineAttributes (display, pp->stippledGC, pp->wallwidth,
569                         LineSolid, CapRound, JoinMiter);
570
571     if (pp->xs < 2 || pp->ys < 2) {
572         switch (pp->level[y * LEVWIDTH + x]) {
573         case ' ':
574         case '=':
575             break;
576         case '.':
577             setdotcolor (mi);
578             (void) XDrawPoint (display, window,
579                                pp->stippledGC,
580                                x * pp->xs + pp->xb, y * pp->ys + pp->yb);
581             break;
582         default:
583             setwallcolor (mi);
584             (void) XDrawPoint (display, window,
585                                pp->stippledGC,
586                                x * pp->xs + pp->xb, y * pp->ys + pp->yb);
587         }
588
589         return;
590     }
591
592     switch (pp->level[y * LEVWIDTH + x]) {
593     case ' ':
594     case '=':
595         break;
596
597     case '.':
598         setdotcolor (mi);
599         if (pp->xs < 8 || pp->ys < 8) {
600             (void) XDrawPoint (display, window,
601                                pp->stippledGC,
602                                x * pp->xs + pp->xb +
603                                pp->xs / 2, y * pp->ys + pp->yb + pp->ys / 2);
604             break;
605         }
606
607         draw_regular_dot (mi, pp, x, y);
608         break;
609         /* What we will probably want to do here is have the pp->level store a 'o' for
610          * the bonus dots. The we can use the drawing routine above just with a bigger
611          * radius.
612          */
613     case 'o':
614         draw_bonus_dot (mi, pp, x, y);
615         break;
616
617
618     case '-':
619         setwallcolor (mi);
620         (void) XDrawLine (display, window, pp->stippledGC,
621                           (pp->xs * x) + pp->xb,
622                           (pp->ys * y) + (pp->ys / 2) + pp->yb,
623                           (pp->xs * (x + 1)) + pp->xb,
624                           (pp->ys * y) + (pp->ys / 2) + pp->yb);
625         break;
626
627     case '|':
628         setwallcolor (mi);
629         (void) XDrawLine (display, window, pp->stippledGC,
630                           (pp->xs * x) + (pp->xs / 2) + pp->xb,
631                           (pp->ys * y) + pp->yb,
632                           (pp->xs * x) + (pp->xs / 2) + pp->xb,
633                           (pp->ys * (y + 1)) + pp->yb);
634         break;
635
636     case '_':
637         setwallcolor (mi);
638         (void) XDrawArc (display, window, pp->stippledGC,
639                          (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
640                          (pp->ys * y) + (pp->ys / 2) + pp->yb,
641                          pp->xs, pp->ys, 0 * 64, 90 * 64);
642         break;
643
644     case ',':
645         setwallcolor (mi);
646         (void) XDrawArc (display, window, pp->stippledGC,
647                          (pp->xs * x) + (pp->ys / 2) + pp->xb,
648                          (pp->ys * y) + (pp->ys / 2) + pp->yb,
649                          pp->xs, pp->ys, 90 * 64, 90 * 64);
650         break;
651
652     case '`':
653         setwallcolor (mi);
654         (void) XDrawArc (display, window, pp->stippledGC,
655                          (pp->xs * x) + (pp->ys / 2) + pp->xb,
656                          (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
657                          pp->xs, pp->ys, 180 * 64, 90 * 64);
658         break;
659
660     case '\'':
661         setwallcolor (mi);
662         (void) XDrawArc (display, window, pp->stippledGC,
663                          (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
664                          (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
665                          pp->xs, pp->ys, 270 * 64, 90 * 64);
666         break;
667
668     }
669 }
670
671 /* Draws a complete level. */
672 static void
673 drawlevel (ModeInfo * mi)
674 {
675     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
676     unsigned int x, y;
677
678     for (y = 0; y < LEVHEIGHT; y++)
679         for (x = 0; x < LEVWIDTH; x++)
680             drawlevelblock (mi, pp, x, y);
681 }
682
683 /* There is some overlap so it can be made more efficient */
684 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
685  if (yl<y) \
686   (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
687   XFillRectangle(d,w,g,xl,yl,xs,ys); \
688  else if (yl>y) \
689   (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
690   XFillRectangle(d,w,g,xl,yl,xs,ys); \
691  if (xl<x) \
692   (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
693   XFillRectangle(d,w,g,xl,yl,xs,ys); \
694  else if (xl>x) \
695   (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
696   XFillRectangle(d,w,g,xl,yl,xs,ys)
697
698
699 /* Draws the pacman sprite, removing the previous location. */
700
701 static void
702 draw_pacman_sprite (ModeInfo * mi)
703 {
704     Display *display = MI_DISPLAY (mi);
705     Window window = MI_WINDOW (mi);
706     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
707     unsigned int dir = 0;
708     int old_mask_dir = 0;
709     int old_mask_mouth = 0;
710     Pixmap old_mask, new_mask;
711     Pixmap pacman;
712
713 #define MAX_MOUTH_DELAY 2
714 #define MAX_DEATH_DELAY 20
715
716     if (pp->pacman.aistate == ps_dieing){
717         pp->pacman.cf = pp->pacman.oldcf;
718         pp->pacman.rf = pp->pacman.oldrf;
719     }
720     else {
721         pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
722             pp->pacman.cfactor + pp->xb + pp->spritedx;
723         pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
724             pp->pacman.rfactor + pp->yb + pp->spritedy;
725     }
726
727     dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
728            ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
729
730     if (pp->pm_mouth_delay == MAX_MOUTH_DELAY) {
731         if (pp->pm_mouth == (MAXMOUTH - 1) || pp->pm_mouth == 0) {
732             pp->pm_open_mouth = !pp->pm_open_mouth;
733         }
734         pp->pm_open_mouth ? pp->pm_mouth++ : pp->pm_mouth--;
735         pp->pm_mouth_delay = 0;
736     }
737     else {
738         pp->pm_mouth_delay++;
739     }
740     
741     if (pp->pacman.aistate == ps_dieing){
742         if (pp->pm_death_frame >= PAC_DEATH_FRAMES) {
743             pp->pacman.aistate = ps_eating;
744             pp->pm_death_frame = 0;
745             pp->pm_death_delay = 0;
746             reset_level (mi, 0, False);
747             return;
748         }
749         else {
750             old_mask   = pp->pacmanMask[0][0];
751             new_mask   = pp->pacmanMask[0][0];
752             pacman     = pp->pacman_ds[pp->pm_death_frame];
753             if (pp->pm_death_delay == MAX_DEATH_DELAY){
754                 pp->pm_death_frame++;
755                 pp->pm_death_delay = 0;
756             }
757             else{
758                 pp->pm_death_delay++;
759             }
760         }
761     }
762     else{
763         old_mask = pp->pacmanMask[old_mask_dir][old_mask_mouth];
764         new_mask = pp->pacmanMask[dir][pp->pm_mouth];
765         pacman   = pp->pacmanPixmap[dir][pp->pm_mouth];
766     }
767
768     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
769
770     XSetClipMask (display, pp->stippledGC, old_mask);
771                   
772     XSetClipOrigin (display, pp->stippledGC, pp->pacman.oldcf,
773                     pp->pacman.oldrf);
774     XFillRectangle (display, window, pp->stippledGC, pp->pacman.oldcf,
775                     pp->pacman.oldrf, pp->spritexs, pp->spriteys);
776     XSetClipMask (display, pp->stippledGC, new_mask);
777     XSetClipOrigin (display, pp->stippledGC, pp->pacman.cf, pp->pacman.rf);
778     XCopyArea (display, pacman, window,
779                pp->stippledGC, 0, 0, pp->spritexs, pp->spriteys,
780                pp->pacman.cf, pp->pacman.rf);
781     XSetClipMask (display, pp->stippledGC, None);
782     if (pp->pacman.aistate != ps_dieing){
783         pp->pacman.oldcf = pp->pacman.cf;
784         pp->pacman.oldrf = pp->pacman.rf;
785     }
786 }
787
788 #if 0
789 static void
790 draw_ghost_position (ModeInfo * mi, ghoststruct * ghost)
791 {
792     draw_position (mi, ghost->oldcf, ghost->oldrf, MI_BLACK_PIXEL (mi));
793     draw_position (mi, ghost->cf, ghost->rf, 0x00ff00);
794 }
795 #endif
796 #if 0
797 static void
798 draw_ghost_ndirs ( ModeInfo *mi, ghoststruct * ghost)
799 {
800     draw_number (mi, ghost->oldcf, ghost->oldrf, ghost->oldndirs, MI_BLACK_PIXEL (mi));
801     ghost->oldndirs = ghost->ndirs;
802     draw_number (mi, ghost->cf, ghost->rf, ghost->ndirs, 0x00ff00);
803 }
804
805 #endif
806
807 static void
808 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
809 {
810     Display *display = MI_DISPLAY (mi);
811     Window window = MI_WINDOW (mi);
812     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
813 #define MAX_WAG_COUNT 50
814     unsigned int dir = 0;
815     unsigned int fs  = 0; /*flash scared*/
816     Pixmap g_pix; /*ghost pixmap*/
817
818     
819     dir = (ABS (pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
820            ABS (pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
821                                              
822
823     fs = pp->ghosts[ghost].flash_scared;
824     assert (fs == 0 || fs == 1);
825
826     /* Choose the pixmap */
827     switch (pp->ghosts[ghost].aistate){
828     case hiding:
829         g_pix = pp->s_ghostPixmap[fs][pp->gh_wag];
830         break;
831     case goingin:
832         g_pix = pp->ghostEyes[dir];
833 #if 1
834         {
835             int i = 0;
836             while ( i < pp->ghosts[ghost].trace_idx ){
837                 XFillRectangle (display,
838                                 window,
839                                 pp->stippledGC,
840                                 pp->ghosts[ghost].trace[i].vx,
841                                 pp->ghosts[ghost].trace[i].vy, 
842                                 pp->spritexs, pp->spriteys);
843
844                 i++;
845             }
846         }
847 #endif
848                 
849         break;
850     default:
851         g_pix = pp->ghostPixmap[ghost][dir][pp->gh_wag];
852     }
853
854     pp->ghosts[ghost].cf =
855         pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
856         pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
857     pp->ghosts[ghost].rf =
858         pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
859         pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
860
861     XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
862
863     XSetClipMask (display, pp->stippledGC, pp->ghostMask);
864     XSetClipOrigin (display, pp->stippledGC,
865                     pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
866     XFillRectangle (display,
867                     window,
868                     pp->stippledGC,
869                     pp->ghosts[ghost].oldcf,
870                     pp->ghosts[ghost].oldrf, pp->spritexs, pp->spriteys);
871
872     
873     if (pp->pacman.aistate != ps_dieing) {
874         drawlevelblock (mi, pp,
875                         (unsigned int) pp->ghosts[ghost].col,
876                         (unsigned int) pp->ghosts[ghost].row);
877
878
879
880         XSetClipOrigin (display, pp->stippledGC,
881                         pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
882
883         XCopyArea (display, g_pix, window, pp->stippledGC, 0, 0,
884                    pp->spritexs, pp->spriteys, pp->ghosts[ghost].cf,
885                    pp->ghosts[ghost].rf);
886     }
887     XSetClipMask (display, pp->stippledGC, None);
888
889 #if 0
890     draw_ghost_position (mi, &(pp->ghosts[ghost]));
891 #endif
892
893 #if 0
894     draw_ghost_ndirs ( mi, &(pp->ghosts[ghost]));
895 #endif
896     
897     if (pp->pacman.aistate != ps_dieing) {
898         pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
899         pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
900         if (pp->gh_wag_count++ == MAX_WAG_COUNT) {
901             pp->gh_wag = !pp->gh_wag;
902             pp->gh_wag_count = 0;
903         }
904     }
905 }
906
907
908 static int
909 ghost_over (ModeInfo * mi, int x, int y)
910 {
911     int ghost = 0;
912     int ret = False;
913     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
914     dot_rc_to_pixel (mi, &x, &y);
915     for (ghost = 0; ghost < pp->nghosts; ghost++) {
916         if ((pp->ghosts[ghost].cf <= x
917              && x <= pp->ghosts[ghost].cf + pp->spritexs)
918             && (pp->ghosts[ghost].rf <= y
919                 && y <= pp->ghosts[ghost].rf + pp->spriteys)) {
920             ret = True;
921             break;
922         }
923     }
924     return ret;
925 }
926 \f
927
928 static void
929 flash_bonus_dots (ModeInfo * mi)
930 {
931 #define MAX_FLASH_COUNT 25
932     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
933     int i, x, y;
934     for (i = 0; i < NUM_BONUS_DOTS; i++) {
935         if (!pacman_bonus_dot_eaten (pp, i)) {
936             pacman_bonus_dot_pos (pp, i, &x, &y);
937             if (ghost_over (mi, x, y))
938                 continue;
939             if (pp->bd_on)
940                 draw_bonus_dot (mi, pp, x, y);
941             else
942                 clear_bonus_dot (mi, pp, x, y);
943         }
944     }
945     if (pp->bd_flash_count-- == 0) {
946         pp->bd_flash_count = MAX_FLASH_COUNT;
947         pp->bd_on = !pp->bd_on;
948     }
949 }
950
951 static unsigned int
952 ate_bonus_dot (ModeInfo * mi)
953 {
954     /*Check pacman's position. If it is over a bonus dot and that dot
955      *has not been eaten, then return true
956      */
957     unsigned int ret = 0;
958     int idx = 0;
959     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
960     if (pacman_is_bonus_dot (pp, pp->pacman.col, pp->pacman.row, &idx)) {
961         ret = !pacman_bonus_dot_eaten (pp, idx);
962         pacman_eat_bonus_dot (pp, idx);
963     }
964     return ret;
965 }
966
967 static void
968 ghost_scared (ModeInfo * mi)
969 {
970     unsigned int ghost;
971     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
972     for (ghost = 0; ghost < pp->nghosts; ghost++) {
973         if (pp->ghosts[ghost].aistate == goingin || 
974             pp->ghosts[ghost].aistate == goingout ||
975             pp->ghosts[ghost].aistate == inbox ) continue;
976         pp->ghosts[ghost].aistate = hiding;
977         pp->ghosts[ghost].flash_scared = 0;
978         if (pp->pacman.aistate != ps_dieing)
979             pp->pacman.aistate = ps_chasing;
980     }
981 }
982
983 static void
984 ghost_not_scared (ModeInfo * mi)
985 {
986     unsigned int ghost;
987     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
988     for (ghost = 0; ghost < pp->nghosts; ghost++){
989         if (pp->ghosts[ghost].aistate == goingin ||
990             pp->ghosts[ghost].aistate == goingout ||
991             pp->ghosts[ghost].aistate == inbox ) continue;
992         pp->ghosts[ghost].aistate = chasing;
993     }
994     if (pp->pacman.aistate != ps_dieing)
995         pp->pacman.aistate = ps_eating;
996     
997 }
998
999 static void
1000 ghost_flash_scared (ModeInfo * mi)
1001 {
1002     unsigned int ghost;
1003     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1004     for (ghost = 0; ghost < pp->nghosts; ghost++)
1005         pp->ghosts[ghost].flash_scared = !pp->ghosts[ghost].flash_scared;
1006 }
1007
1008 /* Does all drawing of moving sprites in the level. */
1009 static void
1010 pacman_tick (ModeInfo * mi)
1011 {
1012 #define DEFAULT_SCARED_TIME 500
1013 #define START_FLASH 200
1014 #define FLASH_COUNT 25
1015
1016     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1017     unsigned int ghost;
1018 #if 0
1019     draw_grid (mi);
1020 #endif
1021     for (ghost = 0; ghost < pp->nghosts; ghost++) {
1022         draw_ghost_sprite (mi, ghost);
1023 #if 0
1024         print_ghost_stats (mi, &(pp->ghosts[ghost]), ghost);
1025 #endif
1026     }
1027 #if 0    
1028     print_pac_stats (mi, &(pp->pacman));
1029 #endif
1030     draw_pacman_sprite (mi);
1031     flash_bonus_dots (mi);
1032     if (ate_bonus_dot (mi)) {
1033         pp->ghost_scared_timer = (random () % 100) + DEFAULT_SCARED_TIME;
1034         ghost_scared (mi);
1035     }
1036
1037     if (pp->ghost_scared_timer > 0) {
1038         if (--pp->ghost_scared_timer == 0)
1039             ghost_not_scared (mi);
1040         else if (pp->ghost_scared_timer <= START_FLASH) {
1041             if (pp->flash_timer <= 0) {
1042                 pp->flash_timer = FLASH_COUNT;
1043                 ghost_flash_scared (mi);
1044             }
1045             pp->flash_timer--;
1046         }
1047     }
1048
1049     /*
1050       We don't want to miss the last death sequence. So if pacman has died three times
1051       we wait for his state to change from dieing to something else before we repopulate
1052       the level. If pacman ate all of the dots then we just repopulate.
1053     */
1054
1055     if (pp->dotsleft == 0 )
1056         repopulate (mi);
1057     else if (pp->pacman.deaths >= 3){
1058         if (pp->old_pac_state == ps_dieing && pp->pacman.aistate != ps_dieing)
1059             repopulate (mi);
1060     }
1061
1062     pp->old_pac_state = pp->pacman.aistate;
1063 }
1064 \f
1065
1066 /* CODE TO LOAD AND SCALE THE PIXMAPS
1067  */
1068
1069 /*  Grabbed the scaling routine off of usenet. 
1070  *  Changed it so that the information specific 
1071  *  to the source pixmap does not have to be a parameter.
1072  *
1073  *  There is probably a better way to scale pixmaps.
1074  *  From: Chris Fiddyment (cxf@itd.dsto.gov.au)
1075  *  Subject: Scaling Pixmap Algorithm.
1076  *  Newsgroups: comp.graphics.algorithms
1077  *  Date: 1994-07-06 18:51:38 PST 
1078  *  -jeremy
1079  */
1080
1081 static Pixmap
1082 scale_pixmap (Display ** dpy, GC gc, Pixmap source, int dwidth, int dheight)
1083 {
1084     Pixmap temp, dest;
1085     int j, end;
1086     float i;
1087     float xscale, yscale;
1088     unsigned int swidth, sheight;
1089     Window window;
1090     int x, y;
1091     unsigned border_width_return, depth;
1092     XGetGeometry (*dpy, source, &window, &x, &y, &swidth, &sheight,
1093                   &border_width_return, &depth);
1094
1095     xscale = (float) swidth / (float) dwidth;   /* Scaling factors */
1096     yscale = (float) sheight / (float) dheight;
1097
1098     dest = XCreatePixmap (*dpy, window, dwidth, dheight, depth);
1099     if (!dest) {
1100         fprintf (stderr, "%s Could not scale image", progname);
1101     }
1102     temp = XCreatePixmap (*dpy, window, dwidth, sheight, depth);
1103     if (!temp) {
1104         fprintf (stderr, "%s Could not scale image", progname);
1105     }
1106
1107     j = 0;
1108     end = dwidth * xscale;
1109     /* Scale width of source into temp pixmap */
1110     for (i = 0; i <= end; i += xscale)
1111         XCopyArea (*dpy, source, temp, gc, i, 0, 1, sheight, j++, 0);
1112
1113     j = 0;
1114     end = dheight * yscale;
1115     /* Scale height of temp into dest pixmap */
1116     for (i = 0; i <= end; i += yscale)
1117         XCopyArea (*dpy, temp, dest, gc, 0, i, dwidth, 1, 0, j++);
1118
1119     XFreePixmap (*dpy, temp);
1120     return (Pixmap) dest;
1121 }
1122
1123 static Pixmap
1124 subpixmap (Display *dpy, Window window, Pixmap src,
1125            int w, int h, int y, int depth)
1126 {
1127     XGCValues gcv;
1128     Pixmap dest = XCreatePixmap (dpy, window, w, h, depth);
1129     GC gc = XCreateGC (dpy, src, 0, &gcv);
1130     XCopyArea (dpy, src, dest, gc, 0, y, w, h, 0, 0);
1131     XFreeGC (dpy, gc);
1132     return dest;
1133 }
1134
1135
1136 /* Load the ghost pixmaps and their mask. */
1137 static void
1138 load_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1139 {
1140     pacmangamestruct *pp = *ps;
1141     Display *display = *dpy;
1142     Pixmap sprites, sprites_mask;
1143     int i, j, k, /* m, */ sw, sh, srcy;
1144 /*    int w = pp->spritexs;
1145     int h = pp->spriteys;*/
1146     GC gc = 0;
1147 /*    Pixmap temp;*/
1148     XGCValues gcv;
1149     XWindowAttributes xgwa;
1150
1151     XGetWindowAttributes (display, window, &xgwa);
1152
1153     sprites = image_data_to_pixmap (display, window,
1154                                     pacman_png, sizeof(pacman_png), &sw, &sh,
1155                                     &sprites_mask);
1156     if (!sprites || !sprites_mask) abort();
1157
1158     srcy = 0;
1159
1160     gc = XCreateGC (display, sprites_mask, 0, &gcv);
1161
1162     pp->ghostMask = subpixmap (display, window, sprites_mask,
1163                                sw, sw, srcy, 1);
1164     pp->ghostMask = scale_pixmap (&display, gc, pp->ghostMask,
1165                                   pp->spritexs, pp->spriteys);
1166
1167     for (i = 0; i < 4; i++) {
1168         /* m = 0; */
1169         for (j = 0; j < MAXGDIR; j++) {
1170             for (k = 0; k < MAXGWAG; k++) {
1171                 pp->ghostPixmap[i][j][k] =
1172                     subpixmap (display, window, sprites, sw, sw, srcy,
1173                                xgwa.depth);
1174                 pp->ghostPixmap[i][j][k] =
1175                     scale_pixmap (&display, pp->stippledGC,
1176                                   pp->ghostPixmap[i][j][k], pp->spritexs,
1177                                   pp->spriteys);
1178                 /* m++; */
1179                 srcy += sw;
1180                 if (srcy >= sh) abort();
1181             }
1182         }
1183     }
1184
1185     /* load the scared ghost */
1186     /* m = 0; */
1187     for (i = 0; i < MAXGFLASH; i++) {
1188         for (j = 0; j < MAXGWAG; j++) {
1189             pp->s_ghostPixmap[i][j] =
1190                 subpixmap (display, window, sprites, sw, sw, srcy,
1191                            xgwa.depth);
1192             /* m++; */
1193             pp->s_ghostPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1194                                                     pp->s_ghostPixmap[i][j],
1195                                                     pp->spritexs,
1196                                                     pp->spriteys);
1197             srcy += sw;
1198             if (srcy >= sh) abort();
1199         }
1200     }
1201
1202     /* load the ghost eyes */
1203     for (i = 0; i < MAXGDIR; i++) {
1204         pp->ghostEyes[i] =
1205             subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1206         pp->ghostEyes[i] = scale_pixmap (&display, pp->stippledGC,
1207                                          pp->ghostEyes[i],
1208                                          pp->spritexs,
1209                                          pp->spriteys);
1210         srcy += sw;
1211         if (srcy >= sh) abort();
1212     }
1213
1214
1215     /* Load the pacman pixmaps and their mask. */
1216
1217     /* m = 0; */
1218     for (i = 0; i < 4; i++) {
1219         for (j = 0; j < MAXMOUTH; j++) {
1220             pp->pacmanPixmap[i][j] =
1221                 subpixmap (display, window, sprites, sw, sw, srcy, 
1222                            xgwa.depth);
1223             pp->pacmanMask[i][j] =
1224                 subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1225             /* m++; */
1226             pp->pacmanPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1227                                                    pp->pacmanPixmap[i][j],
1228                                                    pp->spritexs,
1229                                                    pp->spriteys);
1230             pp->pacmanMask[i][j] =
1231                 scale_pixmap (&display, gc, pp->pacmanMask[i][j],
1232                               pp->spritexs, pp->spriteys);
1233             srcy += sw;
1234             if (srcy >= sh) abort();
1235         }
1236     }
1237     
1238     /* Load pacman death sequence */
1239     for ( i = 0; i < PAC_DEATH_FRAMES; i++ ){
1240         if (srcy > sh - sw) abort();
1241         pp->pacman_ds[i] = 
1242             subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1243         pp->pacman_ds_mask[i] =
1244             subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1245         
1246         pp->pacman_ds[i] = scale_pixmap ( &display, pp->stippledGC,
1247                                           pp->pacman_ds[i],
1248                                           pp->spritexs,
1249                                           pp->spriteys);
1250         pp->pacman_ds_mask[i] = 
1251             scale_pixmap (&display, gc, pp->pacman_ds_mask[i],
1252                           pp->spritexs, pp->spriteys);
1253         srcy += sw;
1254     }
1255     XFreeGC (*dpy, gc);
1256     XFreePixmap (*dpy, sprites);
1257     XFreePixmap (*dpy, sprites_mask);
1258 }
1259
1260
1261 /* Hook function, sets state to initial position. */
1262 ENTRYPOINT void
1263 init_pacman (ModeInfo * mi)
1264 {
1265     Display *display = MI_DISPLAY (mi);
1266     Window window = MI_WINDOW (mi);
1267     long size = MI_SIZE (mi);
1268     pacmangamestruct *pp;
1269     XGCValues gcv;
1270     int i, j, k;
1271
1272     MI_INIT (mi, pacman_games);
1273     pp = &pacman_games[MI_SCREEN (mi)];
1274
1275     pp->width = (unsigned short) MI_WIDTH (mi);
1276     pp->height = (unsigned short) MI_HEIGHT (mi);
1277     for (i = 0; i < 4; i++) {
1278         for (j = 0; j < MAXGDIR; j++) {
1279             for (k = 0; k < MAXGWAG; k++) {
1280                 if (pp->ghostPixmap[i][j][k] != None) {
1281                     XFreePixmap (display, pp->ghostPixmap[i][j][k]);
1282                     pp->ghostPixmap[i][j][k] = None;
1283                     pp->graphics_format = 0 /*IS_NONE */ ;
1284                 }
1285             }
1286         }
1287     }
1288
1289     for (i = 0; i < MAXGFLASH; i++) {
1290         for (j = 0; j < MAXGWAG; j++) {
1291             if (pp->s_ghostPixmap[i][j] != None) {
1292                 XFreePixmap (display, pp->s_ghostPixmap[i][j]);
1293                 pp->s_ghostPixmap[i][j] = None;
1294             }
1295         }
1296     }
1297
1298     if (size == 0 ||
1299         MINGRIDSIZE * size > (int) pp->width ||
1300         MINGRIDSIZE * size > (int) pp->height) {
1301         double scale = MIN (pp->width / LEVWIDTH, pp->height / LEVHEIGHT);
1302
1303         if (pp->width > pp->height * 5 ||  /* weird window aspect ratio */
1304             pp->height > pp->width * 5)
1305             scale = 0.8 * (pp->width / pp->height
1306                            ? pp->width / (double) pp->height
1307                            : pp->height / (double) pp->width);
1308         pp->ys = MAX (scale, 1);
1309         pp->xs = pp->ys;
1310     }
1311     else {
1312         if (size < -MINSIZE)
1313             pp->ys = (short) (NRAND (MIN (-size, MAX (MINSIZE,
1314                                                       MIN (pp->width,
1315                                                            pp->height) /
1316                                                       MINGRIDSIZE))
1317                                      - MINSIZE + 1) + MINSIZE);
1318         else if (size < MINSIZE)
1319             pp->ys = MINSIZE;
1320         else
1321             pp->ys = (short) (MIN (size,
1322                                    MAX (MINSIZE, MIN (pp->width, pp->height) /
1323                                         MINGRIDSIZE)));
1324         pp->xs = pp->ys;
1325     }
1326
1327
1328     pp->wallwidth = (unsigned int) (pp->xs + pp->ys) >> 4;
1329     if (pp->wallwidth < 1)
1330         pp->wallwidth = 1;
1331     pp->incx = (pp->xs >> 3) + 1;
1332     pp->incy = (pp->ys >> 3) + 1;
1333     pp->ncols = (unsigned short) MAX (LEVWIDTH, 2);
1334     pp->nrows = (unsigned short) MAX (LEVHEIGHT, 2);
1335     pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1336     pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1337     pp->spritexs = MAX (pp->xs + (pp->xs >> 1) - 1, 1);
1338     pp->spriteys = MAX (pp->ys + (pp->ys >> 1) - 1, 1);
1339     pp->spritedx = (pp->xs - pp->spritexs) >> 1;
1340     pp->spritedy = (pp->ys - pp->spriteys) >> 1;
1341     pp->old_pac_state = ps_chasing;
1342
1343     if (!pp->stippledGC) {
1344         gcv.foreground = MI_BLACK_PIXEL (mi);
1345         gcv.background = MI_BLACK_PIXEL (mi);
1346         if ((pp->stippledGC = XCreateGC (display, window,
1347                                          GCForeground | GCBackground,
1348                                          &gcv)) == None) {
1349             free_pacman (mi);
1350             return;
1351         }
1352     }
1353
1354 #ifdef HAVE_JWXYZ
1355     jwxyz_XSetAntiAliasing (display, pp->stippledGC, False);
1356 #endif
1357
1358     load_pixmaps (&display, window, &pp);
1359
1360     pp->pacman.lastbox = START;
1361     pp->pacman.mouthdirection = 1;
1362     pp->pacman.nextcol = NOWHERE;
1363     pp->pacman.nextrow = NOWHERE;
1364
1365     if (pp->ghosts != NULL) {
1366         free (pp->ghosts);
1367         pp->ghosts = (ghoststruct *) NULL;
1368     }
1369     pp->nghosts = GHOSTS;
1370
1371     if (!pp->ghosts)
1372         if ((pp->ghosts = (ghoststruct *) calloc ((size_t) pp->nghosts,
1373                                                   sizeof (ghoststruct))) ==
1374             NULL) {
1375             free_pacman (mi);
1376             return;
1377         }
1378
1379     pp->pacman.mouthstage = MAXMOUTH - 1;
1380
1381     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1382     repopulate (mi);
1383 }
1384
1385 /* Callback function called for each tick.  This is the complete machinery of
1386    everything that moves. */
1387 ENTRYPOINT void
1388 draw_pacman (ModeInfo * mi)
1389 {
1390     unsigned int g;
1391     pacmangamestruct *pp;
1392
1393     if (pacman_games == NULL)
1394         return;
1395     pp = &pacman_games[MI_SCREEN (mi)];
1396     if (pp->ghosts == NULL)
1397         return;
1398
1399     pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1400     pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1401     pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1402     pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1403
1404     if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1405         pacman_update (mi, pp, &(pp->pacman));
1406         check_death (mi, pp);
1407         pp->pacman.delta.x = pp->incx;
1408         pp->pacman.delta.y = pp->incy;
1409     }
1410
1411     if (pp->pacman.delta.x > pp->xs + pp->incx)
1412         pp->pacman.delta.x = pp->xs + pp->incx;
1413     if (pp->pacman.delta.y > pp->ys + pp->incy)
1414         pp->pacman.delta.y = pp->ys + pp->incy;
1415
1416     for (g = 0; g < pp->nghosts; g++) {
1417         pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % pp->ghosts[g].speed;
1418         pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % pp->ghosts[g].speed;
1419         pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? pp->incx : 0;
1420         pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? pp->incy : 0; 
1421         
1422         if (pp->ghosts[g].delta.x >= pp->xs &&
1423             pp->ghosts[g].delta.y >= pp->ys) {
1424             pacman_ghost_update (pp, &(pp->ghosts[g]));
1425             pp->ghosts[g].delta.x = pp->incx;
1426             pp->ghosts[g].delta.y = pp->incy;
1427         }
1428
1429         if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1430             pp->ghosts[g].delta.x = pp->xs + pp->incx;
1431         if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1432             pp->ghosts[g].delta.y = pp->ys + pp->incy;
1433     }
1434     pacman_tick (mi);
1435 }
1436
1437 #ifndef STANDALONE
1438 /* Refresh current level. */
1439 ENTRYPOINT void
1440 refresh_pacman (ModeInfo * mi)
1441 {
1442     drawlevel (mi);
1443     pacman_tick (mi);
1444 }
1445 #endif
1446
1447 ENTRYPOINT void
1448 reshape_pacman(ModeInfo * mi, int width, int height)
1449 {
1450     pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1451     pp->width  = width;
1452     pp->height = height;
1453     pp->xb = (pp->width  - pp->ncols * pp->xs) >> 1;
1454     pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1455     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1456     /* repopulate (mi); */
1457     drawlevel (mi);
1458 }
1459
1460 #ifndef STANDALONE
1461 /* Callback to change level. */
1462 ENTRYPOINT void
1463 change_pacman (ModeInfo * mi)
1464 {
1465     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1466     repopulate (mi);
1467 }
1468 #endif /* !STANDALONE */
1469
1470
1471 XSCREENSAVER_MODULE ("Pacman", pacman)
1472
1473 #endif /* MODE_pacman */