http://se.aminet.net/pub/Linux/distributions/slackware/slackware-10.1/source/xap...
[xscreensaver] / hacks / pacman.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
3
4 /*static const char sccsid[] = "@(#)pacman.c    5.00 2000/11/01 xlockmore";*/
5
6 /*-
7  * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
8  *
9  * Permission to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation.
14  *
15  * This file is provided AS IS with no warranties of any kind.  The author
16  * shall have no liability with respect to the infringement of copyrights,
17  * trade secrets or any patents by this file or any part thereof.  In no
18  * event will the author be liable for any lost revenue or profits or
19  * other special, indirect and consequential damages.
20  *
21  * Revision History:
22  * 15-Aug-2004: Added support for pixmap pacman. Jeremy English jenglish@myself.com
23  * 11-Aug-2004: Added support for pixmap ghost. jenglish@myself.com
24  * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.  
25  *              splitted up code into several files.  Retouched AI code, cleaned
26  *              up code.
27  *  3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
28  * 26-Nov-2001: Random level generator added
29  * 01-Nov-2000: Allocation checks
30  * 04-Jun-1997: Compatible with xscreensaver
31  *
32  */
33
34 /* TODO:
35    1. add "bonus" dots
36    2. think of a better level generation algorithm
37 */
38
39 #define DEF_TRACKMOUSE "False"
40
41 #ifdef STANDALONE
42 #       define MODE_pacman
43 #       define PROGCLASS "Pacman"
44 #       define HACK_INIT init_pacman
45 #       define HACK_DRAW draw_pacman
46 #       define pacman_opts xlockmore_opts
47 #       define DEFAULTS "*delay: 10000 \n" \
48                                         "*size: 0 \n" \
49                                         "*ncolors: 6 \n" \
50                                         "*trackmouse: " DEF_TRACKMOUSE "\n"
51 #       define UNIFORM_COLORS
52 #       define BRIGHT_COLORS
53
54 #       include "xlockmore.h"           /* in xscreensaver distribution */
55 #else /* STANDALONE */
56 #       include "xlock.h"               /* in xlockmore distribution */
57 #endif /* STANDALONE */
58
59 #ifdef MODE_pacman
60
61 #include "pacman.h"
62 #include "pacman_ai.h"
63 #include "pacman_level.h"
64
65 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
66 #define USE_PIXMAP
67 #include "xpm-pixmap.h"
68 #else
69 #if defined(USE_PIXMAP)
70 #undef USE_PIXMAP
71 #endif
72 #endif
73
74 #if defined(USE_PIXMAP)
75 # include "images/pacman/ghost-u1.xpm"
76 # include "images/pacman/ghost-u2.xpm"
77 # include "images/pacman/ghost-r1.xpm"
78 # include "images/pacman/ghost-r2.xpm"
79 # include "images/pacman/ghost-l1.xpm"
80 # include "images/pacman/ghost-l2.xpm"
81 # include "images/pacman/ghost-d1.xpm"
82 # include "images/pacman/ghost-d2.xpm"
83 # include "images/pacman/ghost-mask.xpm" /* Used to clean up the dust left by wag. */
84 # include "images/pacman/pacman-u1.xpm"
85 # include "images/pacman/pacman-u2.xpm"
86 # include "images/pacman/pacman-r1.xpm"
87 # include "images/pacman/pacman-r2.xpm"
88 # include "images/pacman/pacman-l1.xpm"
89 # include "images/pacman/pacman-l2.xpm"
90 # include "images/pacman/pacman-d1.xpm"
91 # include "images/pacman/pacman-d2.xpm"
92 # include "images/pacman/pacman-0.xpm"
93 #endif
94
95 #ifdef DISABLE_INTERACTIVE
96 ModeSpecOpt pacman_opts = {
97         0, 
98         (XrmOptionDescRec *) NULL, 
99         0, 
100         (argtype *) NULL, 
101         (OptionStruct *) NULL
102 };
103 #else
104 static XrmOptionDescRec opts[] =
105 {
106         {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
107         {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
108 };
109
110 static argtype vars[] =
111 {
112         {&trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
113 };
114
115 static OptionStruct desc[] =
116 {
117         {"-/+trackmouse", "turn on/off the tracking of the mouse"}
118 };
119
120 ModeSpecOpt pacman_opts =
121 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
122 #endif
123
124 #ifdef USE_MODULES
125 ModStruct   pacman_description = { 
126         "pacman",               /* *cmdline_arg; */
127         "init_pacman",          /* *init_name; */
128         "draw_pacman",          /* *callback_name; */
129         "release_pacman",       /* *release_name; */
130         "refresh_pacman",       /* *refresh_name; */
131         "change_pacman",        /* *change_name; */
132         (char *) NULL,          /* *unused_name; */
133         &pacman_opts,           /* *msopts */
134         10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
135 };
136
137 #endif
138
139 Bool trackmouse;
140 pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;
141
142
143 static void repopulate(ModeInfo * mi);
144 static void drawlevel(ModeInfo * mi);
145
146
147 static void
148 free_pacman(Display *display, pacmangamestruct *pp)
149 {
150         int         dir, mouth, i, j, k;
151
152         if (pp->ghosts != NULL) {
153                 free(pp->ghosts);
154                 pp->ghosts = (ghoststruct *) NULL;
155         }
156         if (pp->stippledGC != None) {
157             XFreeGC(display, pp->stippledGC);
158                 pp->stippledGC = None;
159         }
160         for (i = 0; i < 4; i++){
161           for (j = 0; j < MAXGDIR; j++){
162                 for (k = 0; k < MAXGWAG; k++){
163                   if (pp->ghostPixmap[i][j][k] != None) {
164                         XFreePixmap(display, pp->ghostPixmap[i][j][k]);
165                         pp->ghostPixmap[i][j][k] = None;
166                   }
167                 }
168           }
169         }
170         for (dir = 0; dir < 4; dir++)
171                 for (mouth = 0; mouth < MAXMOUTH; mouth++)
172                         if (pp->pacmanPixmap[dir][mouth] != None) {
173                                 XFreePixmap(display, 
174                                         pp->pacmanPixmap[dir][mouth]);
175                                 pp->pacmanPixmap[dir][mouth] = None;
176                         }
177 }
178
179
180 /* Checks for death of any ghosts/pacman and updates.  It also makes a new
181    level if all ghosts are dead or all dots are eaten. */
182 static void
183 check_death(ModeInfo * mi, pacmangamestruct *pp)
184 {
185         Display *display =      MI_DISPLAY(mi);
186         Window   window  =      MI_WINDOW(mi);
187         unsigned int    ghost;
188         int     alldead;
189
190         alldead = 1;
191         for (ghost = 0; ghost < pp->nghosts; ghost++) {
192                 if (pp->ghosts[ghost].dead == True)
193                         continue;
194
195                 if ((pp->ghosts[ghost].nextrow == NOWHERE &&
196                      pp->ghosts[ghost].nextcol == NOWHERE)) {
197                         alldead = 0;
198                         continue;
199                 }
200
201                 if (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
202                 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
203                     ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
204                      (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
205                      (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
206                      (pp->ghosts[ghost].col == pp->pacman.nextcol))) {
207                         pp->ghosts[ghost].dead = 1;
208                         XSetForeground(display, 
209                                              pp->stippledGC, 
210                                              MI_BLACK_PIXEL(mi));
211                         XFillRectangle(display, window, 
212                                             pp->stippledGC,
213                                             pp->ghosts[ghost].cf, 
214                                             pp->ghosts[ghost].rf, 
215                                             pp->spritexs, 
216                                             pp->spriteys);
217
218                 } else
219                         alldead = 0;
220         }
221
222         if (alldead == 1 || pp->dotsleft == 0)
223                 repopulate(mi);
224 }
225
226 /* Resets state of ghosts + pacman.  Creates a new level, draws that level. */
227 static void
228 repopulate(ModeInfo * mi)
229 {
230         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
231         unsigned int ghost;
232         int i = createnewlevel(mi);
233
234         MI_CLEARWINDOW(mi);
235         drawlevel(mi);
236
237         pp->gamestate = GHOST_DANGER;
238
239         pp->pacman.row = (LEVHEIGHT + JAILHEIGHT)/2 - i;
240         pp->pacman.col = (LEVWIDTH/2);
241         pp->pacman.nextrow = NOWHERE; 
242         pp->pacman.nextcol = NOWHERE;
243         pp->pacman.cf = NOWHERE;
244         pp->pacman.rf = NOWHERE;
245         pp->pacman.oldcf = NOWHERE;
246         pp->pacman.oldrf = NOWHERE;
247         pp->pacman.oldlx = NOWHERE;
248         pp->pacman.oldly = NOWHERE;
249         pp->pacman.aistate = ps_eating;
250         pp->pacman.cur_trace = 0;
251         pp->pacman.roundscore = 0;
252         pp->pacman.speed = 4;
253         pp->pacman.lastturn = 0;
254         pp->pacman.delta.x = 0;
255         pp->pacman.delta.y = 0;
256         pac_clear_trace(&(pp->pacman));
257
258         for (ghost = 0; ghost < pp->nghosts; ghost++) {
259                 pp->ghosts[ghost].col = (LEVWIDTH/2);
260                 pp->ghosts[ghost].row = (LEVHEIGHT/2);
261                 pp->ghosts[ghost].nextcol = NOWHERE;
262                 pp->ghosts[ghost].nextrow = NOWHERE;
263                 pp->ghosts[ghost].dead = 0;
264                 pp->ghosts[ghost].lastbox = START;
265                 pp->ghosts[ghost].cf = NOWHERE;
266                 pp->ghosts[ghost].rf = NOWHERE;
267                 pp->ghosts[ghost].oldcf = NOWHERE;
268                 pp->ghosts[ghost].oldrf = NOWHERE;
269                 pp->ghosts[ghost].aistate = inbox;
270                 pp->ghosts[ghost].timeleft = ghost * 50;
271                 pp->ghosts[ghost].speed = 3;
272                 pp->ghosts[ghost].delta.x = 0;
273                 pp->ghosts[ghost].delta.y = 0;
274
275                 ghost_update(pp, &(pp->ghosts[ghost]));
276         }
277         check_death(mi, pp);
278         pac_update(mi, pp, &(pp->pacman));
279 }
280
281 /* Sets the color to the color of a wall. */
282 static void 
283 setwallcolor(ModeInfo * mi) 
284 {
285         Display    *display = MI_DISPLAY(mi);
286         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
287
288         if (MI_NPIXELS(mi) > 2)
289                 XSetForeground(display, pp->stippledGC, 
290                                 MI_PIXEL(mi, BLUE));
291         else
292                 XSetForeground(display, pp->stippledGC, 
293                                 MI_WHITE_PIXEL(mi));
294 }
295
296 /* Sets the color to the color of a dot. */
297 static void 
298 setdotcolor(ModeInfo * mi) 
299 {
300         Display    *display = MI_DISPLAY(mi);
301         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
302
303         XSetForeground(display, pp->stippledGC, 
304                         MI_WHITE_PIXEL(mi));
305 }
306
307 /* Draws a block in the level at the specified x and y locations. */
308 static void
309 drawlevelblock(ModeInfo * mi, pacmangamestruct *pp, 
310                 const unsigned x, const unsigned y)
311 {
312         Display    *display = MI_DISPLAY(mi);
313         Window      window = MI_WINDOW(mi);
314         int dx = 0, dy = 0;
315
316         if (pp->xs % 2 == 1) dx = -1;
317         if (pp->ys % 2 == 1) dy = -1;
318
319         XSetFillStyle(display, pp->stippledGC, FillSolid);
320         XSetLineAttributes(display, pp->stippledGC, pp->wallwidth,
321                         LineSolid, CapRound, JoinMiter);
322
323         if (pp->xs < 2 || pp->ys < 2) {
324                 switch(pp->level[y*LEVWIDTH + x]) {
325                         case ' ':
326                         case '=':
327                                 break;
328                         case '.':
329                                 setdotcolor(mi);
330                                 (void)XDrawPoint(display, window,
331                                                  pp->stippledGC,
332                                                  x * pp->xs + pp->xb, 
333                                                  y * pp->ys + pp->yb);
334                                 break;
335                         default:
336                                 setwallcolor(mi);
337                                 (void)XDrawPoint(display, window,
338                                                  pp->stippledGC,
339                                                  x * pp->xs + pp->xb, 
340                                                  y * pp->ys + pp->yb);
341                 }
342
343                 return;
344         }
345
346         switch (pp->level[y*LEVWIDTH + x]) {
347                 case ' ':
348                 case '=':
349                         break;
350
351                 case '.':
352                         setdotcolor(mi);
353                         if (pp->xs < 8 || pp->ys < 8) {
354                                 (void)XDrawPoint(display, window,
355                                                  pp->stippledGC,
356                                                  x * pp->xs + pp->xb +
357                                                  pp->xs/2, 
358                                                  y * pp->ys + pp->yb +
359                                                  pp->ys/2);
360                                 break;
361                         }
362
363                         (void)XDrawArc(display, window, pp->stippledGC,
364                                 (pp->xs * x) + 
365                                         (pp->xs / 2) - 
366                                         (pp->xs > 32 ? (pp->xs / 16) : 1) +
367                                         pp->xb,
368                                 (pp->ys * y) + 
369                                         (pp->ys / 2) -
370                                         (pp->ys > 32 ? (pp->ys / 16) : 1) +
371                                         pp->yb,
372                                 (pp->xs > 32 ? (pp->xs / 32) : 1),
373                                 (pp->ys > 32 ? (pp->ys / 32) : 1),
374                                 0, 23040);
375                         break;
376
377                 case '-':
378                         setwallcolor(mi);
379                         (void)XDrawLine(display, window, pp->stippledGC,
380                                 (pp->xs * x) + pp->xb,
381                                 (pp->ys * y) + (pp->ys / 2) + pp->yb,
382                                 (pp->xs * (x + 1)) + pp->xb,
383                                 (pp->ys * y) + (pp->ys / 2) + pp->yb);
384                         break;
385
386                 case '|':
387                         setwallcolor(mi);
388                         (void)XDrawLine(display, window, pp->stippledGC,
389                                 (pp->xs * x) + (pp->xs / 2) + pp->xb,
390                                 (pp->ys * y) + pp->yb,
391                                 (pp->xs * x) + (pp->xs / 2) + pp->xb,
392                                 (pp->ys * (y + 1)) + pp->yb);
393                         break;
394
395                 case '_':
396                         setwallcolor(mi);
397                         (void)XDrawArc(display, window, pp->stippledGC,
398                                 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
399                                 (pp->ys * y) + (pp->ys / 2) + pp->yb,
400                                 pp->xs, pp->ys,
401                                 0*64, 90*64);
402                         break;
403
404                 case ',':
405                         setwallcolor(mi);
406                         (void)XDrawArc(display, window, pp->stippledGC,
407                                 (pp->xs * x) + (pp->ys / 2) + pp->xb,
408                                 (pp->ys * y) + (pp->ys / 2) + pp->yb,
409                                 pp->xs, pp->ys,
410                                 90*64, 90*64);
411                         break;
412
413                 case '`':
414                         setwallcolor(mi);
415                         (void)XDrawArc(display, window, pp->stippledGC,
416                                 (pp->xs * x) + (pp->ys / 2) + pp->xb,
417                                 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
418                                 pp->xs, pp->ys,
419                                 180*64, 90*64);
420                         break;
421
422                 case '\'':
423                         setwallcolor(mi);
424                         (void)XDrawArc(display, window, pp->stippledGC,
425                                 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
426                                 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
427                                 pp->xs, pp->ys,
428                                 270*64, 90*64);
429                         break;
430
431         }
432 }
433
434 /* Draws a complete level. */
435 static void
436 drawlevel(ModeInfo * mi)
437
438         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
439         unsigned int x, y;
440
441         for (y = 0; y < LEVHEIGHT; y++)
442                 for (x = 0; x < LEVWIDTH; x++)
443                         drawlevelblock(mi, pp, x, y);
444 }
445
446 /* There is some overlap so it can be made more efficient */
447 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
448  if (yl<y) \
449   (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
450   XFillRectangle(d,w,g,xl,yl,xs,ys); \
451  else if (yl>y) \
452   (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
453   XFillRectangle(d,w,g,xl,yl,xs,ys); \
454  if (xl<x) \
455   (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
456   XFillRectangle(d,w,g,xl,yl,xs,ys); \
457  else if (xl>x) \
458   (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
459   XFillRectangle(d,w,g,xl,yl,xs,ys)
460
461
462 /* Draws the pacman sprite, removing the previous location. */
463 #if defined(USE_PIXMAP)
464
465 static void
466 draw_pacman_sprite(ModeInfo * mi)
467 {
468         Display    *display = MI_DISPLAY(mi);
469         Window      window = MI_WINDOW(mi);
470         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
471         unsigned int dir = 0;
472         int old_mask_dir = 0;
473         int old_mask_mouth = 0;
474         static int mouth = 0;
475         static int mouth_delay = 0;
476         static int open_mouth = 0;
477
478 #define MAX_MOUTH_DELAY 10
479
480         pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x * 
481                 pp->pacman.cfactor + pp->xb + pp->spritedx;
482         pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y * 
483                 pp->pacman.rfactor + pp->yb + pp->spritedy;
484
485         dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
486                ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
487
488         if (mouth_delay == MAX_MOUTH_DELAY){
489           if (mouth == (MAXMOUTH - 1)|| mouth == 0){
490                 open_mouth = !open_mouth;
491           }
492           open_mouth ? mouth++ : mouth--;
493           mouth_delay = 0;
494         }else {
495           mouth_delay++;
496         }
497
498         XSetForeground(display, 
499                                    pp->stippledGC, 
500                                    MI_BLACK_PIXEL(mi));
501
502         XSetClipMask(display, pp->stippledGC, pp->pacmanMask[old_mask_dir][old_mask_mouth]);
503         XSetClipOrigin(display, pp->stippledGC, 
504                                    pp->pacman.oldcf, pp->pacman.oldrf);
505         XFillRectangle(display, 
506                                    window, 
507                                    pp->stippledGC,
508                                    pp->pacman.oldcf, 
509                                    pp->pacman.oldrf, 
510                                    pp->spritexs, pp->spriteys);
511         XSetClipMask(display, pp->stippledGC, pp->pacmanMask[dir][mouth]);
512         XSetClipOrigin(display, pp->stippledGC, 
513                                    pp->pacman.cf, pp->pacman.rf);
514         XCopyArea(display, pp->pacmanPixmap[dir][mouth], window, 
515                           pp->stippledGC,0,0,pp->spritexs,pp->spriteys,
516                           pp->pacman.cf, pp->pacman.rf);
517         XSetClipMask(display, pp->stippledGC, None);
518         pp->pacman.oldcf = pp->pacman.cf;
519         pp->pacman.oldrf = pp->pacman.rf;
520         old_mask_dir = dir;
521         old_mask_mouth = mouth;
522 }
523
524
525
526 static void
527 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost){
528         Display    *display = MI_DISPLAY(mi);
529         Window      window = MI_WINDOW(mi);
530         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
531         static int wag = 0;
532 #define MAX_WAG_COUNT 50
533         static int wag_count = 0;
534         unsigned int dir = 0;
535         
536         dir = (ABS(pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
537                ABS(pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
538
539         pp->ghosts[ghost].cf = 
540                 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x * 
541                 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
542         pp->ghosts[ghost].rf = 
543                 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y * 
544                 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
545         XSetForeground(display, 
546                                    pp->stippledGC, 
547                                    MI_BLACK_PIXEL(mi));
548
549         XSetClipMask(display, pp->stippledGC, pp->ghostMask);
550         XSetClipOrigin(display, pp->stippledGC, 
551                                    pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
552         XFillRectangle(display, 
553                                    window, 
554                                    pp->stippledGC,
555                                    pp->ghosts[ghost].oldcf, 
556                                    pp->ghosts[ghost].oldrf, 
557                                    pp->spritexs, pp->spriteys);
558         drawlevelblock(mi, pp, 
559                                    (unsigned int)pp->ghosts[ghost].col, 
560                                    (unsigned int)pp->ghosts[ghost].row);
561         XSetClipOrigin(display, pp->stippledGC, 
562                                    pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
563         XCopyArea(display, pp->ghostPixmap[ghost][dir][wag], window, 
564                           pp->stippledGC,0,0,pp->spritexs,pp->spriteys,
565                           pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
566         XSetClipMask(display, pp->stippledGC, None);
567         pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
568         pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
569         if (wag_count++ == MAX_WAG_COUNT){
570           wag = !wag;
571           wag_count = 0;
572         }
573 }
574
575 #else /* USE_PIXMAP */
576
577 /* Draws the pacman sprite, removing the previous location. */
578 static void
579 draw_pacman_sprite(ModeInfo * mi)
580 {
581         Display *display = MI_DISPLAY(mi);
582         Window window  = MI_WINDOW(mi);
583         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
584         unsigned int dir;
585
586         pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x * 
587                 pp->pacman.cfactor + pp->xb + pp->spritedx;
588         pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y * 
589                 pp->pacman.rfactor + pp->yb + pp->spritedy;
590
591         dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
592                ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
593
594         XSetForeground(display, pp->stippledGC, 
595                              MI_BLACK_PIXEL(mi));
596         if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
597 #ifdef FLASH
598                 XFillRectangle(display, window, pp->stippledGC,
599                              pp->pacman.oldcf, pp->pacman.oldrf, 
600                              pp->spritexs, pp->spriteys);
601 #else
602                 ERASE_IMAGE(display, window, pp->stippledGC, 
603                             pp->pacman.cf,    pp->pacman.rf,
604                             pp->pacman.oldcf, pp->pacman.oldrf, 
605                             pp->spritexs, pp->spriteys);
606 #endif
607         }
608
609         XSetTSOrigin(display, pp->stippledGC, 
610                            pp->pacman.cf, pp->pacman.rf);
611         if (MI_NPIXELS(mi) > 2)
612                 XSetForeground(display, pp->stippledGC, 
613                                      MI_PIXEL(mi, YELLOW));
614         else
615                 XSetForeground(display, pp->stippledGC, 
616                                      MI_WHITE_PIXEL(mi));
617
618         XSetStipple(display, pp->stippledGC,
619                     pp->pacmanPixmap[dir][pp->pacman.mouthstage]);
620 #ifdef FLASH
621         XSetFillStyle(display, pp->stippledGC, FillStippled);
622 #else
623         XSetFillStyle(display, pp->stippledGC, 
624                             FillOpaqueStippled);
625 #endif
626         if (pp->xs < 2 || pp->ys < 2)
627                 XDrawPoint(display, window, pp->stippledGC,
628                                 pp->pacman.cf, pp->pacman.rf);
629         else
630                 XFillRectangle(display, window, pp->stippledGC,
631                                pp->pacman.cf, pp->pacman.rf, 
632                                pp->spritexs, pp->spriteys);
633         pp->pacman.mouthstage += pp->pacman.mouthdirection;
634         if ((pp->pacman.mouthstage >= MAXMOUTH) ||
635             (pp->pacman.mouthstage < 0)) {
636                 pp->pacman.mouthdirection *= -1;
637                 pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
638         }
639         pp->pacman.oldcf = pp->pacman.cf;
640         pp->pacman.oldrf = pp->pacman.rf;
641 }
642
643 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
644 static void
645 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost) {
646         Display    *display = MI_DISPLAY(mi);
647         Window      window = MI_WINDOW(mi);
648         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
649
650         pp->ghosts[ghost].cf = 
651                 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x * 
652                 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
653         pp->ghosts[ghost].rf = 
654                 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y * 
655                 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
656         
657         XSetForeground(display, 
658                              pp->stippledGC, 
659                              MI_BLACK_PIXEL(mi));
660         XFillRectangle(display, 
661                                    window, 
662                                    pp->stippledGC,
663                                    pp->ghosts[ghost].cf, 
664                                    pp->ghosts[ghost].rf, 
665                                    pp->spritexs, pp->spriteys);
666
667         if (pp->ghosts[ghost].oldcf != NOWHERE ||
668                 pp->ghosts[ghost].oldrf != NOWHERE) {
669 #ifdef FLASH
670                 XFillRectangle(display, window, 
671                                     pp->stippledGC, pp->ghosts[ghost].oldcf, 
672                                     pp->ghosts[ghost].oldrf, 
673                                     pp->spritexs, pp->spriteys);
674 #else
675                 ERASE_IMAGE(display, window, pp->stippledGC,
676                         pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
677                         pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf, 
678                         pp->spritexs, pp->spriteys);
679 #endif
680         }
681
682         drawlevelblock(mi, pp, 
683                 (unsigned int)pp->ghosts[ghost].col, 
684                 (unsigned int)pp->ghosts[ghost].row);
685
686         XSetTSOrigin(display, pp->stippledGC,
687                 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
688
689         if (MI_NPIXELS(mi) > 2)
690                 XSetForeground(display, 
691                      pp->stippledGC, 
692                      MI_PIXEL(mi, GREEN));
693         else
694                 XSetForeground(display, 
695                      pp->stippledGC, 
696                      MI_WHITE_PIXEL(mi));
697
698         XSetStipple(display, pp->stippledGC, 
699                           pp->ghostPixmap[0][0][0]);
700
701 #ifdef FLASH
702         XSetFillStyle(display, 
703                             pp->stippledGC, 
704                             FillStippled);
705 #else
706         XSetFillStyle(display, 
707                             pp->stippledGC, 
708                             FillOpaqueStippled);
709 #endif
710         if (pp->xs < 2 || pp->ys < 2)
711                 XDrawPoint(display, window, pp->stippledGC,
712                                 pp->ghosts[ghost].cf,
713                                 pp->ghosts[ghost].rf);
714         else
715                 XFillRectangle(display, 
716                                      window, 
717                                      pp->stippledGC,
718                                      pp->ghosts[ghost].cf, 
719                                      pp->ghosts[ghost].rf, 
720                                      pp->spritexs, pp->spriteys);
721
722         pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
723         pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
724 }
725 #endif /* USE_PIXMAP */
726
727 /* Does all drawing of moving sprites in the level. */
728 static void
729 pacman_tick(ModeInfo * mi)
730 {
731         Display    *display = MI_DISPLAY(mi);
732         pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
733         unsigned int ghost;
734
735         for (ghost = 0; ghost < pp->nghosts; ghost++) {
736                 if (pp->ghosts[ghost].dead == True)
737                         continue;
738                 draw_ghost_sprite(mi, ghost);
739         }
740
741         draw_pacman_sprite(mi);
742
743         (void)XFlush(display);
744 }
745
746 #if defined(USE_PIXMAP)
747 /*  Grabbed the scaling routine off of usenet. 
748  *  Changed it so that the information specific 
749  *  to the source pixmap does not have to be a parameter.
750  *
751  *  There is probably a better way to scale pixmaps.
752  *  From: Chris Fiddyment (cxf@itd.dsto.gov.au)
753  *  Subject: Scaling Pixmap Algorithm.
754  *  Newsgroups: comp.graphics.algorithms
755  *  Date: 1994-07-06 18:51:38 PST 
756  *  -jeremy
757  */
758
759 static Pixmap 
760 scale_pixmap( Display **dpy, GC gc, Pixmap source, int dwidth, int dheight)
761 {
762    Pixmap temp,dest;
763    int j,end;
764    float i;
765    float xscale, yscale;
766    unsigned int swidth, sheight;
767    Window window;
768    int x, y;
769    unsigned border_width_return, depth;
770    XGetGeometry(*dpy, source, &window, &x, &y, &swidth, &sheight, &border_width_return, &depth);
771    
772    xscale = (float) swidth / (float) dwidth;         /* Scaling factors */
773    yscale = (float) sheight / (float) dheight;
774  
775    dest = XCreatePixmap(*dpy,window,dwidth,dheight,depth);
776    if (!dest){
777          fprintf(stderr, "%s Could not scale image", progname);
778    }
779    temp = XCreatePixmap(*dpy,window,dwidth,sheight,depth);
780    if (!temp){
781          fprintf(stderr, "%s Could not scale image", progname);
782    }
783  
784    j = 0;
785    end = dwidth*xscale;
786    /* Scale width of source into temp pixmap */
787    for(i=0;i<end;i+=xscale)
788       XCopyArea(*dpy,source,temp,gc,i,0,1,sheight,j++,0);
789  
790    j = 0;
791    end = dheight*yscale;
792    /* Scale height of temp into dest pixmap */
793    for(i=0;i<end;i+=yscale)
794       XCopyArea(*dpy,temp,dest,gc,0,i,dwidth,1,0,j++);
795  
796    XFreePixmap( *dpy, temp );
797    return (Pixmap) dest;
798 }
799
800 static void
801 pacman_fail(char *s){
802   fprintf (stderr, "%s: %s\n", progname, s);
803   exit(1);
804 }  
805
806 /* Load the ghost pixmaps and their mask. */
807 static void
808 load_ghost_pixmaps(Display **dpy, Window window, pacmangamestruct **ps)
809 {
810   pacmangamestruct *pp = *ps;
811   Display *display = *dpy;
812   static char *colors[]= {
813         ".      c #FF0000", /*Red*/
814         ".  c #00FFDE", /*Blue*/
815         ".  c #FFB847", /*Orange*/
816         ".  c #FFB8DE", /*Pink*/
817   };
818   
819   static char **bits[] = {
820         ghost_u1_xpm, ghost_u2_xpm, ghost_r1_xpm, ghost_r2_xpm,
821         ghost_d1_xpm, ghost_d2_xpm, ghost_l1_xpm, ghost_l2_xpm
822   };
823   int i, j, k, m;
824   int w = pp->spritexs;
825   int h = pp->spriteys;
826   GC gc = 0;
827   Pixmap temp;
828   
829   for (i = 0; i < 4; i++){
830         m = 0;
831         for ( j = 0; j < MAXGDIR; j++){
832           for ( k = 0; k < MAXGWAG; k++){
833                 bits[m][2] = colors[i];
834                 pp->ghostPixmap[i][j][k] = xpm_data_to_pixmap (display, window, bits[m],
835                                                                                                                    &w, &h, &pp->ghostMask);
836
837                 if (!pp->ghostPixmap[i][j][k]) pacman_fail("Cannot load ghost images");
838
839                 pp->ghostPixmap[i][j][k] = scale_pixmap(&display, pp->stippledGC, 
840                                                                                                 pp->ghostPixmap[i][j][k], pp->spritexs, pp->spriteys);
841
842                 if (!pp->ghostPixmap[i][j][k]) pacman_fail("Cannot scale ghost images");
843                 m++;
844           }
845         }
846   }
847   /* We really only need a single mask. This saves the headache of getting the bottom of the ghost
848    * to clip just right. What we'll do is mask the top portion of the ghost, but the bottom of the
849    * the ghost will be solid. I did this by setting the pixels between the fringe of their sheets
850    * to black instead of none. -jeremy
851    */
852   temp = xpm_data_to_pixmap (display, window, ghost_mask_xpm,
853                                                          &w, &h, &pp->ghostMask);
854
855   if (!temp) pacman_fail("Cannot load temporary ghost image");
856   
857   temp = scale_pixmap(&display, pp->stippledGC, 
858                                           temp, pp->spritexs, pp->spriteys);
859
860   if (!temp) pacman_fail("Cannot scale temporary ghost image");
861
862   gc = XCreateGC(display, pp->ghostMask, 0, 0);
863
864   pp->ghostMask = scale_pixmap(&display, gc, pp->ghostMask, 
865                                                            pp->spritexs, pp->spriteys);
866   XFreePixmap(display, temp);
867 }
868
869 /* Load the pacman pixmaps and their mask. */
870 static void
871 load_pacman_pixmaps(Display **dpy, Window window, pacmangamestruct **ps)
872 {
873   pacmangamestruct *pp = *ps;
874   Display *display = *dpy;
875
876   static char **bits[] = {
877         pacman_0_xpm,  pacman_u1_xpm, pacman_u2_xpm,
878         pacman_0_xpm,  pacman_r1_xpm, pacman_r2_xpm,
879         pacman_0_xpm,  pacman_d1_xpm, pacman_d2_xpm,
880         pacman_0_xpm,  pacman_l1_xpm, pacman_l2_xpm
881   };
882   int i, j, m;
883   int w = pp->spritexs;
884   int h = pp->spriteys;
885   GC gc = 0;
886
887   m = 0;  
888   for (i = 0; i < 4; i++){
889         for ( j = 0; j < MAXMOUTH; j++){
890           pp->pacmanPixmap[i][j] = xpm_data_to_pixmap (display, window, bits[m++],
891                                                                                                          &w, &h, &pp->pacmanMask[i][j]);
892
893           if (!pp->pacmanPixmap[i][j]) pacman_fail("Cannot load pacman pixmap.");
894
895           pp->pacmanPixmap[i][j] = scale_pixmap(&display, pp->stippledGC, 
896                                                                                                 pp->pacmanPixmap[i][j], pp->spritexs, pp->spriteys);
897
898           if (!pp->pacmanPixmap[i][j]) pacman_fail("Cannot scale pacman pixmap.");
899
900           if (!gc)
901                 gc = XCreateGC(display, pp->pacmanMask[i][j], 0, 0);
902
903           pp->pacmanMask[i][j] = scale_pixmap(&display, gc, pp->pacmanMask[i][j], 
904                                                                    pp->spritexs, pp->spriteys); 
905         }
906   }
907 }
908 #endif /* USE_PIXMAP */
909
910 /* Hook function, sets state to initial position. */
911 void
912 init_pacman(ModeInfo * mi)
913 {
914         Display    *display = MI_DISPLAY(mi);
915         Window      window = MI_WINDOW(mi);
916         int         size = MI_SIZE(mi);
917         pacmangamestruct *pp;
918         XGCValues   gcv;
919         int i, j, k;
920
921 #if (! defined( USE_PIXMAP ))
922         GC          fg_gc, bg_gc;
923         XPoint      points[9];
924         int dir, mouth;
925 #endif
926
927         if (pacmangames == NULL) {
928                 if ((pacmangames = (pacmangamestruct *) 
929                                         calloc((size_t)MI_NUM_SCREENS(mi),
930                                          sizeof (pacmangamestruct))) == NULL)
931                         return;
932         }
933         pp = &pacmangames[MI_SCREEN(mi)];
934
935         pp->width = (unsigned short)MI_WIDTH(mi);
936         pp->height = (unsigned short)MI_HEIGHT(mi); 
937         for (i = 0; i < 4; i++){
938           for (j = 0; j < MAXGDIR; j++){
939                 for (k = 0; k < MAXGWAG; k++){
940                   if (pp->ghostPixmap[i][j][k] != None) {
941                         XFreePixmap(display, pp->ghostPixmap[i][j][k]);
942                         pp->ghostPixmap[i][j][k] = None;
943                         pp->graphics_format = 0 /*IS_NONE*/;
944                   }
945                 }
946           }
947         }
948         if (size == 0 ||
949                 MINGRIDSIZE * size > (int)pp->width ||
950                 MINGRIDSIZE * size > (int)pp->height) {
951
952                 pp->ys = pp->xs = MAX(MIN(pp->width/LEVWIDTH, 
953                                 pp->height/LEVHEIGHT), 1);
954         } else {
955                 if (size < -MINSIZE)
956                         pp->ys = (short)(NRAND( MIN( -size, MAX( MINSIZE, 
957                                    MIN( pp->width, pp->height) / MINGRIDSIZE)) 
958                                         - MINSIZE + 1) + MINSIZE);
959                 else if (size < MINSIZE)
960                         pp->ys = MINSIZE;
961                 else
962                         pp->ys = (short)(MIN(size, 
963                                      MAX(MINSIZE, MIN(pp->width, pp->height) /
964                                           MINGRIDSIZE)));
965                 pp->xs = pp->ys;
966         }
967
968         pp->wallwidth = (unsigned int)(pp->xs + pp->ys) >> 4;
969         if (pp->wallwidth < 1) pp->wallwidth = 1;
970         pp->incx = (pp->xs >> 3) + 1;
971         pp->incy = (pp->ys >> 3) + 1;
972         pp->ncols = (unsigned short)MAX(LEVWIDTH, 2);
973         pp->nrows = (unsigned short)MAX(LEVHEIGHT, 2);
974         pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
975         pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
976         pp->spritexs = MAX(pp->xs + (pp->xs >> 1) - 1, 1);
977         pp->spriteys = MAX(pp->ys + (pp->ys >> 1) - 1, 1);
978         pp->spritedx = (pp->xs - pp->spritexs) >> 1;
979         pp->spritedy = (pp->ys - pp->spriteys) >> 1;
980
981         if (!pp->stippledGC) {
982                 gcv.foreground = MI_BLACK_PIXEL(mi);
983                 gcv.background = MI_BLACK_PIXEL(mi);
984                 if ((pp->stippledGC = XCreateGC(display, window,
985                                 GCForeground | GCBackground, &gcv)) == None) {
986                         free_pacman(display, pp);
987                         return;
988                 }
989         }
990
991 #if defined(USE_PIXMAP)
992         load_ghost_pixmaps(&display,window,&pp);
993         load_pacman_pixmaps(&display,window,&pp);
994 #else
995         if ((pp->ghostPixmap[0][0][0] = XCreatePixmap(display, window,
996                 pp->spritexs, pp->spriteys, 1)) == None) {
997                 free_pacman(display, pp);
998                 return;
999         }
1000
1001         gcv.foreground = 0;
1002         gcv.background = 1;
1003         if ((bg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
1004                         GCForeground | GCBackground, &gcv)) == None) {
1005                 free_pacman(display, pp);
1006                 return;
1007         }
1008
1009         gcv.foreground = 1;
1010         gcv.background = 0;
1011         if ((fg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
1012                         GCForeground | GCBackground, &gcv)) == None) {
1013                 XFreeGC(display, bg_gc);
1014                 free_pacman(display, pp);
1015                 return;
1016         }
1017
1018 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
1019
1020         /* draw the triangles on the bottom (scalable) */
1021         SETPOINT(points[0], 1,                          pp->spriteys * 5 / 6);
1022         SETPOINT(points[1], pp->spritexs / 6,           pp->spriteys);
1023         SETPOINT(points[2], pp->spritexs / 3,           pp->spriteys * 5 / 6);
1024         SETPOINT(points[3], pp->spritexs / 2,           pp->spriteys);
1025         SETPOINT(points[4], pp->spritexs * 2 / 3,       pp->spriteys * 5 / 6);
1026         SETPOINT(points[5], pp->spritexs * 5 / 6,       pp->spriteys);
1027         SETPOINT(points[6], pp->spritexs,               pp->spriteys * 5 / 6);
1028         SETPOINT(points[7], pp->spritexs,               pp->spriteys / 2);
1029         SETPOINT(points[8], 1,                          pp->spriteys / 2);
1030
1031         XFillRectangle(display, pp->ghostPixmap[0][0][0], bg_gc,
1032                 0, 0, pp->spritexs, pp->spriteys);
1033         XFillArc(display, pp->ghostPixmap[0][0][0], fg_gc,
1034                 0, 0, pp->spritexs, pp->spriteys, 0, 11520);
1035         XFillPolygon(display, pp->ghostPixmap[0][0][0], fg_gc,
1036                 points, 9, Nonconvex, CoordModeOrigin);
1037         XFreeGC(display, bg_gc);
1038         XFreeGC(display, fg_gc);
1039
1040
1041         if (pp->pacmanPixmap[0][0] != None)
1042                 for (dir = 0; dir < 4; dir++)
1043                         for (mouth = 0; mouth < MAXMOUTH; mouth++)
1044                                 XFreePixmap(display, 
1045                                                   pp->pacmanPixmap[dir]
1046                                                         [mouth]);
1047
1048         for (dir = 0; dir < 4; dir++)
1049                 for (mouth = 0; mouth < MAXMOUTH; mouth++) {
1050                         if ((pp->pacmanPixmap[dir][mouth] = XCreatePixmap(
1051                                         display, MI_WINDOW(mi), 
1052                                         pp->spritexs, pp->spriteys, 1)) == 
1053                                         None) {
1054                                 free_pacman(display, pp);
1055                                 return;
1056                         }
1057                         gcv.foreground = 1;
1058                         gcv.background = 0;
1059                         if ((fg_gc = XCreateGC(display, pp->pacmanPixmap[dir][mouth],
1060                                         GCForeground | GCBackground, &gcv)) == None) {
1061                                 free_pacman(display, pp);
1062                                 return;
1063                         }
1064                         gcv.foreground = 0;
1065                         gcv.background = 0;
1066                         if ((bg_gc = XCreateGC(display, 
1067                                                 pp->pacmanPixmap[dir][mouth],
1068                                                 GCForeground | 
1069                                                 GCBackground, &gcv)) == 
1070                                         None) {
1071                                 XFreeGC(display, fg_gc);
1072                                 free_pacman(display, pp);
1073                                 return;
1074                         }
1075                         XFillRectangle(display, 
1076                                         pp->pacmanPixmap[dir][mouth], bg_gc,
1077                                         0, 0, pp->spritexs, pp->spriteys);
1078                         if (pp->spritexs == 1 && pp->spriteys == 1)
1079                                 XFillRectangle(display, 
1080                                              pp->pacmanPixmap[dir][mouth], 
1081                                              fg_gc, 0, 0, pp->spritexs, 
1082                                              pp->spriteys);
1083                         else
1084                                 XFillArc(display, 
1085                                                pp->pacmanPixmap[dir][mouth],
1086                                                fg_gc,
1087                                         0, 0, pp->spritexs, pp->spriteys,
1088                                         ((90 - dir * 90) + mouth * 5) * 64,
1089                                         (360 + (-2 * mouth * 5)) * 64);
1090                         XFreeGC(display, fg_gc);
1091                         XFreeGC(display, bg_gc);
1092                 }
1093 #endif /* USE_PIXMAP */
1094
1095         pp->pacman.lastbox = START;
1096         pp->pacman.mouthdirection = 1;
1097         pp->pacman.nextcol = NOWHERE;
1098         pp->pacman.nextrow = NOWHERE;
1099
1100         if (pp->ghosts != NULL) {
1101                         free(pp->ghosts);
1102                         pp->ghosts = (ghoststruct *) NULL;
1103         }
1104         pp->nghosts = GHOSTS;
1105
1106         if (!pp->ghosts)
1107                 if ((pp->ghosts = (ghoststruct *) calloc((size_t)pp->nghosts,
1108                                 sizeof (ghoststruct))) == NULL) {
1109                         free_pacman(display, pp);
1110                         return;
1111                 }
1112
1113         pp->pacman.mouthstage = MAXMOUTH - 1;
1114
1115         MI_CLEARWINDOW(mi);
1116         repopulate(mi);
1117 }
1118
1119 /* Callback function called for each tick.  This is the complete machinery of
1120    everything that moves. */
1121 void
1122 draw_pacman(ModeInfo * mi)
1123 {
1124         unsigned int g;
1125         pacmangamestruct *pp;
1126
1127         if (pacmangames == NULL)
1128                 return;
1129         pp = &pacmangames[MI_SCREEN(mi)];
1130         if (pp->ghosts == NULL)
1131                 return;
1132
1133         pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1134         pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1135         pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1136         pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1137
1138         if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1139                 pac_update(mi, pp, &(pp->pacman));
1140                 check_death(mi, pp);
1141                 pp->pacman.delta.x = pp->incx;
1142                 pp->pacman.delta.y = pp->incy;
1143         }
1144
1145         if (pp->pacman.delta.x > pp->xs + pp->incx)
1146                 pp->pacman.delta.x = pp->xs + pp->incx;
1147         if (pp->pacman.delta.y > pp->ys + pp->incy)
1148                 pp->pacman.delta.y = pp->ys + pp->incy;
1149
1150         for (g = 0; g < pp->nghosts; g++) {
1151                 if (pp->ghosts[g].dead == True) continue;
1152
1153                 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % 
1154                         pp->ghosts[g].speed;
1155                 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % 
1156                         pp->ghosts[g].speed;
1157                 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? 
1158                         pp->incx : 0;
1159                 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? 
1160                         pp->incy : 0;
1161
1162                 if (pp->ghosts[g].delta.x >= pp->xs && 
1163                     pp->ghosts[g].delta.y >= pp->ys) {
1164                         ghost_update(pp, &(pp->ghosts[g]));
1165                         pp->ghosts[g].delta.x = pp->incx;
1166                         pp->ghosts[g].delta.y = pp->incy;
1167                 }
1168
1169                 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1170                         pp->ghosts[g].delta.x = pp->xs + pp->incx;
1171                 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1172                         pp->ghosts[g].delta.y = pp->ys + pp->incy;
1173         }
1174
1175         pacman_tick(mi);
1176 }
1177
1178 /* Releases resources. */
1179 void
1180 release_pacman(ModeInfo * mi)
1181 {
1182         if (pacmangames != NULL) {
1183                 int         screen;
1184
1185                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1186                         free_pacman(MI_DISPLAY(mi), &pacmangames[screen]);
1187                 free(pacmangames);
1188                 pacmangames = (pacmangamestruct *) NULL;
1189         }
1190 }
1191
1192 /* Refresh current level. */
1193 void
1194 refresh_pacman(ModeInfo * mi)
1195 {
1196         drawlevel(mi);
1197         pacman_tick(mi);
1198 }
1199
1200 /* Callback to change level. */
1201 void
1202 change_pacman(ModeInfo * mi)
1203 {
1204         MI_CLEARWINDOW(mi);
1205         repopulate(mi);
1206 }
1207
1208 #endif /* MODE_pacman */