make rank() static again
[NetHack.git] / src / mkmaze.c
blobdf83d2136d27cf6b7d73b278d93652c8ae2385b0
1 /* NetHack 3.7 mkmaze.c $NHDT-Date: 1737387068 2025/01/20 07:31:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.176 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "hack.h"
7 #include "sp_lev.h"
9 staticfn int iswall(coordxy, coordxy);
10 staticfn int iswall_or_stone(coordxy, coordxy);
11 staticfn boolean is_solid(coordxy, coordxy);
12 staticfn int extend_spine(int[3][3], int, int, int);
13 staticfn void wall_cleanup(coordxy, coordxy, coordxy, coordxy);
14 staticfn boolean okay(coordxy, coordxy, coordxy);
15 staticfn void maze0xy(coord *);
16 staticfn boolean put_lregion_here(coordxy, coordxy, coordxy, coordxy, coordxy,
17 coordxy, xint16, boolean, d_level *);
18 staticfn void baalz_fixup(void);
19 staticfn void setup_waterlevel(void);
20 staticfn void unsetup_waterlevel(void);
21 staticfn void check_ransacked(const char *);
22 staticfn void migr_booty_item(int, const char *);
23 staticfn void migrate_orc(struct monst *, unsigned long);
24 staticfn void shiny_orc_stuff(struct monst *);
25 staticfn void stolen_booty(void);
26 staticfn boolean maze_inbounds(coordxy, coordxy);
27 staticfn void maze_remove_deadends(xint16);
28 staticfn void populate_maze(void);
30 /* adjust a coordinate one step in the specified direction */
31 #define mz_move(X, Y, dir) \
32 do { \
33 switch (dir) { \
34 case 0: --(Y); break; \
35 case 1: (X)++; break; \
36 case 2: (Y)++; break; \
37 case 3: --(X); break; \
38 default: panic("mz_move: bad direction %d", dir); \
39 } \
40 } while (0)
42 /* used to determine if wall spines can join this location */
43 staticfn int
44 iswall(coordxy x, coordxy y)
46 int type;
48 if (!isok(x, y))
49 return 0;
50 type = levl[x][y].typ;
51 return (IS_WALL(type) || IS_DOOR(type)
52 || type == LAVAWALL || type == WATER
53 || type == SDOOR || type == IRONBARS);
56 /* used to determine if wall spines can join this location */
57 staticfn int
58 iswall_or_stone(coordxy x, coordxy y)
60 /* out of bounds = stone */
61 if (!isok(x, y))
62 return 1;
64 return (levl[x][y].typ == STONE || iswall(x, y));
67 /* return TRUE if out of bounds, wall or rock */
68 staticfn boolean
69 is_solid(coordxy x, coordxy y)
71 return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
74 /* set map terrain type, handling lava lit, ice melt timers, etc */
75 boolean
76 set_levltyp(coordxy x, coordxy y, schar newtyp)
78 if (isok(x, y) && newtyp >= STONE && newtyp < MAX_TYPE) {
79 if (CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) {
80 schar oldtyp = levl[x][y].typ;
81 /* typ==ICE || (typ==DRAWBRIDGE_UP && drawbridgemask==DB_ICE) */
82 boolean was_ice = is_ice(x, y);
84 levl[x][y].typ = newtyp;
85 /* TODO?
86 * if oldtyp used flags or horizontal differently from
87 * the way newtyp will use them, clear them.
90 if (IS_LAVA(newtyp)) /* [what about IS_LAVA(oldtyp)=>.lit = 0?] */
91 levl[x][y].lit = 1;
92 if (was_ice && newtyp != ICE) {
93 /* frozen corpses resume rotting, no more ice to melt away */
94 obj_ice_effects(x, y, TRUE);
95 spot_stop_timers(x, y, MELT_ICE_AWAY);
97 if ((IS_FOUNTAIN(oldtyp) != IS_FOUNTAIN(newtyp))
98 || (IS_SINK(oldtyp) != IS_SINK(newtyp)))
99 count_level_features(); /* level.flags.nfountains,nsinks */
101 return TRUE;
103 #ifdef EXTRA_SANITY_CHECKS
104 } else {
105 impossible("set_levltyp(%d,%d,%d)%s%s",
106 (int) x, (int) y, (int) newtyp,
107 !isok(x, y) ? " not isok()" : "",
108 (newtyp < STONE || newtyp >= MAX_TYPE) ? " bad type" : "");
109 #endif /*EXTRA_SANITY_CHECKS*/
111 return FALSE;
114 /* set map terrain type and light state */
115 boolean
116 set_levltyp_lit(coordxy x, coordxy y, schar typ, schar lit)
118 boolean ret = set_levltyp(x, y, typ);
120 if (ret && isok(x, y)) {
121 if (lit != SET_LIT_NOCHANGE) {
122 #ifdef EXTRA_SANITY_CHECKS
123 if (lit < SET_LIT_NOCHANGE || lit > 1)
124 impossible("set_levltyp_lit(%d,%d,%d,%d)",
125 (int) x, (int) y, (int) typ, (int) lit);
126 #endif /*EXTRA_SANITY_CHECKS*/
127 if (IS_LAVA(typ))
128 lit = 1;
129 else if (lit == SET_LIT_RANDOM)
130 lit = rn2(2);
132 levl[x][y].lit = lit;
135 return ret;
139 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
140 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
142 * To extend a wall spine in that direction, first there must be a wall there.
143 * Then, extend a spine unless the current position is surrounded by walls
144 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
145 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
146 * (0,1) - South or down - then:
148 * a a a
149 * W x W This would not extend a spine from x down
150 * W W W (a corridor of walls is formed).
152 * a a a
153 * W x W This would extend a spine from x down.
154 * . W W
156 staticfn int
157 extend_spine(int locale[3][3], int wall_there, int dx, int dy)
159 int spine, nx, ny;
161 nx = 1 + dx;
162 ny = 1 + dy;
164 if (wall_there) { /* wall in that direction */
165 if (dx) {
166 if (locale[1][0] && locale[1][2] /* EW are wall/stone */
167 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
168 spine = 0;
169 } else {
170 spine = 1;
172 } else { /* dy */
173 if (locale[0][1] && locale[2][1] /* NS are wall/stone */
174 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
175 spine = 0;
176 } else {
177 spine = 1;
180 } else {
181 spine = 0;
184 return spine;
187 /* Remove walls totally surrounded by stone */
188 staticfn void
189 wall_cleanup(coordxy x1, coordxy y1, coordxy x2, coordxy y2)
191 uchar type;
192 coordxy x, y;
193 struct rm *lev;
195 /* sanity check on incoming variables */
196 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
197 panic("wall_cleanup: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
199 /* change walls surrounded by rock to rock. */
200 for (x = x1; x <= x2; x++)
201 for (y = y1; y <= y2; y++) {
202 if (within_bounded_area(x, y,
203 gb.bughack.inarea.x1, gb.bughack.inarea.y1,
204 gb.bughack.inarea.x2, gb.bughack.inarea.y2))
205 continue;
206 lev = &levl[x][y];
207 type = lev->typ;
208 if (IS_WALL(type) && type != DBWALL) {
209 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
210 && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
211 && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
212 && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
213 lev->typ = STONE;
218 /* Correct wall types so they extend and connect to each other */
219 void
220 fix_wall_spines(coordxy x1, coordxy y1, coordxy x2, coordxy y2)
222 uchar type;
223 coordxy x, y;
224 struct rm *lev;
225 int (*loc_f)(coordxy, coordxy);
226 int bits;
227 int locale[3][3]; /* rock or wall status surrounding positions */
230 * Value 0 represents a free-standing wall. It could be anything,
231 * so even though this table says VWALL, we actually leave whatever
232 * typ was there alone.
234 static xint16 spine_array[16] = { VWALL, HWALL, HWALL, HWALL,
235 VWALL, TRCORNER, TLCORNER, TDWALL,
236 VWALL, BRCORNER, BLCORNER, TUWALL,
237 VWALL, TLWALL, TRWALL, CROSSWALL };
239 /* sanity check on incoming variables */
240 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
241 panic("wall_extends: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
243 /* set the correct wall type. */
244 for (x = x1; x <= x2; x++)
245 for (y = y1; y <= y2; y++) {
246 lev = &levl[x][y];
247 type = lev->typ;
248 if (!(IS_WALL(type) && type != DBWALL))
249 continue;
251 /* set the locations TRUE if rock or wall or out of bounds */
252 loc_f = within_bounded_area(x, y, /* for baalz insect */
253 gb.bughack.inarea.x1, gb.bughack.inarea.y1,
254 gb.bughack.inarea.x2, gb.bughack.inarea.y2)
255 ? iswall
256 : iswall_or_stone;
257 locale[0][0] = (*loc_f)(x - 1, y - 1);
258 locale[1][0] = (*loc_f)(x, y - 1);
259 locale[2][0] = (*loc_f)(x + 1, y - 1);
261 locale[0][1] = (*loc_f)(x - 1, y);
262 locale[2][1] = (*loc_f)(x + 1, y);
264 locale[0][2] = (*loc_f)(x - 1, y + 1);
265 locale[1][2] = (*loc_f)(x, y + 1);
266 locale[2][2] = (*loc_f)(x + 1, y + 1);
268 /* determine if wall should extend to each direction NSEW */
269 bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
270 | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
271 | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
272 | extend_spine(locale, iswall(x - 1, y), -1, 0);
274 /* don't change typ if wall is free-standing */
275 if (bits)
276 lev->typ = spine_array[bits];
280 void
281 wallification(coordxy x1, coordxy y1, coordxy x2, coordxy y2)
283 wall_cleanup(x1, y1, x2, y2);
284 fix_wall_spines(x1, y1, x2, y2);
287 staticfn boolean
288 okay(coordxy x, coordxy y, coordxy dir)
290 mz_move(x, y, dir);
291 mz_move(x, y, dir);
292 if (x < 3 || y < 3 || x > gx.x_maze_max || y > gy.y_maze_max
293 || levl[x][y].typ != STONE)
294 return FALSE;
295 return TRUE;
298 /* find random starting point for maze generation */
299 staticfn void
300 maze0xy(coord *cc)
302 cc->x = 3 + 2 * rn2((gx.x_maze_max >> 1) - 1);
303 cc->y = 3 + 2 * rn2((gy.y_maze_max >> 1) - 1);
304 return;
307 boolean
308 is_exclusion_zone(xint16 type, coordxy x, coordxy y)
310 struct exclusion_zone *ez = sve.exclusion_zones;
312 while (ez) {
313 if (((type == LR_DOWNTELE
314 && (ez->zonetype == LR_DOWNTELE || ez->zonetype == LR_TELE))
315 || (type == LR_UPTELE
316 && (ez->zonetype == LR_UPTELE || ez->zonetype == LR_TELE))
317 || type == ez->zonetype)
318 && within_bounded_area(x, y, ez->lx, ez->ly, ez->hx, ez->hy))
319 return TRUE;
320 ez = ez->next;
322 return FALSE;
326 * Bad if:
327 * pos is occupied OR
328 * pos is inside restricted region (nlx,nly,nhx,nhy) OR
329 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
331 boolean
332 bad_location(
333 coordxy x, coordxy y,
334 coordxy nlx, coordxy nly, coordxy nhx, coordxy nhy)
336 return (boolean) (occupied(x, y)
337 || within_bounded_area(x, y, nlx, nly, nhx, nhy)
338 || !((levl[x][y].typ == CORR
339 && svl.level.flags.is_maze_lev)
340 || levl[x][y].typ == ROOM
341 || levl[x][y].typ == AIR));
344 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
345 and place something (based on rtype) in that region */
346 void
347 place_lregion(
348 coordxy lx, coordxy ly, coordxy hx, coordxy hy,
349 coordxy nlx, coordxy nly, coordxy nhx, coordxy nhy,
350 xint16 rtype,
351 d_level *lev)
353 int trycnt;
354 boolean oneshot;
355 coordxy x, y;
357 if (!lx) { /* default to whole level */
359 * if there are rooms and this a branch, let place_branch choose
360 * the branch location (to avoid putting branches in corridors).
362 if (rtype == LR_BRANCH && svn.nroom) {
363 place_branch(Is_branchlev(&u.uz), 0, 0);
364 return;
367 lx = 1; /* column 0 is not used */
368 hx = COLNO - 1;
369 ly = 0; /* 3.6.0 and earlier erroneously had 1 here */
370 hy = ROWNO - 1;
373 /* clamp the area to the map */
374 if (lx < 1)
375 lx = 1;
376 if (hx > COLNO - 1)
377 hx = COLNO - 1;
378 if (ly < 0)
379 ly = 0;
380 if (hy > ROWNO - 1)
381 hy = ROWNO - 1;
383 /* first a probabilistic approach */
385 oneshot = (lx == hx && ly == hy);
386 for (trycnt = 0; trycnt < 200; trycnt++) {
387 x = rn1((hx - lx) + 1, lx);
388 y = rn1((hy - ly) + 1, ly);
389 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
390 return;
393 /* then a deterministic one */
395 for (x = lx; x <= hx; x++)
396 for (y = ly; y <= hy; y++)
397 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, TRUE, lev))
398 return;
400 impossible("Couldn't place lregion type %d!", rtype);
403 staticfn boolean
404 put_lregion_here(
405 coordxy x, coordxy y,
406 coordxy nlx, coordxy nly, coordxy nhx, coordxy nhy,
407 xint16 rtype,
408 boolean oneshot,
409 d_level *lev)
411 struct monst *mtmp;
413 if (bad_location(x, y, nlx, nly, nhx, nhy)
414 || is_exclusion_zone(rtype, x, y)) {
415 if (!oneshot) {
416 return FALSE; /* caller should try again */
417 } else {
418 /* Must make do with the only location possible;
419 avoid failure due to a misplaced trap.
420 It might still fail if there's a dungeon feature here. */
421 struct trap *t = t_at(x, y);
423 if (t && !undestroyable_trap(t->ttyp)) {
424 if (((mtmp = m_at(x, y)) != 0) && mtmp->mtrapped)
425 mtmp->mtrapped = 0;
426 deltrap(t);
428 if (bad_location(x, y, nlx, nly, nhx, nhy)
429 || is_exclusion_zone(rtype, x, y))
430 return FALSE;
433 switch (rtype) {
434 case LR_TELE:
435 case LR_UPTELE:
436 case LR_DOWNTELE:
437 /* "something" means the player in this case */
438 if ((mtmp = m_at(x, y)) != 0) {
439 /* move the monster if no choice, or just try again */
440 if (oneshot) {
441 if (!rloc(mtmp, RLOC_NOMSG))
442 m_into_limbo(mtmp);
443 } else
444 return FALSE;
446 u_on_newpos(x, y);
447 break;
448 case LR_PORTAL:
449 mkportal(x, y, lev->dnum, lev->dlevel);
450 break;
451 case LR_DOWNSTAIR:
452 case LR_UPSTAIR:
453 mkstairs(x, y, (char) rtype, (struct mkroom *) 0, FALSE);
454 break;
455 case LR_BRANCH:
456 place_branch(Is_branchlev(&u.uz), x, y);
457 break;
459 return TRUE;
462 /* fix up Baalzebub's lair, which depicts a level-sized beetle;
463 its legs are walls within solid rock--regular wallification
464 classifies them as superfluous and gets rid of them */
465 staticfn void
466 baalz_fixup(void)
468 struct monst *mtmp;
469 int x, y, lastx, lasty;
472 * baalz level's nondiggable region surrounds the "insect" and rooms.
473 * The outermost perimeter of that region is subject to wall cleanup
474 * (hence 'x + 1' and 'y + 1' for starting don't-clean column and row,
475 * 'lastx - 1' and 'lasty - 1' for ending don't-clean column and row)
476 * and the interior is protected against that (in wall_cleanup()).
478 * Assumes level.flags.corrmaze is True, otherwise the bug legs will
479 * have already been "cleaned" away by general wallification.
482 /* find low and high x for to-be-wallified portion of level */
483 y = ROWNO / 2;
484 for (lastx = x = 0; x < COLNO; ++x)
485 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
486 if (!lastx)
487 gb.bughack.inarea.x1 = x + 1;
488 lastx = x;
490 gb.bughack.inarea.x2 = ((lastx > gb.bughack.inarea.x1) ? lastx : x) - 1;
491 /* find low and high y for to-be-wallified portion of level */
492 x = gb.bughack.inarea.x1;
493 for (lasty = y = 0; y < ROWNO; ++y)
494 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
495 if (!lasty)
496 gb.bughack.inarea.y1 = y + 1;
497 lasty = y;
499 gb.bughack.inarea.y2 = ((lasty > gb.bughack.inarea.y1) ? lasty : y) - 1;
500 /* two pools mark where special post-wallify fix-ups are needed */
501 for (x = gb.bughack.inarea.x1; x <= gb.bughack.inarea.x2; ++x)
502 for (y = gb.bughack.inarea.y1; y <= gb.bughack.inarea.y2; ++y)
503 if (levl[x][y].typ == POOL) {
504 levl[x][y].typ = HWALL;
505 if (gb.bughack.delarea.x1 == COLNO)
506 gb.bughack.delarea.x1 = x, gb.bughack.delarea.y1 = y;
507 else
508 gb.bughack.delarea.x2 = x, gb.bughack.delarea.y2 = y;
509 } else if (levl[x][y].typ == IRONBARS) {
510 /* novelty effect; allowing digging in front of 'eyes' */
511 if (isok(x - 1, y)
512 && (levl[x - 1][y].wall_info & W_NONDIGGABLE) != 0) {
513 levl[x - 1][y].wall_info &= ~W_NONDIGGABLE;
514 if (isok(x - 2, y))
515 levl[x - 2][y].wall_info &= ~W_NONDIGGABLE;
516 } else if (isok(x + 1, y)
517 && (levl[x + 1][y].wall_info & W_NONDIGGABLE) != 0) {
518 levl[x + 1][y].wall_info &= ~W_NONDIGGABLE;
519 if (isok(x + 2, y))
520 levl[x + 2][y].wall_info &= ~W_NONDIGGABLE;
524 wallification(max(gb.bughack.inarea.x1 - 2, 1),
525 max(gb.bughack.inarea.y1 - 2, 0),
526 min(gb.bughack.inarea.x2 + 2, COLNO - 1),
527 min(gb.bughack.inarea.y2 + 2, ROWNO - 1));
529 /* bughack hack for rear-most legs on baalz level; first joint on
530 both top and bottom gets a bogus extra connection to room area,
531 producing unwanted rectangles; change back to separated legs */
532 x = gb.bughack.delarea.x1, y = gb.bughack.delarea.y1;
533 if (isok(x, y) && (levl[x][y].typ == TLWALL || levl[x][y].typ == TRWALL)
534 && isok(x, y + 1) && levl[x][y + 1].typ == TUWALL) {
535 levl[x][y].typ = (levl[x][y].typ == TLWALL) ? BRCORNER : BLCORNER;
536 levl[x][y + 1].typ = HWALL;
537 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
538 (void) rloc(mtmp, RLOC_ERR|RLOC_NOMSG);
541 x = gb.bughack.delarea.x2, y = gb.bughack.delarea.y2;
542 if (isok(x, y) && (levl[x][y].typ == TLWALL || levl[x][y].typ == TRWALL)
543 && isok(x, y - 1) && levl[x][y - 1].typ == TDWALL) {
544 levl[x][y].typ = (levl[x][y].typ == TLWALL) ? TRCORNER : TLCORNER;
545 levl[x][y - 1].typ = HWALL;
546 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
547 (void) rloc(mtmp, RLOC_ERR|RLOC_NOMSG);
550 /* reset bughack region; set low end to <COLNO,ROWNO> so that
551 within_bounded_region() in fix_wall_spines() will fail
552 most quickly--on its first test--when loading other levels */
553 gb.bughack.inarea.x1 = gb.bughack.delarea.x1 = COLNO;
554 gb.bughack.inarea.y1 = gb.bughack.delarea.y1 = ROWNO;
555 gb.bughack.inarea.x2 = gb.bughack.delarea.x2 = 0;
556 gb.bughack.inarea.y2 = gb.bughack.delarea.y2 = 0;
559 /* this is special stuff that the level compiler cannot (yet) handle */
560 void
561 fixup_special(void)
563 lev_region *r = gl.lregions;
564 s_level *sp;
565 struct d_level lev;
566 int x, y;
567 struct mkroom *croom;
568 boolean added_branch = FALSE;
570 if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
571 svl.level.flags.hero_memory = 0;
572 /* water level is an odd beast - it has to be set up
573 before calling place_lregions etc. */
574 setup_waterlevel();
576 for (x = 0; x < gn.num_lregions; x++, r++) {
577 switch (r->rtype) {
578 case LR_BRANCH:
579 added_branch = TRUE;
580 goto place_it;
582 case LR_PORTAL:
583 if (*r->rname.str >= '0' && *r->rname.str <= '9') {
584 /* "chutes and ladders" */
585 lev = u.uz;
586 lev.dlevel = atoi(r->rname.str);
587 } else {
588 sp = find_level(r->rname.str);
589 lev = sp->dlevel;
591 FALLTHROUGH;
592 /*FALLTHRU*/
594 case LR_UPSTAIR:
595 case LR_DOWNSTAIR:
596 place_it:
597 place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
598 r->inarea.y2, r->delarea.x1, r->delarea.y1,
599 r->delarea.x2, r->delarea.y2, r->rtype, &lev);
600 break;
602 case LR_TELE:
603 case LR_UPTELE:
604 case LR_DOWNTELE:
605 /* save the region outlines for goto_level() */
606 if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
607 svu.updest.lx = r->inarea.x1;
608 svu.updest.ly = r->inarea.y1;
609 svu.updest.hx = r->inarea.x2;
610 svu.updest.hy = r->inarea.y2;
611 svu.updest.nlx = r->delarea.x1;
612 svu.updest.nly = r->delarea.y1;
613 svu.updest.nhx = r->delarea.x2;
614 svu.updest.nhy = r->delarea.y2;
616 if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
617 svd.dndest.lx = r->inarea.x1;
618 svd.dndest.ly = r->inarea.y1;
619 svd.dndest.hx = r->inarea.x2;
620 svd.dndest.hy = r->inarea.y2;
621 svd.dndest.nlx = r->delarea.x1;
622 svd.dndest.nly = r->delarea.y1;
623 svd.dndest.nhx = r->delarea.x2;
624 svd.dndest.nhy = r->delarea.y2;
626 /* place_lregion gets called from goto_level() */
627 break;
630 if (r->rname.str)
631 free((genericptr_t) r->rname.str), r->rname.str = 0;
634 /* place dungeon branch if not placed above */
635 if (!added_branch && Is_branchlev(&u.uz)) {
636 place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
639 /* Still need to add some stuff to level file */
640 if (Is_medusa_level(&u.uz)) {
641 struct obj *otmp;
642 int tryct;
644 croom = &svr.rooms[0]; /* the first room defined on the medusa level */
645 for (tryct = rnd(4); tryct; tryct--) {
646 x = somex(croom);
647 y = somey(croom);
648 if (goodpos(x, y, (struct monst *) 0, 0)) {
649 int tryct2 = 0;
651 otmp = mk_tt_object(STATUE, x, y);
652 while (++tryct2 < 100 && otmp
653 && (poly_when_stoned(&mons[otmp->corpsenm])
654 || pm_resistance(&mons[otmp->corpsenm],
655 MR_STONE))) {
656 /* set_corpsenm() handles weight too */
657 set_corpsenm(otmp, rndmonnum());
662 if (rn2(2))
663 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
664 else /* Medusa statues don't contain books */
665 otmp =
666 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
667 somex(croom), somey(croom), CORPSTAT_NONE);
668 if (otmp) {
669 tryct = 0;
670 while (++tryct < 100
671 && (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
672 || poly_when_stoned(&mons[otmp->corpsenm]))) {
673 /* set_corpsenm() handles weight too */
674 set_corpsenm(otmp, rndmonnum());
677 } else if (Role_if(PM_CLERIC) && In_quest(&u.uz)) {
678 /* less chance for undead corpses (lured from lower morgues) */
679 svl.level.flags.graveyard = 1;
680 } else if (Is_stronghold(&u.uz)) {
681 svl.level.flags.graveyard = 1;
682 } else if (on_level(&u.uz, &baalzebub_level)) {
683 /* custom wallify the "beetle" potion of the level */
684 baalz_fixup();
685 } else if (u.uz.dnum == mines_dnum && gr.ransacked) {
686 stolen_booty();
689 if ((sp = Is_special(&u.uz)) != 0 && sp->flags.town) /* Mine Town */
690 svl.level.flags.has_town = 1;
692 if (gl.lregions)
693 free((genericptr_t) gl.lregions), gl.lregions = 0;
694 gn.num_lregions = 0;
697 staticfn void
698 check_ransacked(const char *s)
700 /* this kludge only works as long as orctown is minetn-1 */
701 gr.ransacked = (u.uz.dnum == mines_dnum && !strcmp(s, "minetn-1"));
704 #define ORC_LEADER 1
705 static const char *const orcfruit[] = { "paddle cactus", "dwarven root" };
707 staticfn void
708 migrate_orc(struct monst *mtmp, unsigned long mflags)
710 int nlev, max_depth, cur_depth;
711 d_level dest;
713 cur_depth = (int) depth(&u.uz);
714 max_depth = dunlevs_in_dungeon(&u.uz)
715 + (svd.dungeons[u.uz.dnum].depth_start - 1);
716 if (mflags == ORC_LEADER) {
717 /* Note that the orc leader will take possession of any
718 * remaining stuff not already delivered to other
719 * orcs between here and the bottom of the mines.
721 nlev = max_depth;
722 /* once in a blue moon, he won't be at the very bottom */
723 if (!rn2(40))
724 nlev--;
725 mtmp->migflags |= MIGR_LEFTOVERS;
726 } else {
727 nlev = rn2((max_depth - cur_depth) + 1) + cur_depth;
728 if (nlev == cur_depth)
729 nlev++;
730 if (nlev > max_depth)
731 nlev = max_depth;
732 mtmp->migflags = (mtmp->migflags & ~MIGR_LEFTOVERS);
734 get_level(&dest, nlev);
735 migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
738 staticfn void
739 shiny_orc_stuff(struct monst *mtmp)
741 int gemprob, goldprob, otyp;
742 struct obj *otmp;
743 boolean is_captain = (mtmp->data == &mons[PM_ORC_CAPTAIN]);
745 /* probabilities */
746 goldprob = is_captain ? 600 : 300;
747 gemprob = goldprob / 4;
748 if (rn2(1000) < goldprob) {
749 if ((otmp = mksobj(GOLD_PIECE, TRUE, FALSE)) != 0) {
750 otmp->quan = 1L + rnd(goldprob);
751 otmp->owt = weight(otmp);
752 add_to_minv(mtmp, otmp);
755 if (rn2(1000) < gemprob) {
756 if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) {
757 if (otmp->otyp == ROCK)
758 dealloc_obj(otmp);
759 else
760 add_to_minv(mtmp, otmp);
763 if (is_captain || !rn2(8)) {
764 otyp = shiny_obj(RING_CLASS);
765 if (otyp != STRANGE_OBJECT && (otmp = mksobj(otyp, TRUE, FALSE)) != 0)
766 add_to_minv(mtmp, otmp);
770 staticfn void
771 migr_booty_item(int otyp, const char *gang)
773 struct obj *otmp;
775 otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, TRUE, FALSE);
776 if (otmp && gang) {
777 new_oname(otmp, Strlen(gang) + 1); /* removes old name if present */
778 Strcpy(ONAME(otmp), gang);
779 if (objects[otyp].oc_class == FOOD_CLASS) {
780 if (otyp == SLIME_MOLD)
781 otmp->spe = fruitadd((char *) ROLL_FROM(orcfruit),
782 (struct fruit *) 0);
783 otmp->quan += (long) rn2(3);
784 otmp->owt = weight(otmp);
789 staticfn void
790 stolen_booty(void)
792 char *gang, gang_name[BUFSZ];
793 struct monst *mtmp;
794 int cnt, i, otyp;
797 * --------------------------------------------------------
798 * Mythos:
800 * A tragic accident has occurred in Frontier Town...
801 * It has been overrun by orcs.
803 * The booty that the orcs took from the town is now
804 * in the possession of the orcs that did this and
805 * have long since fled the level.
806 * --------------------------------------------------------
809 gang = rndorcname(gang_name);
810 /* create the stuff that the gang took */
811 cnt = rnd(4);
812 for (i = 0; i < cnt; ++i)
813 migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
814 cnt = rnd(3);
815 for (i = 0; i < cnt; ++i)
816 migr_booty_item(SKELETON_KEY, gang);
817 otyp = rn1((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1, LEATHER_GLOVES);
818 migr_booty_item(otyp, gang);
819 cnt = rnd(10);
820 for (i = 0; i < cnt; ++i) {
821 /* Food items - but no lembas! (or some other weird things) */
822 otyp = rn1(TIN - TRIPE_RATION + 1, TRIPE_RATION);
823 if (otyp != LEMBAS_WAFER
824 /* exclude meat <anything>, globs of <anything>, kelp
825 which all have random generation probability of 0
826 (K-/C-rations do too, but we want to include those) */
827 && (objects[otyp].oc_prob != 0
828 || otyp == C_RATION || otyp == K_RATION)
829 /* exclude food items which utilize obj->corpsenm because
830 that field is going to be overloaded for delivery purposes */
831 && otyp != CORPSE && otyp != EGG && otyp != TIN)
832 migr_booty_item(otyp, gang);
834 migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
835 /* create the leader of the orc gang */
836 mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
837 if (mtmp) {
838 mtmp = christen_monst(mtmp, upstart(gang));
839 mtmp->mpeaceful = 0;
840 set_malign(mtmp);
841 shiny_orc_stuff(mtmp);
842 migrate_orc(mtmp, ORC_LEADER);
844 /* Make most of the orcs on the level be part of the invading gang */
845 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
846 if (DEADMONSTER(mtmp))
847 continue;
849 if (is_orc(mtmp->data) && !has_mgivenname(mtmp) && rn2(10)) {
851 * We'll consider the orc captain from the level
852 * description to be the captain of a rival orc horde
853 * who is there to see what has transpired, and to
854 * contemplate future action.
856 * Don't christen the orc captain as a subordinate
857 * member of the main orc horde.
859 if (mtmp->data != &mons[PM_ORC_CAPTAIN])
860 mtmp = christen_orc(mtmp, upstart(gang), "");
863 /* Lastly, ensure there's several more orcs from the gang along the way.
864 * The mechanics are such that they aren't actually identified as
865 * members of the invading gang until they get their spoils assigned
866 * to the inventory; handled during that assignment.
868 cnt = rn2(10) + 5;
869 for (i = 0; i < cnt; ++i) {
870 int mtyp;
872 mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
873 mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
874 if (mtmp) {
875 shiny_orc_stuff(mtmp);
876 migrate_orc(mtmp, 0UL);
879 gr.ransacked = 0;
882 #undef ORC_LEADER
884 staticfn boolean
885 maze_inbounds(coordxy x, coordxy y)
887 return (x >= 2 && y >= 2
888 && x < gx.x_maze_max && y < gy.y_maze_max
889 /* isok() test is superfluous here (unless something has
890 clobbered the static *_maze_max variables) */
891 && isok(x, y));
894 staticfn void
895 maze_remove_deadends(xint16 typ)
897 char dirok[4];
898 coordxy x, y, dir, idx, idx2, dx, dy, dx2, dy2;
900 dirok[0] = 0; /* lint suppression */
901 for (x = 2; x < gx.x_maze_max; x++)
902 for (y = 2; y < gy.y_maze_max; y++)
903 if (ACCESSIBLE(levl[x][y].typ) && (x % 2) && (y % 2)) {
904 idx = idx2 = 0;
905 for (dir = 0; dir < 4; dir++) {
906 /* note: mz_move() is a macro which modifies
907 one of its first two parameters */
908 dx = dx2 = x;
909 dy = dy2 = y;
910 mz_move(dx, dy, dir);
911 if (!maze_inbounds(dx, dy)) {
912 idx2++;
913 continue;
915 mz_move(dx2, dy2, dir);
916 mz_move(dx2, dy2, dir);
917 if (!maze_inbounds(dx2, dy2)) {
918 idx2++;
919 continue;
921 if (!ACCESSIBLE(levl[dx][dy].typ)
922 && ACCESSIBLE(levl[dx2][dy2].typ)) {
923 dirok[idx++] = dir;
924 idx2++;
927 if (idx2 >= 3 && idx > 0) {
928 dx = x;
929 dy = y;
930 dir = dirok[rn2(idx)];
931 mz_move(dx, dy, dir);
932 levl[dx][dy].typ = typ;
937 /* Create a maze with specified corridor width and wall thickness
938 * TODO: rewrite walkfrom so it works on temp space, not levl
940 void
941 create_maze(int corrwid, int wallthick, boolean rmdeadends)
943 coordxy x,y;
944 coord mm;
945 int tmp_xmax = gx.x_maze_max;
946 int tmp_ymax = gy.y_maze_max;
947 int rdx = 0;
948 int rdy = 0;
949 int scale;
951 if (corrwid == -1)
952 corrwid = rnd(4);
954 if (wallthick == -1)
955 wallthick = rnd(4) - corrwid;
957 if (wallthick < 1)
958 wallthick = 1;
959 else if (wallthick > 5)
960 wallthick = 5;
962 if (corrwid < 1)
963 corrwid = 1;
964 else if (corrwid > 5)
965 corrwid = 5;
967 scale = corrwid + wallthick;
968 rdx = (gx.x_maze_max / scale);
969 rdy = (gy.y_maze_max / scale);
971 if (svl.level.flags.corrmaze)
972 for (x = 2; x < (rdx * 2); x++)
973 for (y = 2; y < (rdy * 2); y++)
974 levl[x][y].typ = STONE;
975 else
976 for (x = 2; x <= (rdx * 2); x++)
977 for (y = 2; y <= (rdy * 2); y++)
978 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
980 /* set upper bounds for maze0xy and walkfrom */
981 gx.x_maze_max = (rdx * 2);
982 gy.y_maze_max = (rdy * 2);
984 /* create maze */
985 maze0xy(&mm);
986 walkfrom((int) mm.x, (int) mm.y, 0);
988 if (rmdeadends)
989 maze_remove_deadends((svl.level.flags.corrmaze) ? CORR : ROOM);
991 /* restore bounds */
992 gx.x_maze_max = tmp_xmax;
993 gy.y_maze_max = tmp_ymax;
995 /* scale maze up if needed */
996 if (scale > 2) {
997 char tmpmap[COLNO][ROWNO];
998 int mx, my, dx, dy, rx = 1, ry = 1;
1000 /* back up the existing smaller maze */
1001 for (x = 1; x < gx.x_maze_max; x++)
1002 for (y = 1; y < gy.y_maze_max; y++) {
1003 tmpmap[x][y] = levl[x][y].typ;
1006 /* do the scaling */
1007 rx = x = 2;
1008 while (rx < gx.x_maze_max) {
1009 mx = (x % 2) ? corrwid : (x == 2 || x == rdx * 2) ? 1 : wallthick;
1010 ry = y = 2;
1011 while (ry < gy.y_maze_max) {
1012 my = (y % 2) ? corrwid
1013 : (y == 2 || y == rdy * 2) ? 1
1014 : wallthick;
1015 for (dx = 0; dx < mx; dx++)
1016 for (dy = 0; dy < my; dy++) {
1017 if (rx + dx >= gx.x_maze_max
1018 || ry + dy >= gy.y_maze_max)
1019 break;
1020 levl[rx + dx][ry + dy].typ = tmpmap[x][y];
1022 ry += my;
1023 y++;
1025 rx += mx;
1026 x++;
1032 void
1033 pick_vibrasquare_location(void)
1035 coordxy x, y;
1036 stairway *stway;
1037 int trycnt = 0;
1038 /* these are also defined in mklev.c and they may not be appropriate
1039 for mazes with corridors wider than 1 or for cavernous levels */
1040 #define x_maze_min 2
1041 #define y_maze_min 2
1044 * Pick a position where the stairs down to Moloch's Sanctum
1045 * level will ultimately be created. At that time, an area
1046 * will be altered: walls removed, moat and traps generated,
1047 * boulders destroyed. The position picked here must ensure
1048 * that that invocation area won't extend off the map.
1050 * We actually allow up to 2 squares around the usual edge of
1051 * the area to get truncated; see mkinvokearea(mklev.c).
1053 #define INVPOS_X_MARGIN (6 - 2)
1054 #define INVPOS_Y_MARGIN (5 - 2)
1055 #define INVPOS_DISTANCE 11
1056 int x_range = gx.x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
1057 y_range = gy.y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
1059 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
1060 || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
1061 debugpline2("svi.inv_pos: maze is too small! (%d x %d)",
1062 gx.x_maze_max, gy.y_maze_max);
1064 svi.inv_pos.x = svi.inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
1065 do {
1066 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
1067 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
1068 /* we don't want it to be too near the stairs, nor
1069 to be on a spot that's already in use (wall|trap) */
1070 if (++trycnt > 1000)
1071 break;
1072 } while (((stway = stairway_find_dir(TRUE)) != 0)
1073 && (x == stway->sx || y == stway->sy /*(direct line)*/
1074 || abs(x - stway->sx) == abs(y - stway->sy)
1075 || distmin(x, y, stway->sx, stway->sy) <= INVPOS_DISTANCE
1076 || !SPACE_POS(levl[x][y].typ) || occupied(x, y)));
1077 svi.inv_pos.x = x;
1078 svi.inv_pos.y = y;
1079 #undef INVPOS_X_MARGIN
1080 #undef INVPOS_Y_MARGIN
1081 #undef INVPOS_DISTANCE
1082 #undef x_maze_min
1083 #undef y_maze_min
1086 /* add objects and monsters to random maze */
1087 staticfn void
1088 populate_maze(void)
1090 int i;
1091 coord mm;
1093 for (i = rn1(8, 11); i; i--) {
1094 mazexy(&mm);
1095 (void) mkobj_at(rn2(2) ? GEM_CLASS : RANDOM_CLASS, mm.x, mm.y, TRUE);
1097 for (i = rn1(10, 2); i; i--) {
1098 mazexy(&mm);
1099 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
1101 for (i = rn2(3); i; i--) {
1102 mazexy(&mm);
1103 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
1105 for (i = rn1(5, 7); i; i--) {
1106 mazexy(&mm);
1107 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
1109 for (i = rn1(6, 7); i; i--) {
1110 mazexy(&mm);
1111 (void) mkgold(0L, mm.x, mm.y);
1113 for (i = rn1(6, 7); i; i--)
1114 mktrap(0, MKTRAP_MAZEFLAG, (struct mkroom *) 0, (coord *) 0);
1117 void
1118 makemaz(const char *s)
1120 char protofile[20];
1121 s_level *sp = Is_special(&u.uz);
1122 coord mm;
1124 if (*s) {
1125 if (sp && sp->rndlevs)
1126 Snprintf(protofile, sizeof protofile,
1127 "%s-%d", s, rnd((int) sp->rndlevs));
1128 else
1129 Strcpy(protofile, s);
1130 } else if (*(svd.dungeons[u.uz.dnum].proto)) {
1131 if (dunlevs_in_dungeon(&u.uz) > 1) {
1132 if (sp && sp->rndlevs)
1133 Snprintf(protofile, sizeof protofile,
1134 "%s%d-%d", svd.dungeons[u.uz.dnum].proto,
1135 dunlev(&u.uz), rnd((int) sp->rndlevs));
1136 else
1137 Snprintf(protofile, sizeof protofile,
1138 "%s%d", svd.dungeons[u.uz.dnum].proto,
1139 dunlev(&u.uz));
1140 } else if (sp && sp->rndlevs) {
1141 Snprintf(protofile, sizeof protofile,
1142 "%s-%d", svd.dungeons[u.uz.dnum].proto,
1143 rnd((int) sp->rndlevs));
1144 } else
1145 Strcpy(protofile, svd.dungeons[u.uz.dnum].proto);
1147 } else
1148 Strcpy(protofile, "");
1150 /* SPLEVTYPE format is "level-choice,level-choice"... */
1151 if (wizard && *protofile && sp && sp->rndlevs) {
1152 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
1154 if (ep) {
1155 /* strrchr always succeeds due to code in prior block */
1156 int len = (int) ((strrchr(protofile, '-') - protofile) + 1);
1158 while (ep && *ep) {
1159 if (!strncmp(ep, protofile, len)) {
1160 int pick = atoi(ep + len);
1162 /* use choice only if valid */
1163 if (pick > 0 && pick <= (int) sp->rndlevs)
1164 Sprintf(protofile + len, "%d", pick);
1165 break;
1166 } else {
1167 ep = strchr(ep, ',');
1168 if (ep)
1169 ++ep;
1175 if (*protofile) {
1176 check_ransacked(protofile);
1177 Strcat(protofile, LEV_EXT);
1178 gi.in_mk_themerooms = FALSE;
1179 if (load_special(protofile)) {
1180 /* some levels can end up with monsters
1181 on dead mon list, including light source monsters */
1182 dmonsfree();
1183 return; /* no mazification right now */
1185 impossible("Couldn't load \"%s\" - making a maze.", protofile);
1188 svl.level.flags.is_maze_lev = 1;
1189 svl.level.flags.corrmaze = !rn2(3);
1191 if (!Invocation_lev(&u.uz) && rn2(2)) {
1192 create_maze(-1, -1, !rn2(5));
1193 } else {
1194 create_maze(1, 1, FALSE);
1197 if (!svl.level.flags.corrmaze)
1198 wallification(2, 2, gx.x_maze_max, gy.y_maze_max);
1200 mazexy(&mm);
1201 mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0, FALSE); /* up */
1202 if (!Invocation_lev(&u.uz)) {
1203 mazexy(&mm);
1204 mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0, FALSE); /* down */
1205 } else { /* choose "vibrating square" location */
1206 pick_vibrasquare_location();
1207 maketrap(svi.inv_pos.x, svi.inv_pos.y, VIBRATING_SQUARE);
1210 /* place branch stair or portal */
1211 place_branch(Is_branchlev(&u.uz), 0, 0);
1213 populate_maze();
1216 #ifdef MICRO
1217 /* Make the mazewalk iterative by faking a stack. This is needed to
1218 * ensure the mazewalk is successful in the limited stack space of
1219 * the program. This iterative version uses the minimum amount of stack
1220 * that is totally safe.
1222 void
1223 walkfrom(coordxy x, coordxy y, schar typ)
1225 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
1226 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
1227 int q, a, dir, pos;
1228 int dirs[4];
1230 if (!typ) {
1231 if (svl.level.flags.corrmaze)
1232 typ = CORR;
1233 else
1234 typ = ROOM;
1237 pos = 1;
1238 mazex[pos] = (char) x;
1239 mazey[pos] = (char) y;
1240 while (pos) {
1241 x = (int) mazex[pos];
1242 y = (int) mazey[pos];
1243 if (!IS_DOOR(levl[x][y].typ)) {
1244 /* might still be on edge of MAP, so don't overwrite */
1245 levl[x][y].typ = typ;
1246 levl[x][y].flags = 0;
1248 q = 0;
1249 for (a = 0; a < 4; a++)
1250 if (okay(x, y, a))
1251 dirs[q++] = a;
1252 if (!q)
1253 pos--;
1254 else {
1255 dir = dirs[rn2(q)];
1256 mz_move(x, y, dir);
1257 levl[x][y].typ = typ;
1258 mz_move(x, y, dir);
1259 pos++;
1260 if (pos > CELLS)
1261 panic("Overflow in walkfrom");
1262 mazex[pos] = (char) x;
1263 mazey[pos] = (char) y;
1267 #else /* !MICRO */
1269 void
1270 walkfrom(coordxy x, coordxy y, schar typ)
1272 int q, a, dir;
1273 int dirs[4];
1275 if (!typ) {
1276 if (svl.level.flags.corrmaze)
1277 typ = CORR;
1278 else
1279 typ = ROOM;
1282 if (!IS_DOOR(levl[x][y].typ)) {
1283 /* might still be on edge of MAP, so don't overwrite */
1284 levl[x][y].typ = typ;
1285 levl[x][y].flags = 0;
1288 while (1) {
1289 q = 0;
1290 for (a = 0; a < 4; a++)
1291 if (okay(x, y, a))
1292 dirs[q++] = a;
1293 if (!q)
1294 return;
1295 dir = dirs[rn2(q)];
1296 mz_move(x, y, dir);
1297 levl[x][y].typ = typ;
1298 mz_move(x, y, dir);
1299 walkfrom(x, y, typ);
1302 #endif /* ?MICRO */
1304 /* find random point in generated corridors,
1305 so we don't create items in moats, bunkers, or walls */
1306 void
1307 mazexy(coord *cc)
1309 coordxy x, y;
1310 int allowedtyp = (svl.level.flags.corrmaze ? CORR : ROOM);
1311 int cpt = 0;
1313 do {
1314 /* once upon a time this only considered odd values greater than 2
1315 and less than N (for N=={x,y}_maze_max) because even values were
1316 where maze walls always got placed; when wider maze corridors
1317 were introduced it was changed to 1+rn2(N) which is just an
1318 obscure way to get rnd(N); probably ought to be using 2+rn2(N-1)
1319 to exclude the maze's outer boundary walls; trying and rejecting
1320 those walls will waste some of the 100 random attempts... */
1321 x = rnd(gx.x_maze_max);
1322 y = rnd(gy.y_maze_max);
1323 if (levl[x][y].typ == allowedtyp) {
1324 cc->x = x;
1325 cc->y = y;
1326 return;
1328 } while (++cpt < 100);
1329 /* 100 random attempts failed; systematically try every possibility */
1330 for (x = 1; x <= gx.x_maze_max; x++)
1331 for (y = 1; y <= gy.y_maze_max; y++)
1332 if (levl[x][y].typ == allowedtyp) {
1333 cc->x = x;
1334 cc->y = y;
1335 return;
1337 /* every spot on the area of map allowed for mazes has been rejected */
1338 panic("mazexy: can't find a place!");
1339 /*NOTREACHED*/
1340 return;
1343 void
1344 get_level_extends(
1345 coordxy *left, coordxy *top,
1346 coordxy *right, coordxy *bottom)
1348 coordxy x, y;
1349 unsigned typ;
1350 struct rm *lev;
1351 boolean found, nonwall;
1352 coordxy xmin, xmax, ymin, ymax;
1354 found = nonwall = FALSE;
1355 for (xmin = 0; !found && xmin <= COLNO; xmin++) {
1356 lev = &levl[xmin][0];
1357 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1358 typ = lev->typ;
1359 if (typ != STONE) {
1360 found = TRUE;
1361 if (!IS_WALL(typ))
1362 nonwall = TRUE;
1366 xmin -= (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1;
1367 if (xmin < 0)
1368 xmin = 0;
1370 found = nonwall = FALSE;
1371 for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
1372 lev = &levl[xmax][0];
1373 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1374 typ = lev->typ;
1375 if (typ != STONE) {
1376 found = TRUE;
1377 if (!IS_WALL(typ))
1378 nonwall = TRUE;
1382 xmax += (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1;
1383 if (xmax >= COLNO)
1384 xmax = COLNO - 1;
1386 found = nonwall = FALSE;
1387 for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
1388 lev = &levl[xmin][ymin];
1389 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1390 typ = lev->typ;
1391 if (typ != STONE) {
1392 found = TRUE;
1393 if (!IS_WALL(typ))
1394 nonwall = TRUE;
1398 ymin -= (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1;
1400 found = nonwall = FALSE;
1401 for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
1402 lev = &levl[xmin][ymax];
1403 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1404 typ = lev->typ;
1405 if (typ != STONE) {
1406 found = TRUE;
1407 if (!IS_WALL(typ))
1408 nonwall = TRUE;
1412 ymax += (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1;
1414 *left = xmin;
1415 *right = xmax;
1416 *top = ymin;
1417 *bottom = ymax;
1420 /* put a non-diggable boundary around the initial portion of a level map.
1421 * assumes that no level will initially put things beyond the isok() range.
1423 * we can't bound unconditionally on the last line with something in it,
1424 * because that something might be a niche which was already reachable,
1425 * so the boundary would be breached
1427 * we can't bound unconditionally on one beyond the last line, because
1428 * that provides a window of abuse for wallified special levels
1430 void
1431 bound_digging(void)
1433 coordxy x, y;
1434 coordxy xmin, xmax, ymin, ymax;
1436 if (Is_earthlevel(&u.uz))
1437 return; /* everything diggable here */
1439 get_level_extends(&xmin, &ymin, &xmax, &ymax);
1441 for (x = 0; x < COLNO; x++)
1442 for (y = 0; y < ROWNO; y++)
1443 if (IS_STWALL(levl[x][y].typ)
1444 && (y <= ymin || y >= ymax || x <= xmin || x >= xmax))
1445 levl[x][y].wall_info |= W_NONDIGGABLE;
1448 void
1449 mkportal(coordxy x, coordxy y, xint16 todnum, xint16 todlevel)
1451 /* a portal "trap" must be matched by a
1452 portal in the destination dungeon/dlevel */
1453 struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1455 if (!ttmp) {
1456 impossible("portal on top of portal?");
1457 return;
1459 debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1460 svd.dungeons[todnum].dname, todlevel);
1461 ttmp->dst.dnum = todnum;
1462 ttmp->dst.dlevel = todlevel;
1463 return;
1466 /* augment the Plane of Fire; called from goto_level() when arriving and
1467 moveloop_core() when on the level */
1468 void
1469 fumaroles(void)
1471 xint16 n, nmax = rn2(3);
1472 int sizemin = 5;
1473 boolean snd = FALSE, loud = FALSE;
1475 if (Is_firelevel(&u.uz)) {
1476 nmax++;
1477 sizemin += 5;
1479 if (svl.level.flags.temperature > 0) {
1480 nmax++;
1481 sizemin += 5;
1484 for (n = nmax; n; n--) {
1485 coordxy x = rn1(COLNO - 4, 3);
1486 coordxy y = rn1(ROWNO - 4, 3);
1488 if (levl[x][y].typ == LAVAPOOL) {
1489 NhRegion *r = create_gas_cloud(x, y, rn1(10, sizemin), rn1(10, 5));
1491 clear_heros_fault(r);
1492 snd = TRUE;
1493 if (distu(x, y) < 15)
1494 loud = TRUE;
1497 if (snd && !Deaf)
1498 Norep("You hear a %swhoosh!", loud ? "loud " : ""); /* Deaf-aware */
1502 * Special waterlevel stuff in endgame (TH).
1504 * Some of these functions would probably logically belong to some
1505 * other source files, but they are all so nicely encapsulated here.
1508 /* bubble movement boundaries */
1509 #define gbxmin (svx.xmin + 1)
1510 #define gbymin (svy.ymin + 1)
1511 #define gbxmax (svx.xmax - 1)
1512 #define gbymax (svy.ymax - 1)
1514 /* the bubble hero is in */
1515 static struct bubble *hero_bubble = NULL;
1517 staticfn void set_wportal(void);
1518 staticfn void mk_bubble(coordxy, coordxy, int);
1519 staticfn void mv_bubble(struct bubble *, coordxy, coordxy, boolean);
1521 /* augment the Planes of Water (for bubbles) and Air (for clouds); called
1522 from goto_level() when arriving and moveloop_core() when on the level */
1523 void
1524 movebubbles(void)
1526 static const struct rm water_pos = {
1527 cmap_b_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0, 0
1528 }, air_pos = {
1529 cmap_b_to_glyph(S_cloud), AIR, 0, 0, 0, 1, 0, 0, 0, 0
1531 static boolean up = FALSE;
1532 struct bubble *b;
1533 struct container *cons;
1534 struct trap *btrap;
1535 coordxy x, y;
1536 int i, j, bcpin = 0;
1538 /* set up the portal the first time bubbles are moved */
1539 if (!gw.wportal)
1540 set_wportal();
1542 vision_recalc(2);
1544 hero_bubble = NULL;
1546 if (Is_waterlevel(&u.uz)) {
1547 /* keep attached ball&chain separate from bubble objects */
1548 if (Punished)
1549 bcpin = unplacebc_and_covet_placebc();
1552 * Pick up everything inside of a bubble then fill all bubble
1553 * locations.
1555 for (b = up ? svb.bbubbles : ge.ebubbles; b;
1556 b = up ? b->next : b->prev) {
1557 if (b->cons)
1558 panic("movebubbles: cons != null");
1559 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1560 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1561 if (b->bm[j + 2] & (1 << i)) {
1562 if (!isok(x, y)) {
1563 impossible("movebubbles: bad pos (%d,%d)", x, y);
1564 continue;
1567 /* pick up objects, monsters, hero, and traps */
1568 if (OBJ_AT(x, y)) {
1569 struct obj *olist = (struct obj *) 0, *otmp;
1571 while ((otmp = svl.level.objects[x][y]) != 0) {
1572 remove_object(otmp);
1573 otmp->ox = otmp->oy = 0;
1574 otmp->nexthere = olist;
1575 olist = otmp;
1578 cons = (struct container *) alloc(sizeof *cons);
1579 cons->x = x;
1580 cons->y = y;
1581 cons->what = CONS_OBJ;
1582 cons->list = (genericptr_t) olist;
1583 cons->next = b->cons;
1584 b->cons = cons;
1586 if (MON_AT(x, y)) {
1587 struct monst *mon = m_at(x, y);
1589 cons = (struct container *) alloc(sizeof *cons);
1590 cons->x = x;
1591 cons->y = y;
1592 cons->what = CONS_MON;
1593 cons->list = (genericptr_t) mon;
1595 cons->next = b->cons;
1596 b->cons = cons;
1598 if (mon->wormno)
1599 remove_worm(mon);
1600 else
1601 remove_monster(x, y);
1603 newsym(x, y); /* clean up old position */
1604 mon->mx = mon->my = 0;
1605 mon->mstate |= MON_BUBBLEMOVE;
1607 if (!u.uswallow && u_at(x, y)) {
1608 cons = (struct container *) alloc(sizeof *cons);
1609 cons->x = x;
1610 cons->y = y;
1611 cons->what = CONS_HERO;
1612 cons->list = (genericptr_t) 0;
1614 cons->next = b->cons;
1615 b->cons = cons;
1616 hero_bubble = b;
1618 if ((btrap = t_at(x, y)) != 0) {
1619 cons = (struct container *) alloc(sizeof *cons);
1620 cons->x = x;
1621 cons->y = y;
1622 cons->what = CONS_TRAP;
1623 cons->list = (genericptr_t) btrap;
1625 cons->next = b->cons;
1626 b->cons = cons;
1629 levl[x][y] = water_pos;
1630 block_point(x, y);
1633 } else if (Is_airlevel(&u.uz)) {
1634 boolean xedge, yedge;
1636 for (x = 1; x <= (COLNO - 1); x++)
1637 for (y = 0; y <= (ROWNO - 1); y++) {
1638 levl[x][y] = air_pos;
1639 unblock_point(x, y);
1640 /* all air or all cloud around the perimeter of the Air
1641 level tends to look strange; break up the pattern */
1642 xedge = (boolean) (x < gbxmin || x > gbxmax);
1643 yedge = (boolean) (y < gbymin || y > gbymax);
1644 if (xedge || yedge) {
1645 if (!rn2(xedge ? 3 : 5)) {
1646 levl[x][y].typ = CLOUD;
1647 block_point(x, y);
1654 * Every second time traverse down. This is because otherwise
1655 * all the junk that changes owners when bubbles overlap
1656 * would eventually end up in the last bubble in the chain.
1658 up = !up;
1659 for (b = up ? svb.bbubbles : ge.ebubbles; b; b = up ? b->next : b->prev) {
1660 int rx = rn2(3), ry = rn2(3);
1662 mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1663 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1666 /* put attached ball&chain back */
1667 if (Is_waterlevel(&u.uz) && Punished)
1668 lift_covet_and_placebc(bcpin);
1669 gv.vision_full_recalc = 1;
1672 /* when moving in water, possibly (1 in 3) alter the intended destination */
1673 void
1674 water_friction(void)
1676 coordxy x, y, dx, dy;
1677 boolean eff = FALSE;
1679 if (Swimming && rn2(4))
1680 return; /* natural swimmers have advantage */
1682 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1683 /* cancel delta x and choose an arbitrary delta y value */
1684 x = u.ux;
1685 do {
1686 dy = rn2(3) - 1; /* -1, 0, 1 */
1687 y = u.uy + dy;
1688 } while (dy && (!isok(x, y) || !is_pool(x, y)));
1689 u.dx = 0;
1690 u.dy = dy;
1691 eff = TRUE;
1692 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1693 /* cancel delta y and choose an arbitrary delta x value */
1694 y = u.uy;
1695 do {
1696 dx = rn2(3) - 1; /* -1 .. 1 */
1697 x = u.ux + dx;
1698 } while (dx && (!isok(x, y) || !is_pool(x, y)));
1699 u.dy = 0;
1700 u.dx = dx;
1701 eff = TRUE;
1703 if (eff)
1704 pline("Water turbulence affects your movements.");
1707 void
1708 save_waterlevel(NHFILE *nhfp)
1710 struct bubble *b;
1712 if (!svb.bbubbles)
1713 return;
1715 if (perform_bwrite(nhfp)) {
1716 int n = 0;
1717 for (b = svb.bbubbles; b; b = b->next)
1718 ++n;
1719 if (nhfp->structlevel) {
1720 bwrite(nhfp->fd, (genericptr_t) &n, sizeof(int));
1721 bwrite(nhfp->fd, (genericptr_t) &svx.xmin, sizeof(int));
1722 bwrite(nhfp->fd, (genericptr_t) &svy.ymin, sizeof(int));
1723 bwrite(nhfp->fd, (genericptr_t) &svx.xmax, sizeof(int));
1724 bwrite(nhfp->fd, (genericptr_t) &svy.ymax, sizeof(int));
1726 for (b = svb.bbubbles; b; b = b->next) {
1727 if (nhfp->structlevel)
1728 bwrite(nhfp->fd, (genericptr_t) b, sizeof(struct bubble));
1731 if (release_data(nhfp))
1732 unsetup_waterlevel();
1735 /* restoring air bubbles on Plane of Water or clouds on Plane of Air */
1736 void
1737 restore_waterlevel(NHFILE *nhfp)
1739 struct bubble *b = (struct bubble *) 0, *btmp;
1740 int i, n = 0;
1742 svb.bbubbles = (struct bubble *) 0;
1743 set_wportal();
1744 if (nhfp->structlevel) {
1745 mread(nhfp->fd,(genericptr_t) &n, sizeof (int));
1746 mread(nhfp->fd,(genericptr_t) &svx.xmin, sizeof (int));
1747 mread(nhfp->fd,(genericptr_t) &svy.ymin, sizeof (int));
1748 mread(nhfp->fd,(genericptr_t) &svx.xmax, sizeof (int));
1749 mread(nhfp->fd,(genericptr_t) &svy.ymax, sizeof (int));
1751 for (i = 0; i < n; i++) {
1752 btmp = b;
1753 b = (struct bubble *) alloc((unsigned) sizeof *b);
1754 if (nhfp->structlevel)
1755 mread(nhfp->fd, (genericptr_t) b, (unsigned) sizeof *b);
1756 if (btmp) {
1757 btmp->next = b;
1758 b->prev = btmp;
1759 } else {
1760 svb.bbubbles = b;
1761 b->prev = (struct bubble *) 0;
1763 mv_bubble(b, 0, 0, TRUE);
1765 ge.ebubbles = b;
1766 if (b) {
1767 b->next = (struct bubble *) 0;
1768 } else {
1769 /* avoid "saving and reloading may fix this" */
1770 program_state.something_worth_saving = 0;
1771 /* during restore, information about what level this is might not
1772 be available so we're wishy-washy about what we describe */
1773 impossible("No %s to restore?",
1774 (Is_waterlevel(&u.uz) || Is_waterlevel(&gu.uz_save))
1775 ? "air bubbles"
1776 : (Is_airlevel(&u.uz) || Is_airlevel(&gu.uz_save))
1777 ? "clouds"
1778 : "air bubbles or clouds");
1779 program_state.something_worth_saving = 1;
1783 staticfn void
1784 set_wportal(void)
1786 /* there better be only one magic portal on water level... */
1787 for (gw.wportal = gf.ftrap; gw.wportal; gw.wportal = gw.wportal->ntrap)
1788 if (gw.wportal->ttyp == MAGIC_PORTAL)
1789 return;
1790 impossible("set_wportal(): no portal!");
1793 staticfn void
1794 setup_waterlevel(void)
1796 int typ, glyph;
1797 coordxy x, y, xskip, yskip;
1799 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1800 panic("setup_waterlevel(): [%d:%d] neither 'Water' nor 'Air'",
1801 (int) u.uz.dnum, (int) u.uz.dlevel);
1803 /* ouch, hardcoded... (file scope statics and used in bxmin,bymax,&c) */
1804 svx.xmin = 3;
1805 svy.ymin = 1;
1806 /* use separate statements so that compiler won't complain about min()
1807 comparing two constants; the alternative is to do this in the
1808 preprocessor: #if (20 > ROWNO-1) ymax=ROWNO-1 #else ymax=20 #endif */
1809 svx.xmax = 78;
1810 svx.xmax = min(svx.xmax, (COLNO - 1) - 1);
1811 svy.ymax = 20;
1812 svy.ymax = min(svy.ymax, (ROWNO - 1));
1814 /* entire level is remembered as one glyph and any unspecified portion
1815 should default to level's base element rather than to usual stone */
1816 glyph = cmap_to_glyph(Is_waterlevel(&u.uz) ? S_water : S_air);
1817 typ = Is_waterlevel(&u.uz) ? WATER : AIR;
1819 /* set unspecified terrain (stone) and hero's memory to water or air */
1820 for (x = 1; x <= COLNO - 1; x++)
1821 for (y = 0; y <= ROWNO - 1; y++) {
1822 levl[x][y].glyph = glyph;
1823 if (levl[x][y].typ == STONE)
1824 levl[x][y].typ = typ;
1827 /* make bubbles */
1828 if (Is_waterlevel(&u.uz)) {
1829 xskip = 10 + rn2(10);
1830 yskip = 4 + rn2(4);
1831 } else {
1832 xskip = 6 + rn2(4);
1833 yskip = 3 + rn2(3);
1836 for (x = gbxmin; x <= gbxmax; x += xskip)
1837 for (y = gbymin; y <= gbymax; y += yskip)
1838 mk_bubble(x, y, rn2(7));
1841 staticfn void
1842 unsetup_waterlevel(void)
1844 struct bubble *b, *bb;
1846 /* free bubbles */
1847 for (b = svb.bbubbles; b; b = bb) {
1848 bb = b->next;
1849 free((genericptr_t) b);
1851 svb.bbubbles = ge.ebubbles = (struct bubble *) 0;
1854 staticfn void
1855 mk_bubble(coordxy x, coordxy y, int n)
1858 * These bit masks make visually pleasing bubbles on a normal aspect
1859 * 25x80 terminal, which naturally results in them being mathematically
1860 * anything but symmetric. For this reason they cannot be computed
1861 * in situ, either. The first two elements tell the dimensions of
1862 * the bubble's bounding box.
1864 static const uchar
1865 bm2[] = { 2, 1, 0x3 },
1866 bm3[] = { 3, 2, 0x7, 0x7 },
1867 bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1868 bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1869 bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1870 bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1871 bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1872 *const bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1873 struct bubble *b;
1875 if (x >= gbxmax || y >= gbymax)
1876 return;
1877 if (n >= SIZE(bmask)) {
1878 impossible("n too large (mk_bubble)");
1879 n = SIZE(bmask) - 1;
1881 if (bmask[n][1] > MAX_BMASK) {
1882 panic("bmask size is larger than MAX_BMASK");
1884 b = (struct bubble *) alloc(sizeof *b);
1885 if ((x + (int) bmask[n][0] - 1) > gbxmax)
1886 x = gbxmax - bmask[n][0] + 1;
1887 if ((y + (int) bmask[n][1] - 1) > gbymax)
1888 y = gbymax - bmask[n][1] + 1;
1889 b->x = x;
1890 b->y = y;
1891 b->dx = 1 - rn2(3);
1892 b->dy = 1 - rn2(3);
1893 /* y dimension is the length of bitmap data - see bmask above */
1894 (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1895 (bmask[n][1] + 2) * sizeof (b->bm[0]));
1896 b->cons = 0;
1897 if (!svb.bbubbles)
1898 svb.bbubbles = b;
1899 if (ge.ebubbles) {
1900 ge.ebubbles->next = b;
1901 b->prev = ge.ebubbles;
1902 } else
1903 b->prev = (struct bubble *) 0;
1904 b->next = (struct bubble *) 0;
1905 ge.ebubbles = b;
1906 mv_bubble(b, 0, 0, TRUE);
1909 /* maybe change the movement direction of the bubble hero is in */
1910 void
1911 maybe_adjust_hero_bubble(void)
1913 if (!Is_waterlevel(&u.uz))
1914 return;
1916 if (!u.dx && !u.dy)
1917 return;
1919 if (hero_bubble && !rn2(2)) {
1920 hero_bubble->dx = u.dx;
1921 hero_bubble->dy = u.dy;
1926 * The player, the portal and all other objects and monsters
1927 * float along with their associated bubbles. Bubbles may overlap
1928 * freely, and the contents may get associated with other bubbles in
1929 * the process. Bubbles are "sticky", meaning that if the player is
1930 * in the immediate neighborhood of one, he/she may get sucked inside.
1931 * This property also makes leaving a bubble slightly difficult.
1933 staticfn void
1934 mv_bubble(struct bubble *b, coordxy dx, coordxy dy, boolean ini)
1936 int i, j, colli = 0;
1937 coordxy x, y;
1938 struct container *cons, *ctemp;
1940 /* clouds move slowly */
1941 if (!Is_airlevel(&u.uz) || !rn2(6)) {
1942 /* move bubble */
1943 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1944 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1945 dx = sgn(dx);
1946 dy = sgn(dy);
1950 * collision with level borders?
1951 * 1 = horizontal border, 2 = vertical, 3 = corner
1953 if (b->x <= gbxmin)
1954 colli |= 2;
1955 if (b->y <= gbymin)
1956 colli |= 1;
1957 if ((int) (b->x + b->bm[0] - 1) >= gbxmax)
1958 colli |= 2;
1959 if ((int) (b->y + b->bm[1] - 1) >= gbymax)
1960 colli |= 1;
1962 if (b->x < gbxmin) {
1963 pline("bubble xmin: x = %d, xmin = %d", b->x, gbxmin);
1964 b->x = gbxmin;
1966 if (b->y < gbymin) {
1967 pline("bubble ymin: y = %d, ymin = %d", b->y, gbymin);
1968 b->y = gbymin;
1970 if ((int) (b->x + b->bm[0] - 1) > gbxmax) {
1971 pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1972 gbxmax);
1973 b->x = gbxmax - b->bm[0] + 1;
1975 if ((int) (b->y + b->bm[1] - 1) > gbymax) {
1976 pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1977 gbymax);
1978 b->y = gbymax - b->bm[1] + 1;
1981 /* bounce if we're trying to move off the border */
1982 if (b->x == gbxmin && dx < 0)
1983 dx = -dx;
1984 if (b->x + b->bm[0] - 1 == gbxmax && dx > 0)
1985 dx = -dx;
1986 if (b->y == gbymin && dy < 0)
1987 dy = -dy;
1988 if (b->y + b->bm[1] - 1 == gbymax && dy > 0)
1989 dy = -dy;
1991 b->x += dx;
1992 b->y += dy;
1995 /* draw the bubbles */
1996 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1997 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1998 if (b->bm[j + 2] & (1 << i)) {
1999 if (Is_waterlevel(&u.uz)) {
2000 levl[x][y].typ = AIR;
2001 levl[x][y].lit = 1;
2002 unblock_point(x, y);
2003 } else if (Is_airlevel(&u.uz)) {
2004 levl[x][y].typ = CLOUD;
2005 levl[x][y].lit = 1;
2006 block_point(x, y);
2010 if (Is_waterlevel(&u.uz)) {
2011 /* replace contents of bubble */
2012 for (cons = b->cons; cons; cons = ctemp) {
2013 ctemp = cons->next;
2014 cons->x += dx;
2015 cons->y += dy;
2017 switch (cons->what) {
2018 case CONS_OBJ: {
2019 struct obj *olist, *otmp;
2021 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
2022 otmp = olist->nexthere;
2023 place_object(olist, cons->x, cons->y);
2024 stackobj(olist);
2026 break;
2029 case CONS_MON: {
2030 struct monst *mon = (struct monst *) cons->list;
2032 /* mnearto() might fail. We can jump right to elemental_clog
2033 from here rather than deal_with_overcrowding() */
2034 if (!mnearto(mon, cons->x, cons->y, TRUE, RLOC_NOMSG))
2035 elemental_clog(mon);
2036 break;
2039 case CONS_HERO: {
2040 struct monst *mtmp = m_at(cons->x, cons->y);
2041 int ux0 = u.ux, uy0 = u.uy;
2043 u_on_newpos(cons->x, cons->y);
2044 newsym(ux0, uy0); /* clean up old position */
2046 if (mtmp) {
2047 mnexto(mtmp, RLOC_NOMSG);
2049 break;
2052 case CONS_TRAP: {
2053 struct trap *btrap = (struct trap *) cons->list;
2055 btrap->tx = cons->x;
2056 btrap->ty = cons->y;
2057 break;
2060 default:
2061 impossible("mv_bubble: unknown bubble contents");
2062 break;
2064 free((genericptr_t) cons);
2066 b->cons = 0;
2069 /* boing? */
2070 switch (colli) {
2071 case 1:
2072 b->dy = -b->dy;
2073 break;
2074 case 3:
2075 b->dy = -b->dy;
2076 FALLTHROUGH;
2077 /*FALLTHRU*/
2078 case 2:
2079 b->dx = -b->dx;
2080 break;
2081 default:
2082 /* sometimes alter direction for fun anyway
2083 (higher probability for stationary bubbles) */
2084 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
2085 b->dx = 1 - rn2(3);
2086 b->dy = 1 - rn2(3);
2091 /*mkmaze.c*/