2 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
4 * Permission to use, copy, modify, and distribute this software and its
5 * documentation for any purpose and without fee is hereby granted,
6 * provided that the above copyright notice appear in all copies and that
7 * both that copyright notice and this permission notice appear in
8 * supporting documentation.
10 * This file is provided AS IS with no warranties of any kind. The author
11 * shall have no liability with respect to the infringement of copyrights,
12 * trade secrets or any patents by this file or any part thereof. In no
13 * event will the author be liable for any lost revenue or profits or
14 * other special, indirect and consequential damages.
24 #define BLOCK_EMPTY ' '
25 #define BLOCK_DOT_1 '`'
26 #define BLOCK_DOT_2 '.'
27 #define BLOCK_WALL '#'
28 #define BLOCK_GHOST_ONLY '='
29 #define BLOCK_WALL_TL '\''
30 #define BLOCK_WALL_TR '`'
31 #define BLOCK_WALL_BR ','
32 #define BLOCK_WALL_BL '_'
33 #define BLOCK_WALL_HO '-'
34 #define BLOCK_WALL_VE '|'
36 /* This is more or less the standard pacman level (without the left-right
38 static const lev_t stdlevel = {
39 "########################################",
40 "########################################",
41 "#######````````````##````````````#######",
42 "#######`####`#####`##`#####`####`#######",
43 "#######`####`#####`##`#####`####`#######",
44 "#######`####`#####`##`#####`####`#######",
45 "#######``````````````````````````#######",
46 "#######`####`##`########`##`####`#######",
47 "#######`####`##`########`##`####`#######",
48 "#######``````##````##````##``````#######",
49 "############`#####`##`#####`############",
50 "############`#####`##`#####`############",
51 "############`##``````````##`############",
52 "############`##`###==###`##`############",
53 "############`##`########`##`############",
54 "############````########````############",
55 "############`##`########`##`############",
56 "############`##`########`##`############",
57 "############`##``````````##`############",
58 "############`##`########`##`############",
59 "############`##`########`##`############",
60 "#######````````````##````````````#######",
61 "#######`####`#####`##`#####`####`#######",
62 "#######`####`#####`##`#####`####`#######",
63 "#######```##````````````````##```#######",
64 "#########`##`##`########`##`##`#########",
65 "#########`##`##`########`##`##`#########",
66 "#######``````##````##````##``````#######",
67 "#######`##########`##`##########`#######",
68 "#######`##########`##`##########`#######",
69 "#######``````````````````````````#######",
70 "########################################"};
74 #define TILES_COUNT 11U
77 #define GO_LEFT 0x0002U
78 #define GO_RIGHT 0x0004U
79 #define GO_DOWN 0x0008U
81 /* This are tiles which can be places to create a level. */
83 char block[TILEWIDTH * TILEHEIGHT + 1];
87 } tiles[TILES_COUNT] = {
89 * ' ' == dont care == BLOCK_EMPTY
90 * '#' == set wall, and not clear == BLOCK_WALL
91 * '`' == clear == BLOCK_DOT_1
92 * middle position is always set as cleardef
99 { GO_LEFT, GO_RIGHT, 0, 0 }, 2,
100 (unsigned) (1 << 0 | 1 << 6 | 1 << 8 | 1 << 10) },
106 { GO_UP, GO_DOWN, 0, 0 }, 2,
107 (unsigned) (1 << 1 | 1 << 7 | 1 << 9 | 1 << 10) },
113 { GO_UP, GO_RIGHT, 0, 0 }, 2,
114 (unsigned) (1 << 2 | 1 << 6 | 1 << 7 | 1 << 10) },
120 { GO_RIGHT, GO_DOWN, 0, 0 }, 2,
121 (unsigned) (1 << 3 | 1 << 7 | 1 << 8 | 1 << 10) },
127 { GO_LEFT, GO_DOWN, 0, 0 }, 2,
128 (unsigned) (1 << 4 | 1 << 8 | 1 << 9 | 1 << 10) },
134 { GO_LEFT, GO_UP, 0, 0 }, 2,
135 (unsigned) (1 << 5 | 1 << 6 | 1 << 9 | 1 << 10) },
141 { GO_LEFT, GO_UP, GO_RIGHT, 0 }, 3,
148 { GO_UP, GO_RIGHT, GO_DOWN, 0}, 3,
149 (unsigned) (1 << 7) },
155 { GO_LEFT, GO_RIGHT, GO_DOWN, 0}, 3,
156 (unsigned) (1 << 8) },
162 { GO_UP, GO_DOWN, GO_LEFT, 0 }, 3,
163 (unsigned) (1 << 9) },
169 { GO_UP, GO_DOWN, GO_LEFT, GO_RIGHT }, 4,
170 (unsigned) (1 << 10) }
173 /* probability array for each of the tiles */
174 #define MAXTILEPROB 22
175 static const unsigned tileprob[MAXTILEPROB] =
176 { 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10 };
178 /* Sets a block in the level to a certain state. */
180 setblockto(lev_t *level, const unsigned x, const unsigned y,
183 if (!(x < LEVWIDTH && y < LEVHEIGHT)) return;
187 /* check if a block is set */
189 checkset(lev_t *level, const unsigned x, const unsigned y)
191 if (!(x < LEVWIDTH && y < LEVHEIGHT) ||
192 (*level)[y][x] == BLOCK_WALL ||
193 (*level)[y][x] == BLOCK_GHOST_ONLY)
198 /* Check if a block is not set */
200 checksetout(lev_t *level, const unsigned x, const unsigned y)
202 if (!(x < LEVWIDTH && y < LEVHEIGHT) ||
203 checkset(level, x, y) != 0)
209 /* Check if a block cannot be set */
211 checkunsetdef(lev_t *level, const unsigned x, const unsigned y)
213 if (!(x < LEVWIDTH && y < LEVHEIGHT))
215 if ((*level)[y][x] == BLOCK_DOT_1) return True;
219 /* Initializes a level to empty state. */
221 clearlevel(lev_t *level)
225 for (y = 0; y < LEVHEIGHT ; y++)
226 for (x = 0 ; x < LEVWIDTH ; x++)
227 (*level)[y][x] = BLOCK_EMPTY;
230 /* Changes a level from the level creation structure ((array to array) to
233 copylevel(char *dest, lev_t *level)
237 for (y = 0; y < LEVHEIGHT ; y++)
238 for (x = 0; x < LEVWIDTH ; x++)
239 dest[y * LEVWIDTH + x] = (*level)[y][x];
242 /* Creates a jail to work around, so we can finish it later. */
244 createjail(lev_t *level, const unsigned width,
245 const unsigned height)
247 unsigned x, y, xstart, xend, ystart, yend;
249 if (LEVWIDTH < width || LEVHEIGHT < height) return;
251 xstart = LEVWIDTH/2 - width/2;
252 xend = LEVWIDTH/2 + width/2;
253 ystart = LEVHEIGHT/2 - height/2;
254 yend = LEVHEIGHT/2 + height/2;
256 for (y = ystart - 1; y < yend + 1; y++)
257 for (x = xstart - 1; x < xend + 1; x++)
258 setblockto(level, x, y, BLOCK_DOT_1);
260 for (y = ystart; y < yend; y++)
261 for (x = xstart; x < xend; x++)
262 setblockto(level, x, y, BLOCK_WALL);
265 /* Finishes a jail so it is empty and the ghostpass is on top. */
267 finishjail(lev_t *level, const unsigned width,
268 const unsigned height)
270 unsigned x, y, xstart, xend, ystart, yend;
272 xstart = LEVWIDTH/2 - width/2;
273 xend = LEVWIDTH/2 + width/2;
274 ystart = LEVHEIGHT/2 - height/2;
275 yend = LEVHEIGHT/2 + height/2;
277 for (y = ystart + 1; y < yend - 1 ; y++)
278 for (x = xstart + 1; x < xend - 1; x++)
279 setblockto(level, x, y, BLOCK_EMPTY);
281 for (x = xstart - 1; x < xend + 1; x++) {
282 setblockto(level, x, ystart - 1, BLOCK_EMPTY);
283 setblockto(level, x, yend, BLOCK_EMPTY);
286 for (y = ystart - 1; y < yend + 1; y++) {
287 setblockto(level, xstart - 1, y, BLOCK_EMPTY);
288 setblockto(level, xend, y, BLOCK_EMPTY);
291 setblockto(level, xstart + width/2 - 1, ystart, BLOCK_GHOST_ONLY);
292 setblockto(level, xstart + width/2, ystart, BLOCK_GHOST_ONLY);
295 /* Tries to set a block at a certain position. Returns true if possible,
296 and leaves level in new state (plus block), or False if not possible,
297 and leaves level in unpredictable state. */
299 tryset(lev_t *level, const unsigned xpos, const unsigned ypos,
302 register unsigned x, y;
303 register char locchar;
307 if ((*level)[ypos][xpos] == BLOCK_DOT_1) return False;
312 for (y = 0 ; y < TILEHEIGHT ; y++)
313 for (x = 0 ; x < TILEWIDTH ; x++) {
314 locchar = block[y * TILEWIDTH + x];
315 if (locchar == BLOCK_EMPTY)
317 if (locchar == BLOCK_DOT_1 &&
319 xstart + x >= LEVWIDTH - 1 ||
321 ystart + y >= LEVHEIGHT - 1 ||
322 checkset(level, xstart + x, ystart + y) != 0))
324 else if (locchar == BLOCK_WALL &&
326 xstart + x < LEVWIDTH &&
328 ystart + y < LEVHEIGHT - 1) &&
330 (unsigned)(xstart + x),
331 (unsigned)(ystart + y)) != 0)
335 /* and set the block in place */
337 xend = (xstart + TILEWIDTH < LEVWIDTH - 1) ?
338 TILEWIDTH : LEVWIDTH - xstart - 2;
339 yend = (ystart + TILEHEIGHT < LEVHEIGHT - 1) ?
340 TILEHEIGHT : LEVHEIGHT - ystart - 2;
342 for (y = (ystart < 1) ? (unsigned)(1 - ystart) : 0U ;
344 for (x = (xstart < 1) ?
345 (unsigned)(1 - xstart) : 0U ;
347 locchar = block[y * TILEWIDTH + x];
348 if ((locchar == BLOCK_WALL) &&
349 ((*level)[ystart + y][xstart + x] == BLOCK_EMPTY)) {
350 (*level)[ystart + y][xstart + x] = BLOCK_WALL;
352 [LEVWIDTH - (xstart + x + 1)] =
357 (*level)[ypos][xpos] = BLOCK_DOT_1;
358 (*level)[ypos][LEVWIDTH-xpos-1] = BLOCK_DOT_1;
363 /* Tries certain combinations of blocks in the level recursively. */
365 nextstep(lev_t *level, const unsigned x, const unsigned y,
366 unsigned dirvec[], unsigned ndirs)
368 unsigned dirpos, curdir, inc = 0;
377 dirpos = NRAND(ndirs);
378 curdir = dirvec[dirpos];
379 /* nope, no bufoverflow, but ndirs - 1 + 1 */
380 dirvec[dirpos] = dirvec[ndirs];
381 dirvec[ndirs] = curdir;
387 (ret = creatlevelblock(level, x, y - 1))
392 if (x > LEVWIDTH - 2 ||
393 (ret = creatlevelblock(level, x + 1, y))
398 if (y > LEVHEIGHT - 2 ||
399 (ret = creatlevelblock(level, x, y + 1))
405 (ret = creatlevelblock(level, x - 1, y))
410 inc += (unsigned)ret;
412 if (inc == 0) inc = 1;
417 creatlevelblock(lev_t *level, const unsigned x, const unsigned y)
419 unsigned tried = GETNB(TILES_COUNT);
424 if (!((x < LEVWIDTH) && (y < LEVHEIGHT)))
427 if (checkunsetdef(level, x, y) != 0)
433 tried &= ~(1<<4 | 1<<5 | 1<<6 | 1<<8 | 1<<9 | 1<<10);
434 else if (x == LEVWIDTH-1)
436 else if (x == LEVWIDTH-2)
437 tried &= ~(1<<2 | 1<<3 | 1<<6 | 1<<7 | 1<<8 | 1<<10);
440 tried &= ~(1<<2 | 1<<5 | 1<<6 | 1<<7 | 1<<9 | 1<<10);
443 else if (y == LEVHEIGHT-1)
445 else if (y == LEVHEIGHT-2)
446 tried &= ~(1<<3 | 1<<4 | 1<<7 | 1<<8 | 1<<9 | 1<<10);
448 /* make a copy of the current level, so we can go back on the stack */
449 (void) memcpy(&savedlev, level, sizeof(lev_t));
451 /* while there are still some blocks left to try */
452 while (tried != 0x00) {
453 tilenr = tileprob[NRAND(MAXTILEPROB)];
455 if (!TESTNB(tried, tilenr))
458 if (tryset(level, x, y, tiles[tilenr].block) != 0) {
459 if ((ret = nextstep(level, x, y, tiles[tilenr].dirvec,
460 tiles[tilenr].ndirs)) != 0) {
463 (void) memcpy(level, &savedlev, sizeof(lev_t));
465 tried &= ~(tiles[tilenr].simular_to);
470 /* Fills up all empty space so there is wall everywhere. */
472 filllevel(lev_t *level)
476 for (y = 0; y < LEVHEIGHT; y++)
477 for (x = 0; x < LEVWIDTH; x++)
478 if ((*level)[y][x] == BLOCK_EMPTY)
479 (*level)[y][x] = BLOCK_WALL;
482 /* Changes a level from a simple wall/nowall to a wall with rounded corners
483 and such. Stupid algorithm, could be done better! */
485 frmtlevel(lev_t *level)
488 register unsigned x, y;
489 register unsigned poscond;
490 register unsigned poscond2;
492 clearlevel(&frmtlev);
494 for (y = 0; y < LEVHEIGHT; y++)
495 for (x = 0; x < LEVWIDTH; x++) {
497 if (checkset(level, x, y) == 0) {
498 frmtlev[y][x] = BLOCK_DOT_2;
502 if ((*level)[y][x] == BLOCK_GHOST_ONLY) {
503 frmtlev[y][x] = BLOCK_GHOST_ONLY;
508 (checksetout(level, x - 1, y - 1) != 0 ?
510 (checksetout(level, x + 1, y - 1) != 0 ?
512 (checksetout(level, x + 1, y + 1) != 0 ?
514 (checksetout(level, x - 1, y + 1) != 0 ?
518 (checksetout(level, x - 1, y) != 0 ?
520 (checksetout(level, x, y - 1) != 0 ?
522 (checksetout(level, x + 1, y) != 0 ?
524 (checksetout(level, x, y + 1) != 0 ?
528 /* completely filled */
529 case 0x01U | 0x02U | 0x04U | 0x08U:
530 frmtlev[y][x] = BLOCK_EMPTY; continue;
532 /* left to top corner */
534 frmtlev[y][x] = BLOCK_WALL_TL; continue;
535 /* top to right corner */
537 frmtlev[y][x] = BLOCK_WALL_TR; continue;
538 /* right to bottom corner */
540 frmtlev[y][x] = BLOCK_WALL_BR; continue;
541 /* bottom to left corner */
543 frmtlev[y][x] = BLOCK_WALL_BL; continue;
548 case 0x01U | 0x04U | 0x08U:
549 case 0x01U | 0x04U | 0x02U:
550 frmtlev[y][x] = BLOCK_WALL_HO; continue;
552 case 0x02U | 0x08U | 0x01U:
553 case 0x02U | 0x08U | 0x04U:
554 frmtlev[y][x] = BLOCK_WALL_VE; continue;
556 frmtlev[y][x] = BLOCK_WALL_TL; continue;
558 frmtlev[y][x] = BLOCK_WALL_TR; continue;
560 frmtlev[y][x] = BLOCK_WALL_BR; continue;
562 frmtlev[y][x] = BLOCK_WALL_BL; continue;
565 case 0x02U | 0x04U | 0x08U:
566 frmtlev[y][x] = BLOCK_WALL_TL; continue;
567 case 0x01U | 0x04U | 0x08U:
568 frmtlev[y][x] = BLOCK_WALL_TR; continue;
569 case 0x01U | 0x02U | 0x08U:
570 frmtlev[y][x] = BLOCK_WALL_BR; continue;
571 case 0x01U | 0x02U | 0x04U:
572 frmtlev[y][x] = BLOCK_WALL_BL; continue;
574 frmtlev[y][x] = BLOCK_EMPTY;
576 (void) memcpy((lev_t *)level, (lev_t *)&frmtlev, sizeof(lev_t));
579 /* Counts the number of dots in the level, and returns that number. */
581 countdots(ModeInfo * mi)
583 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
584 unsigned i, count = 0;
586 for (i = 0 ; i < LEVWIDTH*LEVHEIGHT ; i++)
587 if (pp->level[i] == BLOCK_DOT_2) count++;
592 /* Creates a new level, and places that in the pacmangamestruct. */
594 createnewlevel(ModeInfo * mi)
596 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
598 unsigned dirvec[1] = { GO_UP };
599 unsigned ret = 0, i = 0;
601 if ((level = (lev_t *)calloc(1, sizeof(lev_t))) == NULL)
608 createjail(level, JAILWIDTH, JAILHEIGHT);
609 if ((ret = nextstep(level, LEVWIDTH/2 - 1,
610 LEVHEIGHT/2 - JAILHEIGHT/2 - 3,
612 (void) free((void *) level);
615 } while (ret * 100 < (LEVWIDTH * LEVHEIGHT * MINDOTPERC));
619 finishjail(level, JAILWIDTH, JAILHEIGHT);
621 (void) memcpy(level, stdlevel, sizeof(lev_t));
625 copylevel(pp->level, level);
626 pp->dotsleft = countdots(mi);
628 (void) free((void *) level);
632 /* Checks if a position is allowable for ghosts/pacs to enter. */
634 check_pos(pacmangamestruct *pp, int y, int x, int ghostpass)
636 if ((pp->level[y*LEVWIDTH + x] == BLOCK_DOT_2) ||
637 (pp->level[y*LEVWIDTH + x] == BLOCK_EMPTY) ||
638 ((pp->level[y*LEVWIDTH + x] == BLOCK_GHOST_ONLY) && ghostpass)) {
644 /* Checks if there is a dot on the specified position in the level. */
646 check_dot(pacmangamestruct *pp, unsigned int x, unsigned int y)
648 if (x >= LEVWIDTH || y >= LEVHEIGHT) return 0;
649 if (pp->level[y * LEVWIDTH + x] == BLOCK_DOT_2) return 1;