1 /*	SCCS Id: @(#)mkmaze.c	3.3	2000/01/03	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include "hack.h"
6 #include "sp_lev.h"
7 #include "lev.h"	/* save & restore info */
8 
9 /* from sp_lev.c, for fixup_special() */
10 extern char *lev_message;
11 extern lev_region *lregions;
12 extern int num_lregions;
13 
14 STATIC_DCL boolean FDECL(iswall,(int,int));
15 STATIC_DCL boolean FDECL(iswall_or_stone,(int,int));
16 STATIC_DCL boolean FDECL(is_solid,(int,int));
17 STATIC_DCL int FDECL(extend_spine, (int [3][3], int, int, int));
18 STATIC_DCL boolean FDECL(okay,(int,int,int));
19 STATIC_DCL void FDECL(maze0xy,(coord *));
20 STATIC_DCL boolean FDECL(put_lregion_here,(XCHAR_P,XCHAR_P,XCHAR_P,
21 	XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,BOOLEAN_P,d_level *));
22 STATIC_DCL void NDECL(fixup_special);
23 STATIC_DCL void FDECL(move, (int *,int *,int));
24 STATIC_DCL void NDECL(setup_waterlevel);
25 STATIC_DCL void NDECL(unsetup_waterlevel);
26 
27 
28 STATIC_OVL boolean
iswall(x,y)29 iswall(x,y)
30 int x,y;
31 {
32 	if (!isok(x,y)) return FALSE;
33 	return (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ)
34 		|| levl[x][y].typ == SDOOR);
35 }
36 
37 STATIC_OVL boolean
iswall_or_stone(x,y)38 iswall_or_stone(x,y)
39     int x,y;
40 {
41     register int type;
42 
43     /* out of bounds = stone */
44     if (!isok(x,y)) return TRUE;
45 
46     type = levl[x][y].typ;
47     return (type == STONE || IS_WALL(type) || IS_DOOR(type) || type == SDOOR);
48 }
49 
50 /* return TRUE if out of bounds, wall or rock */
51 STATIC_OVL boolean
is_solid(x,y)52 is_solid(x,y)
53     int x, y;
54 {
55     return (!isok(x,y) || IS_STWALL(levl[x][y].typ));
56 }
57 
58 
59 /*
60  * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
61  * a wall spine in the (dx,dy) direction.  Return 0 otherwise.
62  *
63  * To extend a wall spine in that direction, first there must be a wall there.
64  * Then, extend a spine unless the current position is surrounded by walls
65  * in the direction given by (dx,dy).  E.g. if 'x' is our location, 'W'
66  * a wall, '.' a room, 'a' anything (we don't care), and our direction is
67  * (0,1) - South or down - then:
68  *
69  *		a a a
70  *		W x W		This would not extend a spine from x down
71  *		W W W		(a corridor of walls is formed).
72  *
73  *		a a a
74  *		W x W		This would extend a spine from x down.
75  *		. W W
76  */
77 STATIC_OVL int
extend_spine(locale,wall_there,dx,dy)78 extend_spine(locale, wall_there, dx, dy)
79     int locale[3][3];
80     int wall_there, dx, dy;
81 {
82     int spine, nx, ny;
83 
84     nx = 1 + dx;
85     ny = 1 + dy;
86 
87     if (wall_there) {	/* wall in that direction */
88 	if (dx) {
89 	    if (locale[ 1][0] && locale[ 1][2] && /* EW are wall/stone */
90 		locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
91 		spine = 0;
92 	    } else {
93 		spine = 1;
94 	    }
95 	} else {	/* dy */
96 	    if (locale[0][ 1] && locale[2][ 1] && /* NS are wall/stone */
97 		locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
98 		spine = 0;
99 	    } else {
100 		spine = 1;
101 	    }
102 	}
103     } else {
104 	spine = 0;
105     }
106 
107     return spine;
108 }
109 
110 
111 /*
112  * Wall cleanup.  This function has two purposes: (1) remove walls that
113  * are totally surrounded by stone - they are redundant.  (2) correct
114  * the types so that they extend and connect to each other.
115  */
116 void
wallification(x1,y1,x2,y2)117 wallification(x1, y1, x2, y2)
118 int x1, y1, x2, y2;
119 {
120 	uchar type;
121 	register int x,y;
122 	struct rm *lev;
123 	int bits;
124 	int locale[3][3];	/* rock or wall status surrounding positions */
125 	/*
126 	 * Value 0 represents a free-standing wall.  It could be anything,
127 	 * so even though this table says VWALL, we actually leave whatever
128 	 * typ was there alone.
129 	 */
130 	static xchar spine_array[16] = {
131 	    VWALL,	HWALL,		HWALL,		HWALL,
132 	    VWALL,	TRCORNER,	TLCORNER,	TDWALL,
133 	    VWALL,	BRCORNER,	BLCORNER,	TUWALL,
134 	    VWALL,	TLWALL,		TRWALL,		CROSSWALL
135 	};
136 
137 	/* sanity check on incoming variables */
138 	if (x1<0 || x2>=COLNO || x1>x2 || y1<0 || y2>=ROWNO || y1>y2)
139 	    panic("wallification: bad bounds (%d,%d) to (%d,%d)",x1,y1,x2,y2);
140 
141 	/* Step 1: change walls surrounded by rock to rock. */
142 	for(x = x1; x <= x2; x++)
143 	    for(y = y1; y <= y2; y++) {
144 		lev = &levl[x][y];
145 		type = lev->typ;
146 		if (IS_WALL(type) && type != DBWALL) {
147 		    if (is_solid(x-1,y-1) &&
148 			is_solid(x-1,y  ) &&
149 			is_solid(x-1,y+1) &&
150 			is_solid(x,  y-1) &&
151 			is_solid(x,  y+1) &&
152 			is_solid(x+1,y-1) &&
153 			is_solid(x+1,y  ) &&
154 			is_solid(x+1,y+1))
155 		    lev->typ = STONE;
156 		}
157 	    }
158 
159 	/*
160 	 * Step 2: set the correct wall type.  We can't combine steps
161 	 * 1 and 2 into a single sweep because we depend on knowing if
162 	 * the surrounding positions are stone.
163 	 */
164 	for(x = x1; x <= x2; x++)
165 	    for(y = y1; y <= y2; y++) {
166 		lev = &levl[x][y];
167 		type = lev->typ;
168 		if ( !(IS_WALL(type) && type != DBWALL)) continue;
169 
170 		/* set the locations TRUE if rock or wall or out of bounds */
171 		locale[0][0] = iswall_or_stone(x-1,y-1);
172 		locale[1][0] = iswall_or_stone(  x,y-1);
173 		locale[2][0] = iswall_or_stone(x+1,y-1);
174 
175 		locale[0][1] = iswall_or_stone(x-1,  y);
176 		locale[2][1] = iswall_or_stone(x+1,  y);
177 
178 		locale[0][2] = iswall_or_stone(x-1,y+1);
179 		locale[1][2] = iswall_or_stone(  x,y+1);
180 		locale[2][2] = iswall_or_stone(x+1,y+1);
181 
182 		/* determine if wall should extend to each direction NSEW */
183 		bits =    (extend_spine(locale, iswall(x,y-1),  0, -1) << 3)
184 			| (extend_spine(locale, iswall(x,y+1),  0,  1) << 2)
185 			| (extend_spine(locale, iswall(x+1,y),  1,  0) << 1)
186 			|  extend_spine(locale, iswall(x-1,y), -1,  0);
187 
188 		/* don't change typ if wall is free-standing */
189 		if (bits) lev->typ = spine_array[bits];
190 	    }
191 }
192 
193 STATIC_OVL boolean
okay(x,y,dir)194 okay(x,y,dir)
195 int x,y;
196 register int dir;
197 {
198 	move(&x,&y,dir);
199 	move(&x,&y,dir);
200 	if(x<3 || y<3 || x>x_maze_max || y>y_maze_max || levl[x][y].typ != 0)
201 		return(FALSE);
202 	return(TRUE);
203 }
204 
205 STATIC_OVL void
maze0xy(cc)206 maze0xy(cc)	/* find random starting point for maze generation */
207 	coord	*cc;
208 {
209 	cc->x = 3 + 2*rn2((x_maze_max>>1) - 1);
210 	cc->y = 3 + 2*rn2((y_maze_max>>1) - 1);
211 	return;
212 }
213 
214 /*
215  * Bad if:
216  *	pos is occupied OR
217  *	pos is inside restricted region (lx,ly,hx,hy) OR
218  *	NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
219  */
220 boolean
bad_location(x,y,lx,ly,hx,hy)221 bad_location(x, y, lx, ly, hx, hy)
222     xchar x, y;
223     xchar lx, ly, hx, hy;
224 {
225     return((boolean)(occupied(x, y) ||
226 	   within_bounded_area(x,y, lx,ly, hx,hy) ||
227 	   !((levl[x][y].typ == CORR && level.flags.is_maze_lev) ||
228 	       levl[x][y].typ == ROOM || levl[x][y].typ == AIR)));
229 }
230 
231 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy) */
232 /* and place something (based on rtype) in that region */
233 void
place_lregion(lx,ly,hx,hy,nlx,nly,nhx,nhy,rtype,lev)234 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
235     xchar	lx, ly, hx, hy;
236     xchar	nlx, nly, nhx, nhy;
237     xchar	rtype;
238     d_level	*lev;
239 {
240     int trycnt;
241     boolean oneshot;
242     xchar x, y;
243 
244     if(!lx) { /* default to whole level */
245 	/*
246 	 * if there are rooms and this a branch, let place_branch choose
247 	 * the branch location (to avoid putting branches in corridors).
248 	 */
249 	if(rtype == LR_BRANCH && nroom) {
250 	    place_branch(Is_branchlev(&u.uz), 0, 0);
251 	    return;
252 	}
253 
254 	lx = 1; hx = COLNO-1;
255 	ly = 1; hy = ROWNO-1;
256     }
257 
258     /* first a probabilistic approach */
259 
260     oneshot = (lx == hx && ly == hy);
261     for(trycnt = 0; trycnt < 100; trycnt ++) {
262 
263 	x = rn1((hx - lx) + 1, lx);
264 	y = rn1((hy - ly) + 1, ly);
265 
266 	if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev))
267 	    return;
268     }
269 
270     /* then a deterministic one */
271 
272     oneshot = TRUE;
273     for (x = lx; x <= hx; x++)
274 	for (y = ly; y <= hy; y++)
275 	    if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev))
276 		return;
277 
278     impossible("Couldn't place lregion type %d!", rtype);
279 }
280 
281 STATIC_OVL boolean
put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)282 put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)
283 xchar x, y;
284 xchar nlx, nly, nhx, nhy;
285 xchar rtype;
286 boolean oneshot;
287 d_level *lev;
288 {
289     if(oneshot) {
290 	/* must make due with the only location possible */
291 	/* avoid failure due to a misplaced trap */
292 	/* it might still fail if there's a dungeon feature here */
293 	struct trap *t = t_at(x,y);
294 	if (t) deltrap(t);
295     }
296     if(bad_location(x, y, nlx, nly, nhx, nhy)) return(FALSE);
297     switch (rtype) {
298     case LR_TELE:
299     case LR_UPTELE:
300     case LR_DOWNTELE:
301 	/* "something" means the player in this case */
302 	if(MON_AT(x, y)) {
303 	    /* move the monster if no choice, or just try again */
304 	    if(oneshot) rloc(m_at(x,y));
305 	    else return(FALSE);
306 	}
307 	u_on_newpos(x, y);
308 	break;
309     case LR_PORTAL:
310 	mkportal(x, y, lev->dnum, lev->dlevel);
311 	break;
312     case LR_DOWNSTAIR:
313     case LR_UPSTAIR:
314 	mkstairs(x, y, (char)rtype, (struct mkroom *)0);
315 	break;
316     case LR_BRANCH:
317 	place_branch(Is_branchlev(&u.uz), x, y);
318 	break;
319     }
320     return(TRUE);
321 }
322 
323 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
324 
325 /* this is special stuff that the level compiler cannot (yet) handle */
326 STATIC_OVL void
fixup_special()327 fixup_special()
328 {
329     register lev_region *r = lregions;
330     struct d_level lev;
331     register int x, y;
332     struct mkroom *croom;
333     boolean added_branch = FALSE;
334 
335     if (was_waterlevel) {
336 	was_waterlevel = FALSE;
337 	u.uinwater = 0;
338 	unsetup_waterlevel();
339     } else if (Is_waterlevel(&u.uz)) {
340 	level.flags.hero_memory = 0;
341 	was_waterlevel = TRUE;
342 	/* water level is an odd beast - it has to be set up
343 	   before calling place_lregions etc. */
344 	setup_waterlevel();
345     }
346     for(x = 0; x < num_lregions; x++, r++) {
347 	switch(r->rtype) {
348 	case LR_BRANCH:
349 	    added_branch = TRUE;
350 	    goto place_it;
351 
352 	case LR_PORTAL:
353 	    if(*r->rname.str >= '0' && *r->rname.str <= '9') {
354 		/* "chutes and ladders" */
355 		lev = u.uz;
356 		lev.dlevel = atoi(r->rname.str);
357 	    } else {
358 		s_level *sp = find_level(r->rname.str);
359 		lev = sp->dlevel;
360 	    }
361 	    /* fall into... */
362 
363 	case LR_UPSTAIR:
364 	case LR_DOWNSTAIR:
365 	place_it:
366 	    place_lregion(r->inarea.x1, r->inarea.y1,
367 			  r->inarea.x2, r->inarea.y2,
368 			  r->delarea.x1, r->delarea.y1,
369 			  r->delarea.x2, r->delarea.y2,
370 			  r->rtype, &lev);
371 	    break;
372 
373 	case LR_TELE:
374 	case LR_UPTELE:
375 	case LR_DOWNTELE:
376 	    /* save the region outlines for goto_level() */
377 	    if(r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
378 		    updest.lx = r->inarea.x1; updest.ly = r->inarea.y1;
379 		    updest.hx = r->inarea.x2; updest.hy = r->inarea.y2;
380 		    updest.nlx = r->delarea.x1; updest.nly = r->delarea.y1;
381 		    updest.nhx = r->delarea.x2; updest.nhy = r->delarea.y2;
382 	    }
383 	    if(r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
384 		    dndest.lx = r->inarea.x1; dndest.ly = r->inarea.y1;
385 		    dndest.hx = r->inarea.x2; dndest.hy = r->inarea.y2;
386 		    dndest.nlx = r->delarea.x1; dndest.nly = r->delarea.y1;
387 		    dndest.nhx = r->delarea.x2; dndest.nhy = r->delarea.y2;
388 	    }
389 	    /* place_lregion gets called from goto_level() */
390 	    break;
391 	}
392 
393 	if (r->rname.str) free((genericptr_t) r->rname.str),  r->rname.str = 0;
394     }
395 
396     /* place dungeon branch if not placed above */
397     if (!added_branch && Is_branchlev(&u.uz)) {
398 	place_lregion(0,0,0,0,0,0,0,0,LR_BRANCH,(d_level *)0);
399     }
400 
401 	/* KMH -- Sokoban levels */
402 	if(In_sokoban(&u.uz))
403 		sokoban_detect();
404 
405     /* Still need to add some stuff to level file */
406     if (Is_medusa_level(&u.uz)) {
407 	struct obj *otmp;
408 	int tryct;
409 
410 	croom = &rooms[0]; /* only one room on the medusa level */
411 	for (tryct = rnd(4); tryct; tryct--) {
412 	    x = somex(croom); y = somey(croom);
413 	    if (goodpos(x, y, (struct monst *)0)) {
414 		otmp = mk_tt_object(STATUE, x, y);
415 		while (otmp && (poly_when_stoned(&mons[otmp->corpsenm]) ||
416 				pm_resistance(&mons[otmp->corpsenm],MR_STONE))) {
417 		    otmp->corpsenm = rndmonnum();
418 		    otmp->owt = weight(otmp);
419 		}
420 	    }
421 	}
422 
423 	if (rn2(2))
424 	    otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
425 	else /* Medusa statues don't contain books */
426 	    otmp = mkcorpstat(STATUE, (struct monst *)0, (struct permonst *)0,
427 			      somex(croom), somey(croom), FALSE);
428 	if (otmp) {
429 	    while (pm_resistance(&mons[otmp->corpsenm],MR_STONE)
430 		   || poly_when_stoned(&mons[otmp->corpsenm])) {
431 		otmp->corpsenm = rndmonnum();
432 		otmp->owt = weight(otmp);
433 	    }
434 	}
435     } else if(Is_wiz1_level(&u.uz)) {
436 	croom = search_special(MORGUE);
437 
438 	create_secret_door(croom, W_SOUTH|W_EAST|W_WEST);
439     } else if(Is_knox(&u.uz)) {
440 	/* using an unfilled morgue for rm id */
441 	croom = search_special(MORGUE);
442 	/* avoid inappropriate morgue-related messages */
443 	level.flags.graveyard = level.flags.has_morgue = 0;
444 	croom->rtype = OROOM;	/* perhaps it should be set to VAULT? */
445 	/* stock the main vault */
446 	for(x = croom->lx; x <= croom->hx; x++)
447 	    for(y = croom->ly; y <= croom->hy; y++) {
448 		(void) mkgold((long) rn1(300, 600), x, y);
449 		if (!rn2(3) && !is_pool(x,y))
450 		    (void)maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
451 	    }
452     } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
453 	/* less chance for undead corpses (lured from lower morgues) */
454 	level.flags.graveyard = 1;
455     } else if (Is_stronghold(&u.uz)) {
456 	level.flags.graveyard = 1;
457     } else if(Is_sanctum(&u.uz)) {
458 	croom = search_special(TEMPLE);
459 
460 	create_secret_door(croom, W_ANY);
461     } else if(on_level(&u.uz, &orcus_level)) {
462 	   register struct monst *mtmp, *mtmp2;
463 
464 	   /* it's a ghost town, get rid of shopkeepers */
465 	    for(mtmp = fmon; mtmp; mtmp = mtmp2) {
466 		    mtmp2 = mtmp->nmon;
467 		    if(mtmp->isshk) mongone(mtmp);
468 	    }
469     }
470 
471     if(lev_message) {
472 	char *str, *nl;
473 	for(str = lev_message; (nl = index(str, '\n')) != 0; str = nl+1) {
474 	    *nl = '\0';
475 	    pline("%s", str);
476 	}
477 	if(*str)
478 	    pline("%s", str);
479 	free((genericptr_t)lev_message);
480 	lev_message = 0;
481     }
482 
483     if (lregions)
484 	free((genericptr_t) lregions),  lregions = 0;
485     num_lregions = 0;
486 }
487 
488 void
makemaz(s)489 makemaz(s)
490 register const char *s;
491 {
492 	int x,y;
493 	char protofile[20];
494 	s_level	*sp = Is_special(&u.uz);
495 	coord mm;
496 
497 	if(*s) {
498 	    if(sp && sp->rndlevs) Sprintf(protofile, "%s-%d", s,
499 						rnd((int) sp->rndlevs));
500 	    else		 Strcpy(protofile, s);
501 	} else if(*(dungeons[u.uz.dnum].proto)) {
502 	    if(dunlevs_in_dungeon(&u.uz) > 1) {
503 		if(sp && sp->rndlevs)
504 		     Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
505 						dunlev(&u.uz),
506 						rnd((int) sp->rndlevs));
507 		else Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
508 						dunlev(&u.uz));
509 	    } else if(sp && sp->rndlevs) {
510 		     Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
511 						rnd((int) sp->rndlevs));
512 	    } else Strcpy(protofile, dungeons[u.uz.dnum].proto);
513 
514 	} else Strcpy(protofile, "");
515 
516 	if(*protofile) {
517 	    Strcat(protofile, LEV_EXT);
518 	    if(load_special(protofile)) {
519 		fixup_special();
520 		/* some levels can end up with monsters
521                    on dead mon list, including light source monsters */
522 		dmonsfree();
523 		return;	/* no mazification right now */
524 	    }
525 	    impossible("Couldn't load \"%s\" - making a maze.", protofile);
526 	}
527 
528 	level.flags.is_maze_lev = TRUE;
529 
530 #ifndef WALLIFIED_MAZE
531 	for(x = 2; x < x_maze_max; x++)
532 		for(y = 2; y < y_maze_max; y++)
533 			levl[x][y].typ = STONE;
534 #else
535 	for(x = 2; x <= x_maze_max; x++)
536 		for(y = 2; y <= y_maze_max; y++)
537 			levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
538 #endif
539 
540 	maze0xy(&mm);
541 	walkfrom((int) mm.x, (int) mm.y);
542 	/* put a boulder at the maze center */
543 	(void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE);
544 
545 #ifdef WALLIFIED_MAZE
546 	wallification(2, 2, x_maze_max, y_maze_max);
547 #endif
548 	mazexy(&mm);
549 	mkstairs(mm.x, mm.y, 1, (struct mkroom *)0);		/* up */
550 	if (!Invocation_lev(&u.uz)) {
551 	    mazexy(&mm);
552 	    mkstairs(mm.x, mm.y, 0, (struct mkroom *)0);	/* down */
553 	} else {	/* choose "vibrating square" location */
554 #define x_maze_min 2
555 #define y_maze_min 2
556 	    /*
557 	     * Pick a position where the stairs down to Moloch's Sanctum
558 	     * level will ultimately be created.  At that time, an area
559 	     * will be altered:  walls removed, moat and traps generated,
560 	     * boulders destroyed.  The position picked here must ensure
561 	     * that that invocation area won't extend off the map.
562 	     *
563 	     * We actually allow up to 2 squares around the usual edge of
564 	     * the area to get truncated; see mkinvokearea(mklev.c).
565 	     */
566 #define INVPOS_X_MARGIN (6 - 2)
567 #define INVPOS_Y_MARGIN (5 - 2)
568 #define INVPOS_DISTANCE 11
569 	    int x_range = x_maze_max - x_maze_min - 2*INVPOS_X_MARGIN - 1,
570 		y_range = y_maze_max - y_maze_min - 2*INVPOS_Y_MARGIN - 1;
571 
572 #ifdef DEBUG
573 	    if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN ||
574 		   (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE))
575 		panic("inv_pos: maze is too small! (%d x %d)",
576 		      x_maze_max, y_maze_max);
577 #endif
578 	    inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
579 	    do {
580 		x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
581 		y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
582 		/* we don't want it to be too near the stairs, nor
583 		   to be on a spot that's already in use (wall|trap) */
584 	    } while (x == xupstair || y == yupstair ||	/*(direct line)*/
585 		     abs(x - xupstair) == abs(y - yupstair) ||
586 		     distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE ||
587 		     !SPACE_POS(levl[x][y].typ) || occupied(x, y));
588 	    inv_pos.x = x;
589 	    inv_pos.y = y;
590 #undef INVPOS_X_MARGIN
591 #undef INVPOS_Y_MARGIN
592 #undef INVPOS_DISTANCE
593 #undef x_maze_min
594 #undef y_maze_min
595 	}
596 
597 	/* place branch stair or portal */
598 	place_branch(Is_branchlev(&u.uz), 0, 0);
599 
600 	for(x = rn1(8,11); x; x--) {
601 		mazexy(&mm);
602 		(void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
603 	}
604 	for(x = rn1(10,2); x; x--) {
605 		mazexy(&mm);
606 		(void) mksobj_at(BOULDER, mm.x, mm.y, TRUE);
607 	}
608 	for (x = rn2(3); x; x--) {
609 		mazexy(&mm);
610 		(void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
611 	}
612 	for(x = rn1(5,7); x; x--) {
613 		mazexy(&mm);
614 		(void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
615 	}
616 	for(x = rn1(6,7); x; x--) {
617 		mazexy(&mm);
618 		(void) mkgold(0L,mm.x,mm.y);
619 	}
620 	for(x = rn1(6,7); x; x--)
621 		mktrap(0,1,(struct mkroom *) 0, (coord*) 0);
622 }
623 
624 #ifdef MICRO
625 /* Make the mazewalk iterative by faking a stack.  This is needed to
626  * ensure the mazewalk is successful in the limited stack space of
627  * the program.  This iterative version uses the minimum amount of stack
628  * that is totally safe.
629  */
630 void
walkfrom(x,y)631 walkfrom(x,y)
632 int x,y;
633 {
634 #define CELLS (ROWNO * COLNO) / 4		/* a maze cell is 4 squares */
635 	char mazex[CELLS + 1], mazey[CELLS + 1];	/* char's are OK */
636 	int q, a, dir, pos;
637 	int dirs[4];
638 
639 	pos = 1;
640 	mazex[pos] = (char) x;
641 	mazey[pos] = (char) y;
642 	while (pos) {
643 		x = (int) mazex[pos];
644 		y = (int) mazey[pos];
645 		if(!IS_DOOR(levl[x][y].typ)) {
646 		    /* might still be on edge of MAP, so don't overwrite */
647 #ifndef WALLIFIED_MAZE
648 		    levl[x][y].typ = CORR;
649 #else
650 		    levl[x][y].typ = ROOM;
651 #endif
652 		    levl[x][y].flags = 0;
653 		}
654 		q = 0;
655 		for (a = 0; a < 4; a++)
656 			if(okay(x, y, a)) dirs[q++]= a;
657 		if (!q)
658 			pos--;
659 		else {
660 			dir = dirs[rn2(q)];
661 			move(&x, &y, dir);
662 #ifndef WALLIFIED_MAZE
663 			levl[x][y].typ = CORR;
664 #else
665 			levl[x][y].typ = ROOM;
666 #endif
667 			move(&x, &y, dir);
668 			pos++;
669 			if (pos > CELLS)
670 				panic("Overflow in walkfrom");
671 			mazex[pos] = (char) x;
672 			mazey[pos] = (char) y;
673 		}
674 	}
675 }
676 #else
677 
678 void
walkfrom(x,y)679 walkfrom(x,y)
680 int x,y;
681 {
682 	register int q,a,dir;
683 	int dirs[4];
684 
685 	if(!IS_DOOR(levl[x][y].typ)) {
686 	    /* might still be on edge of MAP, so don't overwrite */
687 #ifndef WALLIFIED_MAZE
688 	    levl[x][y].typ = CORR;
689 #else
690 	    levl[x][y].typ = ROOM;
691 #endif
692 	    levl[x][y].flags = 0;
693 	}
694 
695 	while(1) {
696 		q = 0;
697 		for(a = 0; a < 4; a++)
698 			if(okay(x,y,a)) dirs[q++]= a;
699 		if(!q) return;
700 		dir = dirs[rn2(q)];
701 		move(&x,&y,dir);
702 #ifndef WALLIFIED_MAZE
703 		levl[x][y].typ = CORR;
704 #else
705 		levl[x][y].typ = ROOM;
706 #endif
707 		move(&x,&y,dir);
708 		walkfrom(x,y);
709 	}
710 }
711 #endif /* MICRO */
712 
713 STATIC_OVL void
move(x,y,dir)714 move(x,y,dir)
715 register int *x, *y;
716 register int dir;
717 {
718 	switch(dir){
719 		case 0: --(*y); break;
720 		case 1: (*x)++; break;
721 		case 2: (*y)++; break;
722 		case 3: --(*x); break;
723 		default: panic("move: bad direction");
724 	}
725 }
726 
727 void
mazexy(cc)728 mazexy(cc)	/* find random point in generated corridors,
729 		   so we don't create items in moats, bunkers, or walls */
730 	coord	*cc;
731 {
732 	int cpt=0;
733 
734 	do {
735 	    cc->x = 3 + 2*rn2((x_maze_max>>1) - 1);
736 	    cc->y = 3 + 2*rn2((y_maze_max>>1) - 1);
737 	    cpt++;
738 	} while (cpt < 100 && levl[cc->x][cc->y].typ !=
739 #ifdef WALLIFIED_MAZE
740 		 ROOM
741 #else
742 		 CORR
743 #endif
744 		);
745 	if (cpt >= 100) {
746 		register int x, y;
747 		/* last try */
748 		for (x = 0; x < (x_maze_max>>1) - 1; x++)
749 		    for (y = 0; y < (y_maze_max>>1) - 1; y++) {
750 			cc->x = 3 + 2 * x;
751 			cc->y = 3 + 2 * y;
752 			if (levl[cc->x][cc->y].typ ==
753 #ifdef WALLIFIED_MAZE
754 			    ROOM
755 #else
756 			    CORR
757 #endif
758 			   ) return;
759 		    }
760 		panic("mazexy: can't find a place!");
761 	}
762 	return;
763 }
764 
765 void
bound_digging()766 bound_digging()
767 /* put a non-diggable boundary around the initial portion of a level map.
768  * assumes that no level will initially put things beyond the isok() range.
769  *
770  * we can't bound unconditionally on the last line with something in it,
771  * because that something might be a niche which was already reachable,
772  * so the boundary would be breached
773  *
774  * we can't bound unconditionally on one beyond the last line, because
775  * that provides a window of abuse for WALLIFIED_MAZE special levels
776  */
777 {
778 	register int x,y;
779 	register unsigned typ;
780 	register struct rm *lev;
781 	boolean found, nonwall;
782 	int xmin,xmax,ymin,ymax;
783 
784 	if(Is_earthlevel(&u.uz)) return; /* everything diggable here */
785 
786 	found = nonwall = FALSE;
787 	for(xmin=0; !found; xmin++) {
788 		lev = &levl[xmin][0];
789 		for(y=0; y<=ROWNO-1; y++, lev++) {
790 			typ = lev->typ;
791 			if(typ != STONE) {
792 				found = TRUE;
793 				if(!IS_WALL(typ)) nonwall = TRUE;
794 			}
795 		}
796 	}
797 	xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
798 	if (xmin < 0) xmin = 0;
799 
800 	found = nonwall = FALSE;
801 	for(xmax=COLNO-1; !found; xmax--) {
802 		lev = &levl[xmax][0];
803 		for(y=0; y<=ROWNO-1; y++, lev++) {
804 			typ = lev->typ;
805 			if(typ != STONE) {
806 				found = TRUE;
807 				if(!IS_WALL(typ)) nonwall = TRUE;
808 			}
809 		}
810 	}
811 	xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
812 	if (xmax >= COLNO) xmax = COLNO-1;
813 
814 	found = nonwall = FALSE;
815 	for(ymin=0; !found; ymin++) {
816 		lev = &levl[xmin][ymin];
817 		for(x=xmin; x<=xmax; x++, lev += ROWNO) {
818 			typ = lev->typ;
819 			if(typ != STONE) {
820 				found = TRUE;
821 				if(!IS_WALL(typ)) nonwall = TRUE;
822 			}
823 		}
824 	}
825 	ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
826 
827 	found = nonwall = FALSE;
828 	for(ymax=ROWNO-1; !found; ymax--) {
829 		lev = &levl[xmin][ymax];
830 		for(x=xmin; x<=xmax; x++, lev += ROWNO) {
831 			typ = lev->typ;
832 			if(typ != STONE) {
833 				found = TRUE;
834 				if(!IS_WALL(typ)) nonwall = TRUE;
835 			}
836 		}
837 	}
838 	ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
839 
840 	for (x = 0; x < COLNO; x++)
841 	  for (y = 0; y < ROWNO; y++)
842 	    if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
843 #ifdef DCC30_BUG
844 		lev = &levl[x][y];
845 		lev->wall_info |= W_NONDIGGABLE;
846 #else
847 		levl[x][y].wall_info |= W_NONDIGGABLE;
848 #endif
849 	    }
850 }
851 
852 void
mkportal(x,y,todnum,todlevel)853 mkportal(x, y, todnum, todlevel)
854 register xchar x, y, todnum, todlevel;
855 {
856 	/* a portal "trap" must be matched by a */
857 	/* portal in the destination dungeon/dlevel */
858 	register struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
859 
860 	if (!ttmp) {
861 		impossible("portal on top of portal??");
862 		return;
863 	}
864 #ifdef DEBUG
865 	pline("mkportal: at (%d,%d), to %s, level %d",
866 		x, y, dungeons[todnum].dname, todlevel);
867 #endif
868 	ttmp->dst.dnum = todnum;
869 	ttmp->dst.dlevel = todlevel;
870 	return;
871 }
872 
873 /*
874  * Special waterlevel stuff in endgame (TH).
875  *
876  * Some of these functions would probably logically belong to some
877  * other source files, but they are all so nicely encapsulated here.
878  */
879 
880 /* to ease the work of debuggers at this stage */
881 #define register
882 
883 struct container {
884 	struct container *next;
885 	xchar x, y;
886 	short what;
887 	genericptr_t list;
888 };
889 #define CONS_OBJ   0
890 #define CONS_MON   1
891 #define CONS_HERO  2
892 #define CONS_TRAP  3
893 
894 static struct bubble {
895 	xchar x, y;	/* coordinates of the upper left corner */
896 	schar dx, dy;	/* the general direction of the bubble's movement */
897 	uchar *bm;	/* pointer to the bubble bit mask */
898 	struct bubble *prev, *next; /* need to traverse the list up and down */
899 	struct container *cons;
900 } *bbubbles, *ebubbles;
901 
902 static struct trap *wportal;
903 static int xmin, ymin, xmax, ymax;	/* level boundaries */
904 /* bubble movement boundaries */
905 #define bxmin (xmin + 1)
906 #define bymin (ymin + 1)
907 #define bxmax (xmax - 1)
908 #define bymax (ymax - 1)
909 
910 STATIC_DCL void NDECL(set_wportal);
911 STATIC_DCL void FDECL(mk_bubble, (int,int,int));
912 STATIC_DCL void FDECL(mv_bubble, (struct bubble *,int,int,BOOLEAN_P));
913 
914 void
movebubbles()915 movebubbles()
916 {
917 	static boolean up;
918 	register struct bubble *b;
919 	register int x, y, i, j;
920 	struct trap *btrap;
921 	static const struct rm water_pos =
922 		{ cmap_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0 };
923 
924 	/* set up the portal the first time bubbles are moved */
925 	if (!wportal) set_wportal();
926 
927 	vision_recalc(2);
928 	/* keep attached ball&chain separate from bubble objects */
929 	if (Punished) unplacebc();
930 
931 	/*
932 	 * Pick up everything inside of a bubble then fill all bubble
933 	 * locations.
934 	 */
935 
936 	for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
937 	    if (b->cons) panic("movebubbles: cons != null");
938 	    for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
939 		for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
940 		    if (b->bm[j + 2] & (1 << i)) {
941 			if (!isok(x,y)) {
942 			    impossible("movebubbles: bad pos (%d,%d)", x,y);
943 			    continue;
944 			}
945 
946 			/* pick up objects, monsters, hero, and traps */
947 			if (OBJ_AT(x,y)) {
948 			    struct obj *olist = (struct obj *) 0, *otmp;
949 			    struct container *cons = (struct container *)
950 				alloc(sizeof(struct container));
951 
952 			    while ((otmp = level.objects[x][y]) != 0) {
953 				remove_object(otmp);
954 				otmp->ox = otmp->oy = 0;
955 				otmp->nexthere = olist;
956 				olist = otmp;
957 			    }
958 
959 			    cons->x = x;
960 			    cons->y = y;
961 			    cons->what = CONS_OBJ;
962 			    cons->list = (genericptr_t) olist;
963 			    cons->next = b->cons;
964 			    b->cons = cons;
965 			}
966 			if (MON_AT(x,y)) {
967 			    struct monst *mon = m_at(x,y);
968 			    struct container *cons = (struct container *)
969 				alloc(sizeof(struct container));
970 
971 			    cons->x = x;
972 			    cons->y = y;
973 			    cons->what = CONS_MON;
974 			    cons->list = (genericptr_t) mon;
975 
976 			    cons->next = b->cons;
977 			    b->cons = cons;
978 
979 			    if(mon->wormno)
980 				remove_worm(mon);
981 			    else
982 				remove_monster(x, y);
983 
984 			    newsym(x,y);	/* clean up old position */
985 			    mon->mx = mon->my = 0;
986 			}
987 			if (!u.uswallow && x == u.ux && y == u.uy) {
988 			    struct container *cons = (struct container *)
989 				alloc(sizeof(struct container));
990 
991 			    cons->x = x;
992 			    cons->y = y;
993 			    cons->what = CONS_HERO;
994 			    cons->list = (genericptr_t) 0;
995 
996 			    cons->next = b->cons;
997 			    b->cons = cons;
998 			}
999 			if ((btrap = t_at(x,y)) != 0) {
1000 			    struct container *cons = (struct container *)
1001 				alloc(sizeof(struct container));
1002 
1003 			    cons->x = x;
1004 			    cons->y = y;
1005 			    cons->what = CONS_TRAP;
1006 			    cons->list = (genericptr_t) btrap;
1007 
1008 			    cons->next = b->cons;
1009 			    b->cons = cons;
1010 			}
1011 
1012 			levl[x][y] = water_pos;
1013 			block_point(x,y);
1014 		    }
1015 	}
1016 
1017 	/*
1018 	 * Every second time traverse down.  This is because otherwise
1019 	 * all the junk that changes owners when bubbles overlap
1020 	 * would eventually end up in the last bubble in the chain.
1021 	 */
1022 
1023 	up = !up;
1024 	for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1025 		register int rx = rn2(3), ry = rn2(3);
1026 
1027 		mv_bubble(b,b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1028 			    b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)),
1029 			    FALSE);
1030 	}
1031 
1032 	/* put attached ball&chain back */
1033 	if (Punished) placebc();
1034 	vision_full_recalc = 1;
1035 }
1036 
1037 /* when moving in water, possibly (1 in 3) alter the intended destination */
1038 void
water_friction()1039 water_friction()
1040 {
1041 	register int x, y, dx, dy;
1042 	register boolean eff = FALSE;
1043 
1044 	if (Swimming && rn2(4))
1045 		return;		/* natural swimmers have advantage */
1046 
1047 	if (u.dx && !rn2(!u.dy ? 3 : 6)) {	/* 1/3 chance or half that */
1048 		/* cancel delta x and choose an arbitrary delta y value */
1049 		x = u.ux;
1050 		do {
1051 		    dy = rn2(3) - 1;		/* -1, 0, 1 */
1052 		    y = u.uy + dy;
1053 		} while (dy && (!isok(x,y) || !is_pool(x,y)));
1054 		u.dx = 0;
1055 		u.dy = dy;
1056 		eff = TRUE;
1057 	} else if (u.dy && !rn2(!u.dx ? 3 : 5)) {	/* 1/3 or 1/5*(5/6) */
1058 		/* cancel delta y and choose an arbitrary delta x value */
1059 		y = u.uy;
1060 		do {
1061 		    dx = rn2(3) - 1;		/* -1 .. 1 */
1062 		    x = u.ux + dx;
1063 		} while (dx && (!isok(x,y) || !is_pool(x,y)));
1064 		u.dy = 0;
1065 		u.dx = dx;
1066 		eff = TRUE;
1067 	}
1068 	if (eff) pline("Water turbulence affects your movements.");
1069 }
1070 
1071 void
save_waterlevel(fd,mode)1072 save_waterlevel(fd, mode)
1073 int fd, mode;
1074 {
1075 	register struct bubble *b;
1076 
1077 	if (!Is_waterlevel(&u.uz)) return;
1078 
1079 	if (perform_bwrite(mode)) {
1080 	    int n = 0;
1081 	    for (b = bbubbles; b; b = b->next) ++n;
1082 	    bwrite(fd, (genericptr_t)&n, sizeof (int));
1083 	    bwrite(fd, (genericptr_t)&xmin, sizeof (int));
1084 	    bwrite(fd, (genericptr_t)&ymin, sizeof (int));
1085 	    bwrite(fd, (genericptr_t)&xmax, sizeof (int));
1086 	    bwrite(fd, (genericptr_t)&ymax, sizeof (int));
1087 	    for (b = bbubbles; b; b = b->next)
1088 		bwrite(fd, (genericptr_t)b, sizeof (struct bubble));
1089 	}
1090 	if (release_data(mode))
1091 	    unsetup_waterlevel();
1092 }
1093 
1094 void
restore_waterlevel(fd)1095 restore_waterlevel(fd)
1096 register int fd;
1097 {
1098 	register struct bubble *b = (struct bubble *)0, *btmp;
1099 	register int i;
1100 	int n;
1101 
1102 	if (!Is_waterlevel(&u.uz)) return;
1103 
1104 	set_wportal();
1105 	mread(fd,(genericptr_t)&n,sizeof(int));
1106 	mread(fd,(genericptr_t)&xmin,sizeof(int));
1107 	mread(fd,(genericptr_t)&ymin,sizeof(int));
1108 	mread(fd,(genericptr_t)&xmax,sizeof(int));
1109 	mread(fd,(genericptr_t)&ymax,sizeof(int));
1110 	for (i = 0; i < n; i++) {
1111 		btmp = b;
1112 		b = (struct bubble *)alloc(sizeof(struct bubble));
1113 		mread(fd,(genericptr_t)b,sizeof(struct bubble));
1114 		if (bbubbles) {
1115 			btmp->next = b;
1116 			b->prev = btmp;
1117 		} else {
1118 			bbubbles = b;
1119 			b->prev = (struct bubble *)0;
1120 		}
1121 		mv_bubble(b,0,0,TRUE);
1122 	}
1123 	ebubbles = b;
1124 	b->next = (struct bubble *)0;
1125 	was_waterlevel = TRUE;
1126 }
1127 
1128 STATIC_OVL void
set_wportal()1129 set_wportal()
1130 {
1131 	/* there better be only one magic portal on water level... */
1132 	for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1133 		if (wportal->ttyp == MAGIC_PORTAL) return;
1134 	impossible("set_wportal(): no portal!");
1135 }
1136 
1137 STATIC_OVL void
setup_waterlevel()1138 setup_waterlevel()
1139 {
1140 	register int x, y;
1141 	register int xskip, yskip;
1142 	register int water_glyph = cmap_to_glyph(S_water);
1143 
1144 	/* ouch, hardcoded... */
1145 
1146 	xmin = 3;
1147 	ymin = 1;
1148 	xmax = 78;
1149 	ymax = 20;
1150 
1151 	/* set hero's memory to water */
1152 
1153 	for (x = xmin; x <= xmax; x++)
1154 		for (y = ymin; y <= ymax; y++)
1155 			levl[x][y].glyph = water_glyph;
1156 
1157 	/* make bubbles */
1158 
1159 	xskip = 10 + rn2(10);
1160 	yskip = 4 + rn2(4);
1161 	for (x = bxmin; x <= bxmax; x += xskip)
1162 		for (y = bymin; y <= bymax; y += yskip)
1163 			mk_bubble(x,y,rn2(7));
1164 }
1165 
1166 STATIC_OVL void
unsetup_waterlevel()1167 unsetup_waterlevel()
1168 {
1169 	register struct bubble *b, *bb;
1170 
1171 	/* free bubbles */
1172 
1173 	for (b = bbubbles; b; b = bb) {
1174 		bb = b->next;
1175 		free((genericptr_t)b);
1176 	}
1177 	bbubbles = ebubbles = (struct bubble *)0;
1178 }
1179 
1180 STATIC_OVL void
mk_bubble(x,y,n)1181 mk_bubble(x,y,n)
1182 register int x, y, n;
1183 {
1184 	/*
1185 	 * These bit masks make visually pleasing bubbles on a normal aspect
1186 	 * 25x80 terminal, which naturally results in them being mathematically
1187 	 * anything but symmetric.  For this reason they cannot be computed
1188 	 * in situ, either.  The first two elements tell the dimensions of
1189 	 * the bubble's bounding box.
1190 	 */
1191 	static uchar
1192 		bm2[] = {2,1,0x3},
1193 		bm3[] = {3,2,0x7,0x7},
1194 		bm4[] = {4,3,0x6,0xf,0x6},
1195 		bm5[] = {5,3,0xe,0x1f,0xe},
1196 		bm6[] = {6,4,0x1e,0x3f,0x3f,0x1e},
1197 		bm7[] = {7,4,0x3e,0x7f,0x7f,0x3e},
1198 		bm8[] = {8,4,0x7e,0xff,0xff,0x7e},
1199 		*bmask[] = {bm2,bm3,bm4,bm5,bm6,bm7,bm8};
1200 
1201 	register struct bubble *b;
1202 
1203 	if (x >= bxmax || y >= bymax) return;
1204 	if (n >= SIZE(bmask)) {
1205 		impossible("n too large (mk_bubble)");
1206 		n = SIZE(bmask) - 1;
1207 	}
1208 	b = (struct bubble *)alloc(sizeof(struct bubble));
1209 	if ((x + (int) bmask[n][0] - 1) > bxmax) x = bxmax - bmask[n][0] + 1;
1210 	if ((y + (int) bmask[n][1] - 1) > bymax) y = bymax - bmask[n][1] + 1;
1211 	b->x = x;
1212 	b->y = y;
1213 	b->dx = 1 - rn2(3);
1214 	b->dy = 1 - rn2(3);
1215 	b->bm = bmask[n];
1216 	b->cons = 0;
1217 	if (!bbubbles) bbubbles = b;
1218 	if (ebubbles) {
1219 		ebubbles->next = b;
1220 		b->prev = ebubbles;
1221 	}
1222 	else
1223 		b->prev = (struct bubble *)0;
1224 	b->next =  (struct bubble *)0;
1225 	ebubbles = b;
1226 	mv_bubble(b,0,0,TRUE);
1227 }
1228 
1229 /*
1230  * The player, the portal and all other objects and monsters
1231  * float along with their associated bubbles.  Bubbles may overlap
1232  * freely, and the contents may get associated with other bubbles in
1233  * the process.  Bubbles are "sticky", meaning that if the player is
1234  * in the immediate neighborhood of one, he/she may get sucked inside.
1235  * This property also makes leaving a bubble slightly difficult.
1236  */
1237 STATIC_OVL void
mv_bubble(b,dx,dy,ini)1238 mv_bubble(b,dx,dy,ini)
1239 register struct bubble *b;
1240 register int dx, dy;
1241 register boolean ini;
1242 {
1243 	register int x, y, i, j, colli = 0;
1244 	struct container *cons, *ctemp;
1245 
1246 	/* move bubble */
1247 	if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1248 	    /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1249 	    dx = sgn(dx);
1250 	    dy = sgn(dy);
1251 	}
1252 
1253 	/*
1254 	 * collision with level borders?
1255 	 *	1 = horizontal border, 2 = vertical, 3 = corner
1256 	 */
1257 	if (b->x <= bxmin) colli |= 2;
1258 	if (b->y <= bymin) colli |= 1;
1259 	if ((int) (b->x + b->bm[0] - 1) >= bxmax) colli |= 2;
1260 	if ((int) (b->y + b->bm[1] - 1) >= bymax) colli |= 1;
1261 
1262 	if (b->x < bxmin) {
1263 	    pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1264 	    b->x = bxmin;
1265 	}
1266 	if (b->y < bymin) {
1267 	    pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1268 	    b->y = bymin;
1269 	}
1270 	if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1271 	    pline("bubble xmax: x = %d, xmax = %d",
1272 			b->x + b->bm[0] - 1, bxmax);
1273 	    b->x = bxmax - b->bm[0] + 1;
1274 	}
1275 	if ((int) (b->y + b->bm[1] - 1) > bymax) {
1276 	    pline("bubble ymax: y = %d, ymax = %d",
1277 			b->y + b->bm[1] - 1, bymax);
1278 	    b->y = bymax - b->bm[1] + 1;
1279 	}
1280 
1281 	/* bounce if we're trying to move off the border */
1282 	if (b->x == bxmin && dx < 0) dx = -dx;
1283 	if (b->x + b->bm[0] - 1 == bxmax && dx > 0) dx = -dx;
1284 	if (b->y == bymin && dy < 0) dy = -dy;
1285 	if (b->y + b->bm[1] - 1 == bymax && dy > 0) dy = -dy;
1286 
1287 	b->x += dx;
1288 	b->y += dy;
1289 
1290 	/* void positions inside bubble */
1291 
1292 	for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1293 	    for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1294 		if (b->bm[j + 2] & (1 << i)) {
1295 		    levl[x][y].typ = AIR;
1296 		    levl[x][y].lit = 1;
1297 		    unblock_point(x,y);
1298 		}
1299 
1300 	/* replace contents of bubble */
1301 	for (cons = b->cons; cons; cons = ctemp) {
1302 	    ctemp = cons->next;
1303 	    cons->x += dx;
1304 	    cons->y += dy;
1305 
1306 	    switch(cons->what) {
1307 		case CONS_OBJ: {
1308 		    struct obj *olist, *otmp;
1309 
1310 		    for (olist=(struct obj *)cons->list; olist; olist=otmp) {
1311 			otmp = olist->nexthere;
1312 			place_object(olist, cons->x, cons->y);
1313 		    }
1314 		    break;
1315 		}
1316 
1317 		case CONS_MON: {
1318 		    struct monst *mon = (struct monst *) cons->list;
1319 		    (void) mnearto(mon, cons->x, cons->y, TRUE);
1320 		    break;
1321 		}
1322 
1323 		case CONS_HERO: {
1324 		    int ux0 = u.ux, uy0 = u.uy;
1325 
1326 		    /* change u.ux0 and u.uy0? */
1327 		    u.ux = cons->x;
1328 		    u.uy = cons->y;
1329 		    newsym(ux0, uy0);	/* clean up old position */
1330 
1331 		    if (MON_AT(cons->x, cons->y)) {
1332 				mnexto(m_at(cons->x,cons->y));
1333 			}
1334 		    break;
1335 		}
1336 
1337 		case CONS_TRAP: {
1338 		    struct trap *btrap = (struct trap *) cons->list;
1339 		    btrap->tx = cons->x;
1340 		    btrap->ty = cons->y;
1341 		    break;
1342 		}
1343 
1344 		default:
1345 		    impossible("mv_bubble: unknown bubble contents");
1346 		    break;
1347 	    }
1348 	    free((genericptr_t)cons);
1349 	}
1350 	b->cons = 0;
1351 
1352 	/* boing? */
1353 
1354 	switch (colli) {
1355 	    case 1: b->dy = -b->dy;	break;
1356 	    case 3: b->dy = -b->dy;	/* fall through */
1357 	    case 2: b->dx = -b->dx;	break;
1358 	    default:
1359 		/* sometimes alter direction for fun anyway
1360 		   (higher probability for stationary bubbles) */
1361 		if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1362 			b->dx = 1 - rn2(3);
1363 			b->dy = 1 - rn2(3);
1364 		}
1365 	}
1366 }
1367 
1368 /*mkmaze.c*/
1369