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