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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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*/