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