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