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