1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation for any purpose and without fee is hereby granted,
7 * provided that the above copyright notice appear in all copies and that
8 * both that copyright notice and this permission notice appear in
9 * supporting documentation.
11 * This file is provided AS IS with no warranties of any kind. The author
12 * shall have no liability with respect to the infringement of copyrights,
13 * trade secrets or any patents by this file or any part thereof. In no
14 * event will the author be liable for any lost revenue or profits or
15 * other special, indirect and consequential damages.
20 #include "pacman_level.h"
23 #define MI_SCREEN(MI) (0)
32 #define BLOCK_EMPTY ' '
33 #define BLOCK_DOT_1 '`'
34 #define BLOCK_DOT_2 '.'
35 #define BLOCK_WALL '#'
36 #define BLOCK_GHOST_ONLY '='
37 #define BLOCK_WALL_TL '\''
38 #define BLOCK_WALL_TR '`'
39 #define BLOCK_WALL_BR ','
40 #define BLOCK_WALL_BL '_'
41 #define BLOCK_WALL_HO '-'
42 #define BLOCK_WALL_VE '|'
43 #define BLOCK_DOT_BONUS 'o'
45 /* This is more or less the standard pacman level (without the left-right
47 static const lev_t stdlevel = {
48 "########################################",
49 "########################################",
50 "#######````````````##````````````#######",
51 "#######`####`#####`##`#####`####`#######",
52 "#######`####`#####`##`#####`####`#######",
53 "#######`####`#####`##`#####`####`#######",
54 "#######``````````````````````````#######",
55 "#######`####`##`########`##`####`#######",
56 "#######`####`##`########`##`####`#######",
57 "#######``````##````##````##``````#######",
58 "############`#####`##`#####`############",
59 "############`#####`##`#####`############",
60 "############`##``````````##`############",
61 "############`##`###==###`##`############",
62 "############`##`########`##`############",
63 "############````########````############",
64 "############`##`########`##`############",
65 "############`##`########`##`############",
66 "############`##``````````##`############",
67 "############`##`########`##`############",
68 "############`##`########`##`############",
69 "#######````````````##````````````#######",
70 "#######`####`#####`##`#####`####`#######",
71 "#######`####`#####`##`#####`####`#######",
72 "#######```##````````````````##```#######",
73 "#########`##`##`########`##`##`#########",
74 "#########`##`##`########`##`##`#########",
75 "#######``````##````##````##``````#######",
76 "#######`##########`##`##########`#######",
77 "#######`##########`##`##########`#######",
78 "#######``````````````````````````#######",
79 "########################################"
84 #define TILES_COUNT 11U
87 #define GO_LEFT 0x0002U
88 #define GO_RIGHT 0x0004U
89 #define GO_DOWN 0x0008U
91 /* This are tiles which can be places to create a level. */
94 char block[TILEWIDTH * TILEHEIGHT + 1];
98 } tiles[TILES_COUNT] = {
100 * ' ' == dont care == BLOCK_EMPTY
101 * '#' == set wall, and not clear == BLOCK_WALL
102 * '`' == clear == BLOCK_DOT_1
103 * middle position is always set as cleardef
106 " # " " # " " ``` " " # " " # ", {
107 GO_LEFT, GO_RIGHT, 0, 0}, 2,
108 (unsigned) (1 << 0 | 1 << 6 | 1 << 8 | 1 << 10)}, {
109 " " " ` " "##`##" " ` " " ", {
110 GO_UP, GO_DOWN, 0, 0}, 2,
111 (unsigned) (1 << 1 | 1 << 7 | 1 << 9 | 1 << 10)}, {
112 " ##" "##`##" "##`` " "#### " "#### ", {
113 GO_UP, GO_RIGHT, 0, 0}, 2,
114 (unsigned) (1 << 2 | 1 << 6 | 1 << 7 | 1 << 10)}, {
115 "#### " "#### " "##`` " "##`##" " ##", {
116 GO_RIGHT, GO_DOWN, 0, 0}, 2,
117 (unsigned) (1 << 3 | 1 << 7 | 1 << 8 | 1 << 10)}, {
118 " ###" " ###" " ``##" "##` " "## ", {
119 GO_LEFT, GO_DOWN, 0, 0}, 2,
120 (unsigned) (1 << 4 | 1 << 8 | 1 << 9 | 1 << 10)}, {
121 "## " "##`##" " ``##" " ####" " ####", {
122 GO_LEFT, GO_UP, 0, 0}, 2,
123 (unsigned) (1 << 5 | 1 << 6 | 1 << 9 | 1 << 10)}, {
124 "##`##" "##`##" "`````" " ### " " ### ", {
125 GO_LEFT, GO_UP, GO_RIGHT, 0}, 3, (unsigned) 1 << 6}, {
126 " `##" "##`##" "##```" "##`##" " `##", {
127 GO_UP, GO_RIGHT, GO_DOWN, 0}, 3, (unsigned) (1 << 7)}, {
128 " ### " " ### " "`````" "##`##" "##`##", {
129 GO_LEFT, GO_RIGHT, GO_DOWN, 0}, 3, (unsigned) (1 << 8)}, {
130 "##` " "##`##" "```##" "##`##" "##` ", {
131 GO_UP, GO_DOWN, GO_LEFT, 0}, 3, (unsigned) (1 << 9)}, {
132 "##`##" "##`##" "`````" "##`##" "##`##", {
133 GO_UP, GO_DOWN, GO_LEFT, GO_RIGHT}, 4, (unsigned) (1 << 10)}
136 /* probability array for each of the tiles */
137 #define MAXTILEPROB 22
138 static const unsigned tileprob[MAXTILEPROB] =
139 { 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10 };
142 static int creatlevelblock (lev_t * level, const unsigned x,
154 { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT };
156 static bonus_dot bonus_dots[NUM_BONUS_DOTS];
158 /* Sets a block in the level to a certain state. */
160 setblockto (lev_t * level, const unsigned x, const unsigned y, const char c)
162 if (!(x < LEVWIDTH && y < LEVHEIGHT))
167 /* check if a block is set */
169 checkset (lev_t * level, const unsigned x, const unsigned y)
171 if (!(x < LEVWIDTH && y < LEVHEIGHT) ||
172 (*level)[y][x] == BLOCK_WALL || (*level)[y][x] == BLOCK_GHOST_ONLY)
177 /* Check if a block is not set */
179 checksetout (lev_t * level, const unsigned x, const unsigned y)
181 if (!(x < LEVWIDTH && y < LEVHEIGHT) || checkset (level, x, y) != 0)
187 /* Check if a block cannot be set */
189 checkunsetdef (lev_t * level, const unsigned x, const unsigned y)
191 if (!(x < LEVWIDTH && y < LEVHEIGHT))
193 if ((*level)[y][x] == BLOCK_DOT_1)
198 /* Initializes a level to empty state. */
200 clearlevel (lev_t * level)
204 for (y = 0; y < LEVHEIGHT; y++)
205 for (x = 0; x < LEVWIDTH; x++)
206 (*level)[y][x] = BLOCK_EMPTY;
209 /* Changes a level from the level creation structure ((array to array) to
212 copylevel (char *dest, lev_t * level)
216 for (y = 0; y < LEVHEIGHT; y++)
217 for (x = 0; x < LEVWIDTH; x++)
218 dest[y * LEVWIDTH + x] = (*level)[y][x];
221 /* Creates a jail to work around, so we can finish it later. */
223 createjail (lev_t * level, const unsigned width, const unsigned height)
225 unsigned x, y, xstart, xend, ystart, yend;
227 if (LEVWIDTH < width || LEVHEIGHT < height)
230 xstart = LEVWIDTH / 2 - width / 2;
231 xend = LEVWIDTH / 2 + width / 2;
232 ystart = LEVHEIGHT / 2 - height / 2;
233 yend = LEVHEIGHT / 2 + height / 2;
235 for (y = ystart - 1; y < yend + 1; y++)
236 for (x = xstart - 1; x < xend + 1; x++)
237 setblockto (level, x, y, BLOCK_DOT_1);
239 for (y = ystart; y < yend; y++)
240 for (x = xstart; x < xend; x++)
241 setblockto (level, x, y, BLOCK_WALL);
245 get_jail_opening ( int *x, int *y)
247 int xstart = LEVWIDTH / 2 - JAILWIDTH / 2;
248 int ystart = LEVHEIGHT / 2 - JAILHEIGHT / 2;
249 *x = xstart + JAILWIDTH / 2;
253 /* Finishes a jail so it is empty and the ghostpass is on top. */
255 finishjail (lev_t * level, const unsigned width, const unsigned height)
257 unsigned x, y, xstart, xend, ystart, yend;
259 xstart = LEVWIDTH / 2 - width / 2;
260 xend = LEVWIDTH / 2 + width / 2;
261 ystart = LEVHEIGHT / 2 - height / 2;
262 yend = LEVHEIGHT / 2 + height / 2;
264 for (y = ystart + 1; y < yend - 1; y++)
265 for (x = xstart + 1; x < xend - 1; x++)
266 setblockto (level, x, y, BLOCK_EMPTY);
268 for (x = xstart - 1; x < xend + 1; x++) {
269 setblockto (level, x, ystart - 1, BLOCK_EMPTY);
270 setblockto (level, x, yend, BLOCK_EMPTY);
273 for (y = ystart - 1; y < yend + 1; y++) {
274 setblockto (level, xstart - 1, y, BLOCK_EMPTY);
275 setblockto (level, xend, y, BLOCK_EMPTY);
278 setblockto (level, xstart + width / 2 - 1, ystart, BLOCK_GHOST_ONLY);
279 setblockto (level, xstart + width / 2, ystart, BLOCK_GHOST_ONLY);
282 /* Tries to set a block at a certain position. Returns true if possible,
283 and leaves level in new state (plus block), or False if not possible,
284 and leaves level in unpredictable state. */
286 tryset (lev_t * level, const unsigned xpos, const unsigned ypos,
289 register unsigned x, y;
290 register char locchar;
294 if ((*level)[ypos][xpos] == BLOCK_DOT_1)
300 for (y = 0; y < TILEHEIGHT; y++)
301 for (x = 0; x < TILEWIDTH; x++) {
302 locchar = block[y * TILEWIDTH + x];
303 if (locchar == BLOCK_EMPTY)
305 if (locchar == BLOCK_DOT_1 &&
307 xstart + x >= LEVWIDTH - 1 ||
309 ystart + y >= LEVHEIGHT - 1 ||
310 checkset (level, xstart + x, ystart + y) != 0))
312 else if (locchar == BLOCK_WALL &&
314 xstart + x < LEVWIDTH &&
316 ystart + y < LEVHEIGHT - 1) &&
317 checkunsetdef (level,
318 (unsigned) (xstart + x),
319 (unsigned) (ystart + y)) != 0)
323 /* and set the block in place */
325 xend = (xstart + TILEWIDTH < LEVWIDTH - 1) ?
326 TILEWIDTH : LEVWIDTH - xstart - 2;
327 yend = (ystart + TILEHEIGHT < LEVHEIGHT - 1) ?
328 TILEHEIGHT : LEVHEIGHT - ystart - 2;
330 for (y = (ystart < 1) ? (unsigned) (1 - ystart) : 0U; y < yend; y++)
331 for (x = (xstart < 1) ? (unsigned) (1 - xstart) : 0U; x < xend; x++) {
332 locchar = block[y * TILEWIDTH + x];
333 if ((locchar == BLOCK_WALL) &&
334 ((*level)[ystart + y][xstart + x] == BLOCK_EMPTY)) {
335 (*level)[ystart + y][xstart + x] = BLOCK_WALL;
337 [LEVWIDTH - (xstart + x + 1)] = BLOCK_WALL;
341 (*level)[ypos][xpos] = BLOCK_DOT_1;
342 (*level)[ypos][LEVWIDTH - xpos - 1] = BLOCK_DOT_1;
347 /* Tries certain combinations of blocks in the level recursively. */
349 nextstep (lev_t * level, const unsigned x, const unsigned y,
350 unsigned dirvec[], unsigned ndirs)
352 unsigned dirpos, curdir, inc = 0;
361 dirpos = NRAND (ndirs);
362 curdir = dirvec[dirpos];
363 /* nope, no bufoverflow, but ndirs - 1 + 1 */
364 dirvec[dirpos] = dirvec[ndirs];
365 dirvec[ndirs] = curdir;
370 if (y < 1 || (ret = creatlevelblock (level, x, y - 1))
375 if (x > LEVWIDTH - 2 || (ret = creatlevelblock (level, x + 1, y))
380 if (y > LEVHEIGHT - 2 || (ret = creatlevelblock (level, x, y + 1))
385 if (x < 1 || (ret = creatlevelblock (level, x - 1, y))
390 inc += (unsigned) ret;
398 creatlevelblock (lev_t * level, const unsigned x, const unsigned y)
400 unsigned tried = GETNB (TILES_COUNT);
405 if (!((x < LEVWIDTH) && (y < LEVHEIGHT)))
408 if (checkunsetdef (level, x, y) != 0)
414 tried &= ~(1 << 4 | 1 << 5 | 1 << 6 | 1 << 8 | 1 << 9 | 1 << 10);
415 else if (x == LEVWIDTH - 1)
417 else if (x == LEVWIDTH - 2)
418 tried &= ~(1 << 2 | 1 << 3 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 10);
421 tried &= ~(1 << 2 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 9 | 1 << 10);
424 else if (y == LEVHEIGHT - 1)
426 else if (y == LEVHEIGHT - 2)
427 tried &= ~(1 << 3 | 1 << 4 | 1 << 7 | 1 << 8 | 1 << 9 | 1 << 10);
429 /* make a copy of the current level, so we can go back on the stack */
430 (void) memcpy (&savedlev, level, sizeof (lev_t));
432 /* while there are still some blocks left to try */
433 while (tried != 0x00) {
434 tilenr = tileprob[NRAND (MAXTILEPROB)];
436 if (!TESTNB (tried, tilenr))
439 if (tryset (level, x, y, tiles[tilenr].block) != 0) {
440 if ((ret = nextstep (level, x, y, tiles[tilenr].dirvec,
441 tiles[tilenr].ndirs)) != 0) {
444 (void) memcpy (level, &savedlev, sizeof (lev_t));
446 tried &= ~(tiles[tilenr].simular_to);
451 /* Fills up all empty space so there is wall everywhere. */
453 filllevel (lev_t * level)
457 for (y = 0; y < LEVHEIGHT; y++)
458 for (x = 0; x < LEVWIDTH; x++)
459 if ((*level)[y][x] == BLOCK_EMPTY)
460 (*level)[y][x] = BLOCK_WALL;
464 /* Check to see if the x and y value are in the corners.
465 * we could probable compute these values once and avoid
466 * go through the loops every time.
469 top_left (lev_t * level, unsigned int *passed_x, unsigned int *passed_y)
472 for (y = 0; y < LEVHEIGHT; y++)
473 for (x = 0; x < LEVWIDTH; x++) {
474 if (checkset (level, x, y) == 0) {
484 bottom_left (lev_t * level, unsigned int *passed_x, unsigned int *passed_y)
487 for (y = LEVHEIGHT; y > -1; y--)
488 for (x = 0; x < LEVWIDTH; x++) {
489 if (checkset (level, x, y) == 0) {
498 top_right (lev_t * level, unsigned int *passed_x, unsigned int *passed_y)
502 for (y = 0; y < LEVHEIGHT; y++)
503 for (x = LEVWIDTH; x >= 0; x--) {
504 if (checkset (level, x, y) == 0) {
513 bottom_right (lev_t * level, unsigned int *passed_x, unsigned int *passed_y)
517 for (y = LEVHEIGHT; y >= 0; y--)
518 for (x = LEVWIDTH; x >= 0; x--) {
519 if (checkset (level, x, y) == 0) {
528 init_bonus_dots (lev_t * level)
530 unsigned int x = 0, y = 0;
531 top_left (level, &x, &y);
532 bonus_dots[TOP_LEFT].x = x;
533 bonus_dots[TOP_LEFT].y = y;
534 bonus_dots[TOP_LEFT].eaten = False;
535 top_right (level, &x, &y);
536 bonus_dots[TOP_RIGHT].x = x;
537 bonus_dots[TOP_RIGHT].y = y;
538 bonus_dots[TOP_RIGHT].eaten = False;
539 bottom_left (level, &x, &y);
540 bonus_dots[BOTTOM_LEFT].x = x;
541 bonus_dots[BOTTOM_LEFT].y = y;
542 bonus_dots[BOTTOM_LEFT].eaten = False;
543 bottom_right (level, &x, &y);
544 bonus_dots[BOTTOM_RIGHT].x = x;
545 bonus_dots[BOTTOM_RIGHT].y = y;
546 bonus_dots[BOTTOM_RIGHT].eaten = False;
550 is_bonus_dot (int x, int y, int *idx)
554 for (i = 0; i <= NUM_BONUS_DOTS; i++) {
555 /* fprintf(stderr,"is bonus: passed x (%d, %d) bonus (%d, %d)\n",x,y,bonus_dots[i].x, bonus_dots[i].y); */
556 if (x == bonus_dots[i].x && y == bonus_dots[i].y) {
566 check_bonus_idx (int idx)
568 assert (0 <= idx && idx <= NUM_BONUS_DOTS);
572 bonus_dot_eaten (int idx)
574 check_bonus_idx (idx);
575 return bonus_dots[idx].eaten;
579 eat_bonus_dot (int idx)
581 check_bonus_idx (idx);
582 bonus_dots[idx].eaten = True;
586 bonus_dot_pos (int idx, int *x, int *y)
588 check_bonus_idx (idx);
589 *x = bonus_dots[idx].x;
590 *y = bonus_dots[idx].y;
593 /* Changes a level from a simple wall/nowall to a wall with rounded corners
594 and such. Stupid algorithm, could be done better! */
596 frmtlevel (lev_t * level)
601 register unsigned poscond;
602 register unsigned poscond2;
604 clearlevel (&frmtlev);
605 init_bonus_dots (level);
606 for (y = 0; y < LEVHEIGHT; y++)
607 for (x = 0; x < LEVWIDTH; x++) {
609 if (checkset (level, x, y) == 0) {
610 if (is_bonus_dot (x, y, &idx)) {
611 frmtlev[y][x] = BLOCK_DOT_BONUS;
614 frmtlev[y][x] = BLOCK_DOT_2;
619 if ((*level)[y][x] == BLOCK_GHOST_ONLY) {
620 frmtlev[y][x] = BLOCK_GHOST_ONLY;
625 (checksetout (level, x - 1, y - 1) != 0 ?
627 (checksetout (level, x + 1, y - 1) != 0 ?
629 (checksetout (level, x + 1, y + 1) != 0 ?
631 (checksetout (level, x - 1, y + 1) != 0 ? 0x08U : 0U);
634 (checksetout (level, x - 1, y) != 0 ?
636 (checksetout (level, x, y - 1) != 0 ?
638 (checksetout (level, x + 1, y) != 0 ?
640 (checksetout (level, x, y + 1) != 0 ? 0x08U : 0);
643 /* completely filled */
644 case 0x01U | 0x02U | 0x04U | 0x08U:
645 frmtlev[y][x] = BLOCK_EMPTY;
648 /* left to top corner */
650 frmtlev[y][x] = BLOCK_WALL_TL;
652 /* top to right corner */
654 frmtlev[y][x] = BLOCK_WALL_TR;
656 /* right to bottom corner */
658 frmtlev[y][x] = BLOCK_WALL_BR;
660 /* bottom to left corner */
662 frmtlev[y][x] = BLOCK_WALL_BL;
668 case 0x01U | 0x04U | 0x08U:
669 case 0x01U | 0x04U | 0x02U:
670 frmtlev[y][x] = BLOCK_WALL_HO;
673 case 0x02U | 0x08U | 0x01U:
674 case 0x02U | 0x08U | 0x04U:
675 frmtlev[y][x] = BLOCK_WALL_VE;
678 frmtlev[y][x] = BLOCK_WALL_TL;
681 frmtlev[y][x] = BLOCK_WALL_TR;
684 frmtlev[y][x] = BLOCK_WALL_BR;
687 frmtlev[y][x] = BLOCK_WALL_BL;
691 case 0x02U | 0x04U | 0x08U:
692 frmtlev[y][x] = BLOCK_WALL_TL;
694 case 0x01U | 0x04U | 0x08U:
695 frmtlev[y][x] = BLOCK_WALL_TR;
697 case 0x01U | 0x02U | 0x08U:
698 frmtlev[y][x] = BLOCK_WALL_BR;
700 case 0x01U | 0x02U | 0x04U:
701 frmtlev[y][x] = BLOCK_WALL_BL;
704 frmtlev[y][x] = BLOCK_EMPTY;
706 (void) memcpy ((lev_t *) level, (lev_t *) & frmtlev, sizeof (lev_t));
709 /* Counts the number of dots in the level, and returns that number. */
711 countdots (ModeInfo * mi)
713 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
714 unsigned i, count = 0;
716 for (i = 0; i < LEVWIDTH * LEVHEIGHT; i++)
717 if (pp->level[i] == BLOCK_DOT_2 || pp->level[i] == BLOCK_DOT_BONUS)
723 /* Creates a new level, and places that in the pacmangamestruct. */
725 createnewlevel (ModeInfo * mi)
727 pacmangamestruct *pp = &pacmangames[MI_SCREEN (mi)];
729 unsigned dirvec[1] = { GO_UP };
730 unsigned ret = 0, i = 0;
732 if ((level = (lev_t *) calloc (1, sizeof (lev_t))) == NULL)
735 if (NRAND (2) == 0) {
739 createjail (level, JAILWIDTH, JAILHEIGHT);
740 if ((ret = nextstep (level, LEVWIDTH / 2 - 1,
741 LEVHEIGHT / 2 - JAILHEIGHT / 2 - 3,
743 (void) free ((void *) level);
746 } while (ret * 100 < (LEVWIDTH * LEVHEIGHT * MINDOTPERC));
750 finishjail (level, JAILWIDTH, JAILHEIGHT);
753 (void) memcpy (level, stdlevel, sizeof (lev_t));
757 copylevel (pp->level, level);
758 pp->dotsleft = countdots (mi);
760 (void) free ((void *) level);
764 /* Checks if a position is allowable for ghosts/pacs to enter. */
766 check_pos (pacmangamestruct * pp, int y, int x, int ghostpass)
768 if ((pp->level[y * LEVWIDTH + x] == BLOCK_DOT_2) ||
769 (pp->level[y * LEVWIDTH + x] == BLOCK_EMPTY) ||
770 (pp->level[y * LEVWIDTH + x] == BLOCK_DOT_BONUS) ||
771 ((pp->level[y * LEVWIDTH + x] == BLOCK_GHOST_ONLY) && ghostpass)) {
777 /* Checks if there is a dot on the specified position in the level. */
779 check_dot (pacmangamestruct * pp, unsigned int x, unsigned int y)
781 if (x >= LEVWIDTH || y >= LEVHEIGHT)
783 if (pp->level[y * LEVWIDTH + x] == BLOCK_DOT_2)