1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)pacman.c 5.00 2000/11/01 xlockmore";
10 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
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.
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.
25 * 25-Feb-2005: Added bonus dots. I am using a recursive back track algorithm
26 * to help the ghost find there way home. This also means that
27 * they do not know the shorts path.
28 * Jeremy English jhe@jeremyenglish.org
29 * 15-Aug-2004: Added support for pixmap pacman.
30 * Jeremy English jhe@jeremyenglish.org
31 * 11-Aug-2004: Added support for pixmap ghost.
32 * Jeremy English jhe@jeremyenglish.org
33 * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.
34 * splitted up code into several files. Retouched AI code, cleaned
36 * 3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
37 * 26-Nov-2001: Random level generator added
38 * 01-Nov-2000: Allocation checks
39 * 04-Jun-1997: Compatible with xscreensaver
44 1. think of a better level generation algorithm
47 #define DEF_TRACKMOUSE "False"
51 # define DEFAULTS "*delay: 10000 \n" \
55 "*fpsSolid: true \n" \
57 # define UNIFORM_COLORS
58 # define BRIGHT_COLORS
59 # define reshape_pacman 0
60 # define pacman_handle_event 0
61 # include "xlockmore.h" /* in xscreensaver distribution */
63 #else /* STANDALONE */
64 # include "xlock.h" /* in xlockmore distribution */
65 #endif /* STANDALONE */
70 #include "pacman_ai.h"
71 #include "pacman_level.h"
73 #if defined(USE_PIXMAP) /* computed in pacman.h */
74 # include "images/pacman/ghost-u1.xpm"
75 # include "images/pacman/ghost-u2.xpm"
76 # include "images/pacman/ghost-r1.xpm"
77 # include "images/pacman/ghost-r2.xpm"
78 # include "images/pacman/ghost-l1.xpm"
79 # include "images/pacman/ghost-l2.xpm"
80 # include "images/pacman/ghost-d1.xpm"
81 # include "images/pacman/ghost-d2.xpm"
82 /* Used to clean up the dust left by wag. */
83 # include "images/pacman/ghost-mask.xpm"
84 # include "images/pacman/pacman-u1.xpm"
85 # include "images/pacman/pacman-u2.xpm"
86 # include "images/pacman/pacman-r1.xpm"
87 # include "images/pacman/pacman-r2.xpm"
88 # include "images/pacman/pacman-l1.xpm"
89 # include "images/pacman/pacman-l2.xpm"
90 # include "images/pacman/pacman-d1.xpm"
91 # include "images/pacman/pacman-d2.xpm"
92 # include "images/pacman/pacman-0.xpm"
93 # include "images/pacman/ghost-s1.xpm"
94 # include "images/pacman/ghost-s2.xpm"
95 # include "images/pacman/ghost-sf1.xpm"
96 # include "images/pacman/ghost-sf2.xpm"
97 # include "images/pacman/eyes-l.xpm"
98 # include "images/pacman/eyes-r.xpm"
99 # include "images/pacman/eyes-u.xpm"
100 # include "images/pacman/eyes-d.xpm"
101 # include "images/pacman/pacman-ds1.xpm"
102 # include "images/pacman/pacman-ds2.xpm"
103 # include "images/pacman/pacman-ds3.xpm"
104 # include "images/pacman/pacman-ds4.xpm"
105 # include "images/pacman/pacman-ds5.xpm"
106 # include "images/pacman/pacman-ds6.xpm"
107 # include "images/pacman/pacman-ds7.xpm"
108 # include "images/pacman/pacman-ds8.xpm"
114 } dirvecs[DIRVECS] = { { -1, 0},
119 #ifdef DISABLE_INTERACTIVE
120 ENTRYPOINT ModeSpecOpt pacman_opts = {
122 (XrmOptionDescRec *) NULL,
125 (OptionStruct *) NULL
128 static XrmOptionDescRec opts[] = {
129 {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
130 {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
133 static argtype vars[] = {
134 {&trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
137 static OptionStruct desc[] = {
138 {"-/+trackmouse", "turn on/off the tracking of the mouse"}
141 ENTRYPOINT ModeSpecOpt pacman_opts =
142 { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars,
147 ModStruct pacman_description = {
148 "pacman", /* *cmdline_arg; */
149 "init_pacman", /* *init_name; */
150 "draw_pacman", /* *callback_name; */
151 "release_pacman", /* *release_name; */
152 "refresh_pacman", /* *refresh_name; */
153 "change_pacman", /* *change_name; */
154 (char *) NULL, /* *unused_name; */
155 &pacman_opts, /* *msopts */
156 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
162 pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;
164 static void repopulate (ModeInfo * mi);
165 static void drawlevel (ModeInfo * mi);
169 free_pacman (Display * display, pacmangamestruct * pp)
171 int dir, mouth, i, j, k;
173 if (pp->ghosts != NULL) {
175 pp->ghosts = (ghoststruct *) NULL;
177 if (pp->stippledGC != None) {
178 XFreeGC (display, pp->stippledGC);
179 pp->stippledGC = None;
181 for (i = 0; i < 4; i++) {
182 for (j = 0; j < MAXGDIR; j++) {
183 for (k = 0; k < MAXGWAG; k++) {
184 if (pp->ghostPixmap[i][j][k] != None) {
185 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
186 pp->ghostPixmap[i][j][k] = None;
191 for (dir = 0; dir < 4; dir++)
192 for (mouth = 0; mouth < MAXMOUTH; mouth++)
193 if (pp->pacmanPixmap[dir][mouth] != None) {
194 XFreePixmap (display, pp->pacmanPixmap[dir][mouth]);
195 pp->pacmanPixmap[dir][mouth] = None;
199 /* set pacman and the ghost in there starting positions, but don't draw a new
202 reset_level (ModeInfo * mi, int n, int pac_init)
204 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
210 pp->gamestate = GHOST_DANGER;
213 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT) / 2 - n;
214 pp->pacman.init_row = pp->pacman.row;
217 pp->pacman.row = pp->pacman.init_row;
219 pp->pacman.col = (LEVWIDTH / 2);
220 pp->pacman.nextrow = NOWHERE;
221 pp->pacman.nextcol = NOWHERE;
222 pp->pacman.cf = NOWHERE;
223 pp->pacman.rf = NOWHERE;
224 pp->pacman.oldcf = NOWHERE;
225 pp->pacman.oldrf = NOWHERE;
226 pp->pacman.oldlx = NOWHERE;
227 pp->pacman.oldly = NOWHERE;
228 pp->pacman.aistate = ps_eating;
229 pp->pacman.cur_trace = 0;
230 pp->pacman.roundscore = 0;
231 pp->pacman.speed = 4;
232 pp->pacman.lastturn = 0;
233 pp->pacman.delta.x = 0;
234 pp->pacman.delta.y = 0;
236 for (ghost = 0; ghost < pp->nghosts; ghost++) {
237 pp->ghosts[ghost].col = (LEVWIDTH / 2);
238 pp->ghosts[ghost].row = (LEVHEIGHT / 2);
239 pp->ghosts[ghost].nextcol = NOWHERE;
240 pp->ghosts[ghost].nextrow = NOWHERE;
241 pp->ghosts[ghost].dead = 0;
242 pp->ghosts[ghost].lastbox = START;
243 pp->ghosts[ghost].cf = NOWHERE;
244 pp->ghosts[ghost].rf = NOWHERE;
245 pp->ghosts[ghost].oldcf = NOWHERE;
246 pp->ghosts[ghost].oldrf = NOWHERE;
247 pp->ghosts[ghost].aistate = inbox;
248 pp->ghosts[ghost].timeleft = ghost * 20;
249 pp->ghosts[ghost].speed = 3;
250 pp->ghosts[ghost].delta.x = 0;
251 pp->ghosts[ghost].delta.y = 0;
252 pp->ghosts[ghost].flash_scared = False;
253 pp->ghosts[ghost].wait_pos = False;
254 ghost_update (pp, &(pp->ghosts[ghost]));
256 pac_update (mi, pp, &(pp->pacman));
260 pacman_ghost_collision(unsigned int ghost, pacmangamestruct * pp)
262 return (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
263 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
264 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
265 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
266 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
267 (pp->ghosts[ghost].col == pp->pacman.nextcol)));
271 /* Checks for death of any ghosts/pacman and updates. It also makes a new
272 level if all ghosts are dead or all dots are eaten. */
274 check_death (ModeInfo * mi, pacmangamestruct * pp)
276 Display *display = MI_DISPLAY (mi);
277 Window window = MI_WINDOW (mi);
280 if (pp->pacman.aistate == ps_dieing) return;
282 for (ghost = 0; ghost < pp->nghosts; ghost++) {
284 /* The ghost have to be scared before you can kill them */
285 if ( pacman_ghost_collision ( ghost, pp ) ) {
286 if (pp->ghosts[ghost].aistate == goingin) continue;
288 if (pp->ghosts[ghost].aistate == hiding) {
289 pp->ghosts[ghost].dead = 1;
290 pp->ghosts[ghost].aistate = goingin;
291 pp->ghosts[ghost].wait_pos = True;
292 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
293 XFillRectangle (display, window,
295 pp->ghosts[ghost].cf,
296 pp->ghosts[ghost].rf,
297 pp->spritexs, pp->spriteys);
302 pp->pacman.aistate = ps_dieing;
310 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
312 repopulate (ModeInfo * mi)
314 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
315 pp->pacman.deaths = 0;
316 reset_level (mi, createnewlevel (pp), True);
317 check_death (mi, pp);
320 /* Sets the color to the color of a wall. */
322 setwallcolor (ModeInfo * mi)
324 Display *display = MI_DISPLAY (mi);
325 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
327 if (MI_NPIXELS (mi) > 2)
328 XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, BLUE));
330 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
333 /* Sets the color to the color of a dot. */
335 setdotcolor (ModeInfo * mi)
337 Display *display = MI_DISPLAY (mi);
338 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
340 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
344 cleardotcolor (ModeInfo * mi)
346 Display *display = MI_DISPLAY (mi);
347 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
349 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
354 draw_position (ModeInfo * mi, int x, int y, int color)
356 Display *display = MI_DISPLAY (mi);
357 Window window = MI_WINDOW (mi);
358 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
359 XFontStruct *font = NULL;
360 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
363 font = XLoadQueryFont (display, f_name);
364 assert (font != NULL);
366 s = (char *) malloc (256);
368 sprintf (s, "(%d,%d)", x, y);
369 XSetForeground (display, pp->stippledGC, color);
370 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
377 draw_number (ModeInfo * mi, int x, int y, int num, int color)
379 Display *display = MI_DISPLAY (mi);
380 Window window = MI_WINDOW (mi);
381 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
382 XFontStruct *font = NULL;
383 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
386 font = XLoadQueryFont (display, f_name);
387 assert (font != NULL);
389 s = (char *) malloc (256);
391 sprintf (s, "%d", num);
392 XSetForeground (display, pp->stippledGC, color);
393 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
400 /* draw_grid - draws a grid on top of the playing field.
401 * Used so that I can determine if I'm converting from rows and columns to x and y
402 * coordinates correctly.
405 draw_grid (ModeInfo * mi)
407 Display *display = MI_DISPLAY (mi);
408 Window window = MI_WINDOW (mi);
409 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
410 int h = MI_HEIGHT (mi);
411 int w = MI_WIDTH (mi);
414 XSetForeground (display, pp->stippledGC, 0xff0000);
417 XDrawLine (display, window, pp->stippledGC, x, 0, x, h);
421 XDrawLine (display, window, pp->stippledGC, 0, y, w, y);
429 draw_string (ModeInfo * mi, int x, int y, char *s, int color)
431 Display *display = MI_DISPLAY (mi);
432 Window window = MI_WINDOW (mi);
433 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
434 XFontStruct *font = NULL;
435 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
437 font = XLoadQueryFont (display, f_name);
438 assert (font != NULL);
441 XSetForeground (display, pp->stippledGC, color);
442 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
446 /* I think this function has a memory leak. Be careful if you enable it. */
447 /* I only used it briefly to help me debug the ghost's aistate. It prints */
448 /* the state of each ghost on the left hand side of the screen */
450 print_ghost_stats (ModeInfo *mi, ghoststruct *g , int ghost_num)
454 sprintf (s, "GHOST: %d", ghost_num );
457 sprintf (s, "%s inbox", s);
460 sprintf (s, "%s goingout", s);
463 sprintf (s, "%s randdir", s);
466 sprintf (s, "%s chasing", s);
469 sprintf (s, "%s hiding", s);
472 sprintf (s, "%s goingin",s);
475 draw_string (mi, 0, (ghost_num *3) *10+50, g->last_stat, 0x000000);
476 draw_string (mi, 0, (ghost_num *3) *10+50, s, 0xff0000);
477 strcpy(g->last_stat,s);
480 /* prints the number of times pacman has died and his aistate on the left hand */
481 /* side of the screen */
483 print_pac_stats ( ModeInfo *mi, pacmanstruct *pac )
485 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
487 sprintf (s, "Pacman, Deaths: %d", pac->deaths );
488 switch ( pac->aistate ){
490 sprintf(s, "%s ps_eating",s );
493 sprintf(s, "%s ps_chasing",s );
496 sprintf(s, "%s ps_hiding",s );
499 sprintf(s, "%s ps_random",s );
502 sprintf(s, "%s ps_dieing",s );
505 draw_string ( mi, 0, 200, pp->last_pac_stat, 0x000000);
506 draw_string ( mi, 0, 200, s, 0xff0000);
507 strcpy(pp->last_pac_stat, s );
512 /*Ok, yeah whatever?*/
513 /*dot_rc_to_pixel - magic that converts row and columns into
514 *the x and y coordinates of the screen.
517 dot_rc_to_pixel (ModeInfo * mi, int *x, int *y)
519 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
521 (pp->xs / 2) - (pp->xs > 32 ? (pp->xs / 16) : 1) + pp->xb;
523 (pp->ys / 2) - (pp->ys > 32 ? (pp->ys / 16) : 1) + pp->yb;
526 /* dot_width_height - magic used to get the width and height of
527 * a dot. This dot can also be scaled by a value.
530 dot_width_height (ModeInfo *mi, int *w, int *h)
532 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
534 *w = *h = (pp->xs / 32 );
541 bonus_dot_width_height (ModeInfo *mi, int *w, int *h )
543 *w = *h = MI_HEIGHT (mi) / 65;
549 draw_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y,
550 void (*width_height)(ModeInfo * mi, int *w, int *h),
551 int (*arc_func) (Display * display, Drawable d, GC gc,
552 int x, int y, unsigned int width,
553 unsigned int height, int angle1,
556 Display *display = MI_DISPLAY (mi);
557 Window window = MI_WINDOW (mi);
559 dot_rc_to_pixel (mi, &x, &y);
560 width_height(mi, &w, &h);
561 (void) arc_func (display, window, pp->stippledGC,
562 x, y, w, h, 0, 23040);
566 draw_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
571 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
572 dot_rc_to_pixel (mi, &x2, &y2);
574 draw_position (mi, x2, y2, 0xff0000);
579 clear_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
582 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
586 draw_regular_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
589 draw_dot (mi, pp, x, y, dot_width_height, XDrawArc);
592 /* Draws a block in the level at the specified x and y locations. */
594 drawlevelblock (ModeInfo * mi, pacmangamestruct * pp,
595 const unsigned x, const unsigned y)
597 Display *display = MI_DISPLAY (mi);
598 Window window = MI_WINDOW (mi);
607 XSetFillStyle (display, pp->stippledGC, FillSolid);
608 #endif /* !HAVE_COCOA */
609 XSetLineAttributes (display, pp->stippledGC, pp->wallwidth,
610 LineSolid, CapRound, JoinMiter);
612 if (pp->xs < 2 || pp->ys < 2) {
613 switch (pp->level[y * LEVWIDTH + x]) {
619 (void) XDrawPoint (display, window,
621 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
625 (void) XDrawPoint (display, window,
627 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
633 switch (pp->level[y * LEVWIDTH + x]) {
640 if (pp->xs < 8 || pp->ys < 8) {
641 (void) XDrawPoint (display, window,
643 x * pp->xs + pp->xb +
644 pp->xs / 2, y * pp->ys + pp->yb + pp->ys / 2);
648 draw_regular_dot (mi, pp, x, y);
650 /* What we will probably want to do here is have the pp->level store a 'o' for
651 * the bonus dots. The we can use the drawing routine above just with a bigger
655 draw_bonus_dot (mi, pp, x, y);
661 (void) XDrawLine (display, window, pp->stippledGC,
662 (pp->xs * x) + pp->xb,
663 (pp->ys * y) + (pp->ys / 2) + pp->yb,
664 (pp->xs * (x + 1)) + pp->xb,
665 (pp->ys * y) + (pp->ys / 2) + pp->yb);
670 (void) XDrawLine (display, window, pp->stippledGC,
671 (pp->xs * x) + (pp->xs / 2) + pp->xb,
672 (pp->ys * y) + pp->yb,
673 (pp->xs * x) + (pp->xs / 2) + pp->xb,
674 (pp->ys * (y + 1)) + pp->yb);
679 (void) XDrawArc (display, window, pp->stippledGC,
680 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
681 (pp->ys * y) + (pp->ys / 2) + pp->yb,
682 pp->xs, pp->ys, 0 * 64, 90 * 64);
687 (void) XDrawArc (display, window, pp->stippledGC,
688 (pp->xs * x) + (pp->ys / 2) + pp->xb,
689 (pp->ys * y) + (pp->ys / 2) + pp->yb,
690 pp->xs, pp->ys, 90 * 64, 90 * 64);
695 (void) XDrawArc (display, window, pp->stippledGC,
696 (pp->xs * x) + (pp->ys / 2) + pp->xb,
697 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
698 pp->xs, pp->ys, 180 * 64, 90 * 64);
703 (void) XDrawArc (display, window, pp->stippledGC,
704 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
705 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
706 pp->xs, pp->ys, 270 * 64, 90 * 64);
712 /* Draws a complete level. */
714 drawlevel (ModeInfo * mi)
716 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
719 for (y = 0; y < LEVHEIGHT; y++)
720 for (x = 0; x < LEVWIDTH; x++)
721 drawlevelblock (mi, pp, x, y);
724 /* There is some overlap so it can be made more efficient */
725 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
727 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
728 XFillRectangle(d,w,g,xl,yl,xs,ys); \
730 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
731 XFillRectangle(d,w,g,xl,yl,xs,ys); \
733 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
734 XFillRectangle(d,w,g,xl,yl,xs,ys); \
736 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
737 XFillRectangle(d,w,g,xl,yl,xs,ys)
740 /* Draws the pacman sprite, removing the previous location. */
741 #if defined(USE_PIXMAP)
744 draw_pacman_sprite (ModeInfo * mi)
746 Display *display = MI_DISPLAY (mi);
747 Window window = MI_WINDOW (mi);
748 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
749 unsigned int dir = 0;
750 int old_mask_dir = 0;
751 int old_mask_mouth = 0;
752 Pixmap old_mask, new_mask;
755 #define MAX_MOUTH_DELAY 2
756 #define MAX_DEATH_DELAY 20
758 if (pp->pacman.aistate == ps_dieing){
759 pp->pacman.cf = pp->pacman.oldcf;
760 pp->pacman.rf = pp->pacman.oldrf;
763 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
764 pp->pacman.cfactor + pp->xb + pp->spritedx;
765 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
766 pp->pacman.rfactor + pp->yb + pp->spritedy;
769 dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
770 ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
772 if (pp->pm_mouth_delay == MAX_MOUTH_DELAY) {
773 if (pp->pm_mouth == (MAXMOUTH - 1) || pp->pm_mouth == 0) {
774 pp->pm_open_mouth = !pp->pm_open_mouth;
776 pp->pm_open_mouth ? pp->pm_mouth++ : pp->pm_mouth--;
777 pp->pm_mouth_delay = 0;
780 pp->pm_mouth_delay++;
783 if (pp->pacman.aistate == ps_dieing){
784 if (pp->pm_death_frame >= PAC_DEATH_FRAMES) {
785 pp->pacman.aistate = ps_eating;
786 pp->pm_death_frame = 0;
787 pp->pm_death_delay = 0;
788 reset_level (mi, 0, False);
792 old_mask = pp->pacmanMask[0][0];
793 new_mask = pp->pacmanMask[0][0];
794 pacman = pp->pacman_ds[pp->pm_death_frame];
795 if (pp->pm_death_delay == MAX_DEATH_DELAY){
796 pp->pm_death_frame++;
797 pp->pm_death_delay = 0;
800 pp->pm_death_delay++;
805 old_mask = pp->pacmanMask[old_mask_dir][old_mask_mouth];
806 new_mask = pp->pacmanMask[dir][pp->pm_mouth];
807 pacman = pp->pacmanPixmap[dir][pp->pm_mouth];
810 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
812 XSetClipMask (display, pp->stippledGC, old_mask);
814 XSetClipOrigin (display, pp->stippledGC, pp->pacman.oldcf,
816 XFillRectangle (display, window, pp->stippledGC, pp->pacman.oldcf,
817 pp->pacman.oldrf, pp->spritexs, pp->spriteys);
818 XSetClipMask (display, pp->stippledGC, new_mask);
819 XSetClipOrigin (display, pp->stippledGC, pp->pacman.cf, pp->pacman.rf);
820 XCopyArea (display, pacman, window,
821 pp->stippledGC, 0, 0, pp->spritexs, pp->spriteys,
822 pp->pacman.cf, pp->pacman.rf);
823 XSetClipMask (display, pp->stippledGC, None);
824 if (pp->pacman.aistate != ps_dieing){
825 pp->pacman.oldcf = pp->pacman.cf;
826 pp->pacman.oldrf = pp->pacman.rf;
832 draw_ghost_position (ModeInfo * mi, ghoststruct * ghost)
834 draw_position (mi, ghost->oldcf, ghost->oldrf, MI_BLACK_PIXEL (mi));
835 draw_position (mi, ghost->cf, ghost->rf, 0x00ff00);
840 draw_ghost_ndirs ( ModeInfo *mi, ghoststruct * ghost)
842 draw_number (mi, ghost->oldcf, ghost->oldrf, ghost->oldndirs, MI_BLACK_PIXEL (mi));
843 ghost->oldndirs = ghost->ndirs;
844 draw_number (mi, ghost->cf, ghost->rf, ghost->ndirs, 0x00ff00);
850 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
852 Display *display = MI_DISPLAY (mi);
853 Window window = MI_WINDOW (mi);
854 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
855 #define MAX_WAG_COUNT 50
856 unsigned int dir = 0;
857 unsigned int fs = 0; /*flash scared*/
858 Pixmap g_pix; /*ghost pixmap*/
861 dir = (ABS (pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
862 ABS (pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
865 fs = pp->ghosts[ghost].flash_scared;
866 assert (fs == 0 || fs == 1);
868 /* Choose the pixmap */
869 switch (pp->ghosts[ghost].aistate){
871 g_pix = pp->s_ghostPixmap[fs][pp->gh_wag];
874 g_pix = pp->ghostEyes[dir];
878 while ( i < pp->ghosts[ghost].trace_idx ){
879 XFillRectangle (display,
882 pp->ghosts[ghost].trace[i].vx,
883 pp->ghosts[ghost].trace[i].vy,
884 pp->spritexs, pp->spriteys);
893 g_pix = pp->ghostPixmap[ghost][dir][pp->gh_wag];
896 pp->ghosts[ghost].cf =
897 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
898 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
899 pp->ghosts[ghost].rf =
900 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
901 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
903 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
905 XSetClipMask (display, pp->stippledGC, pp->ghostMask);
906 XSetClipOrigin (display, pp->stippledGC,
907 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
908 XFillRectangle (display,
911 pp->ghosts[ghost].oldcf,
912 pp->ghosts[ghost].oldrf, pp->spritexs, pp->spriteys);
915 if (pp->pacman.aistate != ps_dieing) {
916 drawlevelblock (mi, pp,
917 (unsigned int) pp->ghosts[ghost].col,
918 (unsigned int) pp->ghosts[ghost].row);
922 XSetClipOrigin (display, pp->stippledGC,
923 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
925 XCopyArea (display, g_pix, window, pp->stippledGC, 0, 0,
926 pp->spritexs, pp->spriteys, pp->ghosts[ghost].cf,
927 pp->ghosts[ghost].rf);
929 XSetClipMask (display, pp->stippledGC, None);
932 draw_ghost_position (mi, &(pp->ghosts[ghost]));
936 draw_ghost_ndirs ( mi, &(pp->ghosts[ghost]));
939 if (pp->pacman.aistate != ps_dieing) {
940 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
941 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
942 if (pp->gh_wag_count++ == MAX_WAG_COUNT) {
943 pp->gh_wag = !pp->gh_wag;
944 pp->gh_wag_count = 0;
949 #else /* USE_PIXMAP */
951 /* Draws the pacman sprite, removing the previous location. */
953 draw_pacman_sprite (ModeInfo * mi)
955 Display *display = MI_DISPLAY (mi);
956 Window window = MI_WINDOW (mi);
957 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
960 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
961 pp->pacman.cfactor + pp->xb + pp->spritedx;
962 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
963 pp->pacman.rfactor + pp->yb + pp->spritedy;
965 dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
966 ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
968 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
969 if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
971 ERASE_IMAGE (display, window, pp->stippledGC,
972 pp->pacman.cf, pp->pacman.rf,
973 pp->pacman.oldcf, pp->pacman.oldrf,
974 pp->spritexs, pp->spriteys);
977 if (MI_NPIXELS (mi) > 2)
978 XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, YELLOW));
980 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
982 XSetFillStyle (display, pp->stippledGC, FillOpaqueStippled);
984 if (pp->xs < 2 || pp->ys < 2)
985 XDrawPoint (display, window, pp->stippledGC,
986 pp->pacman.cf, pp->pacman.rf);
988 XFillRectangle (display, window, pp->stippledGC,
989 pp->pacman.cf, pp->pacman.rf,
990 pp->spritexs, pp->spriteys);
991 pp->pacman.mouthstage += pp->pacman.mouthdirection;
992 if ((pp->pacman.mouthstage >= MAXMOUTH) || (pp->pacman.mouthstage < 0)) {
993 pp->pacman.mouthdirection *= -1;
994 pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
996 pp->pacman.oldcf = pp->pacman.cf;
997 pp->pacman.oldrf = pp->pacman.rf;
1000 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
1002 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
1004 Display *display = MI_DISPLAY (mi);
1005 Window window = MI_WINDOW (mi);
1006 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1008 pp->ghosts[ghost].cf =
1009 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
1010 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
1011 pp->ghosts[ghost].rf =
1012 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
1013 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
1015 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
1016 XFillRectangle (display,
1019 pp->ghosts[ghost].cf,
1020 pp->ghosts[ghost].rf, pp->spritexs, pp->spriteys);
1022 if (pp->ghosts[ghost].oldcf != NOWHERE ||
1023 pp->ghosts[ghost].oldrf != NOWHERE) {
1025 ERASE_IMAGE (display, window, pp->stippledGC,
1026 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
1027 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf,
1028 pp->spritexs, pp->spriteys);
1031 drawlevelblock (mi, pp,
1032 (unsigned int) pp->ghosts[ghost].col,
1033 (unsigned int) pp->ghosts[ghost].row);
1035 if (MI_NPIXELS (mi) > 2)
1036 XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, GREEN));
1038 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
1040 XSetFillStyle (display, pp->stippledGC, FillOpaqueStippled);
1042 if (pp->xs < 2 || pp->ys < 2)
1043 XDrawPoint (display, window, pp->stippledGC,
1044 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
1046 XFillRectangle (display,
1049 pp->ghosts[ghost].cf,
1050 pp->ghosts[ghost].rf, pp->spritexs, pp->spriteys);
1052 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
1053 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
1055 #endif /* USE_PIXMAP */
1058 ghost_over (ModeInfo * mi, int x, int y)
1062 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1063 dot_rc_to_pixel (mi, &x, &y);
1064 for (ghost = 0; ghost < pp->nghosts; ghost++) {
1065 if ((pp->ghosts[ghost].cf <= x
1066 && x <= pp->ghosts[ghost].cf + pp->spritexs)
1067 && (pp->ghosts[ghost].rf <= y
1068 && y <= pp->ghosts[ghost].rf + pp->spriteys)) {
1078 flash_bonus_dots (ModeInfo * mi)
1080 #define MAX_FLASH_COUNT 25
1081 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1083 for (i = 0; i < NUM_BONUS_DOTS; i++) {
1084 if (!bonus_dot_eaten (pp, i)) {
1085 bonus_dot_pos (pp, i, &x, &y);
1086 if (ghost_over (mi, x, y))
1089 draw_bonus_dot (mi, pp, x, y);
1091 clear_bonus_dot (mi, pp, x, y);
1094 if (pp->bd_flash_count-- == 0) {
1095 pp->bd_flash_count = MAX_FLASH_COUNT;
1096 pp->bd_on = !pp->bd_on;
1101 ate_bonus_dot (ModeInfo * mi)
1103 /*Check pacman's position. If it is over a bonus dot and that dot
1104 *has not been eaten, then return true
1106 unsigned int ret = 0;
1108 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1109 if (is_bonus_dot (pp, pp->pacman.col, pp->pacman.row, &idx)) {
1110 ret = !bonus_dot_eaten (pp, idx);
1111 eat_bonus_dot (pp, idx);
1117 ghost_scared (ModeInfo * mi)
1120 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1121 for (ghost = 0; ghost < pp->nghosts; ghost++) {
1122 if (pp->ghosts[ghost].aistate == goingin ||
1123 pp->ghosts[ghost].aistate == goingout ||
1124 pp->ghosts[ghost].aistate == inbox ) continue;
1125 pp->ghosts[ghost].aistate = hiding;
1126 pp->ghosts[ghost].flash_scared = 0;
1127 if (pp->pacman.aistate != ps_dieing)
1128 pp->pacman.aistate = ps_chasing;
1133 ghost_not_scared (ModeInfo * mi)
1136 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1137 for (ghost = 0; ghost < pp->nghosts; ghost++){
1138 if (pp->ghosts[ghost].aistate == goingin ||
1139 pp->ghosts[ghost].aistate == goingout ||
1140 pp->ghosts[ghost].aistate == inbox ) continue;
1141 pp->ghosts[ghost].aistate = chasing;
1143 if (pp->pacman.aistate != ps_dieing)
1144 pp->pacman.aistate = ps_eating;
1149 ghost_flash_scared (ModeInfo * mi)
1152 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1153 for (ghost = 0; ghost < pp->nghosts; ghost++)
1154 pp->ghosts[ghost].flash_scared = !pp->ghosts[ghost].flash_scared;
1157 /* Does all drawing of moving sprites in the level. */
1159 pacman_tick (ModeInfo * mi)
1161 #define DEFAULT_SCARED_TIME 500
1162 #define START_FLASH 200
1163 #define FLASH_COUNT 25
1165 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
1170 for (ghost = 0; ghost < pp->nghosts; ghost++) {
1171 draw_ghost_sprite (mi, ghost);
1173 print_ghost_stats (mi, &(pp->ghosts[ghost]), ghost);
1177 print_pac_stats (mi, &(pp->pacman));
1179 draw_pacman_sprite (mi);
1180 flash_bonus_dots (mi);
1181 if (ate_bonus_dot (mi)) {
1182 pp->ghost_scared_timer = (random () % 100) + DEFAULT_SCARED_TIME;
1186 if (pp->ghost_scared_timer > 0) {
1187 if (--pp->ghost_scared_timer == 0)
1188 ghost_not_scared (mi);
1189 else if (pp->ghost_scared_timer <= START_FLASH) {
1190 if (pp->flash_timer <= 0) {
1191 pp->flash_timer = FLASH_COUNT;
1192 ghost_flash_scared (mi);
1199 We don't want to miss the last death sequence. So if pacman has died three times
1200 we wait for his state to change from dieing to something else before we repopulate
1201 the level. If pacman ate all of the dots then we just repopulate.
1204 if (pp->dotsleft == 0 )
1206 else if (pp->pacman.deaths >= 3){
1207 if (pp->old_pac_state == ps_dieing && pp->pacman.aistate != ps_dieing)
1211 pp->old_pac_state = pp->pacman.aistate;
1215 /* CODE TO LOAD AND SCALE THE PIXMAPS
1218 #if defined(USE_PIXMAP)
1219 /* Grabbed the scaling routine off of usenet.
1220 * Changed it so that the information specific
1221 * to the source pixmap does not have to be a parameter.
1223 * There is probably a better way to scale pixmaps.
1224 * From: Chris Fiddyment (cxf@itd.dsto.gov.au)
1225 * Subject: Scaling Pixmap Algorithm.
1226 * Newsgroups: comp.graphics.algorithms
1227 * Date: 1994-07-06 18:51:38 PST
1232 scale_pixmap (Display ** dpy, GC gc, Pixmap source, int dwidth, int dheight)
1237 float xscale, yscale;
1238 unsigned int swidth, sheight;
1241 unsigned border_width_return, depth;
1242 XGetGeometry (*dpy, source, &window, &x, &y, &swidth, &sheight,
1243 &border_width_return, &depth);
1245 xscale = (float) swidth / (float) dwidth; /* Scaling factors */
1246 yscale = (float) sheight / (float) dheight;
1248 dest = XCreatePixmap (*dpy, window, dwidth, dheight, depth);
1250 fprintf (stderr, "%s Could not scale image", progname);
1252 temp = XCreatePixmap (*dpy, window, dwidth, sheight, depth);
1254 fprintf (stderr, "%s Could not scale image", progname);
1258 end = dwidth * xscale;
1259 /* Scale width of source into temp pixmap */
1260 for (i = 0; i <= end; i += xscale)
1261 XCopyArea (*dpy, source, temp, gc, i, 0, 1, sheight, j++, 0);
1264 end = dheight * yscale;
1265 /* Scale height of temp into dest pixmap */
1266 for (i = 0; i <= end; i += yscale)
1267 XCopyArea (*dpy, temp, dest, gc, 0, i, swidth, 1, 0, j++);
1269 XFreePixmap (*dpy, temp);
1270 return (Pixmap) dest;
1274 pacman_fail (char *s)
1276 fprintf (stderr, "%s: %s\n", progname, s);
1280 /* Load the ghost pixmaps and their mask. */
1282 load_ghost_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1284 pacmangamestruct *pp = *ps;
1285 Display *display = *dpy;
1287 ". c #FF0000", /*Red */
1288 ". c #00FFDE", /*Blue */
1289 ". c #FFB847", /*Orange */
1290 ". c #FFB8DE", /*Pink */
1294 ghost_u1_xpm, ghost_u2_xpm, ghost_r1_xpm, ghost_r2_xpm,
1295 ghost_d1_xpm, ghost_d2_xpm, ghost_l1_xpm, ghost_l2_xpm
1297 char * const *s_bits[] = {
1298 ghost_s1_xpm, ghost_s2_xpm,
1299 ghost_sf1_xpm, ghost_sf2_xpm
1301 char * const *e_bits[] = {
1302 eyes_u_xpm, eyes_r_xpm, eyes_d_xpm, eyes_l_xpm
1306 int w = pp->spritexs;
1307 int h = pp->spriteys;
1311 for (i = 0; i < 4; i++) {
1313 for (j = 0; j < MAXGDIR; j++) {
1314 for (k = 0; k < MAXGWAG; k++) {
1315 bits[m][2] = colors[i];
1316 pp->ghostPixmap[i][j][k] =
1317 xpm_data_to_pixmap (display, window, bits[m], &w, &h,
1320 if (!pp->ghostPixmap[i][j][k])
1321 pacman_fail ("Cannot load ghost images");
1323 pp->ghostPixmap[i][j][k] =
1324 scale_pixmap (&display, pp->stippledGC,
1325 pp->ghostPixmap[i][j][k], pp->spritexs,
1328 if (!pp->ghostPixmap[i][j][k])
1329 pacman_fail ("Cannot scale ghost images");
1334 /* load the scared ghost */
1336 for (i = 0; i < MAXGFLASH; i++) {
1337 for (j = 0; j < MAXGWAG; j++) {
1338 pp->s_ghostPixmap[i][j] =
1339 xpm_data_to_pixmap (display, window, s_bits[m++], &w, &h,
1342 if (!pp->s_ghostPixmap[i][j])
1343 pacman_fail ("Cannot Scare Ghost images");
1344 pp->s_ghostPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1345 pp->s_ghostPixmap[i][j],
1349 if (!pp->s_ghostPixmap[i][j])
1350 pacman_fail ("Cannot scale Scared Ghost images");
1353 /* load the ghost eyes */
1354 for (i = 0; i < MAXGDIR; i++) {
1356 xpm_data_to_pixmap (display, window, e_bits[i], &w, &h,
1359 if (!pp->ghostEyes[i])
1360 pacman_fail ("Cannot open ghost eye images");
1362 pp->ghostEyes[i] = scale_pixmap (&display, pp->stippledGC,
1367 if (!pp->ghostEyes[i])
1368 pacman_fail ("Cannot open ghost eye images");
1373 /* We really only need a single mask. This saves the headache of getting the
1374 * bottom of the ghost to clip just right. What we'll do is mask
1375 * the top portion of the ghost, but the bottom of the ghost will be solid.
1376 * I did this by setting the pixels between the fringe of their sheets
1377 * to black instead of none. -jeremy
1379 temp = xpm_data_to_pixmap (display, window, ghost_mask_xpm,
1380 &w, &h, &pp->ghostMask);
1383 pacman_fail ("Cannot load temporary ghost image");
1385 temp = scale_pixmap (&display, pp->stippledGC,
1386 temp, pp->spritexs, pp->spriteys);
1389 pacman_fail ("Cannot scale temporary ghost image");
1391 gc = XCreateGC (display, pp->ghostMask, 0, 0);
1393 pp->ghostMask = scale_pixmap (&display, gc, pp->ghostMask,
1394 pp->spritexs, pp->spriteys);
1395 XFreePixmap (display, temp);
1398 /* Load the pacman pixmaps and their mask. */
1400 load_pacman_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1402 pacmangamestruct *pp = *ps;
1403 Display *display = *dpy;
1406 pacman_0_xpm, pacman_u1_xpm, pacman_u2_xpm,
1407 pacman_0_xpm, pacman_r1_xpm, pacman_r2_xpm,
1408 pacman_0_xpm, pacman_d1_xpm, pacman_d2_xpm,
1409 pacman_0_xpm, pacman_l1_xpm, pacman_l2_xpm
1412 char * const *ds_bits[] = {
1413 pacman_ds1_xpm, pacman_ds2_xpm, pacman_ds3_xpm, pacman_ds4_xpm,
1414 pacman_ds5_xpm, pacman_ds6_xpm, pacman_ds7_xpm, pacman_ds8_xpm
1418 int w = pp->spritexs;
1419 int h = pp->spriteys;
1423 for (i = 0; i < 4; i++) {
1424 for (j = 0; j < MAXMOUTH; j++) {
1425 pp->pacmanPixmap[i][j] =
1426 xpm_data_to_pixmap (display, window, bits[m++], &w, &h,
1427 &pp->pacmanMask[i][j]);
1429 if (!pp->pacmanPixmap[i][j])
1430 pacman_fail ("Cannot load pacman pixmap.");
1432 pp->pacmanPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1433 pp->pacmanPixmap[i][j],
1437 if (!pp->pacmanPixmap[i][j])
1438 pacman_fail ("Cannot scale pacman pixmap.");
1441 gc = XCreateGC (display, pp->pacmanMask[i][j], 0, 0);
1443 pp->pacmanMask[i][j] =
1444 scale_pixmap (&display, gc, pp->pacmanMask[i][j],
1445 pp->spritexs, pp->spriteys);
1449 /* Load pacman death sequence */
1450 for ( i = 0; i < PAC_DEATH_FRAMES; i++ ){
1452 xpm_data_to_pixmap (display, window, ds_bits[i], &w, &h,
1453 &pp->pacman_ds_mask[i]);
1455 if (!pp->pacman_ds[i])
1456 pacman_fail ("Cannot load pacman death frame.");
1458 pp->pacman_ds[i] = scale_pixmap ( &display, pp->stippledGC,
1463 if (!pp->pacman_ds[i])
1464 pacman_fail ("Cannot scale pixmap.");
1467 gc = XCreateGC (display, pp->pacman_ds_mask[i], 0, 0);
1469 pp->pacman_ds_mask[i] =
1470 scale_pixmap (&display, gc, pp->pacman_ds_mask[i],
1471 pp->spritexs, pp->spriteys);
1475 #endif /* USE_PIXMAP */
1477 /* Hook function, sets state to initial position. */
1479 init_pacman (ModeInfo * mi)
1481 Display *display = MI_DISPLAY (mi);
1482 Window window = MI_WINDOW (mi);
1483 int size = MI_SIZE (mi);
1484 pacmangamestruct *pp;
1488 #if (! defined( USE_PIXMAP ))
1494 if (pacmangames == NULL) {
1495 if ((pacmangames = (pacmangamestruct *)
1496 calloc ((size_t) MI_NUM_SCREENS (mi),
1497 sizeof (pacmangamestruct))) == NULL)
1500 pp = &pacmangames[MI_SCREEN (mi)];
1502 pp->width = (unsigned short) MI_WIDTH (mi);
1503 pp->height = (unsigned short) MI_HEIGHT (mi);
1504 for (i = 0; i < 4; i++) {
1505 for (j = 0; j < MAXGDIR; j++) {
1506 for (k = 0; k < MAXGWAG; k++) {
1507 if (pp->ghostPixmap[i][j][k] != None) {
1508 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
1509 pp->ghostPixmap[i][j][k] = None;
1510 pp->graphics_format = 0 /*IS_NONE */ ;
1516 for (i = 0; i < MAXGFLASH; i++) {
1517 for (j = 0; j < MAXGWAG; j++) {
1518 if (pp->s_ghostPixmap[i][j] != None) {
1519 XFreePixmap (display, pp->s_ghostPixmap[i][j]);
1520 pp->s_ghostPixmap[i][j] = None;
1526 MINGRIDSIZE * size > (int) pp->width ||
1527 MINGRIDSIZE * size > (int) pp->height) {
1529 pp->ys = pp->xs = MAX (MIN (pp->width / LEVWIDTH,
1530 pp->height / LEVHEIGHT), 1);
1533 if (size < -MINSIZE)
1534 pp->ys = (short) (NRAND (MIN (-size, MAX (MINSIZE,
1538 - MINSIZE + 1) + MINSIZE);
1539 else if (size < MINSIZE)
1542 pp->ys = (short) (MIN (size,
1543 MAX (MINSIZE, MIN (pp->width, pp->height) /
1548 pp->wallwidth = (unsigned int) (pp->xs + pp->ys) >> 4;
1549 if (pp->wallwidth < 1)
1551 pp->incx = (pp->xs >> 3) + 1;
1552 pp->incy = (pp->ys >> 3) + 1;
1553 pp->ncols = (unsigned short) MAX (LEVWIDTH, 2);
1554 pp->nrows = (unsigned short) MAX (LEVHEIGHT, 2);
1555 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1556 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1557 pp->spritexs = MAX (pp->xs + (pp->xs >> 1) - 1, 1);
1558 pp->spriteys = MAX (pp->ys + (pp->ys >> 1) - 1, 1);
1559 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
1560 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
1561 pp->old_pac_state = chasing;
1563 if (!pp->stippledGC) {
1564 gcv.foreground = MI_BLACK_PIXEL (mi);
1565 gcv.background = MI_BLACK_PIXEL (mi);
1566 if ((pp->stippledGC = XCreateGC (display, window,
1567 GCForeground | GCBackground,
1569 free_pacman (display, pp);
1575 jwxyz_XSetAntiAliasing (display, pp->stippledGC, False);
1578 #if defined(USE_PIXMAP)
1579 load_ghost_pixmaps (&display, window, &pp);
1580 load_pacman_pixmaps (&display, window, &pp);
1582 if ((pp->ghostPixmap[0][0][0] = XCreatePixmap (display, window,
1583 pp->spritexs, pp->spriteys,
1585 free_pacman (display, pp);
1591 if ((bg_gc = XCreateGC (display, pp->ghostPixmap[0][0][0],
1592 GCForeground | GCBackground, &gcv)) == None) {
1593 free_pacman (display, pp);
1599 if ((fg_gc = XCreateGC (display, pp->ghostPixmap[0][0][0],
1600 GCForeground | GCBackground, &gcv)) == None) {
1601 XFreeGC (display, bg_gc);
1602 free_pacman (display, pp);
1606 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
1608 /* draw the triangles on the bottom (scalable) */
1609 SETPOINT (points[0], 1, pp->spriteys * 5 / 6);
1610 SETPOINT (points[1], pp->spritexs / 6, pp->spriteys);
1611 SETPOINT (points[2], pp->spritexs / 3, pp->spriteys * 5 / 6);
1612 SETPOINT (points[3], pp->spritexs / 2, pp->spriteys);
1613 SETPOINT (points[4], pp->spritexs * 2 / 3, pp->spriteys * 5 / 6);
1614 SETPOINT (points[5], pp->spritexs * 5 / 6, pp->spriteys);
1615 SETPOINT (points[6], pp->spritexs, pp->spriteys * 5 / 6);
1616 SETPOINT (points[7], pp->spritexs, pp->spriteys / 2);
1617 SETPOINT (points[8], 1, pp->spriteys / 2);
1619 XFillRectangle (display, pp->ghostPixmap[0][0][0], bg_gc,
1620 0, 0, pp->spritexs, pp->spriteys);
1621 XFillArc (display, pp->ghostPixmap[0][0][0], fg_gc,
1622 0, 0, pp->spritexs, pp->spriteys, 0, 11520);
1623 XFillPolygon (display, pp->ghostPixmap[0][0][0], fg_gc,
1624 points, 9, Nonconvex, CoordModeOrigin);
1625 XFreeGC (display, bg_gc);
1626 XFreeGC (display, fg_gc);
1629 if (pp->pacmanPixmap[0][0] != None)
1630 for (dir = 0; dir < 4; dir++)
1631 for (mouth = 0; mouth < MAXMOUTH; mouth++)
1632 XFreePixmap (display, pp->pacmanPixmap[dir]
1635 for (dir = 0; dir < 4; dir++)
1636 for (mouth = 0; mouth < MAXMOUTH; mouth++) {
1637 if ((pp->pacmanPixmap[dir][mouth] =
1638 XCreatePixmap (display, MI_WINDOW (mi), pp->spritexs,
1639 pp->spriteys, 1)) == None) {
1640 free_pacman (display, pp);
1645 if ((fg_gc = XCreateGC (display, pp->pacmanPixmap[dir][mouth],
1646 GCForeground | GCBackground,
1648 free_pacman (display, pp);
1653 if ((bg_gc = XCreateGC (display,
1654 pp->pacmanPixmap[dir][mouth],
1656 GCBackground, &gcv)) == None) {
1657 XFreeGC (display, fg_gc);
1658 free_pacman (display, pp);
1661 XFillRectangle (display,
1662 pp->pacmanPixmap[dir][mouth], bg_gc,
1663 0, 0, pp->spritexs, pp->spriteys);
1664 if (pp->spritexs == 1 && pp->spriteys == 1)
1665 XFillRectangle (display,
1666 pp->pacmanPixmap[dir][mouth],
1667 fg_gc, 0, 0, pp->spritexs, pp->spriteys);
1670 pp->pacmanPixmap[dir][mouth],
1672 0, 0, pp->spritexs, pp->spriteys,
1673 ((90 - dir * 90) + mouth * 5) * 64,
1674 (360 + (-2 * mouth * 5)) * 64);
1675 XFreeGC (display, fg_gc);
1676 XFreeGC (display, bg_gc);
1678 #endif /* USE_PIXMAP */
1680 pp->pacman.lastbox = START;
1681 pp->pacman.mouthdirection = 1;
1682 pp->pacman.nextcol = NOWHERE;
1683 pp->pacman.nextrow = NOWHERE;
1685 if (pp->ghosts != NULL) {
1687 pp->ghosts = (ghoststruct *) NULL;
1689 pp->nghosts = GHOSTS;
1692 if ((pp->ghosts = (ghoststruct *) calloc ((size_t) pp->nghosts,
1693 sizeof (ghoststruct))) ==
1695 free_pacman (display, pp);
1699 pp->pacman.mouthstage = MAXMOUTH - 1;
1701 MI_CLEARWINDOW (mi);
1705 /* Callback function called for each tick. This is the complete machinery of
1706 everything that moves. */
1708 draw_pacman (ModeInfo * mi)
1711 pacmangamestruct *pp;
1713 if (pacmangames == NULL)
1715 pp = &pacmangames[MI_SCREEN (mi)];
1716 if (pp->ghosts == NULL)
1719 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1720 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1721 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1722 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1724 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1725 pac_update (mi, pp, &(pp->pacman));
1726 check_death (mi, pp);
1727 pp->pacman.delta.x = pp->incx;
1728 pp->pacman.delta.y = pp->incy;
1731 if (pp->pacman.delta.x > pp->xs + pp->incx)
1732 pp->pacman.delta.x = pp->xs + pp->incx;
1733 if (pp->pacman.delta.y > pp->ys + pp->incy)
1734 pp->pacman.delta.y = pp->ys + pp->incy;
1736 for (g = 0; g < pp->nghosts; g++) {
1737 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % pp->ghosts[g].speed;
1738 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % pp->ghosts[g].speed;
1739 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? pp->incx : 0;
1740 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? pp->incy : 0;
1742 if (pp->ghosts[g].delta.x >= pp->xs &&
1743 pp->ghosts[g].delta.y >= pp->ys) {
1744 ghost_update (pp, &(pp->ghosts[g]));
1745 pp->ghosts[g].delta.x = pp->incx;
1746 pp->ghosts[g].delta.y = pp->incy;
1749 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1750 pp->ghosts[g].delta.x = pp->xs + pp->incx;
1751 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1752 pp->ghosts[g].delta.y = pp->ys + pp->incy;
1757 /* Releases resources. */
1759 release_pacman (ModeInfo * mi)
1761 if (pacmangames != NULL) {
1764 for (screen = 0; screen < MI_NUM_SCREENS (mi); screen++)
1765 free_pacman (MI_DISPLAY (mi), &pacmangames[screen]);
1767 pacmangames = (pacmangamestruct *) NULL;
1771 /* Refresh current level. */
1773 refresh_pacman (ModeInfo * mi)
1780 /* Callback to change level. */
1782 change_pacman (ModeInfo * mi)
1784 MI_CLEARWINDOW (mi);
1787 #endif /* !STANDALONE */
1790 XSCREENSAVER_MODULE ("Pacman", pacman)
1792 #endif /* MODE_pacman */