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