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. */
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) \
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); \
42 /* used to determine if wall spines can join this location */
44 iswall(coordxy x
, coordxy y
)
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 */
58 iswall_or_stone(coordxy x
, coordxy y
)
60 /* out of bounds = stone */
64 return (levl
[x
][y
].typ
== STONE
|| iswall(x
, y
));
67 /* return TRUE if out of bounds, wall or rock */
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 */
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
;
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?] */
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 */
103 #ifdef EXTRA_SANITY_CHECKS
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*/
114 /* set map terrain type and light state */
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*/
129 else if (lit
== SET_LIT_RANDOM
)
132 levl
[x
][y
].lit
= lit
;
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:
149 * W x W This would not extend a spine from x down
150 * W W W (a corridor of walls is formed).
153 * W x W This would extend a spine from x down.
157 extend_spine(int locale
[3][3], int wall_there
, int dx
, int dy
)
164 if (wall_there
) { /* wall in that direction */
166 if (locale
[1][0] && locale
[1][2] /* EW are wall/stone */
167 && locale
[nx
][0] && locale
[nx
][2]) { /* diag are wall/stone */
173 if (locale
[0][1] && locale
[2][1] /* NS are wall/stone */
174 && locale
[0][ny
] && locale
[2][ny
]) { /* diag are wall/stone */
187 /* Remove walls totally surrounded by stone */
189 wall_cleanup(coordxy x1
, coordxy y1
, coordxy x2
, coordxy y2
)
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
))
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))
218 /* Correct wall types so they extend and connect to each other */
220 fix_wall_spines(coordxy x1
, coordxy y1
, coordxy x2
, coordxy y2
)
225 int (*loc_f
)(coordxy
, coordxy
);
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
++) {
248 if (!(IS_WALL(type
) && type
!= DBWALL
))
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
)
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 */
276 lev
->typ
= spine_array
[bits
];
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
);
288 okay(coordxy x
, coordxy y
, coordxy dir
)
292 if (x
< 3 || y
< 3 || x
> gx
.x_maze_max
|| y
> gy
.y_maze_max
293 || levl
[x
][y
].typ
!= STONE
)
298 /* find random starting point for maze generation */
302 cc
->x
= 3 + 2 * rn2((gx
.x_maze_max
>> 1) - 1);
303 cc
->y
= 3 + 2 * rn2((gy
.y_maze_max
>> 1) - 1);
308 is_exclusion_zone(xint16 type
, coordxy x
, coordxy y
)
310 struct exclusion_zone
*ez
= sve
.exclusion_zones
;
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
))
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)
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 */
348 coordxy lx
, coordxy ly
, coordxy hx
, coordxy hy
,
349 coordxy nlx
, coordxy nly
, coordxy nhx
, coordxy nhy
,
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);
367 lx
= 1; /* column 0 is not used */
369 ly
= 0; /* 3.6.0 and earlier erroneously had 1 here */
373 /* clamp the area to the map */
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
))
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
))
400 impossible("Couldn't place lregion type %d!", rtype
);
405 coordxy x
, coordxy y
,
406 coordxy nlx
, coordxy nly
, coordxy nhx
, coordxy nhy
,
413 if (bad_location(x
, y
, nlx
, nly
, nhx
, nhy
)
414 || is_exclusion_zone(rtype
, x
, y
)) {
416 return FALSE
; /* caller should try again */
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
)
428 if (bad_location(x
, y
, nlx
, nly
, nhx
, nhy
)
429 || is_exclusion_zone(rtype
, x
, y
))
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 */
441 if (!rloc(mtmp
, RLOC_NOMSG
))
449 mkportal(x
, y
, lev
->dnum
, lev
->dlevel
);
453 mkstairs(x
, y
, (char) rtype
, (struct mkroom
*) 0, FALSE
);
456 place_branch(Is_branchlev(&u
.uz
), x
, y
);
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 */
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 */
484 for (lastx
= x
= 0; x
< COLNO
; ++x
)
485 if ((levl
[x
][y
].wall_info
& W_NONDIGGABLE
) != 0) {
487 gb
.bughack
.inarea
.x1
= x
+ 1;
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) {
496 gb
.bughack
.inarea
.y1
= y
+ 1;
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
;
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' */
512 && (levl
[x
- 1][y
].wall_info
& W_NONDIGGABLE
) != 0) {
513 levl
[x
- 1][y
].wall_info
&= ~W_NONDIGGABLE
;
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
;
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 */
563 lev_region
*r
= gl
.lregions
;
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. */
576 for (x
= 0; x
< gn
.num_lregions
; x
++, r
++) {
583 if (*r
->rname
.str
>= '0' && *r
->rname
.str
<= '9') {
584 /* "chutes and ladders" */
586 lev
.dlevel
= atoi(r
->rname
.str
);
588 sp
= find_level(r
->rname
.str
);
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
);
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() */
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
)) {
644 croom
= &svr
.rooms
[0]; /* the first room defined on the medusa level */
645 for (tryct
= rnd(4); tryct
; tryct
--) {
648 if (goodpos(x
, y
, (struct monst
*) 0, 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
],
656 /* set_corpsenm() handles weight too */
657 set_corpsenm(otmp
, rndmonnum());
663 otmp
= mk_tt_object(STATUE
, somex(croom
), somey(croom
));
664 else /* Medusa statues don't contain books */
666 mkcorpstat(STATUE
, (struct monst
*) 0, (struct permonst
*) 0,
667 somex(croom
), somey(croom
), CORPSTAT_NONE
);
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 */
685 } else if (u
.uz
.dnum
== mines_dnum
&& gr
.ransacked
) {
689 if ((sp
= Is_special(&u
.uz
)) != 0 && sp
->flags
.town
) /* Mine Town */
690 svl
.level
.flags
.has_town
= 1;
693 free((genericptr_t
) gl
.lregions
), gl
.lregions
= 0;
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"));
705 static const char *const orcfruit
[] = { "paddle cactus", "dwarven root" };
708 migrate_orc(struct monst
*mtmp
, unsigned long mflags
)
710 int nlev
, max_depth
, cur_depth
;
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.
722 /* once in a blue moon, he won't be at the very bottom */
725 mtmp
->migflags
|= MIGR_LEFTOVERS
;
727 nlev
= rn2((max_depth
- cur_depth
) + 1) + cur_depth
;
728 if (nlev
== cur_depth
)
730 if (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);
739 shiny_orc_stuff(struct monst
*mtmp
)
741 int gemprob
, goldprob
, otyp
;
743 boolean is_captain
= (mtmp
->data
== &mons
[PM_ORC_CAPTAIN
]);
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
)
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
);
771 migr_booty_item(int otyp
, const char *gang
)
775 otmp
= mksobj_migr_to_species(otyp
, (unsigned long) M2_ORC
, TRUE
, FALSE
);
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
),
783 otmp
->quan
+= (long) rn2(3);
784 otmp
->owt
= weight(otmp
);
792 char *gang
, gang_name
[BUFSZ
];
797 * --------------------------------------------------------
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 */
812 for (i
= 0; i
< cnt
; ++i
)
813 migr_booty_item(rn2(4) ? TALLOW_CANDLE
: WAX_CANDLE
, gang
);
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
);
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
);
838 mtmp
= christen_monst(mtmp
, upstart(gang
));
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
))
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.
869 for (i
= 0; i
< cnt
; ++i
) {
872 mtyp
= rn2((PM_ORC_SHAMAN
- PM_ORC
) + 1) + PM_ORC
;
873 mtmp
= makemon(&mons
[mtyp
], 0, 0, MM_NONAME
);
875 shiny_orc_stuff(mtmp
);
876 migrate_orc(mtmp
, 0UL);
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) */
895 maze_remove_deadends(xint16 typ
)
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)) {
905 for (dir
= 0; dir
< 4; dir
++) {
906 /* note: mz_move() is a macro which modifies
907 one of its first two parameters */
910 mz_move(dx
, dy
, dir
);
911 if (!maze_inbounds(dx
, dy
)) {
915 mz_move(dx2
, dy2
, dir
);
916 mz_move(dx2
, dy2
, dir
);
917 if (!maze_inbounds(dx2
, dy2
)) {
921 if (!ACCESSIBLE(levl
[dx
][dy
].typ
)
922 && ACCESSIBLE(levl
[dx2
][dy2
].typ
)) {
927 if (idx2
>= 3 && idx
> 0) {
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
941 create_maze(int corrwid
, int wallthick
, boolean rmdeadends
)
945 int tmp_xmax
= gx
.x_maze_max
;
946 int tmp_ymax
= gy
.y_maze_max
;
955 wallthick
= rnd(4) - corrwid
;
959 else if (wallthick
> 5)
964 else if (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
;
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);
986 walkfrom((int) mm
.x
, (int) mm
.y
, 0);
989 maze_remove_deadends((svl
.level
.flags
.corrmaze
) ? CORR
: ROOM
);
992 gx
.x_maze_max
= tmp_xmax
;
993 gy
.y_maze_max
= tmp_ymax
;
995 /* scale maze up if needed */
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 */
1008 while (rx
< gx
.x_maze_max
) {
1009 mx
= (x
% 2) ? corrwid
: (x
== 2 || x
== rdx
* 2) ? 1 : wallthick
;
1011 while (ry
< gy
.y_maze_max
) {
1012 my
= (y
% 2) ? corrwid
1013 : (y
== 2 || y
== rdy
* 2) ? 1
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
)
1020 levl
[rx
+ dx
][ry
+ dy
].typ
= tmpmap
[x
][y
];
1033 pick_vibrasquare_location(void)
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()}*/
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)
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
)));
1079 #undef INVPOS_X_MARGIN
1080 #undef INVPOS_Y_MARGIN
1081 #undef INVPOS_DISTANCE
1086 /* add objects and monsters to random maze */
1093 for (i
= rn1(8, 11); i
; i
--) {
1095 (void) mkobj_at(rn2(2) ? GEM_CLASS
: RANDOM_CLASS
, mm
.x
, mm
.y
, TRUE
);
1097 for (i
= rn1(10, 2); i
; i
--) {
1099 (void) mksobj_at(BOULDER
, mm
.x
, mm
.y
, TRUE
, FALSE
);
1101 for (i
= rn2(3); i
; i
--) {
1103 (void) makemon(&mons
[PM_MINOTAUR
], mm
.x
, mm
.y
, NO_MM_FLAGS
);
1105 for (i
= rn1(5, 7); i
; i
--) {
1107 (void) makemon((struct permonst
*) 0, mm
.x
, mm
.y
, NO_MM_FLAGS
);
1109 for (i
= rn1(6, 7); i
; i
--) {
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);
1118 makemaz(const char *s
)
1121 s_level
*sp
= Is_special(&u
.uz
);
1125 if (sp
&& sp
->rndlevs
)
1126 Snprintf(protofile
, sizeof protofile
,
1127 "%s-%d", s
, rnd((int) sp
->rndlevs
));
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
));
1137 Snprintf(protofile
, sizeof protofile
,
1138 "%s%d", svd
.dungeons
[u
.uz
.dnum
].proto
,
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
));
1145 Strcpy(protofile
, svd
.dungeons
[u
.uz
.dnum
].proto
);
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 */
1155 /* strrchr always succeeds due to code in prior block */
1156 int len
= (int) ((strrchr(protofile
, '-') - protofile
) + 1);
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
);
1167 ep
= strchr(ep
, ',');
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 */
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));
1194 create_maze(1, 1, FALSE
);
1197 if (!svl
.level
.flags
.corrmaze
)
1198 wallification(2, 2, gx
.x_maze_max
, gy
.y_maze_max
);
1201 mkstairs(mm
.x
, mm
.y
, 1, (struct mkroom
*) 0, FALSE
); /* up */
1202 if (!Invocation_lev(&u
.uz
)) {
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);
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.
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 */
1231 if (svl
.level
.flags
.corrmaze
)
1238 mazex
[pos
] = (char) x
;
1239 mazey
[pos
] = (char) y
;
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;
1249 for (a
= 0; a
< 4; a
++)
1257 levl
[x
][y
].typ
= typ
;
1261 panic("Overflow in walkfrom");
1262 mazex
[pos
] = (char) x
;
1263 mazey
[pos
] = (char) y
;
1270 walkfrom(coordxy x
, coordxy y
, schar typ
)
1276 if (svl
.level
.flags
.corrmaze
)
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;
1290 for (a
= 0; a
< 4; a
++)
1297 levl
[x
][y
].typ
= typ
;
1299 walkfrom(x
, y
, typ
);
1304 /* find random point in generated corridors,
1305 so we don't create items in moats, bunkers, or walls */
1310 int allowedtyp
= (svl
.level
.flags
.corrmaze
? CORR
: ROOM
);
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
) {
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
) {
1337 /* every spot on the area of map allowed for mazes has been rejected */
1338 panic("mazexy: can't find a place!");
1345 coordxy
*left
, coordxy
*top
,
1346 coordxy
*right
, coordxy
*bottom
)
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
++) {
1366 xmin
-= (nonwall
|| !svl
.level
.flags
.is_maze_lev
) ? 2 : 1;
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
++) {
1382 xmax
+= (nonwall
|| !svl
.level
.flags
.is_maze_lev
) ? 2 : 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
) {
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
) {
1412 ymax
+= (nonwall
|| !svl
.level
.flags
.is_maze_lev
) ? 2 : 1;
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
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
;
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
);
1456 impossible("portal on top of portal?");
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
;
1466 /* augment the Plane of Fire; called from goto_level() when arriving and
1467 moveloop_core() when on the level */
1471 xint16 n
, nmax
= rn2(3);
1473 boolean snd
= FALSE
, loud
= FALSE
;
1475 if (Is_firelevel(&u
.uz
)) {
1479 if (svl
.level
.flags
.temperature
> 0) {
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
);
1493 if (distu(x
, y
) < 15)
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 */
1526 static const struct rm water_pos
= {
1527 cmap_b_to_glyph(S_water
), WATER
, 0, 0, 0, 0, 0, 0, 0, 0
1529 cmap_b_to_glyph(S_cloud
), AIR
, 0, 0, 0, 1, 0, 0, 0, 0
1531 static boolean up
= FALSE
;
1533 struct container
*cons
;
1536 int i
, j
, bcpin
= 0;
1538 /* set up the portal the first time bubbles are moved */
1546 if (Is_waterlevel(&u
.uz
)) {
1547 /* keep attached ball&chain separate from bubble objects */
1549 bcpin
= unplacebc_and_covet_placebc();
1552 * Pick up everything inside of a bubble then fill all bubble
1555 for (b
= up
? svb
.bbubbles
: ge
.ebubbles
; b
;
1556 b
= up
? b
->next
: b
->prev
) {
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
)) {
1563 impossible("movebubbles: bad pos (%d,%d)", x
, y
);
1567 /* pick up objects, monsters, hero, and traps */
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
;
1578 cons
= (struct container
*) alloc(sizeof *cons
);
1581 cons
->what
= CONS_OBJ
;
1582 cons
->list
= (genericptr_t
) olist
;
1583 cons
->next
= b
->cons
;
1587 struct monst
*mon
= m_at(x
, y
);
1589 cons
= (struct container
*) alloc(sizeof *cons
);
1592 cons
->what
= CONS_MON
;
1593 cons
->list
= (genericptr_t
) mon
;
1595 cons
->next
= b
->cons
;
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
);
1611 cons
->what
= CONS_HERO
;
1612 cons
->list
= (genericptr_t
) 0;
1614 cons
->next
= b
->cons
;
1618 if ((btrap
= t_at(x
, y
)) != 0) {
1619 cons
= (struct container
*) alloc(sizeof *cons
);
1622 cons
->what
= CONS_TRAP
;
1623 cons
->list
= (genericptr_t
) btrap
;
1625 cons
->next
= b
->cons
;
1629 levl
[x
][y
] = water_pos
;
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
;
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.
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 */
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 */
1686 dy
= rn2(3) - 1; /* -1, 0, 1 */
1688 } while (dy
&& (!isok(x
, y
) || !is_pool(x
, y
)));
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 */
1696 dx
= rn2(3) - 1; /* -1 .. 1 */
1698 } while (dx
&& (!isok(x
, y
) || !is_pool(x
, y
)));
1704 pline("Water turbulence affects your movements.");
1708 save_waterlevel(NHFILE
*nhfp
)
1715 if (perform_bwrite(nhfp
)) {
1717 for (b
= svb
.bbubbles
; b
; b
= b
->next
)
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 */
1737 restore_waterlevel(NHFILE
*nhfp
)
1739 struct bubble
*b
= (struct bubble
*) 0, *btmp
;
1742 svb
.bbubbles
= (struct bubble
*) 0;
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
++) {
1753 b
= (struct bubble
*) alloc((unsigned) sizeof *b
);
1754 if (nhfp
->structlevel
)
1755 mread(nhfp
->fd
, (genericptr_t
) b
, (unsigned) sizeof *b
);
1761 b
->prev
= (struct bubble
*) 0;
1763 mv_bubble(b
, 0, 0, TRUE
);
1767 b
->next
= (struct bubble
*) 0;
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
))
1776 : (Is_airlevel(&u
.uz
) || Is_airlevel(&gu
.uz_save
))
1778 : "air bubbles or clouds");
1779 program_state
.something_worth_saving
= 1;
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
)
1790 impossible("set_wportal(): no portal!");
1794 setup_waterlevel(void)
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) */
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 */
1810 svx
.xmax
= min(svx
.xmax
, (COLNO
- 1) - 1);
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
;
1828 if (Is_waterlevel(&u
.uz
)) {
1829 xskip
= 10 + rn2(10);
1836 for (x
= gbxmin
; x
<= gbxmax
; x
+= xskip
)
1837 for (y
= gbymin
; y
<= gbymax
; y
+= yskip
)
1838 mk_bubble(x
, y
, rn2(7));
1842 unsetup_waterlevel(void)
1844 struct bubble
*b
, *bb
;
1847 for (b
= svb
.bbubbles
; b
; b
= bb
) {
1849 free((genericptr_t
) b
);
1851 svb
.bbubbles
= ge
.ebubbles
= (struct bubble
*) 0;
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.
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
};
1875 if (x
>= gbxmax
|| y
>= gbymax
)
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;
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]));
1900 ge
.ebubbles
->next
= b
;
1901 b
->prev
= ge
.ebubbles
;
1903 b
->prev
= (struct bubble
*) 0;
1904 b
->next
= (struct bubble
*) 0;
1906 mv_bubble(b
, 0, 0, TRUE
);
1909 /* maybe change the movement direction of the bubble hero is in */
1911 maybe_adjust_hero_bubble(void)
1913 if (!Is_waterlevel(&u
.uz
))
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.
1934 mv_bubble(struct bubble
*b
, coordxy dx
, coordxy dy
, boolean ini
)
1936 int i
, j
, colli
= 0;
1938 struct container
*cons
, *ctemp
;
1940 /* clouds move slowly */
1941 if (!Is_airlevel(&u
.uz
) || !rn2(6)) {
1943 if (dx
< -1 || dx
> 1 || dy
< -1 || dy
> 1) {
1944 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1950 * collision with level borders?
1951 * 1 = horizontal border, 2 = vertical, 3 = corner
1957 if ((int) (b
->x
+ b
->bm
[0] - 1) >= gbxmax
)
1959 if ((int) (b
->y
+ b
->bm
[1] - 1) >= gbymax
)
1962 if (b
->x
< gbxmin
) {
1963 pline("bubble xmin: x = %d, xmin = %d", b
->x
, gbxmin
);
1966 if (b
->y
< gbymin
) {
1967 pline("bubble ymin: y = %d, ymin = %d", 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,
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,
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)
1984 if (b
->x
+ b
->bm
[0] - 1 == gbxmax
&& dx
> 0)
1986 if (b
->y
== gbymin
&& dy
< 0)
1988 if (b
->y
+ b
->bm
[1] - 1 == gbymax
&& dy
> 0)
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
;
2002 unblock_point(x
, y
);
2003 } else if (Is_airlevel(&u
.uz
)) {
2004 levl
[x
][y
].typ
= CLOUD
;
2010 if (Is_waterlevel(&u
.uz
)) {
2011 /* replace contents of bubble */
2012 for (cons
= b
->cons
; cons
; cons
= ctemp
) {
2017 switch (cons
->what
) {
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
);
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
);
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 */
2047 mnexto(mtmp
, RLOC_NOMSG
);
2053 struct trap
*btrap
= (struct trap
*) cons
->list
;
2055 btrap
->tx
= cons
->x
;
2056 btrap
->ty
= cons
->y
;
2061 impossible("mv_bubble: unknown bubble contents");
2064 free((genericptr_t
) cons
);
2082 /* sometimes alter direction for fun anyway
2083 (higher probability for stationary bubbles) */
2084 if (!ini
&& ((b
->dx
|| b
->dy
) ? !rn2(20) : !rn2(5))) {