1 /* -*- Mode: C; tab-width: 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 * 11-Aug-2004: Added support for pixmap ghost. jenglish@myself.com
26 * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.
27 * splitted up code into several files. Retouched AI code, cleaned
29 * 3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
30 * 26-Nov-2001: Random level generator added
31 * 01-Nov-2000: Allocation checks
32 * 04-Jun-1997: Compatible with xscreensaver
38 2. make a bit better pacman sprite (mouth should be larger)
39 3. think of a better level generation algorithm
42 #define DEF_TRACKMOUSE "False"
46 # define PROGCLASS "Pacman"
47 # define HACK_INIT init_pacman
48 # define HACK_DRAW draw_pacman
49 # define pacman_opts xlockmore_opts
50 # define DEFAULTS "*delay: 10000 \n" \
53 "*trackmouse: " DEF_TRACKMOUSE "\n"
54 # define UNIFORM_COLORS
55 # define BRIGHT_COLORS
57 # include "xlockmore.h" /* in xscreensaver distribution */
58 #else /* STANDALONE */
59 # include "xlock.h" /* in xlockmore distribution */
60 #endif /* STANDALONE */
65 #include "pacman_ai.h"
66 #include "pacman_level.h"
68 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
70 #include "xpm-pixmap.h"
72 #if defined(USE_PIXMAP)
77 #if defined(USE_PIXMAP)
78 # include "images/pacman/ghost-u1.xpm"
79 # include "images/pacman/ghost-u2.xpm"
80 # include "images/pacman/ghost-r1.xpm"
81 # include "images/pacman/ghost-r2.xpm"
82 # include "images/pacman/ghost-l1.xpm"
83 # include "images/pacman/ghost-l2.xpm"
84 # include "images/pacman/ghost-d1.xpm"
85 # include "images/pacman/ghost-d2.xpm"
86 # include "images/pacman/ghost-mask.xpm" /* Used to clean up the dust left by wag. */
89 static const struct { int dx, dy; } dirvecs[DIRVECS] =
90 { {-1, 0}, {0, 1}, {1, 0}, {0, -1}};
92 #ifdef DISABLE_INTERACTIVE
93 ModeSpecOpt pacman_opts = {
95 (XrmOptionDescRec *) NULL,
101 static XrmOptionDescRec opts[] =
103 {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
104 {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
107 static argtype vars[] =
109 {&trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
112 static OptionStruct desc[] =
114 {"-/+trackmouse", "turn on/off the tracking of the mouse"}
117 ModeSpecOpt pacman_opts =
118 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
122 ModStruct pacman_description = {
123 "pacman", /* *cmdline_arg; */
124 "init_pacman", /* *init_name; */
125 "draw_pacman", /* *callback_name; */
126 "release_pacman", /* *release_name; */
127 "refresh_pacman", /* *refresh_name; */
128 "change_pacman", /* *change_name; */
129 (char *) NULL, /* *unused_name; */
130 &pacman_opts, /* *msopts */
131 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
137 pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;
140 static void repopulate(ModeInfo * mi);
141 static void drawlevel(ModeInfo * mi);
145 free_pacman(Display *display, pacmangamestruct *pp)
147 int dir, mouth, i, j, k;
149 if (pp->ghosts != NULL) {
151 pp->ghosts = (ghoststruct *) NULL;
153 if (pp->stippledGC != None) {
154 XFreeGC(display, pp->stippledGC);
155 pp->stippledGC = None;
157 for (i = 0; i < 4; i++){
158 for (j = 0; j < MAXGDIR; j++){
159 for (k = 0; k < MAXGWAG; k++){
160 if (pp->ghostPixmap[i][j][k] != None) {
161 XFreePixmap(display, pp->ghostPixmap[i][j][k]);
162 pp->ghostPixmap[i][j][k] = None;
167 for (dir = 0; dir < 4; dir++)
168 for (mouth = 0; mouth < MAXMOUTH; mouth++)
169 if (pp->pacmanPixmap[dir][mouth] != None) {
171 pp->pacmanPixmap[dir][mouth]);
172 pp->pacmanPixmap[dir][mouth] = None;
177 /* Checks for death of any ghosts/pacman and updates. It also makes a new
178 level if all ghosts are dead or all dots are eaten. */
180 check_death(ModeInfo * mi, pacmangamestruct *pp)
182 Display *display = MI_DISPLAY(mi);
183 Window window = MI_WINDOW(mi);
188 for (ghost = 0; ghost < pp->nghosts; ghost++) {
189 if (pp->ghosts[ghost].dead == True)
192 if ((pp->ghosts[ghost].nextrow == NOWHERE &&
193 pp->ghosts[ghost].nextcol == NOWHERE)) {
198 if (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
199 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
200 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
201 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
202 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
203 (pp->ghosts[ghost].col == pp->pacman.nextcol))) {
204 pp->ghosts[ghost].dead = 1;
205 XSetForeground(display,
208 XFillRectangle(display, window,
210 pp->ghosts[ghost].cf,
211 pp->ghosts[ghost].rf,
219 if (alldead == 1 || pp->dotsleft == 0)
223 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
225 repopulate(ModeInfo * mi)
227 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
229 int i = createnewlevel(mi);
234 pp->gamestate = GHOST_DANGER;
236 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT)/2 - i;
237 pp->pacman.col = (LEVWIDTH/2);
238 pp->pacman.nextrow = NOWHERE;
239 pp->pacman.nextcol = NOWHERE;
240 pp->pacman.cf = NOWHERE;
241 pp->pacman.rf = NOWHERE;
242 pp->pacman.oldcf = NOWHERE;
243 pp->pacman.oldrf = NOWHERE;
244 pp->pacman.oldlx = NOWHERE;
245 pp->pacman.oldly = NOWHERE;
246 pp->pacman.aistate = ps_eating;
247 pp->pacman.cur_trace = 0;
248 pp->pacman.roundscore = 0;
249 pp->pacman.speed = 4;
250 pp->pacman.lastturn = 0;
251 pp->pacman.delta.x = 0;
252 pp->pacman.delta.y = 0;
253 pac_clear_trace(&(pp->pacman));
255 for (ghost = 0; ghost < pp->nghosts; ghost++) {
256 pp->ghosts[ghost].col = (LEVWIDTH/2);
257 pp->ghosts[ghost].row = (LEVHEIGHT/2);
258 pp->ghosts[ghost].nextcol = NOWHERE;
259 pp->ghosts[ghost].nextrow = NOWHERE;
260 pp->ghosts[ghost].dead = 0;
261 pp->ghosts[ghost].lastbox = START;
262 pp->ghosts[ghost].cf = NOWHERE;
263 pp->ghosts[ghost].rf = NOWHERE;
264 pp->ghosts[ghost].oldcf = NOWHERE;
265 pp->ghosts[ghost].oldrf = NOWHERE;
266 pp->ghosts[ghost].aistate = inbox;
267 pp->ghosts[ghost].timeleft = ghost * 50;
268 pp->ghosts[ghost].speed = 3;
269 pp->ghosts[ghost].delta.x = 0;
270 pp->ghosts[ghost].delta.y = 0;
272 ghost_update(pp, &(pp->ghosts[ghost]));
275 pac_update(mi, pp, &(pp->pacman));
278 /* Sets the color to the color of a wall. */
280 setwallcolor(ModeInfo * mi)
282 Display *display = MI_DISPLAY(mi);
283 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
285 if (MI_NPIXELS(mi) > 2)
286 XSetForeground(display, pp->stippledGC,
289 XSetForeground(display, pp->stippledGC,
293 /* Sets the color to the color of a dot. */
295 setdotcolor(ModeInfo * mi)
297 Display *display = MI_DISPLAY(mi);
298 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
300 XSetForeground(display, pp->stippledGC,
304 /* Draws a block in the level at the specified x and y locations. */
306 drawlevelblock(ModeInfo * mi, pacmangamestruct *pp,
307 const unsigned x, const unsigned y)
309 Display *display = MI_DISPLAY(mi);
310 Window window = MI_WINDOW(mi);
313 if (pp->xs % 2 == 1) dx = -1;
314 if (pp->ys % 2 == 1) dy = -1;
316 XSetFillStyle(display, pp->stippledGC, FillSolid);
317 XSetLineAttributes(display, pp->stippledGC, pp->wallwidth,
318 LineSolid, CapRound, JoinMiter);
320 if (pp->xs < 2 || pp->ys < 2) {
321 switch(pp->level[y*LEVWIDTH + x]) {
327 (void)XDrawPoint(display, window,
330 y * pp->ys + pp->yb);
334 (void)XDrawPoint(display, window,
337 y * pp->ys + pp->yb);
343 switch (pp->level[y*LEVWIDTH + x]) {
350 if (pp->xs < 8 || pp->ys < 8) {
351 (void)XDrawPoint(display, window,
353 x * pp->xs + pp->xb +
355 y * pp->ys + pp->yb +
360 (void)XDrawArc(display, window, pp->stippledGC,
363 (pp->xs > 32 ? (pp->xs / 16) : 1) +
367 (pp->ys > 32 ? (pp->ys / 16) : 1) +
369 (pp->xs > 32 ? (pp->xs / 32) : 1),
370 (pp->ys > 32 ? (pp->ys / 32) : 1),
376 (void)XDrawLine(display, window, pp->stippledGC,
377 (pp->xs * x) + pp->xb,
378 (pp->ys * y) + (pp->ys / 2) + pp->yb,
379 (pp->xs * (x + 1)) + pp->xb,
380 (pp->ys * y) + (pp->ys / 2) + pp->yb);
385 (void)XDrawLine(display, window, pp->stippledGC,
386 (pp->xs * x) + (pp->xs / 2) + pp->xb,
387 (pp->ys * y) + pp->yb,
388 (pp->xs * x) + (pp->xs / 2) + pp->xb,
389 (pp->ys * (y + 1)) + pp->yb);
394 (void)XDrawArc(display, window, pp->stippledGC,
395 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
396 (pp->ys * y) + (pp->ys / 2) + pp->yb,
403 (void)XDrawArc(display, window, pp->stippledGC,
404 (pp->xs * x) + (pp->ys / 2) + pp->xb,
405 (pp->ys * y) + (pp->ys / 2) + pp->yb,
412 (void)XDrawArc(display, window, pp->stippledGC,
413 (pp->xs * x) + (pp->ys / 2) + pp->xb,
414 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
421 (void)XDrawArc(display, window, pp->stippledGC,
422 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
423 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
431 /* Draws a complete level. */
433 drawlevel(ModeInfo * mi)
435 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
438 for (y = 0; y < LEVHEIGHT; y++)
439 for (x = 0; x < LEVWIDTH; x++)
440 drawlevelblock(mi, pp, x, y);
443 /* There is some overlap so it can be made more efficient */
444 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
446 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
447 XFillRectangle(d,w,g,xl,yl,xs,ys); \
449 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
450 XFillRectangle(d,w,g,xl,yl,xs,ys); \
452 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
453 XFillRectangle(d,w,g,xl,yl,xs,ys); \
455 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
456 XFillRectangle(d,w,g,xl,yl,xs,ys)
459 /* Draws the pacman sprite, removing the previous location. */
461 draw_pacman_sprite(ModeInfo * mi)
463 Display *display = MI_DISPLAY(mi);
464 Window window = MI_WINDOW(mi);
465 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
468 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
469 pp->pacman.cfactor + pp->xb + pp->spritedx;
470 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
471 pp->pacman.rfactor + pp->yb + pp->spritedy;
473 dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
474 ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
476 XSetForeground(display, pp->stippledGC,
478 if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
480 XFillRectangle(display, window, pp->stippledGC,
481 pp->pacman.oldcf, pp->pacman.oldrf,
482 pp->spritexs, pp->spriteys);
484 ERASE_IMAGE(display, window, pp->stippledGC,
485 pp->pacman.cf, pp->pacman.rf,
486 pp->pacman.oldcf, pp->pacman.oldrf,
487 pp->spritexs, pp->spriteys);
491 XSetTSOrigin(display, pp->stippledGC,
492 pp->pacman.cf, pp->pacman.rf);
493 if (MI_NPIXELS(mi) > 2)
494 XSetForeground(display, pp->stippledGC,
495 MI_PIXEL(mi, YELLOW));
497 XSetForeground(display, pp->stippledGC,
500 XSetStipple(display, pp->stippledGC,
501 pp->pacmanPixmap[dir][pp->pacman.mouthstage]);
503 XSetFillStyle(display, pp->stippledGC, FillStippled);
505 XSetFillStyle(display, pp->stippledGC,
508 if (pp->xs < 2 || pp->ys < 2)
509 XDrawPoint(display, window, pp->stippledGC,
510 pp->pacman.cf, pp->pacman.rf);
512 XFillRectangle(display, window, pp->stippledGC,
513 pp->pacman.cf, pp->pacman.rf,
514 pp->spritexs, pp->spriteys);
515 pp->pacman.mouthstage += pp->pacman.mouthdirection;
516 if ((pp->pacman.mouthstage >= MAXMOUTH) ||
517 (pp->pacman.mouthstage < 0)) {
518 pp->pacman.mouthdirection *= -1;
519 pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
521 pp->pacman.oldcf = pp->pacman.cf;
522 pp->pacman.oldrf = pp->pacman.rf;
525 #if defined(USE_PIXMAP)
527 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost){
528 Display *display = MI_DISPLAY(mi);
529 Window window = MI_WINDOW(mi);
530 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
532 #define MAX_WAG_COUNT 50
533 static int wag_count = 0;
534 unsigned int dir = 0;
536 dir = (ABS(pp->ghosts[ghost].cfactor) * (2 - pp->ghosts[ghost].cfactor) +
537 ABS(pp->ghosts[ghost].rfactor) * (1 + pp->ghosts[ghost].rfactor)) % 4;
539 pp->ghosts[ghost].cf =
540 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
541 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
542 pp->ghosts[ghost].rf =
543 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
544 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
545 XSetForeground(display,
549 XSetClipMask(display, pp->stippledGC, pp->ghostMask);
550 XSetClipOrigin(display, pp->stippledGC,
551 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf);
552 XFillRectangle(display,
555 pp->ghosts[ghost].oldcf,
556 pp->ghosts[ghost].oldrf,
557 pp->spritexs, pp->spriteys);
558 XSetClipOrigin(display, pp->stippledGC,
559 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
560 XCopyArea(display, pp->ghostPixmap[ghost][dir][wag], window,
561 pp->stippledGC,0,0,pp->spritexs,pp->spriteys,
562 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
563 XSetClipMask(display, pp->stippledGC, None);
564 drawlevelblock(mi, pp,
565 (unsigned int)pp->ghosts[ghost].col,
566 (unsigned int)pp->ghosts[ghost].row);
567 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
568 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
569 if (wag_count++ == MAX_WAG_COUNT){
577 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
579 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost) {
580 Display *display = MI_DISPLAY(mi);
581 Window window = MI_WINDOW(mi);
582 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
584 pp->ghosts[ghost].cf =
585 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
586 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
587 pp->ghosts[ghost].rf =
588 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
589 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
591 XSetForeground(display,
594 XFillRectangle(display,
597 pp->ghosts[ghost].cf,
598 pp->ghosts[ghost].rf,
599 pp->spritexs, pp->spriteys);
601 if (pp->ghosts[ghost].oldcf != NOWHERE ||
602 pp->ghosts[ghost].oldrf != NOWHERE) {
604 XFillRectangle(display, window,
605 pp->stippledGC, pp->ghosts[ghost].oldcf,
606 pp->ghosts[ghost].oldrf,
607 pp->spritexs, pp->spriteys);
609 ERASE_IMAGE(display, window, pp->stippledGC,
610 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
611 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf,
612 pp->spritexs, pp->spriteys);
616 drawlevelblock(mi, pp,
617 (unsigned int)pp->ghosts[ghost].col,
618 (unsigned int)pp->ghosts[ghost].row);
620 XSetTSOrigin(display, pp->stippledGC,
621 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
623 if (MI_NPIXELS(mi) > 2)
624 XSetForeground(display,
626 MI_PIXEL(mi, GREEN));
628 XSetForeground(display,
632 XSetStipple(display, pp->stippledGC,
633 pp->ghostPixmap[0][0][0]);
636 XSetFillStyle(display,
640 XSetFillStyle(display,
644 if (pp->xs < 2 || pp->ys < 2)
645 XDrawPoint(display, window, pp->stippledGC,
646 pp->ghosts[ghost].cf,
647 pp->ghosts[ghost].rf);
649 XFillRectangle(display,
652 pp->ghosts[ghost].cf,
653 pp->ghosts[ghost].rf,
654 pp->spritexs, pp->spriteys);
656 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
657 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
661 /* Does all drawing of moving sprites in the level. */
663 pacman_tick(ModeInfo * mi)
665 Display *display = MI_DISPLAY(mi);
666 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
669 for (ghost = 0; ghost < pp->nghosts; ghost++) {
670 if (pp->ghosts[ghost].dead == True)
672 draw_ghost_sprite(mi, ghost);
675 draw_pacman_sprite(mi);
677 (void)XFlush(display);
680 #if defined(USE_PIXMAP)
681 /* Grabbed the scaling routine off of usenet.
682 * Changed it so that the information specific
683 * to the source pixmap does not have to be a parameter.
685 * There is probable a better way to scale pixmaps.
686 * From: Chris Fiddyment (cxf@itd.dsto.gov.au)
687 * Subject: Scaling Pixmap Algorithm.
688 * Newsgroups: comp.graphics.algorithms
689 * Date: 1994-07-06 18:51:38 PST
694 scale_pixmap( Display **dpy, GC gc, Pixmap source, int dwidth, int dheight)
699 float xscale, yscale;
700 unsigned int swidth, sheight;
703 unsigned border_width_return, depth;
704 XGetGeometry(*dpy, source, &window, &x, &y, &swidth, &sheight, &border_width_return, &depth);
706 xscale = (float) swidth / (float) dwidth; /* Scaling factors */
707 yscale = (float) sheight / (float) dheight;
709 dest = XCreatePixmap(*dpy,window,dwidth,dheight,depth);
711 fprintf(stderr, "%s Could not scale image", progname);
713 temp = XCreatePixmap(*dpy,window,dwidth,sheight,depth);
715 fprintf(stderr, "%s Could not scale image", progname);
720 /* Scale width of source into temp pixmap */
721 for(i=0;i<end;i+=xscale)
722 XCopyArea(*dpy,source,temp,gc,i,0,1,sheight,j++,0);
725 end = dheight*yscale;
726 /* Scale height of temp into dest pixmap */
727 for(i=0;i<end;i+=yscale)
728 XCopyArea(*dpy,temp,dest,gc,0,i,dwidth,1,0,j++);
730 XFreePixmap( *dpy, temp );
731 return (Pixmap) dest;
734 /* Load any needed pixmaps and their mask. */
736 load_pixmaps(Display **dpy, Window window, pacmangamestruct **ps)
738 pacmangamestruct *pp = *ps;
739 Display *display = *dpy;
740 static char *colors[]= {
741 ". c #FF0000", /*Red*/
742 ". c #00FFDE", /*Blue*/
743 ". c #FFB847", /*Orange*/
744 ". c #FFB8DE", /*Pink*/
747 static char **bits[] = {
748 ghost_u1_xpm, ghost_u2_xpm, ghost_r1_xpm, ghost_r2_xpm,
749 ghost_d1_xpm, ghost_d2_xpm, ghost_l1_xpm, ghost_l2_xpm
752 int w = pp->spritexs;
753 int h = pp->spriteys;
757 for (i = 0; i < 4; i++){
759 for ( j = 0; j < MAXGDIR; j++){
760 for ( k = 0; k < MAXGWAG; k++){
761 bits[m][2] = colors[i];
762 pp->ghostPixmap[i][j][k] = xpm_data_to_pixmap (display, window, bits[m],
763 &w, &h, &pp->ghostMask);
764 if (!pp->ghostPixmap[i][j][k])
766 fprintf (stderr, "%s: Can't load ghost images\n", progname);
770 pp->ghostPixmap[i][j][k] = scale_pixmap(&display, pp->stippledGC,
771 pp->ghostPixmap[i][j][k], pp->spritexs, pp->spriteys);
772 if (!pp->ghostPixmap[i][j][k])
774 fprintf (stderr, "%s: Can't load ghost images\n", progname);
782 /* We really only need a single mask. This saves the headache of getting the bottom of the ghost
783 * to clip just right. What we'll do is mask the top portion of the ghost, but the bottom of the
784 * the ghost will be solid. I did this by setting the pixels between the fringe of their sheets
785 * to black instead of none. -jeremy
787 temp = xpm_data_to_pixmap (display, window, ghost_mask_xpm,
788 &w, &h, &pp->ghostMask);
791 fprintf (stderr, "%s: Can't load ghost images\n", progname);
795 temp = scale_pixmap(&display, pp->stippledGC,
796 temp, pp->spritexs, pp->spriteys);
799 fprintf (stderr, "%s: Can't load ghost images\n", progname);
802 gc = XCreateGC(display, pp->ghostMask, 0, 0);
803 pp->ghostMask = scale_pixmap(&display, gc, pp->ghostMask,
804 pp->spritexs, pp->spriteys);
805 XFreePixmap(display, temp);
809 /* Hook function, sets state to initial position. */
811 init_pacman(ModeInfo * mi)
813 Display *display = MI_DISPLAY(mi);
814 Window window = MI_WINDOW(mi);
815 int size = MI_SIZE(mi);
816 pacmangamestruct *pp;
818 int dir, mouth, i, j, k;
820 /* XPoint points[9]; */
822 if (pacmangames == NULL) {
823 if ((pacmangames = (pacmangamestruct *)
824 calloc((size_t)MI_NUM_SCREENS(mi),
825 sizeof (pacmangamestruct))) == NULL)
828 pp = &pacmangames[MI_SCREEN(mi)];
830 pp->width = (unsigned short)MI_WIDTH(mi);
831 pp->height = (unsigned short)MI_HEIGHT(mi);
832 for (i = 0; i < 4; i++){
833 for (j = 0; j < MAXGDIR; j++){
834 for (k = 0; k < MAXGWAG; k++){
835 if (pp->ghostPixmap[i][j][k] != None) {
836 XFreePixmap(display, pp->ghostPixmap[i][j][k]);
837 pp->ghostPixmap[i][j][k] = None;
838 pp->graphics_format = 0 /*IS_NONE*/;
844 MINGRIDSIZE * size > (int)pp->width ||
845 MINGRIDSIZE * size > (int)pp->height) {
847 pp->ys = pp->xs = MAX(MIN(pp->width/LEVWIDTH,
848 pp->height/LEVHEIGHT), 1);
851 pp->ys = (short)(NRAND( MIN( -size, MAX( MINSIZE,
852 MIN( pp->width, pp->height) / MINGRIDSIZE))
853 - MINSIZE + 1) + MINSIZE);
854 else if (size < MINSIZE)
857 pp->ys = (short)(MIN(size,
858 MAX(MINSIZE, MIN(pp->width, pp->height) /
863 pp->wallwidth = (unsigned int)(pp->xs + pp->ys) >> 4;
864 if (pp->wallwidth < 1) pp->wallwidth = 1;
865 pp->incx = (pp->xs >> 3) + 1;
866 pp->incy = (pp->ys >> 3) + 1;
867 pp->ncols = (unsigned short)MAX(LEVWIDTH, 2);
868 pp->nrows = (unsigned short)MAX(LEVHEIGHT, 2);
869 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
870 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
871 pp->spritexs = MAX(pp->xs + (pp->xs >> 1) - 1, 1);
872 pp->spriteys = MAX(pp->ys + (pp->ys >> 1) - 1, 1);
873 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
874 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
876 if (!pp->stippledGC) {
877 gcv.foreground = MI_BLACK_PIXEL(mi);
878 gcv.background = MI_BLACK_PIXEL(mi);
879 if ((pp->stippledGC = XCreateGC(display, window,
880 GCForeground | GCBackground, &gcv)) == None) {
881 free_pacman(display, pp);
886 #if defined(USE_PIXMAP)
887 load_pixmaps(&display,window,&pp);
889 if ((pp->ghostPixmap[0][0][0] = XCreatePixmap(display, window,
890 pp->spritexs, pp->spriteys, 1)) == None) {
891 free_pacman(display, pp);
897 if ((bg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
898 GCForeground | GCBackground, &gcv)) == None) {
899 free_pacman(display, pp);
905 if ((fg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
906 GCForeground | GCBackground, &gcv)) == None) {
907 XFreeGC(display, bg_gc);
908 free_pacman(display, pp);
912 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
914 /* draw the triangles on the bottom (scalable) */
915 SETPOINT(points[0], 1, pp->spriteys * 5 / 6);
916 SETPOINT(points[1], pp->spritexs / 6, pp->spriteys);
917 SETPOINT(points[2], pp->spritexs / 3, pp->spriteys * 5 / 6);
918 SETPOINT(points[3], pp->spritexs / 2, pp->spriteys);
919 SETPOINT(points[4], pp->spritexs * 2 / 3, pp->spriteys * 5 / 6);
920 SETPOINT(points[5], pp->spritexs * 5 / 6, pp->spriteys);
921 SETPOINT(points[6], pp->spritexs, pp->spriteys * 5 / 6);
922 SETPOINT(points[7], pp->spritexs, pp->spriteys / 2);
923 SETPOINT(points[8], 1, pp->spriteys / 2);
925 XFillRectangle(display, pp->ghostPixmap[0][0][0], bg_gc,
926 0, 0, pp->spritexs, pp->spriteys);
927 XFillArc(display, pp->ghostPixmap[0][0][0], fg_gc,
928 0, 0, pp->spritexs, pp->spriteys, 0, 11520);
929 XFillPolygon(display, pp->ghostPixmap[0][0][0], fg_gc,
930 points, 9, Nonconvex, CoordModeOrigin);
931 XFreeGC(display, bg_gc);
932 XFreeGC(display, fg_gc);
936 if (pp->pacmanPixmap[0][0] != None)
937 for (dir = 0; dir < 4; dir++)
938 for (mouth = 0; mouth < MAXMOUTH; mouth++)
940 pp->pacmanPixmap[dir]
943 for (dir = 0; dir < 4; dir++)
944 for (mouth = 0; mouth < MAXMOUTH; mouth++) {
945 if ((pp->pacmanPixmap[dir][mouth] = XCreatePixmap(
946 display, MI_WINDOW(mi),
947 pp->spritexs, pp->spriteys, 1)) ==
949 free_pacman(display, pp);
954 if ((fg_gc = XCreateGC(display, pp->pacmanPixmap[dir][mouth],
955 GCForeground | GCBackground, &gcv)) == None) {
956 free_pacman(display, pp);
961 if ((bg_gc = XCreateGC(display,
962 pp->pacmanPixmap[dir][mouth],
964 GCBackground, &gcv)) ==
966 XFreeGC(display, fg_gc);
967 free_pacman(display, pp);
970 XFillRectangle(display,
971 pp->pacmanPixmap[dir][mouth], bg_gc,
972 0, 0, pp->spritexs, pp->spriteys);
973 if (pp->spritexs == 1 && pp->spriteys == 1)
974 XFillRectangle(display,
975 pp->pacmanPixmap[dir][mouth],
976 fg_gc, 0, 0, pp->spritexs,
980 pp->pacmanPixmap[dir][mouth],
982 0, 0, pp->spritexs, pp->spriteys,
983 ((90 - dir * 90) + mouth * 5) * 64,
984 (360 + (-2 * mouth * 5)) * 64);
985 XFreeGC(display, fg_gc);
986 XFreeGC(display, bg_gc);
989 pp->pacman.lastbox = START;
990 pp->pacman.mouthdirection = 1;
991 pp->pacman.nextcol = NOWHERE;
992 pp->pacman.nextrow = NOWHERE;
994 if (pp->ghosts != NULL) {
996 pp->ghosts = (ghoststruct *) NULL;
998 pp->nghosts = GHOSTS;
1001 if ((pp->ghosts = (ghoststruct *) calloc((size_t)pp->nghosts,
1002 sizeof (ghoststruct))) == NULL) {
1003 free_pacman(display, pp);
1007 pp->pacman.mouthstage = MAXMOUTH - 1;
1013 /* Callback function called for each tick. This is the complete machinery of
1014 everything that moves. */
1016 draw_pacman(ModeInfo * mi)
1019 pacmangamestruct *pp;
1021 if (pacmangames == NULL)
1023 pp = &pacmangames[MI_SCREEN(mi)];
1024 if (pp->ghosts == NULL)
1027 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1028 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1029 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1030 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1032 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1033 pac_update(mi, pp, &(pp->pacman));
1034 check_death(mi, pp);
1035 pp->pacman.delta.x = pp->incx;
1036 pp->pacman.delta.y = pp->incy;
1039 if (pp->pacman.delta.x > pp->xs + pp->incx)
1040 pp->pacman.delta.x = pp->xs + pp->incx;
1041 if (pp->pacman.delta.y > pp->ys + pp->incy)
1042 pp->pacman.delta.y = pp->ys + pp->incy;
1044 for (g = 0; g < pp->nghosts; g++) {
1045 if (pp->ghosts[g].dead == True) continue;
1047 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) %
1048 pp->ghosts[g].speed;
1049 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) %
1050 pp->ghosts[g].speed;
1051 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ?
1053 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ?
1056 if (pp->ghosts[g].delta.x >= pp->xs &&
1057 pp->ghosts[g].delta.y >= pp->ys) {
1058 ghost_update(pp, &(pp->ghosts[g]));
1059 pp->ghosts[g].delta.x = pp->incx;
1060 pp->ghosts[g].delta.y = pp->incy;
1063 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1064 pp->ghosts[g].delta.x = pp->xs + pp->incx;
1065 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1066 pp->ghosts[g].delta.y = pp->ys + pp->incy;
1072 /* Releases resources. */
1074 release_pacman(ModeInfo * mi)
1076 if (pacmangames != NULL) {
1079 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1080 free_pacman(MI_DISPLAY(mi), &pacmangames[screen]);
1082 pacmangames = (pacmangamestruct *) NULL;
1086 /* Refresh current level. */
1088 refresh_pacman(ModeInfo * mi)
1094 /* Callback to change level. */
1096 change_pacman(ModeInfo * mi)
1102 #endif /* MODE_pacman */