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.
18 #include "pacman_level.h"
20 #define MI_SCREEN(MI) (0)
29 #define BLOCK_EMPTY ' '
30 #define BLOCK_DOT_1 '`'
31 #define BLOCK_DOT_2 '.'
32 #define BLOCK_WALL '#'
33 #define BLOCK_GHOST_ONLY '='
34 #define BLOCK_WALL_TL '\''
35 #define BLOCK_WALL_TR '`'
36 #define BLOCK_WALL_BR ','
37 #define BLOCK_WALL_BL '_'
38 #define BLOCK_WALL_HO '-'
39 #define BLOCK_WALL_VE '|'
41 /* This is more or less the standard pacman level (without the left-right
43 static const lev_t stdlevel = {
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 "#########`##`##`########`##`##`#########",
71 "#######``````##````##````##``````#######",
72 "#######`##########`##`##########`#######",
73 "#######`##########`##`##########`#######",
74 "#######``````````````````````````#######",
75 "########################################"};
79 #define TILES_COUNT 11U
82 #define GO_LEFT 0x0002U
83 #define GO_RIGHT 0x0004U
84 #define GO_DOWN 0x0008U
86 /* This are tiles which can be places to create a level. */
88 char block[TILEWIDTH * TILEHEIGHT + 1];
92 } tiles[TILES_COUNT] = {
94 * ' ' == dont care == BLOCK_EMPTY
95 * '#' == set wall, and not clear == BLOCK_WALL
96 * '`' == clear == BLOCK_DOT_1
97 * middle position is always set as cleardef
104 { GO_LEFT, GO_RIGHT, 0, 0 }, 2,
105 (unsigned) (1 << 0 | 1 << 6 | 1 << 8 | 1 << 10) },
111 { GO_UP, GO_DOWN, 0, 0 }, 2,
112 (unsigned) (1 << 1 | 1 << 7 | 1 << 9 | 1 << 10) },
118 { GO_UP, GO_RIGHT, 0, 0 }, 2,
119 (unsigned) (1 << 2 | 1 << 6 | 1 << 7 | 1 << 10) },
125 { GO_RIGHT, GO_DOWN, 0, 0 }, 2,
126 (unsigned) (1 << 3 | 1 << 7 | 1 << 8 | 1 << 10) },
132 { GO_LEFT, GO_DOWN, 0, 0 }, 2,
133 (unsigned) (1 << 4 | 1 << 8 | 1 << 9 | 1 << 10) },
139 { GO_LEFT, GO_UP, 0, 0 }, 2,
140 (unsigned) (1 << 5 | 1 << 6 | 1 << 9 | 1 << 10) },
146 { GO_LEFT, GO_UP, GO_RIGHT, 0 }, 3,
153 { GO_UP, GO_RIGHT, GO_DOWN, 0}, 3,
154 (unsigned) (1 << 7) },
160 { GO_LEFT, GO_RIGHT, GO_DOWN, 0}, 3,
161 (unsigned) (1 << 8) },
167 { GO_UP, GO_DOWN, GO_LEFT, 0 }, 3,
168 (unsigned) (1 << 9) },
174 { GO_UP, GO_DOWN, GO_LEFT, GO_RIGHT }, 4,
175 (unsigned) (1 << 10) }
178 /* probability array for each of the tiles */
179 #define MAXTILEPROB 22
180 static const unsigned tileprob[MAXTILEPROB] =
181 { 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10 };
184 static int creatlevelblock(lev_t *level, const unsigned x,
189 /* Sets a block in the level to a certain state. */
191 setblockto(lev_t *level, const unsigned x, const unsigned y,
194 if (!(x < LEVWIDTH && y < LEVHEIGHT)) return;
198 /* check if a block is set */
200 checkset(lev_t *level, const unsigned x, const unsigned y)
202 if (!(x < LEVWIDTH && y < LEVHEIGHT) ||
203 (*level)[y][x] == BLOCK_WALL ||
204 (*level)[y][x] == BLOCK_GHOST_ONLY)
209 /* Check if a block is not set */
211 checksetout(lev_t *level, const unsigned x, const unsigned y)
213 if (!(x < LEVWIDTH && y < LEVHEIGHT) ||
214 checkset(level, x, y) != 0)
220 /* Check if a block cannot be set */
222 checkunsetdef(lev_t *level, const unsigned x, const unsigned y)
224 if (!(x < LEVWIDTH && y < LEVHEIGHT))
226 if ((*level)[y][x] == BLOCK_DOT_1) return True;
230 /* Initializes a level to empty state. */
232 clearlevel(lev_t *level)
236 for (y = 0; y < LEVHEIGHT ; y++)
237 for (x = 0 ; x < LEVWIDTH ; x++)
238 (*level)[y][x] = BLOCK_EMPTY;
241 /* Changes a level from the level creation structure ((array to array) to
244 copylevel(char *dest, lev_t *level)
248 for (y = 0; y < LEVHEIGHT ; y++)
249 for (x = 0; x < LEVWIDTH ; x++)
250 dest[y * LEVWIDTH + x] = (*level)[y][x];
253 /* Creates a jail to work around, so we can finish it later. */
255 createjail(lev_t *level, const unsigned width,
256 const unsigned height)
258 unsigned x, y, xstart, xend, ystart, yend;
260 if (LEVWIDTH < width || LEVHEIGHT < height) return;
262 xstart = LEVWIDTH/2 - width/2;
263 xend = LEVWIDTH/2 + width/2;
264 ystart = LEVHEIGHT/2 - height/2;
265 yend = LEVHEIGHT/2 + height/2;
267 for (y = ystart - 1; y < yend + 1; y++)
268 for (x = xstart - 1; x < xend + 1; x++)
269 setblockto(level, x, y, BLOCK_DOT_1);
271 for (y = ystart; y < yend; y++)
272 for (x = xstart; x < xend; x++)
273 setblockto(level, x, y, BLOCK_WALL);
276 /* Finishes a jail so it is empty and the ghostpass is on top. */
278 finishjail(lev_t *level, const unsigned width,
279 const unsigned height)
281 unsigned x, y, xstart, xend, ystart, yend;
283 xstart = LEVWIDTH/2 - width/2;
284 xend = LEVWIDTH/2 + width/2;
285 ystart = LEVHEIGHT/2 - height/2;
286 yend = LEVHEIGHT/2 + height/2;
288 for (y = ystart + 1; y < yend - 1 ; y++)
289 for (x = xstart + 1; x < xend - 1; x++)
290 setblockto(level, x, y, BLOCK_EMPTY);
292 for (x = xstart - 1; x < xend + 1; x++) {
293 setblockto(level, x, ystart - 1, BLOCK_EMPTY);
294 setblockto(level, x, yend, BLOCK_EMPTY);
297 for (y = ystart - 1; y < yend + 1; y++) {
298 setblockto(level, xstart - 1, y, BLOCK_EMPTY);
299 setblockto(level, xend, y, BLOCK_EMPTY);
302 setblockto(level, xstart + width/2 - 1, ystart, BLOCK_GHOST_ONLY);
303 setblockto(level, xstart + width/2, ystart, BLOCK_GHOST_ONLY);
306 /* Tries to set a block at a certain position. Returns true if possible,
307 and leaves level in new state (plus block), or False if not possible,
308 and leaves level in unpredictable state. */
310 tryset(lev_t *level, const unsigned xpos, const unsigned ypos,
313 register unsigned x, y;
314 register char locchar;
318 if ((*level)[ypos][xpos] == BLOCK_DOT_1) return False;
323 for (y = 0 ; y < TILEHEIGHT ; y++)
324 for (x = 0 ; x < TILEWIDTH ; x++) {
325 locchar = block[y * TILEWIDTH + x];
326 if (locchar == BLOCK_EMPTY)
328 if (locchar == BLOCK_DOT_1 &&
330 xstart + x >= LEVWIDTH - 1 ||
332 ystart + y >= LEVHEIGHT - 1 ||
333 checkset(level, xstart + x, ystart + y) != 0))
335 else if (locchar == BLOCK_WALL &&
337 xstart + x < LEVWIDTH &&
339 ystart + y < LEVHEIGHT - 1) &&
341 (unsigned)(xstart + x),
342 (unsigned)(ystart + y)) != 0)
346 /* and set the block in place */
348 xend = (xstart + TILEWIDTH < LEVWIDTH - 1) ?
349 TILEWIDTH : LEVWIDTH - xstart - 2;
350 yend = (ystart + TILEHEIGHT < LEVHEIGHT - 1) ?
351 TILEHEIGHT : LEVHEIGHT - ystart - 2;
353 for (y = (ystart < 1) ? (unsigned)(1 - ystart) : 0U ;
355 for (x = (xstart < 1) ?
356 (unsigned)(1 - xstart) : 0U ;
358 locchar = block[y * TILEWIDTH + x];
359 if ((locchar == BLOCK_WALL) &&
360 ((*level)[ystart + y][xstart + x] == BLOCK_EMPTY)) {
361 (*level)[ystart + y][xstart + x] = BLOCK_WALL;
363 [LEVWIDTH - (xstart + x + 1)] =
368 (*level)[ypos][xpos] = BLOCK_DOT_1;
369 (*level)[ypos][LEVWIDTH-xpos-1] = BLOCK_DOT_1;
374 /* Tries certain combinations of blocks in the level recursively. */
376 nextstep(lev_t *level, const unsigned x, const unsigned y,
377 unsigned dirvec[], unsigned ndirs)
379 unsigned dirpos, curdir, inc = 0;
388 dirpos = NRAND(ndirs);
389 curdir = dirvec[dirpos];
390 /* nope, no bufoverflow, but ndirs - 1 + 1 */
391 dirvec[dirpos] = dirvec[ndirs];
392 dirvec[ndirs] = curdir;
398 (ret = creatlevelblock(level, x, y - 1))
403 if (x > LEVWIDTH - 2 ||
404 (ret = creatlevelblock(level, x + 1, y))
409 if (y > LEVHEIGHT - 2 ||
410 (ret = creatlevelblock(level, x, y + 1))
416 (ret = creatlevelblock(level, x - 1, y))
421 inc += (unsigned)ret;
423 if (inc == 0) inc = 1;
428 creatlevelblock(lev_t *level, const unsigned x, const unsigned y)
430 unsigned tried = GETNB(TILES_COUNT);
435 if (!((x < LEVWIDTH) && (y < LEVHEIGHT)))
438 if (checkunsetdef(level, x, y) != 0)
444 tried &= ~(1<<4 | 1<<5 | 1<<6 | 1<<8 | 1<<9 | 1<<10);
445 else if (x == LEVWIDTH-1)
447 else if (x == LEVWIDTH-2)
448 tried &= ~(1<<2 | 1<<3 | 1<<6 | 1<<7 | 1<<8 | 1<<10);
451 tried &= ~(1<<2 | 1<<5 | 1<<6 | 1<<7 | 1<<9 | 1<<10);
454 else if (y == LEVHEIGHT-1)
456 else if (y == LEVHEIGHT-2)
457 tried &= ~(1<<3 | 1<<4 | 1<<7 | 1<<8 | 1<<9 | 1<<10);
459 /* make a copy of the current level, so we can go back on the stack */
460 (void) memcpy(&savedlev, level, sizeof(lev_t));
462 /* while there are still some blocks left to try */
463 while (tried != 0x00) {
464 tilenr = tileprob[NRAND(MAXTILEPROB)];
466 if (!TESTNB(tried, tilenr))
469 if (tryset(level, x, y, tiles[tilenr].block) != 0) {
470 if ((ret = nextstep(level, x, y, tiles[tilenr].dirvec,
471 tiles[tilenr].ndirs)) != 0) {
474 (void) memcpy(level, &savedlev, sizeof(lev_t));
476 tried &= ~(tiles[tilenr].simular_to);
481 /* Fills up all empty space so there is wall everywhere. */
483 filllevel(lev_t *level)
487 for (y = 0; y < LEVHEIGHT; y++)
488 for (x = 0; x < LEVWIDTH; x++)
489 if ((*level)[y][x] == BLOCK_EMPTY)
490 (*level)[y][x] = BLOCK_WALL;
493 /* Changes a level from a simple wall/nowall to a wall with rounded corners
494 and such. Stupid algorithm, could be done better! */
496 frmtlevel(lev_t *level)
499 register unsigned x, y;
500 register unsigned poscond;
501 register unsigned poscond2;
503 clearlevel(&frmtlev);
505 for (y = 0; y < LEVHEIGHT; y++)
506 for (x = 0; x < LEVWIDTH; x++) {
508 if (checkset(level, x, y) == 0) {
509 frmtlev[y][x] = BLOCK_DOT_2;
513 if ((*level)[y][x] == BLOCK_GHOST_ONLY) {
514 frmtlev[y][x] = BLOCK_GHOST_ONLY;
519 (checksetout(level, x - 1, y - 1) != 0 ?
521 (checksetout(level, x + 1, y - 1) != 0 ?
523 (checksetout(level, x + 1, y + 1) != 0 ?
525 (checksetout(level, x - 1, y + 1) != 0 ?
529 (checksetout(level, x - 1, y) != 0 ?
531 (checksetout(level, x, y - 1) != 0 ?
533 (checksetout(level, x + 1, y) != 0 ?
535 (checksetout(level, x, y + 1) != 0 ?
539 /* completely filled */
540 case 0x01U | 0x02U | 0x04U | 0x08U:
541 frmtlev[y][x] = BLOCK_EMPTY; continue;
543 /* left to top corner */
545 frmtlev[y][x] = BLOCK_WALL_TL; continue;
546 /* top to right corner */
548 frmtlev[y][x] = BLOCK_WALL_TR; continue;
549 /* right to bottom corner */
551 frmtlev[y][x] = BLOCK_WALL_BR; continue;
552 /* bottom to left corner */
554 frmtlev[y][x] = BLOCK_WALL_BL; continue;
559 case 0x01U | 0x04U | 0x08U:
560 case 0x01U | 0x04U | 0x02U:
561 frmtlev[y][x] = BLOCK_WALL_HO; continue;
563 case 0x02U | 0x08U | 0x01U:
564 case 0x02U | 0x08U | 0x04U:
565 frmtlev[y][x] = BLOCK_WALL_VE; continue;
567 frmtlev[y][x] = BLOCK_WALL_TL; continue;
569 frmtlev[y][x] = BLOCK_WALL_TR; continue;
571 frmtlev[y][x] = BLOCK_WALL_BR; continue;
573 frmtlev[y][x] = BLOCK_WALL_BL; continue;
576 case 0x02U | 0x04U | 0x08U:
577 frmtlev[y][x] = BLOCK_WALL_TL; continue;
578 case 0x01U | 0x04U | 0x08U:
579 frmtlev[y][x] = BLOCK_WALL_TR; continue;
580 case 0x01U | 0x02U | 0x08U:
581 frmtlev[y][x] = BLOCK_WALL_BR; continue;
582 case 0x01U | 0x02U | 0x04U:
583 frmtlev[y][x] = BLOCK_WALL_BL; continue;
585 frmtlev[y][x] = BLOCK_EMPTY;
587 (void) memcpy((lev_t *)level, (lev_t *)&frmtlev, sizeof(lev_t));
590 /* Counts the number of dots in the level, and returns that number. */
592 countdots(ModeInfo * mi)
594 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
595 unsigned i, count = 0;
597 for (i = 0 ; i < LEVWIDTH*LEVHEIGHT ; i++)
598 if (pp->level[i] == BLOCK_DOT_2) count++;
603 /* Creates a new level, and places that in the pacmangamestruct. */
605 createnewlevel(ModeInfo * mi)
607 pacmangamestruct *pp = &pacmangames[MI_SCREEN(mi)];
609 unsigned dirvec[1] = { GO_UP };
610 unsigned ret = 0, i = 0;
612 if ((level = (lev_t *)calloc(1, sizeof(lev_t))) == NULL)
619 createjail(level, JAILWIDTH, JAILHEIGHT);
620 if ((ret = nextstep(level, LEVWIDTH/2 - 1,
621 LEVHEIGHT/2 - JAILHEIGHT/2 - 3,
623 (void) free((void *) level);
626 } while (ret * 100 < (LEVWIDTH * LEVHEIGHT * MINDOTPERC));
630 finishjail(level, JAILWIDTH, JAILHEIGHT);
632 (void) memcpy(level, stdlevel, sizeof(lev_t));
636 copylevel(pp->level, level);
637 pp->dotsleft = countdots(mi);
639 (void) free((void *) level);
643 /* Checks if a position is allowable for ghosts/pacs to enter. */
645 check_pos(pacmangamestruct *pp, int y, int x, int ghostpass)
647 if ((pp->level[y*LEVWIDTH + x] == BLOCK_DOT_2) ||
648 (pp->level[y*LEVWIDTH + x] == BLOCK_EMPTY) ||
649 ((pp->level[y*LEVWIDTH + x] == BLOCK_GHOST_ONLY) && ghostpass)) {
655 /* Checks if there is a dot on the specified position in the level. */
657 check_dot(pacmangamestruct *pp, unsigned int x, unsigned int y)
659 if (x >= LEVWIDTH || y >= LEVHEIGHT) return 0;
660 if (pp->level[y * LEVWIDTH + x] == BLOCK_DOT_2) return 1;