1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
5 static const char sccsid[] = "@(#)pacman.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
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.
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.
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
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
43 1. think of a better level generation algorithm
46 #define DEF_TRACKMOUSE "False"
50 # define DEFAULTS "*delay: 10000 \n" \
54 "*fpsSolid: true \n" \
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 */
62 #else /* STANDALONE */
63 # include "xlock.h" /* in xlockmore distribution */
64 #endif /* STANDALONE */
69 #include "pacman_ai.h"
70 #include "pacman_level.h"
71 #include "ximage-loader.h"
72 #include "images/gen/pacman_png.h"
74 #ifdef DISABLE_INTERACTIVE
75 ENTRYPOINT ModeSpecOpt pacman_opts = {
77 (XrmOptionDescRec *) NULL,
83 static XrmOptionDescRec opts[] = {
84 {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
85 {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
88 static argtype vars[] = {
89 {&pacman_trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
92 static OptionStruct desc[] = {
93 {"-/+trackmouse", "turn on/off the tracking of the mouse"}
96 ENTRYPOINT ModeSpecOpt pacman_opts =
97 { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars,
102 ModStruct pacman_description = {
103 "pacman", /* *cmdline_arg; */
104 "init_pacman", /* *init_name; */
105 "draw_pacman", /* *callback_name; */
106 (char *) NULL, /* *release_name; */
107 "refresh_pacman", /* *refresh_name; */
108 "change_pacman", /* *change_name; */
109 "free_pacman", /* *free_name; */
110 &pacman_opts, /* *msopts */
111 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
116 Bool pacman_trackmouse;
117 pacmangamestruct *pacman_games = (pacmangamestruct *) NULL;
119 static void repopulate (ModeInfo * mi);
120 static void drawlevel (ModeInfo * mi);
124 free_pacman (ModeInfo * mi)
126 Display * display = MI_DISPLAY (mi);
127 pacmangamestruct * pp = &pacman_games[MI_SCREEN (mi)];
128 int dir, mouth, i, j, k;
130 if (pp->tiles) free (pp->tiles);
132 if (pp->ghosts != NULL) {
134 pp->ghosts = (ghoststruct *) NULL;
136 if (pp->stippledGC != None) {
137 XFreeGC (display, pp->stippledGC);
138 pp->stippledGC = None;
140 for (i = 0; i < 4; i++) {
141 for (j = 0; j < MAXGDIR; j++) {
142 for (k = 0; k < MAXGWAG; k++) {
143 if (pp->ghostPixmap[i][j][k] != None) {
144 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
145 pp->ghostPixmap[i][j][k] = None;
150 for (dir = 0; dir < 4; dir++)
151 for (mouth = 0; mouth < MAXMOUTH; mouth++)
152 if (pp->pacmanPixmap[dir][mouth] != None) {
153 XFreePixmap (display, pp->pacmanPixmap[dir][mouth]);
154 pp->pacmanPixmap[dir][mouth] = None;
158 /* set pacman and the ghost in there starting positions, but don't draw a new
161 reset_level (ModeInfo * mi, int n, int pac_init)
163 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
166 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
169 pp->gamestate = GHOST_DANGER;
172 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT) / 2 - n;
173 pp->pacman.init_row = pp->pacman.row;
176 pp->pacman.row = pp->pacman.init_row;
178 pp->pacman.col = (LEVWIDTH / 2);
179 pp->pacman.nextrow = NOWHERE;
180 pp->pacman.nextcol = NOWHERE;
181 pp->pacman.cf = NOWHERE;
182 pp->pacman.rf = NOWHERE;
183 pp->pacman.oldcf = NOWHERE;
184 pp->pacman.oldrf = NOWHERE;
185 pp->pacman.oldlx = NOWHERE;
186 pp->pacman.oldly = NOWHERE;
187 pp->pacman.aistate = ps_eating;
188 pp->pacman.cur_trace = 0;
189 pp->pacman.roundscore = 0;
190 pp->pacman.speed = 4;
191 pp->pacman.lastturn = 0;
192 pp->pacman.delta.x = 0;
193 pp->pacman.delta.y = 0;
195 for (ghost = 0; ghost < pp->nghosts; ghost++) {
196 pp->ghosts[ghost].col = (LEVWIDTH / 2);
197 pp->ghosts[ghost].row = (LEVHEIGHT / 2);
198 pp->ghosts[ghost].nextcol = NOWHERE;
199 pp->ghosts[ghost].nextrow = NOWHERE;
200 pp->ghosts[ghost].dead = 0;
201 pp->ghosts[ghost].lastbox = START;
202 pp->ghosts[ghost].cf = NOWHERE;
203 pp->ghosts[ghost].rf = NOWHERE;
204 pp->ghosts[ghost].oldcf = NOWHERE;
205 pp->ghosts[ghost].oldrf = NOWHERE;
206 pp->ghosts[ghost].aistate = inbox;
207 pp->ghosts[ghost].timeleft = ghost * 20;
208 pp->ghosts[ghost].speed = 3;
209 pp->ghosts[ghost].delta.x = 0;
210 pp->ghosts[ghost].delta.y = 0;
211 pp->ghosts[ghost].flash_scared = False;
212 pp->ghosts[ghost].wait_pos = False;
213 pacman_ghost_update (pp, &(pp->ghosts[ghost]));
215 pacman_update (mi, pp, &(pp->pacman));
219 pacman_ghost_collision(unsigned int ghost, pacmangamestruct * pp)
221 return (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
222 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
223 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
224 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
225 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
226 (pp->ghosts[ghost].col == pp->pacman.nextcol)));
230 /* Checks for death of any ghosts/pacman and updates. It also makes a new
231 level if all ghosts are dead or all dots are eaten. */
233 check_death (ModeInfo * mi, pacmangamestruct * pp)
235 Display *display = MI_DISPLAY (mi);
236 Window window = MI_WINDOW (mi);
239 if (pp->pacman.aistate == ps_dieing) return;
241 for (ghost = 0; ghost < pp->nghosts; ghost++) {
243 /* The ghost have to be scared before you can kill them */
244 if ( pacman_ghost_collision ( ghost, pp ) ) {
245 if (pp->ghosts[ghost].aistate == goingin) continue;
247 if (pp->ghosts[ghost].aistate == hiding) {
248 pp->ghosts[ghost].dead = 1;
249 pp->ghosts[ghost].aistate = goingin;
250 pp->ghosts[ghost].wait_pos = True;
251 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
252 XFillRectangle (display, window,
254 pp->ghosts[ghost].cf,
255 pp->ghosts[ghost].rf,
256 pp->spritexs, pp->spriteys);
261 pp->pacman.aistate = ps_dieing;
269 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
271 repopulate (ModeInfo * mi)
273 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
274 pp->pacman.deaths = 0;
275 reset_level (mi, pacman_createnewlevel (pp), True);
276 check_death (mi, pp);
279 /* Sets the color to the color of a wall. */
281 setwallcolor (ModeInfo * mi)
283 Display *display = MI_DISPLAY (mi);
284 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
286 if (MI_NPIXELS (mi) > 2)
287 XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, BLUE));
289 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
292 /* Sets the color to the color of a dot. */
294 setdotcolor (ModeInfo * mi)
296 Display *display = MI_DISPLAY (mi);
297 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
299 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
303 cleardotcolor (ModeInfo * mi)
305 Display *display = MI_DISPLAY (mi);
306 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
308 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
313 draw_position (ModeInfo * mi, int x, int y, int color)
315 Display *display = MI_DISPLAY (mi);
316 Window window = MI_WINDOW (mi);
317 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
318 XFontStruct *font = NULL;
319 char *f_name = "Utopia 60, Helvetica 60";
322 font = load_font_retry (display, f_name);
323 assert (font != NULL);
325 s = (char *) malloc (256);
327 sprintf (s, "(%d,%d)", x, y);
328 XSetForeground (display, pp->stippledGC, color);
329 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
336 draw_number (ModeInfo * mi, int x, int y, int num, int color)
338 Display *display = MI_DISPLAY (mi);
339 Window window = MI_WINDOW (mi);
340 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
341 XFontStruct *font = NULL;
342 char *f_name = "Utopia 60, Helvetica 60";
345 font = load_font_retry (display, f_name);
346 assert (font != NULL);
348 s = (char *) malloc (256);
350 sprintf (s, "%d", num);
351 XSetForeground (display, pp->stippledGC, color);
352 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
359 /* draw_grid - draws a grid on top of the playing field.
360 * Used so that I can determine if I'm converting from rows and columns to x and y
361 * coordinates correctly.
364 draw_grid (ModeInfo * mi)
366 Display *display = MI_DISPLAY (mi);
367 Window window = MI_WINDOW (mi);
368 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
369 int h = MI_HEIGHT (mi);
370 int w = MI_WIDTH (mi);
373 XSetForeground (display, pp->stippledGC, 0xff0000);
376 XDrawLine (display, window, pp->stippledGC, x, 0, x, h);
380 XDrawLine (display, window, pp->stippledGC, 0, y, w, y);
388 draw_string (ModeInfo * mi, int x, int y, char *s, int color)
390 Display *display = MI_DISPLAY (mi);
391 Window window = MI_WINDOW (mi);
392 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
393 XFontStruct *font = NULL;
394 char *f_name = "Utopia 60, Helvetica 60";
396 font = load_font_retry (display, f_name);
397 assert (font != NULL);
400 XSetForeground (display, pp->stippledGC, color);
401 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
405 /* I think this function has a memory leak. Be careful if you enable it. */
406 /* I only used it briefly to help me debug the ghost's aistate. It prints */
407 /* the state of each ghost on the left hand side of the screen */
409 print_ghost_stats (ModeInfo *mi, ghoststruct *g , int ghost_num)
413 sprintf (s, "GHOST: %d", ghost_num );
416 sprintf (s, "%s inbox", s);
419 sprintf (s, "%s goingout", s);
422 sprintf (s, "%s randdir", s);
425 sprintf (s, "%s chasing", s);
428 sprintf (s, "%s hiding", s);
431 sprintf (s, "%s goingin",s);
434 draw_string (mi, 0, (ghost_num *3) *10+50, g->last_stat, 0x000000);
435 draw_string (mi, 0, (ghost_num *3) *10+50, s, 0xff0000);
436 strcpy(g->last_stat,s);
439 /* prints the number of times pacman has died and his aistate on the left hand */
440 /* side of the screen */
442 print_pac_stats ( ModeInfo *mi, pacmanstruct *pac )
444 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
446 sprintf (s, "Pacman, Deaths: %d", pac->deaths );
447 switch ( pac->aistate ){
449 sprintf(s, "%s ps_eating",s );
452 sprintf(s, "%s ps_chasing",s );
455 sprintf(s, "%s ps_hiding",s );
458 sprintf(s, "%s ps_random",s );
461 sprintf(s, "%s ps_dieing",s );
464 draw_string ( mi, 0, 200, pp->last_pac_stat, 0x000000);
465 draw_string ( mi, 0, 200, s, 0xff0000);
466 strcpy(pp->last_pac_stat, s );
471 /*Ok, yeah whatever?*/
472 /*dot_rc_to_pixel - magic that converts row and columns into
473 *the x and y coordinates of the screen.
476 dot_rc_to_pixel (ModeInfo * mi, int *x, int *y)
478 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
480 (pp->xs / 2) - (pp->xs > 32 ? (pp->xs / 16) : 1) + pp->xb;
482 (pp->ys / 2) - (pp->ys > 32 ? (pp->ys / 16) : 1) + pp->yb;
485 /* dot_width_height - magic used to get the width and height of
486 * a dot. This dot can also be scaled by a value.
489 dot_width_height (ModeInfo *mi, int *w, int *h)
491 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
493 *w = *h = (pp->xs / 16 );
500 bonus_dot_width_height (ModeInfo *mi, int *w, int *h )
502 *w = *h = MI_HEIGHT (mi) / 65;
508 draw_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y,
509 void (*width_height)(ModeInfo * mi, int *w, int *h),
510 int (*arc_func) (Display * display, Drawable d, GC gc,
511 int x, int y, unsigned int width,
512 unsigned int height, int angle1,
515 Display *display = MI_DISPLAY (mi);
516 Window window = MI_WINDOW (mi);
518 dot_rc_to_pixel (mi, &x, &y);
519 width_height(mi, &w, &h);
520 (void) arc_func (display, window, pp->stippledGC,
521 x, y, w, h, 0, 23040);
525 draw_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
530 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
531 dot_rc_to_pixel (mi, &x2, &y2);
533 draw_position (mi, x2, y2, 0xff0000);
538 clear_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
541 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
545 draw_regular_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
548 draw_dot (mi, pp, x, y, dot_width_height, XDrawArc);
551 /* Draws a block in the level at the specified x and y locations. */
553 drawlevelblock (ModeInfo * mi, pacmangamestruct * pp,
554 const unsigned x, const unsigned y)
556 Display *display = MI_DISPLAY (mi);
557 Window window = MI_WINDOW (mi);
566 XSetFillStyle (display, pp->stippledGC, FillSolid);
567 #endif /* !HAVE_JWXYZ */
568 XSetLineAttributes (display, pp->stippledGC, pp->wallwidth,
569 LineSolid, CapRound, JoinMiter);
571 if (pp->xs < 2 || pp->ys < 2) {
572 switch (pp->level[y * LEVWIDTH + x]) {
578 (void) XDrawPoint (display, window,
580 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
584 (void) XDrawPoint (display, window,
586 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
592 switch (pp->level[y * LEVWIDTH + x]) {
599 if (pp->xs < 8 || pp->ys < 8) {
600 (void) XDrawPoint (display, window,
602 x * pp->xs + pp->xb +
603 pp->xs / 2, y * pp->ys + pp->yb + pp->ys / 2);
607 draw_regular_dot (mi, pp, x, y);
609 /* What we will probably want to do here is have the pp->level store a 'o' for
610 * the bonus dots. The we can use the drawing routine above just with a bigger
614 draw_bonus_dot (mi, pp, x, y);
620 (void) XDrawLine (display, window, pp->stippledGC,
621 (pp->xs * x) + pp->xb,
622 (pp->ys * y) + (pp->ys / 2) + pp->yb,
623 (pp->xs * (x + 1)) + pp->xb,
624 (pp->ys * y) + (pp->ys / 2) + pp->yb);
629 (void) XDrawLine (display, window, pp->stippledGC,
630 (pp->xs * x) + (pp->xs / 2) + pp->xb,
631 (pp->ys * y) + pp->yb,
632 (pp->xs * x) + (pp->xs / 2) + pp->xb,
633 (pp->ys * (y + 1)) + pp->yb);
638 (void) XDrawArc (display, window, pp->stippledGC,
639 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
640 (pp->ys * y) + (pp->ys / 2) + pp->yb,
641 pp->xs, pp->ys, 0 * 64, 90 * 64);
646 (void) XDrawArc (display, window, pp->stippledGC,
647 (pp->xs * x) + (pp->ys / 2) + pp->xb,
648 (pp->ys * y) + (pp->ys / 2) + pp->yb,
649 pp->xs, pp->ys, 90 * 64, 90 * 64);
654 (void) XDrawArc (display, window, pp->stippledGC,
655 (pp->xs * x) + (pp->ys / 2) + pp->xb,
656 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
657 pp->xs, pp->ys, 180 * 64, 90 * 64);
662 (void) XDrawArc (display, window, pp->stippledGC,
663 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
664 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
665 pp->xs, pp->ys, 270 * 64, 90 * 64);
671 /* Draws a complete level. */
673 drawlevel (ModeInfo * mi)
675 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
678 for (y = 0; y < LEVHEIGHT; y++)
679 for (x = 0; x < LEVWIDTH; x++)
680 drawlevelblock (mi, pp, x, y);
683 /* There is some overlap so it can be made more efficient */
684 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
686 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
687 XFillRectangle(d,w,g,xl,yl,xs,ys); \
689 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
690 XFillRectangle(d,w,g,xl,yl,xs,ys); \
692 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
693 XFillRectangle(d,w,g,xl,yl,xs,ys); \
695 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
696 XFillRectangle(d,w,g,xl,yl,xs,ys)
699 /* Draws the pacman sprite, removing the previous location. */
702 draw_pacman_sprite (ModeInfo * mi)
704 Display *display = MI_DISPLAY (mi);
705 Window window = MI_WINDOW (mi);
706 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
707 unsigned int dir = 0;
708 int old_mask_dir = 0;
709 int old_mask_mouth = 0;
710 Pixmap old_mask, new_mask;
713 #define MAX_MOUTH_DELAY 2
714 #define MAX_DEATH_DELAY 20
716 if (pp->pacman.aistate == ps_dieing){
717 pp->pacman.cf = pp->pacman.oldcf;
718 pp->pacman.rf = pp->pacman.oldrf;
721 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
722 pp->pacman.cfactor + pp->xb + pp->spritedx;
723 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
724 pp->pacman.rfactor + pp->yb + pp->spritedy;
727 dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
728 ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
730 if (pp->pm_mouth_delay == MAX_MOUTH_DELAY) {
731 if (pp->pm_mouth == (MAXMOUTH - 1) || pp->pm_mouth == 0) {
732 pp->pm_open_mouth = !pp->pm_open_mouth;
734 pp->pm_open_mouth ? pp->pm_mouth++ : pp->pm_mouth--;
735 pp->pm_mouth_delay = 0;
738 pp->pm_mouth_delay++;
741 if (pp->pacman.aistate == ps_dieing){
742 if (pp->pm_death_frame >= PAC_DEATH_FRAMES) {
743 pp->pacman.aistate = ps_eating;
744 pp->pm_death_frame = 0;
745 pp->pm_death_delay = 0;
746 reset_level (mi, 0, False);
750 old_mask = pp->pacmanMask[0][0];
751 new_mask = pp->pacmanMask[0][0];
752 pacman = pp->pacman_ds[pp->pm_death_frame];
753 if (pp->pm_death_delay == MAX_DEATH_DELAY){
754 pp->pm_death_frame++;
755 pp->pm_death_delay = 0;
758 pp->pm_death_delay++;
763 old_mask = pp->pacmanMask[old_mask_dir][old_mask_mouth];
764 new_mask = pp->pacmanMask[dir][pp->pm_mouth];
765 pacman = pp->pacmanPixmap[dir][pp->pm_mouth];
768 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
770 XSetClipMask (display, pp->stippledGC, old_mask);
772 XSetClipOrigin (display, pp->stippledGC, pp->pacman.oldcf,
774 XFillRectangle (display, window, pp->stippledGC, pp->pacman.oldcf,
775 pp->pacman.oldrf, pp->spritexs, pp->spriteys);
776 XSetClipMask (display, pp->stippledGC, new_mask);
777 XSetClipOrigin (display, pp->stippledGC, pp->pacman.cf, pp->pacman.rf);
778 XCopyArea (display, pacman, window,
779 pp->stippledGC, 0, 0, pp->spritexs, pp->spriteys,
780 pp->pacman.cf, pp->pacman.rf);
781 XSetClipMask (display, pp->stippledGC, None);
782 if (pp->pacman.aistate != ps_dieing){
783 pp->pacman.oldcf = pp->pacman.cf;
784 pp->pacman.oldrf = pp->pacman.rf;
790 draw_ghost_position (ModeInfo * mi, ghoststruct * ghost)
792 draw_position (mi, ghost->oldcf, ghost->oldrf, MI_BLACK_PIXEL (mi));
793 draw_position (mi, ghost->cf, ghost->rf, 0x00ff00);
798 draw_ghost_ndirs ( ModeInfo *mi, ghoststruct * ghost)
800 draw_number (mi, ghost->oldcf, ghost->oldrf, ghost->oldndirs, MI_BLACK_PIXEL (mi));
801 ghost->oldndirs = ghost->ndirs;
802 draw_number (mi, ghost->cf, ghost->rf, ghost->ndirs, 0x00ff00);
808 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
810 Display *display = MI_DISPLAY (mi);
811 Window window = MI_WINDOW (mi);
812 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
813 #define MAX_WAG_COUNT 50
814 unsigned int dir = 0;
815 unsigned int fs = 0; /*flash scared*/
816 Pixmap g_pix; /*ghost pixmap*/
819 dir = (ABS (pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
820 ABS (pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
823 fs = pp->ghosts[ghost].flash_scared;
824 assert (fs == 0 || fs == 1);
826 /* Choose the pixmap */
827 switch (pp->ghosts[ghost].aistate){
829 g_pix = pp->s_ghostPixmap[fs][pp->gh_wag];
832 g_pix = pp->ghostEyes[dir];
836 while ( i < pp->ghosts[ghost].trace_idx ){
837 XFillRectangle (display,
840 pp->ghosts[ghost].trace[i].vx,
841 pp->ghosts[ghost].trace[i].vy,
842 pp->spritexs, pp->spriteys);
851 g_pix = pp->ghostPixmap[ghost][dir][pp->gh_wag];
854 pp->ghosts[ghost].cf =
855 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
856 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
857 pp->ghosts[ghost].rf =
858 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
859 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
861 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
863 XSetClipMask (display, pp->stippledGC, pp->ghostMask);
864 XSetClipOrigin (display, pp->stippledGC,
865 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
866 XFillRectangle (display,
869 pp->ghosts[ghost].oldcf,
870 pp->ghosts[ghost].oldrf, pp->spritexs, pp->spriteys);
873 if (pp->pacman.aistate != ps_dieing) {
874 drawlevelblock (mi, pp,
875 (unsigned int) pp->ghosts[ghost].col,
876 (unsigned int) pp->ghosts[ghost].row);
880 XSetClipOrigin (display, pp->stippledGC,
881 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
883 XCopyArea (display, g_pix, window, pp->stippledGC, 0, 0,
884 pp->spritexs, pp->spriteys, pp->ghosts[ghost].cf,
885 pp->ghosts[ghost].rf);
887 XSetClipMask (display, pp->stippledGC, None);
890 draw_ghost_position (mi, &(pp->ghosts[ghost]));
894 draw_ghost_ndirs ( mi, &(pp->ghosts[ghost]));
897 if (pp->pacman.aistate != ps_dieing) {
898 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
899 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
900 if (pp->gh_wag_count++ == MAX_WAG_COUNT) {
901 pp->gh_wag = !pp->gh_wag;
902 pp->gh_wag_count = 0;
909 ghost_over (ModeInfo * mi, int x, int y)
913 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
914 dot_rc_to_pixel (mi, &x, &y);
915 for (ghost = 0; ghost < pp->nghosts; ghost++) {
916 if ((pp->ghosts[ghost].cf <= x
917 && x <= pp->ghosts[ghost].cf + pp->spritexs)
918 && (pp->ghosts[ghost].rf <= y
919 && y <= pp->ghosts[ghost].rf + pp->spriteys)) {
929 flash_bonus_dots (ModeInfo * mi)
931 #define MAX_FLASH_COUNT 25
932 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
934 for (i = 0; i < NUM_BONUS_DOTS; i++) {
935 if (!pacman_bonus_dot_eaten (pp, i)) {
936 pacman_bonus_dot_pos (pp, i, &x, &y);
937 if (ghost_over (mi, x, y))
940 draw_bonus_dot (mi, pp, x, y);
942 clear_bonus_dot (mi, pp, x, y);
945 if (pp->bd_flash_count-- == 0) {
946 pp->bd_flash_count = MAX_FLASH_COUNT;
947 pp->bd_on = !pp->bd_on;
952 ate_bonus_dot (ModeInfo * mi)
954 /*Check pacman's position. If it is over a bonus dot and that dot
955 *has not been eaten, then return true
957 unsigned int ret = 0;
959 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
960 if (pacman_is_bonus_dot (pp, pp->pacman.col, pp->pacman.row, &idx)) {
961 ret = !pacman_bonus_dot_eaten (pp, idx);
962 pacman_eat_bonus_dot (pp, idx);
968 ghost_scared (ModeInfo * mi)
971 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
972 for (ghost = 0; ghost < pp->nghosts; ghost++) {
973 if (pp->ghosts[ghost].aistate == goingin ||
974 pp->ghosts[ghost].aistate == goingout ||
975 pp->ghosts[ghost].aistate == inbox ) continue;
976 pp->ghosts[ghost].aistate = hiding;
977 pp->ghosts[ghost].flash_scared = 0;
978 if (pp->pacman.aistate != ps_dieing)
979 pp->pacman.aistate = ps_chasing;
984 ghost_not_scared (ModeInfo * mi)
987 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
988 for (ghost = 0; ghost < pp->nghosts; ghost++){
989 if (pp->ghosts[ghost].aistate == goingin ||
990 pp->ghosts[ghost].aistate == goingout ||
991 pp->ghosts[ghost].aistate == inbox ) continue;
992 pp->ghosts[ghost].aistate = chasing;
994 if (pp->pacman.aistate != ps_dieing)
995 pp->pacman.aistate = ps_eating;
1000 ghost_flash_scared (ModeInfo * mi)
1003 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1004 for (ghost = 0; ghost < pp->nghosts; ghost++)
1005 pp->ghosts[ghost].flash_scared = !pp->ghosts[ghost].flash_scared;
1008 /* Does all drawing of moving sprites in the level. */
1010 pacman_tick (ModeInfo * mi)
1012 #define DEFAULT_SCARED_TIME 500
1013 #define START_FLASH 200
1014 #define FLASH_COUNT 25
1016 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1021 for (ghost = 0; ghost < pp->nghosts; ghost++) {
1022 draw_ghost_sprite (mi, ghost);
1024 print_ghost_stats (mi, &(pp->ghosts[ghost]), ghost);
1028 print_pac_stats (mi, &(pp->pacman));
1030 draw_pacman_sprite (mi);
1031 flash_bonus_dots (mi);
1032 if (ate_bonus_dot (mi)) {
1033 pp->ghost_scared_timer = (random () % 100) + DEFAULT_SCARED_TIME;
1037 if (pp->ghost_scared_timer > 0) {
1038 if (--pp->ghost_scared_timer == 0)
1039 ghost_not_scared (mi);
1040 else if (pp->ghost_scared_timer <= START_FLASH) {
1041 if (pp->flash_timer <= 0) {
1042 pp->flash_timer = FLASH_COUNT;
1043 ghost_flash_scared (mi);
1050 We don't want to miss the last death sequence. So if pacman has died three times
1051 we wait for his state to change from dieing to something else before we repopulate
1052 the level. If pacman ate all of the dots then we just repopulate.
1055 if (pp->dotsleft == 0 )
1057 else if (pp->pacman.deaths >= 3){
1058 if (pp->old_pac_state == ps_dieing && pp->pacman.aistate != ps_dieing)
1062 pp->old_pac_state = pp->pacman.aistate;
1066 /* CODE TO LOAD AND SCALE THE PIXMAPS
1069 /* Grabbed the scaling routine off of usenet.
1070 * Changed it so that the information specific
1071 * to the source pixmap does not have to be a parameter.
1073 * There is probably a better way to scale pixmaps.
1074 * From: Chris Fiddyment (cxf@itd.dsto.gov.au)
1075 * Subject: Scaling Pixmap Algorithm.
1076 * Newsgroups: comp.graphics.algorithms
1077 * Date: 1994-07-06 18:51:38 PST
1082 scale_pixmap (Display ** dpy, GC gc, Pixmap source, int dwidth, int dheight)
1087 float xscale, yscale;
1088 unsigned int swidth, sheight;
1091 unsigned border_width_return, depth;
1092 XGetGeometry (*dpy, source, &window, &x, &y, &swidth, &sheight,
1093 &border_width_return, &depth);
1095 xscale = (float) swidth / (float) dwidth; /* Scaling factors */
1096 yscale = (float) sheight / (float) dheight;
1098 dest = XCreatePixmap (*dpy, window, dwidth, dheight, depth);
1100 fprintf (stderr, "%s Could not scale image", progname);
1102 temp = XCreatePixmap (*dpy, window, dwidth, sheight, depth);
1104 fprintf (stderr, "%s Could not scale image", progname);
1108 end = dwidth * xscale;
1109 /* Scale width of source into temp pixmap */
1110 for (i = 0; i <= end; i += xscale)
1111 XCopyArea (*dpy, source, temp, gc, i, 0, 1, sheight, j++, 0);
1114 end = dheight * yscale;
1115 /* Scale height of temp into dest pixmap */
1116 for (i = 0; i <= end; i += yscale)
1117 XCopyArea (*dpy, temp, dest, gc, 0, i, dwidth, 1, 0, j++);
1119 XFreePixmap (*dpy, temp);
1120 return (Pixmap) dest;
1124 subpixmap (Display *dpy, Window window, Pixmap src,
1125 int w, int h, int y, int depth)
1128 Pixmap dest = XCreatePixmap (dpy, window, w, h, depth);
1129 GC gc = XCreateGC (dpy, src, 0, &gcv);
1130 XCopyArea (dpy, src, dest, gc, 0, y, w, h, 0, 0);
1136 /* Load the ghost pixmaps and their mask. */
1138 load_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1140 pacmangamestruct *pp = *ps;
1141 Display *display = *dpy;
1142 Pixmap sprites, sprites_mask;
1143 int i, j, k, /* m, */ sw, sh, srcy;
1144 /* int w = pp->spritexs;
1145 int h = pp->spriteys;*/
1149 XWindowAttributes xgwa;
1151 XGetWindowAttributes (display, window, &xgwa);
1153 sprites = image_data_to_pixmap (display, window,
1154 pacman_png, sizeof(pacman_png), &sw, &sh,
1156 if (!sprites || !sprites_mask) abort();
1160 gc = XCreateGC (display, sprites_mask, 0, &gcv);
1162 pp->ghostMask = subpixmap (display, window, sprites_mask,
1164 pp->ghostMask = scale_pixmap (&display, gc, pp->ghostMask,
1165 pp->spritexs, pp->spriteys);
1167 for (i = 0; i < 4; i++) {
1169 for (j = 0; j < MAXGDIR; j++) {
1170 for (k = 0; k < MAXGWAG; k++) {
1171 pp->ghostPixmap[i][j][k] =
1172 subpixmap (display, window, sprites, sw, sw, srcy,
1174 pp->ghostPixmap[i][j][k] =
1175 scale_pixmap (&display, pp->stippledGC,
1176 pp->ghostPixmap[i][j][k], pp->spritexs,
1180 if (srcy >= sh) abort();
1185 /* load the scared ghost */
1187 for (i = 0; i < MAXGFLASH; i++) {
1188 for (j = 0; j < MAXGWAG; j++) {
1189 pp->s_ghostPixmap[i][j] =
1190 subpixmap (display, window, sprites, sw, sw, srcy,
1193 pp->s_ghostPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1194 pp->s_ghostPixmap[i][j],
1198 if (srcy >= sh) abort();
1202 /* load the ghost eyes */
1203 for (i = 0; i < MAXGDIR; i++) {
1205 subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1206 pp->ghostEyes[i] = scale_pixmap (&display, pp->stippledGC,
1211 if (srcy >= sh) abort();
1215 /* Load the pacman pixmaps and their mask. */
1218 for (i = 0; i < 4; i++) {
1219 for (j = 0; j < MAXMOUTH; j++) {
1220 pp->pacmanPixmap[i][j] =
1221 subpixmap (display, window, sprites, sw, sw, srcy,
1223 pp->pacmanMask[i][j] =
1224 subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1226 pp->pacmanPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1227 pp->pacmanPixmap[i][j],
1230 pp->pacmanMask[i][j] =
1231 scale_pixmap (&display, gc, pp->pacmanMask[i][j],
1232 pp->spritexs, pp->spriteys);
1234 if (srcy >= sh) abort();
1238 /* Load pacman death sequence */
1239 for ( i = 0; i < PAC_DEATH_FRAMES; i++ ){
1240 if (srcy > sh - sw) abort();
1242 subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1243 pp->pacman_ds_mask[i] =
1244 subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1246 pp->pacman_ds[i] = scale_pixmap ( &display, pp->stippledGC,
1250 pp->pacman_ds_mask[i] =
1251 scale_pixmap (&display, gc, pp->pacman_ds_mask[i],
1252 pp->spritexs, pp->spriteys);
1256 XFreePixmap (*dpy, sprites);
1257 XFreePixmap (*dpy, sprites_mask);
1261 /* Hook function, sets state to initial position. */
1263 init_pacman (ModeInfo * mi)
1265 Display *display = MI_DISPLAY (mi);
1266 Window window = MI_WINDOW (mi);
1267 long size = MI_SIZE (mi);
1268 pacmangamestruct *pp;
1272 MI_INIT (mi, pacman_games);
1273 pp = &pacman_games[MI_SCREEN (mi)];
1275 pp->width = (unsigned short) MI_WIDTH (mi);
1276 pp->height = (unsigned short) MI_HEIGHT (mi);
1277 for (i = 0; i < 4; i++) {
1278 for (j = 0; j < MAXGDIR; j++) {
1279 for (k = 0; k < MAXGWAG; k++) {
1280 if (pp->ghostPixmap[i][j][k] != None) {
1281 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
1282 pp->ghostPixmap[i][j][k] = None;
1283 pp->graphics_format = 0 /*IS_NONE */ ;
1289 for (i = 0; i < MAXGFLASH; i++) {
1290 for (j = 0; j < MAXGWAG; j++) {
1291 if (pp->s_ghostPixmap[i][j] != None) {
1292 XFreePixmap (display, pp->s_ghostPixmap[i][j]);
1293 pp->s_ghostPixmap[i][j] = None;
1299 MINGRIDSIZE * size > (int) pp->width ||
1300 MINGRIDSIZE * size > (int) pp->height) {
1301 double scale = MIN (pp->width / LEVWIDTH, pp->height / LEVHEIGHT);
1303 if (pp->width > pp->height * 5 || /* weird window aspect ratio */
1304 pp->height > pp->width * 5)
1305 scale = 0.8 * (pp->width / pp->height
1306 ? pp->width / (double) pp->height
1307 : pp->height / (double) pp->width);
1308 pp->ys = MAX (scale, 1);
1312 if (size < -MINSIZE)
1313 pp->ys = (short) (NRAND (MIN (-size, MAX (MINSIZE,
1317 - MINSIZE + 1) + MINSIZE);
1318 else if (size < MINSIZE)
1321 pp->ys = (short) (MIN (size,
1322 MAX (MINSIZE, MIN (pp->width, pp->height) /
1328 pp->wallwidth = (unsigned int) (pp->xs + pp->ys) >> 4;
1329 if (pp->wallwidth < 1)
1331 pp->incx = (pp->xs >> 3) + 1;
1332 pp->incy = (pp->ys >> 3) + 1;
1333 pp->ncols = (unsigned short) MAX (LEVWIDTH, 2);
1334 pp->nrows = (unsigned short) MAX (LEVHEIGHT, 2);
1335 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1336 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1337 pp->spritexs = MAX (pp->xs + (pp->xs >> 1) - 1, 1);
1338 pp->spriteys = MAX (pp->ys + (pp->ys >> 1) - 1, 1);
1339 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
1340 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
1341 pp->old_pac_state = ps_chasing;
1343 if (!pp->stippledGC) {
1344 gcv.foreground = MI_BLACK_PIXEL (mi);
1345 gcv.background = MI_BLACK_PIXEL (mi);
1346 if ((pp->stippledGC = XCreateGC (display, window,
1347 GCForeground | GCBackground,
1355 jwxyz_XSetAntiAliasing (display, pp->stippledGC, False);
1358 load_pixmaps (&display, window, &pp);
1360 pp->pacman.lastbox = START;
1361 pp->pacman.mouthdirection = 1;
1362 pp->pacman.nextcol = NOWHERE;
1363 pp->pacman.nextrow = NOWHERE;
1365 if (pp->ghosts != NULL) {
1367 pp->ghosts = (ghoststruct *) NULL;
1369 pp->nghosts = GHOSTS;
1372 if ((pp->ghosts = (ghoststruct *) calloc ((size_t) pp->nghosts,
1373 sizeof (ghoststruct))) ==
1379 pp->pacman.mouthstage = MAXMOUTH - 1;
1381 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1385 /* Callback function called for each tick. This is the complete machinery of
1386 everything that moves. */
1388 draw_pacman (ModeInfo * mi)
1391 pacmangamestruct *pp;
1393 if (pacman_games == NULL)
1395 pp = &pacman_games[MI_SCREEN (mi)];
1396 if (pp->ghosts == NULL)
1399 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1400 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1401 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1402 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1404 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1405 pacman_update (mi, pp, &(pp->pacman));
1406 check_death (mi, pp);
1407 pp->pacman.delta.x = pp->incx;
1408 pp->pacman.delta.y = pp->incy;
1411 if (pp->pacman.delta.x > pp->xs + pp->incx)
1412 pp->pacman.delta.x = pp->xs + pp->incx;
1413 if (pp->pacman.delta.y > pp->ys + pp->incy)
1414 pp->pacman.delta.y = pp->ys + pp->incy;
1416 for (g = 0; g < pp->nghosts; g++) {
1417 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % pp->ghosts[g].speed;
1418 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % pp->ghosts[g].speed;
1419 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? pp->incx : 0;
1420 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? pp->incy : 0;
1422 if (pp->ghosts[g].delta.x >= pp->xs &&
1423 pp->ghosts[g].delta.y >= pp->ys) {
1424 pacman_ghost_update (pp, &(pp->ghosts[g]));
1425 pp->ghosts[g].delta.x = pp->incx;
1426 pp->ghosts[g].delta.y = pp->incy;
1429 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1430 pp->ghosts[g].delta.x = pp->xs + pp->incx;
1431 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1432 pp->ghosts[g].delta.y = pp->ys + pp->incy;
1438 /* Refresh current level. */
1440 refresh_pacman (ModeInfo * mi)
1448 reshape_pacman(ModeInfo * mi, int width, int height)
1450 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1452 pp->height = height;
1453 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1454 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1455 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1456 /* repopulate (mi); */
1461 /* Callback to change level. */
1463 change_pacman (ModeInfo * mi)
1465 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1468 #endif /* !STANDALONE */
1471 XSCREENSAVER_MODULE ("Pacman", pacman)
1473 #endif /* MODE_pacman */