1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pacman --- Mr. Pacman and his ghost friends */
4 /*static const char sccsid[] = "@(#)pacman.c 5.00 2000/11/01 xlockmore";*/
7 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
9 * Permission to use, copy, modify, and distribute this software and its
10 * documentation for any purpose and without fee is hereby granted,
11 * provided that the above copyright notice appear in all copies and that
12 * both that copyright notice and this permission notice appear in
13 * supporting documentation.
15 * This file is provided AS IS with no warranties of any kind. The author
16 * shall have no liability with respect to the infringement of copyrights,
17 * trade secrets or any patents by this file or any part thereof. In no
18 * event will the author be liable for any lost revenue or profits or
19 * other special, indirect and consequential damages.
22 * 15-Aug-2004: Added support for pixmap pacman. Jeremy English jenglish@myself.com
23 * 11-Aug-2004: Added support for pixmap ghost. jenglish@myself.com
24 * 13-May-2002: Added -trackmouse feature thanks to code from 'maze.c'.
25 * splitted up code into several files. Retouched AI code, cleaned
27 * 3-May-2002: Added AI to pacman and ghosts, slowed down ghosts.
28 * 26-Nov-2001: Random level generator added
29 * 01-Nov-2000: Allocation checks
30 * 04-Jun-1997: Compatible with xscreensaver
36 2. think of a better level generation algorithm
39 #define DEF_TRACKMOUSE "False"
43 # define PROGCLASS "Pacman"
44 # define HACK_INIT init_pacman
45 # define HACK_DRAW draw_pacman
46 # define pacman_opts xlockmore_opts
47 # define DEFAULTS "*delay: 10000 \n" \
50 "*trackmouse: " DEF_TRACKMOUSE "\n"
51 # define UNIFORM_COLORS
52 # define BRIGHT_COLORS
54 # include "xlockmore.h" /* in xscreensaver distribution */
55 #else /* STANDALONE */
56 # include "xlock.h" /* in xlockmore distribution */
57 #endif /* STANDALONE */
62 #include "pacman_ai.h"
63 #include "pacman_level.h"
65 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
67 #include "xpm-pixmap.h"
69 #if defined(USE_PIXMAP)
74 #if defined(USE_PIXMAP)
75 # include "images/pacman/ghost-u1.xpm"
76 # include "images/pacman/ghost-u2.xpm"
77 # include "images/pacman/ghost-r1.xpm"
78 # include "images/pacman/ghost-r2.xpm"
79 # include "images/pacman/ghost-l1.xpm"
80 # include "images/pacman/ghost-l2.xpm"
81 # include "images/pacman/ghost-d1.xpm"
82 # include "images/pacman/ghost-d2.xpm"
83 # include "images/pacman/ghost-mask.xpm" /* Used to clean up the dust left by wag. */
84 # include "images/pacman/pacman-u1.xpm"
85 # include "images/pacman/pacman-u2.xpm"
86 # include "images/pacman/pacman-r1.xpm"
87 # include "images/pacman/pacman-r2.xpm"
88 # include "images/pacman/pacman-l1.xpm"
89 # include "images/pacman/pacman-l2.xpm"
90 # include "images/pacman/pacman-d1.xpm"
91 # include "images/pacman/pacman-d2.xpm"
92 # include "images/pacman/pacman-0.xpm"
95 #ifdef DISABLE_INTERACTIVE
96 ModeSpecOpt pacman_opts = {
98 (XrmOptionDescRec *) NULL,
101 (OptionStruct *) NULL
104 static XrmOptionDescRec opts[] =
106 {"-trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "on"},
107 {"+trackmouse", ".pacman.trackmouse", XrmoptionNoArg, "off"}
110 static argtype vars[] =
112 {&trackmouse, "trackmouse", "TrackMouse", DEF_TRACKMOUSE, t_Bool}
115 static OptionStruct desc[] =
117 {"-/+trackmouse", "turn on/off the tracking of the mouse"}
120 ModeSpecOpt pacman_opts =
121 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
125 ModStruct pacman_description = {
126 "pacman", /* *cmdline_arg; */
127 "init_pacman", /* *init_name; */
128 "draw_pacman", /* *callback_name; */
129 "release_pacman", /* *release_name; */
130 "refresh_pacman", /* *refresh_name; */
131 "change_pacman", /* *change_name; */
132 (char *) NULL, /* *unused_name; */
133 &pacman_opts, /* *msopts */
134 10000, 4, 1, 0, 64, 1.0, "", "Shows Pacman(tm)", 0, NULL
140 pacmangamestruct *pacmangames = (pacmangamestruct *) NULL;
143 static void repopulate(ModeInfo * mi);
144 static void drawlevel(ModeInfo * mi);
148 free_pacman(Display *display, pacmangamestruct *pp)
150 int dir, mouth, i, j, k;
152 if (pp->ghosts != NULL) {
154 pp->ghosts = (ghoststruct *) NULL;
156 if (pp->stippledGC != None) {
157 XFreeGC(display, pp->stippledGC);
158 pp->stippledGC = None;
160 for (i = 0; i < 4; i++){
161 for (j = 0; j < MAXGDIR; j++){
162 for (k = 0; k < MAXGWAG; k++){
163 if (pp->ghostPixmap[i][j][k] != None) {
164 XFreePixmap(display, pp->ghostPixmap[i][j][k]);
165 pp->ghostPixmap[i][j][k] = None;
170 for (dir = 0; dir < 4; dir++)
171 for (mouth = 0; mouth < MAXMOUTH; mouth++)
172 if (pp->pacmanPixmap[dir][mouth] != None) {
174 pp->pacmanPixmap[dir][mouth]);
175 pp->pacmanPixmap[dir][mouth] = None;
180 /* Checks for death of any ghosts/pacman and updates. It also makes a new
181 level if all ghosts are dead or all dots are eaten. */
183 check_death(ModeInfo * mi, pacmangamestruct *pp)
185 Display *display = MI_DISPLAY(mi);
186 Window window = MI_WINDOW(mi);
191 for (ghost = 0; ghost < pp->nghosts; ghost++) {
192 if (pp->ghosts[ghost].dead == True)
195 if ((pp->ghosts[ghost].nextrow == NOWHERE &&
196 pp->ghosts[ghost].nextcol == NOWHERE)) {
201 if (((pp->ghosts[ghost].nextrow == pp->pacman.nextrow) &&
202 (pp->ghosts[ghost].nextcol == pp->pacman.nextcol)) ||
203 ((pp->ghosts[ghost].nextrow == pp->pacman.row) &&
204 (pp->ghosts[ghost].nextcol == pp->pacman.col) &&
205 (pp->ghosts[ghost].row == pp->pacman.nextrow) &&
206 (pp->ghosts[ghost].col == pp->pacman.nextcol))) {
207 pp->ghosts[ghost].dead = 1;
208 XSetForeground(display,
211 XFillRectangle(display, window,
213 pp->ghosts[ghost].cf,
214 pp->ghosts[ghost].rf,
222 if (alldead == 1 || pp->dotsleft == 0)
226 /* Resets state of ghosts + pacman. Creates a new level, draws that level. */
228 repopulate(ModeInfo * mi)
230 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
232 int i = createnewlevel(mi);
237 pp->gamestate = GHOST_DANGER;
239 pp->pacman.row = (LEVHEIGHT + JAILHEIGHT)/2 - i;
240 pp->pacman.col = (LEVWIDTH/2);
241 pp->pacman.nextrow = NOWHERE;
242 pp->pacman.nextcol = NOWHERE;
243 pp->pacman.cf = NOWHERE;
244 pp->pacman.rf = NOWHERE;
245 pp->pacman.oldcf = NOWHERE;
246 pp->pacman.oldrf = NOWHERE;
247 pp->pacman.oldlx = NOWHERE;
248 pp->pacman.oldly = NOWHERE;
249 pp->pacman.aistate = ps_eating;
250 pp->pacman.cur_trace = 0;
251 pp->pacman.roundscore = 0;
252 pp->pacman.speed = 4;
253 pp->pacman.lastturn = 0;
254 pp->pacman.delta.x = 0;
255 pp->pacman.delta.y = 0;
256 pac_clear_trace(&(pp->pacman));
258 for (ghost = 0; ghost < pp->nghosts; ghost++) {
259 pp->ghosts[ghost].col = (LEVWIDTH/2);
260 pp->ghosts[ghost].row = (LEVHEIGHT/2);
261 pp->ghosts[ghost].nextcol = NOWHERE;
262 pp->ghosts[ghost].nextrow = NOWHERE;
263 pp->ghosts[ghost].dead = 0;
264 pp->ghosts[ghost].lastbox = START;
265 pp->ghosts[ghost].cf = NOWHERE;
266 pp->ghosts[ghost].rf = NOWHERE;
267 pp->ghosts[ghost].oldcf = NOWHERE;
268 pp->ghosts[ghost].oldrf = NOWHERE;
269 pp->ghosts[ghost].aistate = inbox;
270 pp->ghosts[ghost].timeleft = ghost * 50;
271 pp->ghosts[ghost].speed = 3;
272 pp->ghosts[ghost].delta.x = 0;
273 pp->ghosts[ghost].delta.y = 0;
275 ghost_update(pp, &(pp->ghosts[ghost]));
278 pac_update(mi, pp, &(pp->pacman));
281 /* Sets the color to the color of a wall. */
283 setwallcolor(ModeInfo * mi)
285 Display *display = MI_DISPLAY(mi);
286 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
288 if (MI_NPIXELS(mi) > 2)
289 XSetForeground(display, pp->stippledGC,
292 XSetForeground(display, pp->stippledGC,
296 /* Sets the color to the color of a dot. */
298 setdotcolor(ModeInfo * mi)
300 Display *display = MI_DISPLAY(mi);
301 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
303 XSetForeground(display, pp->stippledGC,
307 /* Draws a block in the level at the specified x and y locations. */
309 drawlevelblock(ModeInfo * mi, pacmangamestruct *pp,
310 const unsigned x, const unsigned y)
312 Display *display = MI_DISPLAY(mi);
313 Window window = MI_WINDOW(mi);
316 if (pp->xs % 2 == 1) dx = -1;
317 if (pp->ys % 2 == 1) dy = -1;
319 XSetFillStyle(display, pp->stippledGC, FillSolid);
320 XSetLineAttributes(display, pp->stippledGC, pp->wallwidth,
321 LineSolid, CapRound, JoinMiter);
323 if (pp->xs < 2 || pp->ys < 2) {
324 switch(pp->level[y*LEVWIDTH + x]) {
330 (void)XDrawPoint(display, window,
333 y * pp->ys + pp->yb);
337 (void)XDrawPoint(display, window,
340 y * pp->ys + pp->yb);
346 switch (pp->level[y*LEVWIDTH + x]) {
353 if (pp->xs < 8 || pp->ys < 8) {
354 (void)XDrawPoint(display, window,
356 x * pp->xs + pp->xb +
358 y * pp->ys + pp->yb +
363 (void)XDrawArc(display, window, pp->stippledGC,
366 (pp->xs > 32 ? (pp->xs / 16) : 1) +
370 (pp->ys > 32 ? (pp->ys / 16) : 1) +
372 (pp->xs > 32 ? (pp->xs / 32) : 1),
373 (pp->ys > 32 ? (pp->ys / 32) : 1),
379 (void)XDrawLine(display, window, pp->stippledGC,
380 (pp->xs * x) + pp->xb,
381 (pp->ys * y) + (pp->ys / 2) + pp->yb,
382 (pp->xs * (x + 1)) + pp->xb,
383 (pp->ys * y) + (pp->ys / 2) + pp->yb);
388 (void)XDrawLine(display, window, pp->stippledGC,
389 (pp->xs * x) + (pp->xs / 2) + pp->xb,
390 (pp->ys * y) + pp->yb,
391 (pp->xs * x) + (pp->xs / 2) + pp->xb,
392 (pp->ys * (y + 1)) + pp->yb);
397 (void)XDrawArc(display, window, pp->stippledGC,
398 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
399 (pp->ys * y) + (pp->ys / 2) + pp->yb,
406 (void)XDrawArc(display, window, pp->stippledGC,
407 (pp->xs * x) + (pp->ys / 2) + pp->xb,
408 (pp->ys * y) + (pp->ys / 2) + pp->yb,
415 (void)XDrawArc(display, window, pp->stippledGC,
416 (pp->xs * x) + (pp->ys / 2) + pp->xb,
417 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
424 (void)XDrawArc(display, window, pp->stippledGC,
425 (pp->xs * x) - (pp->ys / 2) + pp->xb + dx,
426 (pp->ys * y) - (pp->ys / 2) + pp->yb + dy,
434 /* Draws a complete level. */
436 drawlevel(ModeInfo * mi)
438 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
441 for (y = 0; y < LEVHEIGHT; y++)
442 for (x = 0; x < LEVWIDTH; x++)
443 drawlevelblock(mi, pp, x, y);
446 /* There is some overlap so it can be made more efficient */
447 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
449 (y<(yl+ys))?XFillRectangle(d,w,g,xl,yl,xs,y-(yl)): \
450 XFillRectangle(d,w,g,xl,yl,xs,ys); \
452 (y>(yl-(ys)))?XFillRectangle(d,w,g,xl,y+ys,xs,yl-(y)): \
453 XFillRectangle(d,w,g,xl,yl,xs,ys); \
455 (x<(xl+xs))?XFillRectangle(d,w,g,xl,yl,x-(xl),ys): \
456 XFillRectangle(d,w,g,xl,yl,xs,ys); \
458 (x>(xl-(xs)))?XFillRectangle(d,w,g,x+xs,yl,xl-(x),ys): \
459 XFillRectangle(d,w,g,xl,yl,xs,ys)
462 /* Draws the pacman sprite, removing the previous location. */
463 #if defined(USE_PIXMAP)
466 draw_pacman_sprite(ModeInfo * mi)
468 Display *display = MI_DISPLAY(mi);
469 Window window = MI_WINDOW(mi);
470 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
471 unsigned int dir = 0;
472 int old_mask_dir = 0;
473 int old_mask_mouth = 0;
474 static int mouth = 0;
475 static int mouth_delay = 0;
476 static int open_mouth = 0;
478 #define MAX_MOUTH_DELAY 10
480 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
481 pp->pacman.cfactor + pp->xb + pp->spritedx;
482 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
483 pp->pacman.rfactor + pp->yb + pp->spritedy;
485 dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
486 ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
488 if (mouth_delay == MAX_MOUTH_DELAY){
489 if (mouth == (MAXMOUTH - 1)|| mouth == 0){
490 open_mouth = !open_mouth;
492 open_mouth ? mouth++ : mouth--;
498 XSetForeground(display,
502 XSetClipMask(display, pp->stippledGC, pp->pacmanMask[old_mask_dir][old_mask_mouth]);
503 XSetClipOrigin(display, pp->stippledGC,
504 pp->pacman.oldcf, pp->pacman.oldrf);
505 XFillRectangle(display,
510 pp->spritexs, pp->spriteys);
511 XSetClipMask(display, pp->stippledGC, pp->pacmanMask[dir][mouth]);
512 XSetClipOrigin(display, pp->stippledGC,
513 pp->pacman.cf, pp->pacman.rf);
514 XCopyArea(display, pp->pacmanPixmap[dir][mouth], window,
515 pp->stippledGC,0,0,pp->spritexs,pp->spriteys,
516 pp->pacman.cf, pp->pacman.rf);
517 XSetClipMask(display, pp->stippledGC, None);
518 pp->pacman.oldcf = pp->pacman.cf;
519 pp->pacman.oldrf = pp->pacman.rf;
521 old_mask_mouth = mouth;
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 drawlevelblock(mi, pp,
559 (unsigned int)pp->ghosts[ghost].col,
560 (unsigned int)pp->ghosts[ghost].row);
561 XSetClipOrigin(display, pp->stippledGC,
562 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
563 XCopyArea(display, pp->ghostPixmap[ghost][dir][wag], window,
564 pp->stippledGC,0,0,pp->spritexs,pp->spriteys,
565 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
566 XSetClipMask(display, pp->stippledGC, None);
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){
575 #else /* USE_PIXMAP */
577 /* Draws the pacman sprite, removing the previous location. */
579 draw_pacman_sprite(ModeInfo * mi)
581 Display *display = MI_DISPLAY(mi);
582 Window window = MI_WINDOW(mi);
583 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
586 pp->pacman.cf = pp->pacman.col * pp->xs + pp->pacman.delta.x *
587 pp->pacman.cfactor + pp->xb + pp->spritedx;
588 pp->pacman.rf = pp->pacman.row * pp->ys + pp->pacman.delta.y *
589 pp->pacman.rfactor + pp->yb + pp->spritedy;
591 dir = (ABS(pp->pacman.cfactor) * (2 - pp->pacman.cfactor) +
592 ABS(pp->pacman.rfactor) * (1 + pp->pacman.rfactor)) % 4;
594 XSetForeground(display, pp->stippledGC,
596 if (pp->pacman.oldcf != NOWHERE && pp->pacman.oldrf != NOWHERE) {
598 XFillRectangle(display, window, pp->stippledGC,
599 pp->pacman.oldcf, pp->pacman.oldrf,
600 pp->spritexs, pp->spriteys);
602 ERASE_IMAGE(display, window, pp->stippledGC,
603 pp->pacman.cf, pp->pacman.rf,
604 pp->pacman.oldcf, pp->pacman.oldrf,
605 pp->spritexs, pp->spriteys);
609 XSetTSOrigin(display, pp->stippledGC,
610 pp->pacman.cf, pp->pacman.rf);
611 if (MI_NPIXELS(mi) > 2)
612 XSetForeground(display, pp->stippledGC,
613 MI_PIXEL(mi, YELLOW));
615 XSetForeground(display, pp->stippledGC,
618 XSetStipple(display, pp->stippledGC,
619 pp->pacmanPixmap[dir][pp->pacman.mouthstage]);
621 XSetFillStyle(display, pp->stippledGC, FillStippled);
623 XSetFillStyle(display, pp->stippledGC,
626 if (pp->xs < 2 || pp->ys < 2)
627 XDrawPoint(display, window, pp->stippledGC,
628 pp->pacman.cf, pp->pacman.rf);
630 XFillRectangle(display, window, pp->stippledGC,
631 pp->pacman.cf, pp->pacman.rf,
632 pp->spritexs, pp->spriteys);
633 pp->pacman.mouthstage += pp->pacman.mouthdirection;
634 if ((pp->pacman.mouthstage >= MAXMOUTH) ||
635 (pp->pacman.mouthstage < 0)) {
636 pp->pacman.mouthdirection *= -1;
637 pp->pacman.mouthstage += pp->pacman.mouthdirection * 2;
639 pp->pacman.oldcf = pp->pacman.cf;
640 pp->pacman.oldrf = pp->pacman.rf;
643 /* Draws a ghost sprite, removing the previous sprite and restores the level. */
645 draw_ghost_sprite(ModeInfo * mi, const unsigned ghost) {
646 Display *display = MI_DISPLAY(mi);
647 Window window = MI_WINDOW(mi);
648 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
650 pp->ghosts[ghost].cf =
651 pp->ghosts[ghost].col * pp->xs + pp->ghosts[ghost].delta.x *
652 pp->ghosts[ghost].cfactor + pp->xb + pp->spritedx;
653 pp->ghosts[ghost].rf =
654 pp->ghosts[ghost].row * pp->ys + pp->ghosts[ghost].delta.y *
655 pp->ghosts[ghost].rfactor + pp->yb + pp->spritedy;
657 XSetForeground(display,
660 XFillRectangle(display,
663 pp->ghosts[ghost].cf,
664 pp->ghosts[ghost].rf,
665 pp->spritexs, pp->spriteys);
667 if (pp->ghosts[ghost].oldcf != NOWHERE ||
668 pp->ghosts[ghost].oldrf != NOWHERE) {
670 XFillRectangle(display, window,
671 pp->stippledGC, pp->ghosts[ghost].oldcf,
672 pp->ghosts[ghost].oldrf,
673 pp->spritexs, pp->spriteys);
675 ERASE_IMAGE(display, window, pp->stippledGC,
676 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf,
677 pp->ghosts[ghost].oldcf, pp->ghosts[ghost].oldrf,
678 pp->spritexs, pp->spriteys);
682 drawlevelblock(mi, pp,
683 (unsigned int)pp->ghosts[ghost].col,
684 (unsigned int)pp->ghosts[ghost].row);
686 XSetTSOrigin(display, pp->stippledGC,
687 pp->ghosts[ghost].cf, pp->ghosts[ghost].rf);
689 if (MI_NPIXELS(mi) > 2)
690 XSetForeground(display,
692 MI_PIXEL(mi, GREEN));
694 XSetForeground(display,
698 XSetStipple(display, pp->stippledGC,
699 pp->ghostPixmap[0][0][0]);
702 XSetFillStyle(display,
706 XSetFillStyle(display,
710 if (pp->xs < 2 || pp->ys < 2)
711 XDrawPoint(display, window, pp->stippledGC,
712 pp->ghosts[ghost].cf,
713 pp->ghosts[ghost].rf);
715 XFillRectangle(display,
718 pp->ghosts[ghost].cf,
719 pp->ghosts[ghost].rf,
720 pp->spritexs, pp->spriteys);
722 pp->ghosts[ghost].oldcf = pp->ghosts[ghost].cf;
723 pp->ghosts[ghost].oldrf = pp->ghosts[ghost].rf;
725 #endif /* USE_PIXMAP */
727 /* Does all drawing of moving sprites in the level. */
729 pacman_tick(ModeInfo * mi)
731 Display *display = MI_DISPLAY(mi);
732 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
735 for (ghost = 0; ghost < pp->nghosts; ghost++) {
736 if (pp->ghosts[ghost].dead == True)
738 draw_ghost_sprite(mi, ghost);
741 draw_pacman_sprite(mi);
743 (void)XFlush(display);
746 #if defined(USE_PIXMAP)
747 /* Grabbed the scaling routine off of usenet.
748 * Changed it so that the information specific
749 * to the source pixmap does not have to be a parameter.
751 * There is probably a better way to scale pixmaps.
752 * From: Chris Fiddyment (cxf@itd.dsto.gov.au)
753 * Subject: Scaling Pixmap Algorithm.
754 * Newsgroups: comp.graphics.algorithms
755 * Date: 1994-07-06 18:51:38 PST
760 scale_pixmap( Display **dpy, GC gc, Pixmap source, int dwidth, int dheight)
765 float xscale, yscale;
766 unsigned int swidth, sheight;
769 unsigned border_width_return, depth;
770 XGetGeometry(*dpy, source, &window, &x, &y, &swidth, &sheight, &border_width_return, &depth);
772 xscale = (float) swidth / (float) dwidth; /* Scaling factors */
773 yscale = (float) sheight / (float) dheight;
775 dest = XCreatePixmap(*dpy,window,dwidth,dheight,depth);
777 fprintf(stderr, "%s Could not scale image", progname);
779 temp = XCreatePixmap(*dpy,window,dwidth,sheight,depth);
781 fprintf(stderr, "%s Could not scale image", progname);
786 /* Scale width of source into temp pixmap */
787 for(i=0;i<end;i+=xscale)
788 XCopyArea(*dpy,source,temp,gc,i,0,1,sheight,j++,0);
791 end = dheight*yscale;
792 /* Scale height of temp into dest pixmap */
793 for(i=0;i<end;i+=yscale)
794 XCopyArea(*dpy,temp,dest,gc,0,i,dwidth,1,0,j++);
796 XFreePixmap( *dpy, temp );
797 return (Pixmap) dest;
801 pacman_fail(char *s){
802 fprintf (stderr, "%s: %s\n", progname, s);
806 /* Load the ghost pixmaps and their mask. */
808 load_ghost_pixmaps(Display **dpy, Window window, pacmangamestruct **ps)
810 pacmangamestruct *pp = *ps;
811 Display *display = *dpy;
812 static char *colors[]= {
813 ". c #FF0000", /*Red*/
814 ". c #00FFDE", /*Blue*/
815 ". c #FFB847", /*Orange*/
816 ". c #FFB8DE", /*Pink*/
819 static char **bits[] = {
820 ghost_u1_xpm, ghost_u2_xpm, ghost_r1_xpm, ghost_r2_xpm,
821 ghost_d1_xpm, ghost_d2_xpm, ghost_l1_xpm, ghost_l2_xpm
824 int w = pp->spritexs;
825 int h = pp->spriteys;
829 for (i = 0; i < 4; i++){
831 for ( j = 0; j < MAXGDIR; j++){
832 for ( k = 0; k < MAXGWAG; k++){
833 bits[m][2] = colors[i];
834 pp->ghostPixmap[i][j][k] = xpm_data_to_pixmap (display, window, bits[m],
835 &w, &h, &pp->ghostMask);
837 if (!pp->ghostPixmap[i][j][k]) pacman_fail("Cannot load ghost images");
839 pp->ghostPixmap[i][j][k] = scale_pixmap(&display, pp->stippledGC,
840 pp->ghostPixmap[i][j][k], pp->spritexs, pp->spriteys);
842 if (!pp->ghostPixmap[i][j][k]) pacman_fail("Cannot scale ghost images");
847 /* We really only need a single mask. This saves the headache of getting the bottom of the ghost
848 * to clip just right. What we'll do is mask the top portion of the ghost, but the bottom of the
849 * the ghost will be solid. I did this by setting the pixels between the fringe of their sheets
850 * to black instead of none. -jeremy
852 temp = xpm_data_to_pixmap (display, window, ghost_mask_xpm,
853 &w, &h, &pp->ghostMask);
855 if (!temp) pacman_fail("Cannot load temporary ghost image");
857 temp = scale_pixmap(&display, pp->stippledGC,
858 temp, pp->spritexs, pp->spriteys);
860 if (!temp) pacman_fail("Cannot scale temporary ghost image");
862 gc = XCreateGC(display, pp->ghostMask, 0, 0);
864 pp->ghostMask = scale_pixmap(&display, gc, pp->ghostMask,
865 pp->spritexs, pp->spriteys);
866 XFreePixmap(display, temp);
869 /* Load the pacman pixmaps and their mask. */
871 load_pacman_pixmaps(Display **dpy, Window window, pacmangamestruct **ps)
873 pacmangamestruct *pp = *ps;
874 Display *display = *dpy;
876 static char **bits[] = {
877 pacman_0_xpm, pacman_u1_xpm, pacman_u2_xpm,
878 pacman_0_xpm, pacman_r1_xpm, pacman_r2_xpm,
879 pacman_0_xpm, pacman_d1_xpm, pacman_d2_xpm,
880 pacman_0_xpm, pacman_l1_xpm, pacman_l2_xpm
883 int w = pp->spritexs;
884 int h = pp->spriteys;
888 for (i = 0; i < 4; i++){
889 for ( j = 0; j < MAXMOUTH; j++){
890 pp->pacmanPixmap[i][j] = xpm_data_to_pixmap (display, window, bits[m++],
891 &w, &h, &pp->pacmanMask[i][j]);
893 if (!pp->pacmanPixmap[i][j]) pacman_fail("Cannot load pacman pixmap.");
895 pp->pacmanPixmap[i][j] = scale_pixmap(&display, pp->stippledGC,
896 pp->pacmanPixmap[i][j], pp->spritexs, pp->spriteys);
898 if (!pp->pacmanPixmap[i][j]) pacman_fail("Cannot scale pacman pixmap.");
901 gc = XCreateGC(display, pp->pacmanMask[i][j], 0, 0);
903 pp->pacmanMask[i][j] = scale_pixmap(&display, gc, pp->pacmanMask[i][j],
904 pp->spritexs, pp->spriteys);
908 #endif /* USE_PIXMAP */
910 /* Hook function, sets state to initial position. */
912 init_pacman(ModeInfo * mi)
914 Display *display = MI_DISPLAY(mi);
915 Window window = MI_WINDOW(mi);
916 int size = MI_SIZE(mi);
917 pacmangamestruct *pp;
921 #if (! defined( USE_PIXMAP ))
927 if (pacmangames == NULL) {
928 if ((pacmangames = (pacmangamestruct *)
929 calloc((size_t)MI_NUM_SCREENS(mi),
930 sizeof (pacmangamestruct))) == NULL)
933 pp = &pacmangames[MI_SCREEN(mi)];
935 pp->width = (unsigned short)MI_WIDTH(mi);
936 pp->height = (unsigned short)MI_HEIGHT(mi);
937 for (i = 0; i < 4; i++){
938 for (j = 0; j < MAXGDIR; j++){
939 for (k = 0; k < MAXGWAG; k++){
940 if (pp->ghostPixmap[i][j][k] != None) {
941 XFreePixmap(display, pp->ghostPixmap[i][j][k]);
942 pp->ghostPixmap[i][j][k] = None;
943 pp->graphics_format = 0 /*IS_NONE*/;
949 MINGRIDSIZE * size > (int)pp->width ||
950 MINGRIDSIZE * size > (int)pp->height) {
952 pp->ys = pp->xs = MAX(MIN(pp->width/LEVWIDTH,
953 pp->height/LEVHEIGHT), 1);
956 pp->ys = (short)(NRAND( MIN( -size, MAX( MINSIZE,
957 MIN( pp->width, pp->height) / MINGRIDSIZE))
958 - MINSIZE + 1) + MINSIZE);
959 else if (size < MINSIZE)
962 pp->ys = (short)(MIN(size,
963 MAX(MINSIZE, MIN(pp->width, pp->height) /
968 pp->wallwidth = (unsigned int)(pp->xs + pp->ys) >> 4;
969 if (pp->wallwidth < 1) pp->wallwidth = 1;
970 pp->incx = (pp->xs >> 3) + 1;
971 pp->incy = (pp->ys >> 3) + 1;
972 pp->ncols = (unsigned short)MAX(LEVWIDTH, 2);
973 pp->nrows = (unsigned short)MAX(LEVHEIGHT, 2);
974 pp->xb = (pp->width - pp->ncols * pp->xs) >> 1;
975 pp->yb = (pp->height - pp->nrows * pp->ys) >> 1;
976 pp->spritexs = MAX(pp->xs + (pp->xs >> 1) - 1, 1);
977 pp->spriteys = MAX(pp->ys + (pp->ys >> 1) - 1, 1);
978 pp->spritedx = (pp->xs - pp->spritexs) >> 1;
979 pp->spritedy = (pp->ys - pp->spriteys) >> 1;
981 if (!pp->stippledGC) {
982 gcv.foreground = MI_BLACK_PIXEL(mi);
983 gcv.background = MI_BLACK_PIXEL(mi);
984 if ((pp->stippledGC = XCreateGC(display, window,
985 GCForeground | GCBackground, &gcv)) == None) {
986 free_pacman(display, pp);
991 #if defined(USE_PIXMAP)
992 load_ghost_pixmaps(&display,window,&pp);
993 load_pacman_pixmaps(&display,window,&pp);
995 if ((pp->ghostPixmap[0][0][0] = XCreatePixmap(display, window,
996 pp->spritexs, pp->spriteys, 1)) == None) {
997 free_pacman(display, pp);
1003 if ((bg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
1004 GCForeground | GCBackground, &gcv)) == None) {
1005 free_pacman(display, pp);
1011 if ((fg_gc = XCreateGC(display, pp->ghostPixmap[0][0][0],
1012 GCForeground | GCBackground, &gcv)) == None) {
1013 XFreeGC(display, bg_gc);
1014 free_pacman(display, pp);
1018 #define SETPOINT(p, xp, yp) p.x = xp; p.y = yp
1020 /* draw the triangles on the bottom (scalable) */
1021 SETPOINT(points[0], 1, pp->spriteys * 5 / 6);
1022 SETPOINT(points[1], pp->spritexs / 6, pp->spriteys);
1023 SETPOINT(points[2], pp->spritexs / 3, pp->spriteys * 5 / 6);
1024 SETPOINT(points[3], pp->spritexs / 2, pp->spriteys);
1025 SETPOINT(points[4], pp->spritexs * 2 / 3, pp->spriteys * 5 / 6);
1026 SETPOINT(points[5], pp->spritexs * 5 / 6, pp->spriteys);
1027 SETPOINT(points[6], pp->spritexs, pp->spriteys * 5 / 6);
1028 SETPOINT(points[7], pp->spritexs, pp->spriteys / 2);
1029 SETPOINT(points[8], 1, pp->spriteys / 2);
1031 XFillRectangle(display, pp->ghostPixmap[0][0][0], bg_gc,
1032 0, 0, pp->spritexs, pp->spriteys);
1033 XFillArc(display, pp->ghostPixmap[0][0][0], fg_gc,
1034 0, 0, pp->spritexs, pp->spriteys, 0, 11520);
1035 XFillPolygon(display, pp->ghostPixmap[0][0][0], fg_gc,
1036 points, 9, Nonconvex, CoordModeOrigin);
1037 XFreeGC(display, bg_gc);
1038 XFreeGC(display, fg_gc);
1041 if (pp->pacmanPixmap[0][0] != None)
1042 for (dir = 0; dir < 4; dir++)
1043 for (mouth = 0; mouth < MAXMOUTH; mouth++)
1044 XFreePixmap(display,
1045 pp->pacmanPixmap[dir]
1048 for (dir = 0; dir < 4; dir++)
1049 for (mouth = 0; mouth < MAXMOUTH; mouth++) {
1050 if ((pp->pacmanPixmap[dir][mouth] = XCreatePixmap(
1051 display, MI_WINDOW(mi),
1052 pp->spritexs, pp->spriteys, 1)) ==
1054 free_pacman(display, pp);
1059 if ((fg_gc = XCreateGC(display, pp->pacmanPixmap[dir][mouth],
1060 GCForeground | GCBackground, &gcv)) == None) {
1061 free_pacman(display, pp);
1066 if ((bg_gc = XCreateGC(display,
1067 pp->pacmanPixmap[dir][mouth],
1069 GCBackground, &gcv)) ==
1071 XFreeGC(display, fg_gc);
1072 free_pacman(display, pp);
1075 XFillRectangle(display,
1076 pp->pacmanPixmap[dir][mouth], bg_gc,
1077 0, 0, pp->spritexs, pp->spriteys);
1078 if (pp->spritexs == 1 && pp->spriteys == 1)
1079 XFillRectangle(display,
1080 pp->pacmanPixmap[dir][mouth],
1081 fg_gc, 0, 0, pp->spritexs,
1085 pp->pacmanPixmap[dir][mouth],
1087 0, 0, pp->spritexs, pp->spriteys,
1088 ((90 - dir * 90) + mouth * 5) * 64,
1089 (360 + (-2 * mouth * 5)) * 64);
1090 XFreeGC(display, fg_gc);
1091 XFreeGC(display, bg_gc);
1093 #endif /* USE_PIXMAP */
1095 pp->pacman.lastbox = START;
1096 pp->pacman.mouthdirection = 1;
1097 pp->pacman.nextcol = NOWHERE;
1098 pp->pacman.nextrow = NOWHERE;
1100 if (pp->ghosts != NULL) {
1102 pp->ghosts = (ghoststruct *) NULL;
1104 pp->nghosts = GHOSTS;
1107 if ((pp->ghosts = (ghoststruct *) calloc((size_t)pp->nghosts,
1108 sizeof (ghoststruct))) == NULL) {
1109 free_pacman(display, pp);
1113 pp->pacman.mouthstage = MAXMOUTH - 1;
1119 /* Callback function called for each tick. This is the complete machinery of
1120 everything that moves. */
1122 draw_pacman(ModeInfo * mi)
1125 pacmangamestruct *pp;
1127 if (pacmangames == NULL)
1129 pp = &pacmangames[MI_SCREEN(mi)];
1130 if (pp->ghosts == NULL)
1133 pp->pacman.err.x = (pp->pacman.err.x + 1) % pp->pacman.speed;
1134 pp->pacman.err.y = (pp->pacman.err.y + 1) % pp->pacman.speed;
1135 pp->pacman.delta.x += pp->pacman.err.x != 0 ? pp->incx : 0;
1136 pp->pacman.delta.y += pp->pacman.err.y != 0 ? pp->incy : 0;
1138 if (pp->pacman.delta.x >= pp->xs && pp->pacman.delta.y >= pp->ys) {
1139 pac_update(mi, pp, &(pp->pacman));
1140 check_death(mi, pp);
1141 pp->pacman.delta.x = pp->incx;
1142 pp->pacman.delta.y = pp->incy;
1145 if (pp->pacman.delta.x > pp->xs + pp->incx)
1146 pp->pacman.delta.x = pp->xs + pp->incx;
1147 if (pp->pacman.delta.y > pp->ys + pp->incy)
1148 pp->pacman.delta.y = pp->ys + pp->incy;
1150 for (g = 0; g < pp->nghosts; g++) {
1151 if (pp->ghosts[g].dead == True) continue;
1153 pp->ghosts[g].err.x = (pp->ghosts[g].err.x + 1) %
1154 pp->ghosts[g].speed;
1155 pp->ghosts[g].err.y = (pp->ghosts[g].err.y + 1) %
1156 pp->ghosts[g].speed;
1157 pp->ghosts[g].delta.x += pp->ghosts[g].err.x != 0 ?
1159 pp->ghosts[g].delta.y += pp->ghosts[g].err.y != 0 ?
1162 if (pp->ghosts[g].delta.x >= pp->xs &&
1163 pp->ghosts[g].delta.y >= pp->ys) {
1164 ghost_update(pp, &(pp->ghosts[g]));
1165 pp->ghosts[g].delta.x = pp->incx;
1166 pp->ghosts[g].delta.y = pp->incy;
1169 if (pp->ghosts[g].delta.x > pp->xs + pp->incx)
1170 pp->ghosts[g].delta.x = pp->xs + pp->incx;
1171 if (pp->ghosts[g].delta.y > pp->ys + pp->incy)
1172 pp->ghosts[g].delta.y = pp->ys + pp->incy;
1178 /* Releases resources. */
1180 release_pacman(ModeInfo * mi)
1182 if (pacmangames != NULL) {
1185 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1186 free_pacman(MI_DISPLAY(mi), &pacmangames[screen]);
1188 pacmangames = (pacmangamestruct *) NULL;
1192 /* Refresh current level. */
1194 refresh_pacman(ModeInfo * mi)
1200 /* Callback to change level. */
1202 change_pacman(ModeInfo * mi)
1208 #endif /* MODE_pacman */