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 "images/gen/pacman_png.h"
73 #ifdef DISABLE_INTERACTIVE
74 ENTRYPOINT ModeSpecOpt pacman_opts = {
76 (XrmOptionDescRec *) NULL,
82 static XrmOptionDescRec opts[] = {
83 {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
84 {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
87 static argtype vars[] = {
88 {&pacman_trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
91 static OptionStruct desc[] = {
92 {"-/+trackmouse", "turn on/off the tracking of the mouse"}
95 ENTRYPOINT ModeSpecOpt pacman_opts =
96 { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars,
101 ModStruct pacman_description = {
102 "pacman", /* *cmdline_arg; */
103 "init_pacman", /* *init_name; */
104 "draw_pacman", /* *callback_name; */
105 (char *) NULL, /* *release_name; */
106 "refresh_pacman", /* *refresh_name; */
107 "change_pacman", /* *change_name; */
108 "free_pacman", /* *free_name; */
109 &pacman_opts, /* *msopts */
110 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
115 Bool pacman_trackmouse;
116 pacmangamestruct *pacman_games = (pacmangamestruct *) NULL;
118 static void repopulate (ModeInfo * mi);
119 static void drawlevel (ModeInfo * mi);
123 free_pacman (ModeInfo * mi)
125 Display * display = MI_DISPLAY (mi);
126 pacmangamestruct * pp = &pacman_games[MI_SCREEN (mi)];
127 int dir, mouth, i, j, k;
129 if (pp->ghosts != NULL) {
131 pp->ghosts = (ghoststruct *) NULL;
133 if (pp->stippledGC != None) {
134 XFreeGC (display, pp->stippledGC);
135 pp->stippledGC = None;
137 for (i = 0; i < 4; i++) {
138 for (j = 0; j < MAXGDIR; j++) {
139 for (k = 0; k < MAXGWAG; k++) {
140 if (pp->ghostPixmap[i][j][k] != None) {
141 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
142 pp->ghostPixmap[i][j][k] = None;
147 for (dir = 0; dir < 4; dir++)
148 for (mouth = 0; mouth < MAXMOUTH; mouth++)
149 if (pp->pacmanPixmap[dir][mouth] != None) {
150 XFreePixmap (display, pp->pacmanPixmap[dir][mouth]);
151 pp->pacmanPixmap[dir][mouth] = None;
155 /* set pacman and the ghost in there starting positions, but don't draw a new
158 reset_level (ModeInfo * mi, int n, int pac_init)
160 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
163 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
166 pp->gamestate = GHOST_DANGER;
169 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT) / 2 - n;
170 pp->pacman.init_row = pp->pacman.row;
173 pp->pacman.row = pp->pacman.init_row;
175 pp->pacman.col = (LEVWIDTH / 2);
176 pp->pacman.nextrow = NOWHERE;
177 pp->pacman.nextcol = NOWHERE;
178 pp->pacman.cf = NOWHERE;
179 pp->pacman.rf = NOWHERE;
180 pp->pacman.oldcf = NOWHERE;
181 pp->pacman.oldrf = NOWHERE;
182 pp->pacman.oldlx = NOWHERE;
183 pp->pacman.oldly = NOWHERE;
184 pp->pacman.aistate = ps_eating;
185 pp->pacman.cur_trace = 0;
186 pp->pacman.roundscore = 0;
187 pp->pacman.speed = 4;
188 pp->pacman.lastturn = 0;
189 pp->pacman.delta.x = 0;
190 pp->pacman.delta.y = 0;
192 for (ghost = 0; ghost < pp->nghosts; ghost++) {
193 pp->ghosts[ghost].col = (LEVWIDTH / 2);
194 pp->ghosts[ghost].row = (LEVHEIGHT / 2);
195 pp->ghosts[ghost].nextcol = NOWHERE;
196 pp->ghosts[ghost].nextrow = NOWHERE;
197 pp->ghosts[ghost].dead = 0;
198 pp->ghosts[ghost].lastbox = START;
199 pp->ghosts[ghost].cf = NOWHERE;
200 pp->ghosts[ghost].rf = NOWHERE;
201 pp->ghosts[ghost].oldcf = NOWHERE;
202 pp->ghosts[ghost].oldrf = NOWHERE;
203 pp->ghosts[ghost].aistate = inbox;
204 pp->ghosts[ghost].timeleft = ghost * 20;
205 pp->ghosts[ghost].speed = 3;
206 pp->ghosts[ghost].delta.x = 0;
207 pp->ghosts[ghost].delta.y = 0;
208 pp->ghosts[ghost].flash_scared = False;
209 pp->ghosts[ghost].wait_pos = False;
210 pacman_ghost_update (pp, &(pp->ghosts[ghost]));
212 pacman_update (mi, pp, &(pp->pacman));
216 pacman_ghost_collision(unsigned int ghost, pacmangamestruct * pp)
218 return (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
219 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
220 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
221 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
222 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
223 (pp->ghosts[ghost].col == pp->pacman.nextcol)));
227 /* Checks for death of any ghosts/pacman and updates. It also makes a new
228 level if all ghosts are dead or all dots are eaten. */
230 check_death (ModeInfo * mi, pacmangamestruct * pp)
232 Display *display = MI_DISPLAY (mi);
233 Window window = MI_WINDOW (mi);
236 if (pp->pacman.aistate == ps_dieing) return;
238 for (ghost = 0; ghost < pp->nghosts; ghost++) {
240 /* The ghost have to be scared before you can kill them */
241 if ( pacman_ghost_collision ( ghost, pp ) ) {
242 if (pp->ghosts[ghost].aistate == goingin) continue;
244 if (pp->ghosts[ghost].aistate == hiding) {
245 pp->ghosts[ghost].dead = 1;
246 pp->ghosts[ghost].aistate = goingin;
247 pp->ghosts[ghost].wait_pos = True;
248 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
249 XFillRectangle (display, window,
251 pp->ghosts[ghost].cf,
252 pp->ghosts[ghost].rf,
253 pp->spritexs, pp->spriteys);
258 pp->pacman.aistate = ps_dieing;
266 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
268 repopulate (ModeInfo * mi)
270 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
271 pp->pacman.deaths = 0;
272 reset_level (mi, pacman_createnewlevel (pp), True);
273 check_death (mi, pp);
276 /* Sets the color to the color of a wall. */
278 setwallcolor (ModeInfo * mi)
280 Display *display = MI_DISPLAY (mi);
281 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
283 if (MI_NPIXELS (mi) > 2)
284 XSetForeground (display, pp->stippledGC, MI_PIXEL (mi, BLUE));
286 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
289 /* Sets the color to the color of a dot. */
291 setdotcolor (ModeInfo * mi)
293 Display *display = MI_DISPLAY (mi);
294 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
296 XSetForeground (display, pp->stippledGC, MI_WHITE_PIXEL (mi));
300 cleardotcolor (ModeInfo * mi)
302 Display *display = MI_DISPLAY (mi);
303 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
305 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
310 draw_position (ModeInfo * mi, int x, int y, int color)
312 Display *display = MI_DISPLAY (mi);
313 Window window = MI_WINDOW (mi);
314 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
315 XFontStruct *font = NULL;
316 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
319 font = load_font_retry (display, f_name);
320 assert (font != NULL);
322 s = (char *) malloc (256);
324 sprintf (s, "(%d,%d)", x, y);
325 XSetForeground (display, pp->stippledGC, color);
326 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
333 draw_number (ModeInfo * mi, int x, int y, int num, int color)
335 Display *display = MI_DISPLAY (mi);
336 Window window = MI_WINDOW (mi);
337 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
338 XFontStruct *font = NULL;
339 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
342 font = load_font_retry (display, f_name);
343 assert (font != NULL);
345 s = (char *) malloc (256);
347 sprintf (s, "%d", num);
348 XSetForeground (display, pp->stippledGC, color);
349 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
356 /* draw_grid - draws a grid on top of the playing field.
357 * Used so that I can determine if I'm converting from rows and columns to x and y
358 * coordinates correctly.
361 draw_grid (ModeInfo * mi)
363 Display *display = MI_DISPLAY (mi);
364 Window window = MI_WINDOW (mi);
365 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
366 int h = MI_HEIGHT (mi);
367 int w = MI_WIDTH (mi);
370 XSetForeground (display, pp->stippledGC, 0xff0000);
373 XDrawLine (display, window, pp->stippledGC, x, 0, x, h);
377 XDrawLine (display, window, pp->stippledGC, 0, y, w, y);
385 draw_string (ModeInfo * mi, int x, int y, char *s, int color)
387 Display *display = MI_DISPLAY (mi);
388 Window window = MI_WINDOW (mi);
389 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
390 XFontStruct *font = NULL;
391 char *f_name = "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*";
393 font = load_font_retry (display, f_name);
394 assert (font != NULL);
397 XSetForeground (display, pp->stippledGC, color);
398 XDrawString (display, window, pp->stippledGC, x, y, s, strlen (s));
402 /* I think this function has a memory leak. Be careful if you enable it. */
403 /* I only used it briefly to help me debug the ghost's aistate. It prints */
404 /* the state of each ghost on the left hand side of the screen */
406 print_ghost_stats (ModeInfo *mi, ghoststruct *g , int ghost_num)
410 sprintf (s, "GHOST: %d", ghost_num );
413 sprintf (s, "%s inbox", s);
416 sprintf (s, "%s goingout", s);
419 sprintf (s, "%s randdir", s);
422 sprintf (s, "%s chasing", s);
425 sprintf (s, "%s hiding", s);
428 sprintf (s, "%s goingin",s);
431 draw_string (mi, 0, (ghost_num *3) *10+50, g->last_stat, 0x000000);
432 draw_string (mi, 0, (ghost_num *3) *10+50, s, 0xff0000);
433 strcpy(g->last_stat,s);
436 /* prints the number of times pacman has died and his aistate on the left hand */
437 /* side of the screen */
439 print_pac_stats ( ModeInfo *mi, pacmanstruct *pac )
441 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
443 sprintf (s, "Pacman, Deaths: %d", pac->deaths );
444 switch ( pac->aistate ){
446 sprintf(s, "%s ps_eating",s );
449 sprintf(s, "%s ps_chasing",s );
452 sprintf(s, "%s ps_hiding",s );
455 sprintf(s, "%s ps_random",s );
458 sprintf(s, "%s ps_dieing",s );
461 draw_string ( mi, 0, 200, pp->last_pac_stat, 0x000000);
462 draw_string ( mi, 0, 200, s, 0xff0000);
463 strcpy(pp->last_pac_stat, s );
468 /*Ok, yeah whatever?*/
469 /*dot_rc_to_pixel - magic that converts row and columns into
470 *the x and y coordinates of the screen.
473 dot_rc_to_pixel (ModeInfo * mi, int *x, int *y)
475 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
477 (pp->xs / 2) - (pp->xs > 32 ? (pp->xs / 16) : 1) + pp->xb;
479 (pp->ys / 2) - (pp->ys > 32 ? (pp->ys / 16) : 1) + pp->yb;
482 /* dot_width_height - magic used to get the width and height of
483 * a dot. This dot can also be scaled by a value.
486 dot_width_height (ModeInfo *mi, int *w, int *h)
488 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
490 *w = *h = (pp->xs / 16 );
497 bonus_dot_width_height (ModeInfo *mi, int *w, int *h )
499 *w = *h = MI_HEIGHT (mi) / 65;
505 draw_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y,
506 void (*width_height)(ModeInfo * mi, int *w, int *h),
507 int (*arc_func) (Display * display, Drawable d, GC gc,
508 int x, int y, unsigned int width,
509 unsigned int height, int angle1,
512 Display *display = MI_DISPLAY (mi);
513 Window window = MI_WINDOW (mi);
515 dot_rc_to_pixel (mi, &x, &y);
516 width_height(mi, &w, &h);
517 (void) arc_func (display, window, pp->stippledGC,
518 x, y, w, h, 0, 23040);
522 draw_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
527 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
528 dot_rc_to_pixel (mi, &x2, &y2);
530 draw_position (mi, x2, y2, 0xff0000);
535 clear_bonus_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
538 draw_dot (mi, pp, x, y, bonus_dot_width_height, XFillArc);
542 draw_regular_dot (ModeInfo * mi, pacmangamestruct * pp, int x, int y)
545 draw_dot (mi, pp, x, y, dot_width_height, XDrawArc);
548 /* Draws a block in the level at the specified x and y locations. */
550 drawlevelblock (ModeInfo * mi, pacmangamestruct * pp,
551 const unsigned x, const unsigned y)
553 Display *display = MI_DISPLAY (mi);
554 Window window = MI_WINDOW (mi);
563 XSetFillStyle (display, pp->stippledGC, FillSolid);
564 #endif /* !HAVE_JWXYZ */
565 XSetLineAttributes (display, pp->stippledGC, pp->wallwidth,
566 LineSolid, CapRound, JoinMiter);
568 if (pp->xs < 2 || pp->ys < 2) {
569 switch (pp->level[y * LEVWIDTH + x]) {
575 (void) XDrawPoint (display, window,
577 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
581 (void) XDrawPoint (display, window,
583 x * pp->xs + pp->xb, y * pp->ys + pp->yb);
589 switch (pp->level[y * LEVWIDTH + x]) {
596 if (pp->xs < 8 || pp->ys < 8) {
597 (void) XDrawPoint (display, window,
599 x * pp->xs + pp->xb +
600 pp->xs / 2, y * pp->ys + pp->yb + pp->ys / 2);
604 draw_regular_dot (mi, pp, x, y);
606 /* What we will probably want to do here is have the pp->level store a 'o' for
607 * the bonus dots. The we can use the drawing routine above just with a bigger
611 draw_bonus_dot (mi, pp, x, y);
617 (void) XDrawLine (display, window, pp->stippledGC,
618 (pp->xs * x) + pp->xb,
619 (pp->ys * y) + (pp->ys / 2) + pp->yb,
620 (pp->xs * (x + 1)) + pp->xb,
621 (pp->ys * y) + (pp->ys / 2) + pp->yb);
626 (void) XDrawLine (display, window, pp->stippledGC,
627 (pp->xs * x) + (pp->xs / 2) + pp->xb,
628 (pp->ys * y) + pp->yb,
629 (pp->xs * x) + (pp->xs / 2) + pp->xb,
630 (pp->ys * (y + 1)) + pp->yb);
635 (void) XDrawArc (display, window, pp->stippledGC,
636 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
637 (pp->ys * y) + (pp->ys / 2) + pp->yb,
638 pp->xs, pp->ys, 0 * 64, 90 * 64);
643 (void) XDrawArc (display, window, pp->stippledGC,
644 (pp->xs * x) + (pp->ys / 2) + pp->xb,
645 (pp->ys * y) + (pp->ys / 2) + pp->yb,
646 pp->xs, pp->ys, 90 * 64, 90 * 64);
651 (void) XDrawArc (display, window, pp->stippledGC,
652 (pp->xs * x) + (pp->ys / 2) + pp->xb,
653 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
654 pp->xs, pp->ys, 180 * 64, 90 * 64);
659 (void) XDrawArc (display, window, pp->stippledGC,
660 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
661 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
662 pp->xs, pp->ys, 270 * 64, 90 * 64);
668 /* Draws a complete level. */
670 drawlevel (ModeInfo * mi)
672 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
675 for (y = 0; y < LEVHEIGHT; y++)
676 for (x = 0; x < LEVWIDTH; x++)
677 drawlevelblock (mi, pp, x, y);
680 /* There is some overlap so it can be made more efficient */
681 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
683 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
684 XFillRectangle(d,w,g,xl,yl,xs,ys); \
686 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
687 XFillRectangle(d,w,g,xl,yl,xs,ys); \
689 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
690 XFillRectangle(d,w,g,xl,yl,xs,ys); \
692 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
693 XFillRectangle(d,w,g,xl,yl,xs,ys)
696 /* Draws the pacman sprite, removing the previous location. */
699 draw_pacman_sprite (ModeInfo * mi)
701 Display *display = MI_DISPLAY (mi);
702 Window window = MI_WINDOW (mi);
703 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
704 unsigned int dir = 0;
705 int old_mask_dir = 0;
706 int old_mask_mouth = 0;
707 Pixmap old_mask, new_mask;
710 #define MAX_MOUTH_DELAY 2
711 #define MAX_DEATH_DELAY 20
713 if (pp->pacman.aistate == ps_dieing){
714 pp->pacman.cf = pp->pacman.oldcf;
715 pp->pacman.rf = pp->pacman.oldrf;
718 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
719 pp->pacman.cfactor + pp->xb + pp->spritedx;
720 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
721 pp->pacman.rfactor + pp->yb + pp->spritedy;
724 dir = (ABS (pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
725 ABS (pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
727 if (pp->pm_mouth_delay == MAX_MOUTH_DELAY) {
728 if (pp->pm_mouth == (MAXMOUTH - 1) || pp->pm_mouth == 0) {
729 pp->pm_open_mouth = !pp->pm_open_mouth;
731 pp->pm_open_mouth ? pp->pm_mouth++ : pp->pm_mouth--;
732 pp->pm_mouth_delay = 0;
735 pp->pm_mouth_delay++;
738 if (pp->pacman.aistate == ps_dieing){
739 if (pp->pm_death_frame >= PAC_DEATH_FRAMES) {
740 pp->pacman.aistate = ps_eating;
741 pp->pm_death_frame = 0;
742 pp->pm_death_delay = 0;
743 reset_level (mi, 0, False);
747 old_mask = pp->pacmanMask[0][0];
748 new_mask = pp->pacmanMask[0][0];
749 pacman = pp->pacman_ds[pp->pm_death_frame];
750 if (pp->pm_death_delay == MAX_DEATH_DELAY){
751 pp->pm_death_frame++;
752 pp->pm_death_delay = 0;
755 pp->pm_death_delay++;
760 old_mask = pp->pacmanMask[old_mask_dir][old_mask_mouth];
761 new_mask = pp->pacmanMask[dir][pp->pm_mouth];
762 pacman = pp->pacmanPixmap[dir][pp->pm_mouth];
765 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
767 XSetClipMask (display, pp->stippledGC, old_mask);
769 XSetClipOrigin (display, pp->stippledGC, pp->pacman.oldcf,
771 XFillRectangle (display, window, pp->stippledGC, pp->pacman.oldcf,
772 pp->pacman.oldrf, pp->spritexs, pp->spriteys);
773 XSetClipMask (display, pp->stippledGC, new_mask);
774 XSetClipOrigin (display, pp->stippledGC, pp->pacman.cf, pp->pacman.rf);
775 XCopyArea (display, pacman, window,
776 pp->stippledGC, 0, 0, pp->spritexs, pp->spriteys,
777 pp->pacman.cf, pp->pacman.rf);
778 XSetClipMask (display, pp->stippledGC, None);
779 if (pp->pacman.aistate != ps_dieing){
780 pp->pacman.oldcf = pp->pacman.cf;
781 pp->pacman.oldrf = pp->pacman.rf;
787 draw_ghost_position (ModeInfo * mi, ghoststruct * ghost)
789 draw_position (mi, ghost->oldcf, ghost->oldrf, MI_BLACK_PIXEL (mi));
790 draw_position (mi, ghost->cf, ghost->rf, 0x00ff00);
795 draw_ghost_ndirs ( ModeInfo *mi, ghoststruct * ghost)
797 draw_number (mi, ghost->oldcf, ghost->oldrf, ghost->oldndirs, MI_BLACK_PIXEL (mi));
798 ghost->oldndirs = ghost->ndirs;
799 draw_number (mi, ghost->cf, ghost->rf, ghost->ndirs, 0x00ff00);
805 draw_ghost_sprite (ModeInfo * mi, const unsigned ghost)
807 Display *display = MI_DISPLAY (mi);
808 Window window = MI_WINDOW (mi);
809 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
810 #define MAX_WAG_COUNT 50
811 unsigned int dir = 0;
812 unsigned int fs = 0; /*flash scared*/
813 Pixmap g_pix; /*ghost pixmap*/
816 dir = (ABS (pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
817 ABS (pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
820 fs = pp->ghosts[ghost].flash_scared;
821 assert (fs == 0 || fs == 1);
823 /* Choose the pixmap */
824 switch (pp->ghosts[ghost].aistate){
826 g_pix = pp->s_ghostPixmap[fs][pp->gh_wag];
829 g_pix = pp->ghostEyes[dir];
833 while ( i < pp->ghosts[ghost].trace_idx ){
834 XFillRectangle (display,
837 pp->ghosts[ghost].trace[i].vx,
838 pp->ghosts[ghost].trace[i].vy,
839 pp->spritexs, pp->spriteys);
848 g_pix = pp->ghostPixmap[ghost][dir][pp->gh_wag];
851 pp->ghosts[ghost].cf =
852 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
853 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
854 pp->ghosts[ghost].rf =
855 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
856 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
858 XSetForeground (display, pp->stippledGC, MI_BLACK_PIXEL (mi));
860 XSetClipMask (display, pp->stippledGC, pp->ghostMask);
861 XSetClipOrigin (display, pp->stippledGC,
862 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
863 XFillRectangle (display,
866 pp->ghosts[ghost].oldcf,
867 pp->ghosts[ghost].oldrf, pp->spritexs, pp->spriteys);
870 if (pp->pacman.aistate != ps_dieing) {
871 drawlevelblock (mi, pp,
872 (unsigned int) pp->ghosts[ghost].col,
873 (unsigned int) pp->ghosts[ghost].row);
877 XSetClipOrigin (display, pp->stippledGC,
878 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
880 XCopyArea (display, g_pix, window, pp->stippledGC, 0, 0,
881 pp->spritexs, pp->spriteys, pp->ghosts[ghost].cf,
882 pp->ghosts[ghost].rf);
884 XSetClipMask (display, pp->stippledGC, None);
887 draw_ghost_position (mi, &(pp->ghosts[ghost]));
891 draw_ghost_ndirs ( mi, &(pp->ghosts[ghost]));
894 if (pp->pacman.aistate != ps_dieing) {
895 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
896 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
897 if (pp->gh_wag_count++ == MAX_WAG_COUNT) {
898 pp->gh_wag = !pp->gh_wag;
899 pp->gh_wag_count = 0;
906 ghost_over (ModeInfo * mi, int x, int y)
910 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
911 dot_rc_to_pixel (mi, &x, &y);
912 for (ghost = 0; ghost < pp->nghosts; ghost++) {
913 if ((pp->ghosts[ghost].cf <= x
914 && x <= pp->ghosts[ghost].cf + pp->spritexs)
915 && (pp->ghosts[ghost].rf <= y
916 && y <= pp->ghosts[ghost].rf + pp->spriteys)) {
926 flash_bonus_dots (ModeInfo * mi)
928 #define MAX_FLASH_COUNT 25
929 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
931 for (i = 0; i < NUM_BONUS_DOTS; i++) {
932 if (!pacman_bonus_dot_eaten (pp, i)) {
933 pacman_bonus_dot_pos (pp, i, &x, &y);
934 if (ghost_over (mi, x, y))
937 draw_bonus_dot (mi, pp, x, y);
939 clear_bonus_dot (mi, pp, x, y);
942 if (pp->bd_flash_count-- == 0) {
943 pp->bd_flash_count = MAX_FLASH_COUNT;
944 pp->bd_on = !pp->bd_on;
949 ate_bonus_dot (ModeInfo * mi)
951 /*Check pacman's position. If it is over a bonus dot and that dot
952 *has not been eaten, then return true
954 unsigned int ret = 0;
956 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
957 if (pacman_is_bonus_dot (pp, pp->pacman.col, pp->pacman.row, &idx)) {
958 ret = !pacman_bonus_dot_eaten (pp, idx);
959 pacman_eat_bonus_dot (pp, idx);
965 ghost_scared (ModeInfo * mi)
968 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
969 for (ghost = 0; ghost < pp->nghosts; ghost++) {
970 if (pp->ghosts[ghost].aistate == goingin ||
971 pp->ghosts[ghost].aistate == goingout ||
972 pp->ghosts[ghost].aistate == inbox ) continue;
973 pp->ghosts[ghost].aistate = hiding;
974 pp->ghosts[ghost].flash_scared = 0;
975 if (pp->pacman.aistate != ps_dieing)
976 pp->pacman.aistate = ps_chasing;
981 ghost_not_scared (ModeInfo * mi)
984 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
985 for (ghost = 0; ghost < pp->nghosts; ghost++){
986 if (pp->ghosts[ghost].aistate == goingin ||
987 pp->ghosts[ghost].aistate == goingout ||
988 pp->ghosts[ghost].aistate == inbox ) continue;
989 pp->ghosts[ghost].aistate = chasing;
991 if (pp->pacman.aistate != ps_dieing)
992 pp->pacman.aistate = ps_eating;
997 ghost_flash_scared (ModeInfo * mi)
1000 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1001 for (ghost = 0; ghost < pp->nghosts; ghost++)
1002 pp->ghosts[ghost].flash_scared = !pp->ghosts[ghost].flash_scared;
1005 /* Does all drawing of moving sprites in the level. */
1007 pacman_tick (ModeInfo * mi)
1009 #define DEFAULT_SCARED_TIME 500
1010 #define START_FLASH 200
1011 #define FLASH_COUNT 25
1013 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1018 for (ghost = 0; ghost < pp->nghosts; ghost++) {
1019 draw_ghost_sprite (mi, ghost);
1021 print_ghost_stats (mi, &(pp->ghosts[ghost]), ghost);
1025 print_pac_stats (mi, &(pp->pacman));
1027 draw_pacman_sprite (mi);
1028 flash_bonus_dots (mi);
1029 if (ate_bonus_dot (mi)) {
1030 pp->ghost_scared_timer = (random () % 100) + DEFAULT_SCARED_TIME;
1034 if (pp->ghost_scared_timer > 0) {
1035 if (--pp->ghost_scared_timer == 0)
1036 ghost_not_scared (mi);
1037 else if (pp->ghost_scared_timer <= START_FLASH) {
1038 if (pp->flash_timer <= 0) {
1039 pp->flash_timer = FLASH_COUNT;
1040 ghost_flash_scared (mi);
1047 We don't want to miss the last death sequence. So if pacman has died three times
1048 we wait for his state to change from dieing to something else before we repopulate
1049 the level. If pacman ate all of the dots then we just repopulate.
1052 if (pp->dotsleft == 0 )
1054 else if (pp->pacman.deaths >= 3){
1055 if (pp->old_pac_state == ps_dieing && pp->pacman.aistate != ps_dieing)
1059 pp->old_pac_state = pp->pacman.aistate;
1063 /* CODE TO LOAD AND SCALE THE PIXMAPS
1066 /* Grabbed the scaling routine off of usenet.
1067 * Changed it so that the information specific
1068 * to the source pixmap does not have to be a parameter.
1070 * There is probably a better way to scale pixmaps.
1071 * From: Chris Fiddyment (cxf@itd.dsto.gov.au)
1072 * Subject: Scaling Pixmap Algorithm.
1073 * Newsgroups: comp.graphics.algorithms
1074 * Date: 1994-07-06 18:51:38 PST
1079 scale_pixmap (Display ** dpy, GC gc, Pixmap source, int dwidth, int dheight)
1084 float xscale, yscale;
1085 unsigned int swidth, sheight;
1088 unsigned border_width_return, depth;
1089 XGetGeometry (*dpy, source, &window, &x, &y, &swidth, &sheight,
1090 &border_width_return, &depth);
1092 xscale = (float) swidth / (float) dwidth; /* Scaling factors */
1093 yscale = (float) sheight / (float) dheight;
1095 dest = XCreatePixmap (*dpy, window, dwidth, dheight, depth);
1097 fprintf (stderr, "%s Could not scale image", progname);
1099 temp = XCreatePixmap (*dpy, window, dwidth, sheight, depth);
1101 fprintf (stderr, "%s Could not scale image", progname);
1105 end = dwidth * xscale;
1106 /* Scale width of source into temp pixmap */
1107 for (i = 0; i <= end; i += xscale)
1108 XCopyArea (*dpy, source, temp, gc, i, 0, 1, sheight, j++, 0);
1111 end = dheight * yscale;
1112 /* Scale height of temp into dest pixmap */
1113 for (i = 0; i <= end; i += yscale)
1114 XCopyArea (*dpy, temp, dest, gc, 0, i, dwidth, 1, 0, j++);
1116 XFreePixmap (*dpy, temp);
1117 return (Pixmap) dest;
1121 subpixmap (Display *dpy, Window window, Pixmap src,
1122 int w, int h, int y, int depth)
1125 Pixmap dest = XCreatePixmap (dpy, window, w, h, depth);
1126 GC gc = XCreateGC (dpy, src, 0, &gcv);
1127 XCopyArea (dpy, src, dest, gc, 0, y, w, h, 0, 0);
1133 /* Load the ghost pixmaps and their mask. */
1135 load_pixmaps (Display ** dpy, Window window, pacmangamestruct ** ps)
1137 pacmangamestruct *pp = *ps;
1138 Display *display = *dpy;
1139 Pixmap sprites, sprites_mask;
1140 int i, j, k, m, sw, sh, srcy;
1141 /* int w = pp->spritexs;
1142 int h = pp->spriteys;*/
1146 XWindowAttributes xgwa;
1148 XGetWindowAttributes (display, window, &xgwa);
1150 sprites = image_data_to_pixmap (display, window,
1151 pacman_png, sizeof(pacman_png), &sw, &sh,
1153 if (!sprites || !sprites_mask) abort();
1157 gc = XCreateGC (display, sprites_mask, 0, &gcv);
1159 pp->ghostMask = subpixmap (display, window, sprites_mask,
1161 pp->ghostMask = scale_pixmap (&display, gc, pp->ghostMask,
1162 pp->spritexs, pp->spriteys);
1164 for (i = 0; i < 4; i++) {
1166 for (j = 0; j < MAXGDIR; j++) {
1167 for (k = 0; k < MAXGWAG; k++) {
1168 pp->ghostPixmap[i][j][k] =
1169 subpixmap (display, window, sprites, sw, sw, srcy,
1171 pp->ghostPixmap[i][j][k] =
1172 scale_pixmap (&display, pp->stippledGC,
1173 pp->ghostPixmap[i][j][k], pp->spritexs,
1177 if (srcy >= sh) abort();
1182 /* load the scared ghost */
1184 for (i = 0; i < MAXGFLASH; i++) {
1185 for (j = 0; j < MAXGWAG; j++) {
1186 pp->s_ghostPixmap[i][j] =
1187 subpixmap (display, window, sprites, sw, sw, srcy,
1190 pp->s_ghostPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1191 pp->s_ghostPixmap[i][j],
1195 if (srcy >= sh) abort();
1199 /* load the ghost eyes */
1200 for (i = 0; i < MAXGDIR; i++) {
1202 subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1203 pp->ghostEyes[i] = scale_pixmap (&display, pp->stippledGC,
1208 if (srcy >= sh) abort();
1212 /* Load the pacman pixmaps and their mask. */
1215 for (i = 0; i < 4; i++) {
1216 for (j = 0; j < MAXMOUTH; j++) {
1217 pp->pacmanPixmap[i][j] =
1218 subpixmap (display, window, sprites, sw, sw, srcy,
1220 pp->pacmanMask[i][j] =
1221 subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1223 pp->pacmanPixmap[i][j] = scale_pixmap (&display, pp->stippledGC,
1224 pp->pacmanPixmap[i][j],
1227 pp->pacmanMask[i][j] =
1228 scale_pixmap (&display, gc, pp->pacmanMask[i][j],
1229 pp->spritexs, pp->spriteys);
1231 if (srcy >= sh) abort();
1235 /* Load pacman death sequence */
1236 for ( i = 0; i < PAC_DEATH_FRAMES; i++ ){
1237 if (srcy > sh - sw) abort();
1239 subpixmap (display, window, sprites, sw, sw, srcy, xgwa.depth);
1240 pp->pacman_ds_mask[i] =
1241 subpixmap (display, window, sprites_mask, sw, sw, srcy, 1);
1243 pp->pacman_ds[i] = scale_pixmap ( &display, pp->stippledGC,
1247 pp->pacman_ds_mask[i] =
1248 scale_pixmap (&display, gc, pp->pacman_ds_mask[i],
1249 pp->spritexs, pp->spriteys);
1255 /* Hook function, sets state to initial position. */
1257 init_pacman (ModeInfo * mi)
1259 Display *display = MI_DISPLAY (mi);
1260 Window window = MI_WINDOW (mi);
1261 long size = MI_SIZE (mi);
1262 pacmangamestruct *pp;
1266 MI_INIT (mi, pacman_games);
1267 pp = &pacman_games[MI_SCREEN (mi)];
1269 pp->width = (unsigned short) MI_WIDTH (mi);
1270 pp->height = (unsigned short) MI_HEIGHT (mi);
1271 for (i = 0; i < 4; i++) {
1272 for (j = 0; j < MAXGDIR; j++) {
1273 for (k = 0; k < MAXGWAG; k++) {
1274 if (pp->ghostPixmap[i][j][k] != None) {
1275 XFreePixmap (display, pp->ghostPixmap[i][j][k]);
1276 pp->ghostPixmap[i][j][k] = None;
1277 pp->graphics_format = 0 /*IS_NONE */ ;
1283 for (i = 0; i < MAXGFLASH; i++) {
1284 for (j = 0; j < MAXGWAG; j++) {
1285 if (pp->s_ghostPixmap[i][j] != None) {
1286 XFreePixmap (display, pp->s_ghostPixmap[i][j]);
1287 pp->s_ghostPixmap[i][j] = None;
1293 MINGRIDSIZE * size > (int) pp->width ||
1294 MINGRIDSIZE * size > (int) pp->height) {
1295 double scale = MIN (pp->width / LEVWIDTH, pp->height / LEVHEIGHT);
1297 if (pp->width > pp->height * 5 || /* weird window aspect ratio */
1298 pp->height > pp->width * 5)
1299 scale = 0.8 * (pp->width / pp->height
1300 ? pp->width / (double) pp->height
1301 : pp->height / (double) pp->width);
1302 pp->ys = MAX (scale, 1);
1306 if (size < -MINSIZE)
1307 pp->ys = (short) (NRAND (MIN (-size, MAX (MINSIZE,
1311 - MINSIZE + 1) + MINSIZE);
1312 else if (size < MINSIZE)
1315 pp->ys = (short) (MIN (size,
1316 MAX (MINSIZE, MIN (pp->width, pp->height) /
1322 pp->wallwidth = (unsigned int) (pp->xs + pp->ys) >> 4;
1323 if (pp->wallwidth < 1)
1325 pp->incx = (pp->xs >> 3) + 1;
1326 pp->incy = (pp->ys >> 3) + 1;
1327 pp->ncols = (unsigned short) MAX (LEVWIDTH, 2);
1328 pp->nrows = (unsigned short) MAX (LEVHEIGHT, 2);
1329 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1330 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1331 pp->spritexs = MAX (pp->xs + (pp->xs >> 1) - 1, 1);
1332 pp->spriteys = MAX (pp->ys + (pp->ys >> 1) - 1, 1);
1333 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
1334 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
1335 pp->old_pac_state = ps_chasing;
1337 if (!pp->stippledGC) {
1338 gcv.foreground = MI_BLACK_PIXEL (mi);
1339 gcv.background = MI_BLACK_PIXEL (mi);
1340 if ((pp->stippledGC = XCreateGC (display, window,
1341 GCForeground | GCBackground,
1349 jwxyz_XSetAntiAliasing (display, pp->stippledGC, False);
1352 load_pixmaps (&display, window, &pp);
1354 pp->pacman.lastbox = START;
1355 pp->pacman.mouthdirection = 1;
1356 pp->pacman.nextcol = NOWHERE;
1357 pp->pacman.nextrow = NOWHERE;
1359 if (pp->ghosts != NULL) {
1361 pp->ghosts = (ghoststruct *) NULL;
1363 pp->nghosts = GHOSTS;
1366 if ((pp->ghosts = (ghoststruct *) calloc ((size_t) pp->nghosts,
1367 sizeof (ghoststruct))) ==
1373 pp->pacman.mouthstage = MAXMOUTH - 1;
1375 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1379 /* Callback function called for each tick. This is the complete machinery of
1380 everything that moves. */
1382 draw_pacman (ModeInfo * mi)
1385 pacmangamestruct *pp;
1387 if (pacman_games == NULL)
1389 pp = &pacman_games[MI_SCREEN (mi)];
1390 if (pp->ghosts == NULL)
1393 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1394 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1395 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1396 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1398 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1399 pacman_update (mi, pp, &(pp->pacman));
1400 check_death (mi, pp);
1401 pp->pacman.delta.x = pp->incx;
1402 pp->pacman.delta.y = pp->incy;
1405 if (pp->pacman.delta.x > pp->xs + pp->incx)
1406 pp->pacman.delta.x = pp->xs + pp->incx;
1407 if (pp->pacman.delta.y > pp->ys + pp->incy)
1408 pp->pacman.delta.y = pp->ys + pp->incy;
1410 for (g = 0; g < pp->nghosts; g++) {
1411 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) % pp->ghosts[g].speed;
1412 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) % pp->ghosts[g].speed;
1413 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ? pp->incx : 0;
1414 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ? pp->incy : 0;
1416 if (pp->ghosts[g].delta.x >= pp->xs &&
1417 pp->ghosts[g].delta.y >= pp->ys) {
1418 pacman_ghost_update (pp, &(pp->ghosts[g]));
1419 pp->ghosts[g].delta.x = pp->incx;
1420 pp->ghosts[g].delta.y = pp->incy;
1423 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1424 pp->ghosts[g].delta.x = pp->xs + pp->incx;
1425 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1426 pp->ghosts[g].delta.y = pp->ys + pp->incy;
1432 /* Refresh current level. */
1434 refresh_pacman (ModeInfo * mi)
1442 reshape_pacman(ModeInfo * mi, int width, int height)
1444 pacmangamestruct *pp = &pacman_games[MI_SCREEN (mi)];
1446 pp->height = height;
1447 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
1448 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
1449 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1450 /* repopulate (mi); */
1455 /* Callback to change level. */
1457 change_pacman (ModeInfo * mi)
1459 XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
1462 #endif /* !STANDALONE */
1465 XSCREENSAVER_MODULE ("Pacman", pacman)
1467 #endif /* MODE_pacman */