1 /* SCCS Id: @(#)teleport.c 3.4 2003/08/11 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 STATIC_DCL boolean
tele_jump_ok(int,int,int,int);
8 STATIC_DCL boolean
teleokX(int,int,BOOLEAN_P
);
9 STATIC_DCL boolean
teleokXconfused(int,int,BOOLEAN_P
);
10 STATIC_DCL
void vault_tele(void);
11 STATIC_DCL boolean
rloc_pos_ok(int,int,struct monst
*);
12 STATIC_DCL
void mvault_tele(struct monst
*);
15 * Is (x, y) a bad position of mtmp? If mtmp is NULL, then is (x, y) bad
18 * Caller is responsible for checking (x, y) with isok() if required.
20 * Returns: -1: Inaccessible, 0: Good pos, 1: Temporally inacessible
23 badpos(x
, y
, mtmp
, gpflags
)
28 int is_badpos
= 0, pool
, crystalpool
;
29 struct permonst
*mdat
= NULL
;
30 boolean ignorewater
= ((gpflags
& MM_IGNOREWATER
) != 0);
31 boolean crystalornot
= ((gpflags
& MM_CRYSTALORNOT
) != 0);
34 /* in many cases, we're trying to create a new monster, which
35 * can't go on top of the player or any existing monster.
36 * however, occasionally we are relocating engravings or objects,
37 * which could be co-located and thus get restricted a bit too much.
40 if (mtmp
!= &youmonst
&& x
== u
.ux
&& y
== u
.uy
&& (!u
.usteed
|| mtmp
!= u
.usteed
) )
46 /* Be careful with long worms. A monster may be placed back in
47 * its own location. Normally, if m_at() returns the same monster
48 * that we're trying to place, the monster is being placed in its
49 * own location. However, that is not correct for worm segments,
50 * because all the segments of the worm return the same m_at().
51 * Actually we overdo the check a little bit--a worm can't be placed
52 * in its own location, period. If we just checked for mtmp->mx
53 * != x || mtmp->my != y, we'd miss the case where we're called
54 * to place the worm segment and the worm's head is at x,y.
56 if (mtmp2
&& (mtmp2
!= mtmp
|| mtmp
->wormno
))
60 pool
= (is_waterypool(x
,y
) && !is_crystalwater(x
,y
));
62 if (crystalornot
) pool
= (is_drowningpool(x
,y
) && !is_crystalwater(x
,y
));
64 crystalpool
= is_crystalwater(x
,y
);
65 if (mdat
->mlet
== S_EEL
&& !pool
&& rn2(13) && !ignorewater
)
68 if ((mtmp
== &youmonst
) && (Flying
|| Levitation
) && crystalpool
) return -1;
70 if (pool
&& !ignorewater
&& !(crystalornot
&& (Flying
|| Levitation
|| Wwalking
|| Race_if(PM_KORONST
)) ) ) {
72 if (mtmp
== &youmonst
)
73 return (HLevitation
|| Flying
|| Wwalking
|| Race_if(PM_KORONST
) ||
74 Swimming
|| Amphibious
) ? is_badpos
: -1;
75 else return (is_flyer(mdat
) || is_swimmer(mdat
) ||
76 is_clinger(mdat
)) ? is_badpos
: -1;
77 /* note by Amy: the egotype check causes eternal phantom bugs in this function. According to FIQ, mtmp is
78 * never initialized correctly due to being a filler monster, instead of the actual one teleported. */
80 } else if (is_lava(x
,y
)) {
81 if (mtmp
== &youmonst
)
82 return HLevitation
? is_badpos
: -1;
84 return (is_flyer(mdat
) || likes_lava(mdat
)) ?
87 if (passes_walls(mdat
) && may_passwall(x
,y
)) return is_badpos
;
92 if (!ACCESSIBLE(levl
[x
][y
].typ
) ) {
93 if (!is_farmland(x
,y
) && !(is_waterypool(x
,y
) && !u
.roommatehack
&& (ignorewater
|| crystalornot
))) return -1;
96 if (closed_door(x
, y
) && (!mdat
|| !amorphous(mdat
)))
97 return mdat
&& (nohands(mdat
) || verysmall(mdat
)) ? -1 : 1;
98 if (sobj_at(BOULDER
, x
, y
) && (!mdat
|| !throws_rocks(mdat
)))
100 if (is_raincloud(x
,y
) && !crystalornot
) return -1;
106 * Is (x,y) a good position of mtmp? If mtmp is NULL, then is (x,y) good
109 * This function will only look at mtmp->mdat, so makemon, mplayer, etc can
110 * call it to generate new monster positions with fake monster structures.
113 goodpos(x
, y
, mtmp
, gpflags
)
118 if (!isok(x
, y
)) return FALSE
;
120 return !badpos(x
, y
, mtmp
, gpflags
);
126 * Attempt to find a good place for the given monster type in the closest
127 * position to (xx,yy). Do so in successive square rings around (xx,yy).
128 * If there is more than one valid positon in the ring, choose one randomly.
129 * Return TRUE and the position chosen when successful, FALSE otherwise.
132 enexto(cc
, xx
, yy
, mdat
)
134 register xchar xx
, yy
;
135 struct permonst
*mdat
;
137 return enexto_core(cc
, xx
, yy
, mdat
, 0);
141 enexto_core(cc
, xx
, yy
, mdat
, entflags
)
143 register xchar xx
, yy
;
144 struct permonst
*mdat
;
148 coord good
[MAX_GOOD
], *good_ptr
;
150 int xmin
, xmax
, ymin
, ymax
;
151 struct monst fakemon
; /* dummy monster */
155 pline("enexto() called with mdat==0");
157 /* default to player's original monster type */
158 mdat
= &mons
[u
.umonster
];
160 fakemon
.data
= mdat
; /* set up for goodpos */
164 * Walk around the border of the square with center (xx,yy) and
165 * radius range. Stop when we find at least one valid position.
168 xmin
= max(1, xx
-range
);
169 xmax
= min(COLNO
-1, xx
+range
);
170 ymin
= max(0, yy
-range
);
171 ymax
= min(ROWNO
-1, yy
+range
);
173 for (x
= xmin
; x
<= xmax
; x
++)
174 if (goodpos(x
, ymin
, &fakemon
, entflags
)) {
177 /* beware of accessing beyond segment boundaries.. */
178 if (good_ptr
++ == &good
[MAX_GOOD
-1]) goto full
;
180 for (x
= xmin
; x
<= xmax
; x
++)
181 if (goodpos(x
, ymax
, &fakemon
, entflags
)) {
184 /* beware of accessing beyond segment boundaries.. */
185 if (good_ptr
++ == &good
[MAX_GOOD
-1]) goto full
;
187 for (y
= ymin
+1; y
< ymax
; y
++)
188 if (goodpos(xmin
, y
, &fakemon
, entflags
)) {
191 /* beware of accessing beyond segment boundaries.. */
192 if (good_ptr
++ == &good
[MAX_GOOD
-1]) goto full
;
194 for (y
= ymin
+1; y
< ymax
; y
++)
195 if (goodpos(xmax
, y
, &fakemon
, entflags
)) {
198 /* beware of accessing beyond segment boundaries.. */
199 if (good_ptr
++ == &good
[MAX_GOOD
-1]) goto full
;
203 /* return if we've grown too big (nothing is valid) */
204 if (range
> ROWNO
&& range
> COLNO
) return FALSE
;
205 } while (good_ptr
== good
);
208 i
= rn2((int)(good_ptr
- good
));
217 * Attempt to find nc good places for the given monster type with the shortest
218 * path to (xx,yy). Where there is more than one valid set of positions, one
219 * will be chosen at random. Return the number of positions found.
220 * Warning: This routine is much slower than enexto and should be used
224 #define EPATHTO_UNSEEN 0x0
225 #define EPATHTO_INACCESSIBLE 0x1
226 #define EPATHTO_DONE 0x2
227 #define EPATHTO_TAIL(n) (0x3 + ((n) & 1))
229 #define EPATHTO_XY(x,y) (((y) + 1) * COLNO + (x))
230 #define EPATHTO_Y(xy) ((xy) / COLNO - 1)
231 #define EPATHTO_X(xy) ((xy) % COLNO)
234 coord epathto_debug_cc
[100];
238 epathto(cc
, nc
, xx
, yy
, mdat
)
241 register xchar xx
, yy
;
242 struct permonst
*mdat
;
244 int i
, j
, dir
, ndirs
, xy
, x
, y
, r
;
245 int path_len
, postype
;
246 int first_col
, last_col
;
249 static const int dirs
[8] =
250 /* N, S, E, W, NW, NE, SE, SW */
251 { -COLNO
, COLNO
, 1, -1, -COLNO
-1, -COLNO
+1, COLNO
+1, COLNO
-1};
252 struct monst fakemon
; /* dummy monster */
253 fakemon
.data
= mdat
; /* set up for badpos */
254 map
= (unsigned char *)alloc(COLNO
* (ROWNO
+ 2));
255 (void) memset((void *)map
, EPATHTO_INACCESSIBLE
, COLNO
* (ROWNO
+ 2));
256 for(i
= 1; i
< COLNO
; i
++)
257 for(j
= 0; j
< ROWNO
; j
++)
258 map
[EPATHTO_XY(i
, j
)] = EPATHTO_UNSEEN
;
259 map
[EPATHTO_XY(xx
, yy
)] = EPATHTO_TAIL(0);
260 if (badpos(xx
, yy
, &fakemon
, 0) == 0) {
267 for(path_len
= 0; nd
< nc
; path_len
++)
269 first_col
= max(1, xx
- path_len
);
270 last_col
= min(COLNO
- 1, xx
+ path_len
);
271 for(j
= max(0, yy
- path_len
); j
<= min(ROWNO
- 1, yy
+ path_len
); j
++)
272 for(i
= first_col
; i
<= last_col
; i
++)
273 if (map
[EPATHTO_XY(i
, j
)] == EPATHTO_TAIL(path_len
)) {
274 map
[EPATHTO_XY(i
, j
)] = EPATHTO_DONE
;
275 ndirs
= (isgridbug(mdat
)) ? 4 : 8;
276 for(dir
= 0; dir
< ndirs
; dir
++) {
277 xy
= EPATHTO_XY(i
, j
) + dirs
[dir
];
278 if (map
[xy
] == EPATHTO_UNSEEN
) {
281 postype
= badpos(x
, y
, &fakemon
, 0);
282 map
[xy
] = postype
< 0 ? EPATHTO_INACCESSIBLE
:
283 EPATHTO_TAIL(path_len
+ 1);
290 else if (rn2(n
- nd
+ 1) < nc
- nd
)
292 r
= rn2(nc
- nd
) + nd
;
302 break; /* No more positions */
309 if (cc
== epathto_debug_cc
)
315 win
= create_nhwindow(NHW_TEXT
);
317 for (y
= 0; y
< ROWNO
; y
++) {
318 for (x
= 1; x
< COLNO
; x
++) {
319 xy
= EPATHTO_XY(x
, y
);
320 if (map
[xy
] == EPATHTO_INACCESSIBLE
) {
321 glyph
= back_to_glyph(x
, y
);
322 row
[x
] = showsyms
[glyph_to_cmap(glyph
)];
327 for (i
= 0; i
< nd
; i
++)
329 row
[cc
[i
].x
] = i
< 10 ? '0' + i
:
330 i
< 36 ? 'a' + i
- 10 :
331 i
< 62 ? 'A' + i
- 36 :
333 /* remove trailing spaces */
334 for (x
= COLNO
-1; x
>= 1; x
--)
335 if (row
[x
] != ' ') break;
338 putstr(win
, 0, &row
[1]);
340 display_nhwindow(win
, TRUE
);
341 destroy_nhwindow(win
);
350 * func should return 1 if the location should be counted as inaccessible
351 * (path won't continue through this point) or 0 if it is accessible.
355 xpathto(r
, xx
, yy
, func
, data
)
357 register xchar xx
, yy
;
358 int (*func
)(void *, int, int);
361 int i
, j
, dir
, xy
, x
, y
;
362 int path_len
, postype
;
363 int first_col
, last_col
;
366 static const int dirs
[8] =
367 /* N, S, E, W, NW, NE, SE, SW */
368 { -COLNO
, COLNO
, 1, -1, -COLNO
-1, -COLNO
+1, COLNO
+1, COLNO
-1};
369 map
= (unsigned char *)alloc(COLNO
* (ROWNO
+ 2));
370 (void) memset((void *)map
, EPATHTO_INACCESSIBLE
, COLNO
* (ROWNO
+ 2));
371 for(i
= 1; i
< COLNO
; i
++)
372 for(j
= 0; j
< ROWNO
; j
++)
373 map
[EPATHTO_XY(i
, j
)] = EPATHTO_UNSEEN
;
374 map
[EPATHTO_XY(xx
, yy
)] = EPATHTO_TAIL(0);
375 if (func(data
, xx
, yy
) == 0)
379 for(path_len
= 0; path_len
< r
; path_len
++)
381 first_col
= max(1, xx
- path_len
);
382 last_col
= min(COLNO
- 1, xx
+ path_len
);
383 for(j
= max(0, yy
- path_len
); j
<= min(ROWNO
- 1, yy
+ path_len
); j
++)
384 for(i
= first_col
; i
<= last_col
; i
++)
385 if (map
[EPATHTO_XY(i
, j
)] == EPATHTO_TAIL(path_len
)) {
386 map
[EPATHTO_XY(i
, j
)] = EPATHTO_DONE
;
387 for(dir
= 0; dir
< 8; dir
++) {
388 xy
= EPATHTO_XY(i
, j
) + dirs
[dir
];
389 if (map
[xy
] == EPATHTO_UNSEEN
) {
392 postype
= func(data
, x
, y
);
393 map
[xy
] = postype
? EPATHTO_INACCESSIBLE
:
394 EPATHTO_TAIL(path_len
+ 1);
401 break; /* No more positions */
410 wiz_debug_cmd() /* in this case, run epathto on arbitary monster & goal */
412 struct permonst
*ptr
;
418 pline(thats_enough_tries
);
421 getlin("What monster do you want to test? [type the name]", buf
);
423 mndx
= name_to_mon(buf
);
424 if (mndx
== NON_PM
) {
425 pline("Such creatures do not exist in this world.");
429 pline("Which position do you want to aim for?");
432 if (getpos(&cc
, TRUE
, "the goal position") < 0)
434 epathto(epathto_debug_cc
, SIZE(epathto_debug_cc
), cc
.x
, cc
.y
, ptr
);
441 * Check for restricted areas present in some special levels. (This might
442 * need to be augmented to allow deliberate passage in wizard mode, but
443 * only for explicitly chosen destinations.)
446 tele_jump_ok(x1
, y1
, x2
, y2
)
449 if (dndest
.nlx
> 0) {
450 /* if inside a restricted region, can't teleport outside */
451 if (within_bounded_area(x1
, y1
, dndest
.nlx
, dndest
.nly
,
452 dndest
.nhx
, dndest
.nhy
) &&
453 !within_bounded_area(x2
, y2
, dndest
.nlx
, dndest
.nly
,
454 dndest
.nhx
, dndest
.nhy
))
459 /* and if outside, can't teleport inside */
460 if (!within_bounded_area(x1
, y1
, dndest
.nlx
, dndest
.nly
,
461 dndest
.nhx
, dndest
.nhy
) &&
462 within_bounded_area(x2
, y2
, dndest
.nlx
, dndest
.nly
,
463 dndest
.nhx
, dndest
.nhy
))
469 if (updest
.nlx
> 0) { /* ditto */
470 if (within_bounded_area(x1
, y1
, updest
.nlx
, updest
.nly
,
471 updest
.nhx
, updest
.nhy
) &&
472 !within_bounded_area(x2
, y2
, updest
.nlx
, updest
.nly
,
473 updest
.nhx
, updest
.nhy
))
478 if (!within_bounded_area(x1
, y1
, updest
.nlx
, updest
.nly
,
479 updest
.nhx
, updest
.nhy
) &&
480 within_bounded_area(x2
, y2
, updest
.nlx
, updest
.nly
,
481 updest
.nhx
, updest
.nhy
))
495 if (!trapok
&& t_at(x
, y
)) return FALSE
;
496 if (!goodpos(x
, y
, &youmonst
, 0)) return FALSE
;
498 /* In Soviet Russia, water is considered safe as long as you can swim, because hehehe. --Amy */
500 if (is_waterypool(x
, y
) && !(HLevitation
|| Flying
|| Wwalking
|| Race_if(PM_KORONST
) || (issoviet
&& (Swimming
|| Amphibious
) ) )) return FALSE
;
501 if (is_watertunnel(x
,y
) && (Levitation
|| Flying
) && !Passes_walls
) return FALSE
;
502 if (is_watertunnel(x
,y
) && !(Levitation
|| Flying
|| (issoviet
&& (Swimming
|| Amphibious
) ))) return FALSE
;
504 if (!tele_jump_ok(u
.ux
, u
.uy
, x
, y
)) return FALSE
;
505 if (!in_out_region(x
, y
)) return FALSE
;
510 teleok_normalterrain(x
, y
, trapok
)
514 if (!trapok
&& t_at(x
, y
)) return FALSE
;
515 if (!goodpos(x
, y
, &youmonst
, 0)) return FALSE
;
517 /* In Soviet Russia, water is considered safe as long as you can swim, because hehehe. --Amy */
519 if (is_waterypool(x
, y
)) return FALSE
;
520 if (is_watertunnel(x
,y
)) return FALSE
;
521 if (!ACCESSIBLE(levl
[x
][y
].typ
) ) return FALSE
;
523 if (closed_door(x
, y
)) return FALSE
;
524 if (is_raincloud(x
,y
)) return FALSE
;
526 if (!tele_jump_ok(u
.ux
, u
.uy
, x
, y
)) return FALSE
;
527 if (!in_out_region(x
, y
)) return FALSE
;
532 teleokX(x
, y
, trapok
)
536 int udist
= distu(x
,y
);
538 if (!trapok
&& t_at(x
, y
)) return FALSE
;
539 if (!goodpos(x
, y
, &youmonst
, 0)) return FALSE
;
541 if (is_waterypool(x
, y
) && !(HLevitation
|| Flying
|| Wwalking
|| Race_if(PM_KORONST
))) return FALSE
;
542 if (is_watertunnel(x
,y
) && (Levitation
|| Flying
) && !Passes_walls
) return FALSE
;
543 if (is_watertunnel(x
,y
) && !(Levitation
|| Flying
)) return FALSE
;
545 if (!tele_jump_ok(u
.ux
, u
.uy
, x
, y
)) return FALSE
;
546 if (!in_out_region(x
, y
)) return FALSE
;
547 if (udist
< 3) return FALSE
;
548 if (udist
> 100) return FALSE
;
554 teleokXconfused(x
, y
, trapok
)
558 int udist
= distu(x
,y
);
560 if (!trapok
&& t_at(x
, y
)) return FALSE
;
561 if (!goodpos(x
, y
, &youmonst
, 0)) return FALSE
;
563 if (is_waterypool(x
, y
) && !(HLevitation
|| Flying
|| Wwalking
|| Race_if(PM_KORONST
))) return FALSE
;
564 if (is_watertunnel(x
,y
) && (Levitation
|| Flying
) && !Passes_walls
) return FALSE
;
565 if (is_watertunnel(x
,y
) && !(Levitation
|| Flying
)) return FALSE
;
567 if (!tele_jump_ok(u
.ux
, u
.uy
, x
, y
)) return FALSE
;
568 if (!in_out_region(x
, y
)) return FALSE
;
569 if (udist
< 3) return FALSE
;
570 if (udist
> 200) return FALSE
;
576 teleds(nux
, nuy
, allow_drag
)
577 register int nux
,nuy
;
580 boolean ball_active
= (Punished
&& uball
->where
!= OBJ_FREE
),
581 ball_still_in_range
= FALSE
;
583 /* If they have to move the ball, then drag if allow_drag is true;
584 * otherwise they are teleporting, so unplacebc().
585 * If they don't have to move the ball, then always "drag" whether or
586 * not allow_drag is true, because we are calling that function, not
587 * to drag, but to move the chain. *However* there are some dumb
590 * _X move east -----> X_
592 * These are permissible if teleporting, but not if dragging. As a
593 * result, drag_ball() needs to know about allow_drag and might end
594 * up dragging the ball anyway. Also, drag_ball() might find that
595 * dragging the ball is completely impossible (ball in range but there's
596 * rock in the way), in which case it teleports the ball on its own.
599 if (!carried(uball
) && distmin(nux
, nuy
, uball
->ox
, uball
->oy
) <= 2)
600 ball_still_in_range
= TRUE
; /* don't have to move the ball */
602 /* have to move the ball */
603 if (!allow_drag
|| distmin(u
.ux
, u
.uy
, nux
, nuy
) > 1) {
604 /* we should not have dist > 1 and allow_drag at the same
605 * time, but just in case, we must then revert to teleport.
617 if (hides_under(youmonst
.data
) || (uarmh
&& itemhasappearance(uarmh
, APP_SECRET_HELMET
) ) || (!night() && uarmg
&& uarmg
->oartifact
== ART_NIGHTLY_HIGHWAY
) || (uarmc
&& uarmc
->oartifact
== ART_JANA_S_EXTREME_HIDE_AND_SE
))
618 u
.uundetected
= OBJ_AT(nux
, nuy
);
619 else if (is_wagon(nux
, nuy
))
620 u
.uundetected
= TRUE
;
621 else if (youmonst
.data
->mlet
== S_EEL
)
622 u
.uundetected
= is_waterypool(nux
, nuy
);
625 /* mimics stop being unnoticed */
626 if (youmonst
.data
->mlet
== S_MIMIC
)
627 youmonst
.m_ap_type
= M_AP_NOTHING
;
631 u
.uswldtim
= u
.uswallow
= 0;
632 if (Punished
&& !ball_active
) {
633 /* ensure ball placement, like unstuck */
640 if (ball_still_in_range
|| allow_drag
) {
642 xchar ballx
, bally
, chainx
, chainy
;
645 if (drag_ball(nux
, nuy
, &bc_control
, &ballx
, &bally
,
646 &chainx
, &chainy
, &cause_delay
, allow_drag
))
647 move_bc(0, bc_control
, ballx
, bally
, chainx
, chainy
);
650 /* must set u.ux, u.uy after drag_ball(), which may need to know
651 the old position if allow_drag is true... */
654 fill_pit(u
.ux0
, u
.uy0
);
656 if (!ball_still_in_range
&& !allow_drag
)
659 initrack(); /* teleports mess up tracking monsters without this */
660 update_player_regions();
661 /* Move your steed, too */
668 * Make sure the hero disappears from the old location. This will
669 * not happen if she is teleported within sight of her previous
670 * location. Force a full vision recalculation because the hero
671 * is now in a new location.
675 vision_full_recalc
= 1;
677 vision_recalc(0); /* vision before effects */
679 invocation_message();
683 safe_teleds(allow_drag
)
686 register int nux
, nuy
, tcnt
= 0;
691 } while (!teleok(nux
, nuy
, (boolean
)(tcnt
> 200)) && ++tcnt
<= 400);
694 teleds(nux
, nuy
, allow_drag
);
700 /* safer teleportation that always ignores stuff like phaseable walls, water and so on --Amy
701 * used for e.g. automatic relocation when entering forging chambers, where we really don't want players to end up
702 * outside of the actual playing field with sucky odds of getting back (e.g. sokoban level clones) */
704 safe_teleds_normalterrain(allow_drag
)
707 register int nux
, nuy
, tcnt
= 0;
712 } while (!teleok_normalterrain(nux
, nuy
, (boolean
)(tcnt
> 200)) && ++tcnt
<= 4000);
715 teleds(nux
, nuy
, allow_drag
);
717 } else /* still didn't find a place? grudgingly use the regular method then */
722 } while (!teleok(nux
, nuy
, (boolean
)(tcnt
> 200)) && ++tcnt
<= 400);
725 teleds(nux
, nuy
, allow_drag
);
735 safe_teledsPD(confused
)
738 register int nux
, nuy
, tcnt
, udist
, goodspot
= 0;
744 } while (!teleokXconfused(nux
, nuy
, (boolean
)(tcnt
> 200)) && ++tcnt
<= 2000 );
749 } while (!teleokX(nux
, nuy
, (boolean
)(tcnt
> 200)) && ++tcnt
<= 2000 );
752 /*pline("Phase door destination: %d,%d", nux, nuy);*/
753 /* this debug information is no longer needed now --Amy */
758 /*pline("Phase door failed. Destination would have been: %d,%d", nux, nuy);*/
765 safe_teledsNOTRAP(allow_drag
)
768 register int nux
, nuy
, tcnt
= 0;
773 } while ((!teleok(nux
, nuy
, (boolean
)(tcnt
> 200)) || (t_at(nux
, nuy
)) ) && ++tcnt
<= 400);
776 teleds(nux
, nuy
, allow_drag
);
785 register struct mkroom
*croom
= search_special(VAULT
);
788 if (croom
&& somexy(croom
, &c
) && teleok(c
.x
,c
.y
,FALSE
)) {
789 teleds(c
.x
,c
.y
,FALSE
);
796 teleport_pet(mtmp
, force_it
)
797 register struct monst
*mtmp
;
800 register struct obj
*otmp
;
802 if (mtmp
== u
.usteed
)
805 if (mtmp
->mleashed
) {
806 otmp
= get_mleash(mtmp
);
808 impossible("%s is leashed, without a leash.", Monnam(mtmp
));
811 if (otmp
->cursed
&& !force_it
) {
815 Your("leash goes slack.");
817 m_unleash(mtmp
, FALSE
);
829 /* Disable teleportation in stronghold && Vlad's Tower */
830 if ((level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
) && !Race_if(PM_RODNEYAN
) ) {
834 pline("A mysterious force prevents you from teleporting!");
841 /* don't show trap if "Sorry..." */
842 if (!Blinded
) make_blinded(0L,FALSE
);
848 ((u
.uhave
.amulet
&& !u
.freeplaymode
&& (u
.amuletcompletelyimbued
|| !rn2(3))) || CannotTeleport
|| On_W_tower_level(&u
.uz
)
849 || (u
.usteed
&& mon_has_amulet(u
.usteed
))
855 You_feel("disoriented for a moment.");
859 /* at this point, you will definitely be teleported... unless something prevents it */
862 boolean teletrapactive
= FALSE
;
863 struct monst
*teletrapmon
;
864 for (teletrapmon
= fmon
; teletrapmon
; teletrapmon
= teletrapmon
->nmon
) {
865 if (DEADMONSTER(teletrapmon
)) continue;
866 if (teletrapmon
->mnum
== PM_TELEPORT_TRAP
) {
867 teletrapactive
= TRUE
;
872 if (teletrapactive
) {
874 int nastytrapdur
= (Role_if(PM_GRADUATE
) ? 6 : Role_if(PM_GEEK
) ? 12 : 24);
875 if (!nastytrapdur
) nastytrapdur
= 24; /* fail safe */
876 int blackngdur
= (Role_if(PM_GRADUATE
) ? 2000 : Role_if(PM_GEEK
) ? 1000 : 500);
877 if (!blackngdur
) blackngdur
= 500; /* fail safe */
878 randomnastytrapeffect(rnz(nastytrapdur
* (monster_difficulty() + 1)), blackngdur
- (monster_difficulty() * 3));
886 if ((Teleport_control
&& !Stunned
&& (!level
.flags
.has_insideroom
|| !rn2(5)) && rn2(StrongTeleport_control
? 20 : 4)) /* low chance for tele control to fail --Amy */
888 || (wizard
&& yn_function("Invoke wizard-mode teleport control?", ynchars
, 'y') == 'y')
892 pline("Being unconscious, you cannot control your teleport.");
895 if (u
.usteed
) sprintf(buf
," and %s", mon_nam(u
.usteed
));
896 pline("To what position do you%s want to be teleported?", u
.usteed
? buf
: "");
899 if (getpos(&cc
, TRUE
, "the desired position") < 0)
901 /* possible extensions: introduce a small error if
902 magic power is low; allow transfer to solid rock */
904 /* Amy edit: teleporting on a trap will trigger it, instead of being invalid. I've specifically made it
905 * so that weird-looking rooms make teleport control likely to fail, but if you spam-spam-spam Ctrl-T to
906 * skip past that and teleport on the pentagram anyway, and there's a trap on it, well... :P */
907 if (teleok(cc
.x
, cc
.y
, TRUE
)) {
908 teleds(cc
.x
, cc
.y
, FALSE
);
915 u
.cnd_teleportcount
++;
916 (void) safe_teleds(FALSE
);
922 /* Disable teleportation in stronghold && Vlad's Tower */
923 if ((level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
) && !Race_if(PM_RODNEYAN
) ) {
924 pline("A mysterious force prevents you from teleporting!");
928 /* don't show trap if "Sorry..." */
929 if (!Blinded
) make_blinded(0L,FALSE
);
932 ((u
.uhave
.amulet
&& !u
.freeplaymode
&& (u
.amuletcompletelyimbued
|| !rn2(3))) || CannotTeleport
|| On_W_tower_level(&u
.uz
)
933 || (u
.usteed
&& mon_has_amulet(u
.usteed
))
936 You_feel("disoriented for a moment.");
939 u
.cnd_teleportcount
++;
940 (void) safe_teleds(FALSE
);
947 /* Disable teleportation in stronghold && Vlad's Tower */
948 if ((level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
) && !Race_if(PM_RODNEYAN
) ) {
949 pline("A mysterious force prevents you from phasing!");
954 ((u
.uhave
.amulet
&& !u
.freeplaymode
&& (u
.amuletcompletelyimbued
|| !rn2(3))) || CannotTeleport
|| On_W_tower_level(&u
.uz
)
955 || (u
.usteed
&& mon_has_amulet(u
.usteed
))
958 You_feel("disoriented for a moment.");
962 pline("Your body is transported to another location!"); /* for debug purposes --Amy */
964 u
.cnd_phasedoorcount
++;
965 (void) safe_teledsPD(confused
);
968 /* the player has used the Ctrl-T or "dotele" command, as opposed to automatically trying to teleport after falling into
969 * a pool of water or something. This is a very powerful ability that only costs 30 Pw, so there needs to be a downside */
973 return dotele_post(TRUE
);
981 boolean reallycostly
= FALSE
;
983 trap
= t_at(u
.ux
, u
.uy
);
984 if (trap
&& (!trap
->tseen
|| trap
->ttyp
!= TELEP_TRAP
))
989 pline("This is a vault teleport, usable once only.");
990 if (yn("Jump in?") == 'n')
998 You("%s onto the teleportation trap.",
999 locomotion(youmonst
.data
, "jump"));
1002 boolean castit
= FALSE
;
1003 register int sp_no
= 0, energy
= 0;
1005 if ((!Teleportation
|| (u
.ulevel
< (Race_if(PM_LICH_WARRIOR
) ? 1 : Race_if(PM_RODNEYAN
) ? 1 : Role_if(PM_WIZARD
) ? 8 : 12) && !can_teleport(youmonst
.data
))) && !(uarmf
&& uarmf
->oartifact
== ART_HAWAIIAN_KAMEHAMEHA
) && !(uarmh
&& uarmh
->oartifact
== ART_TRIP_TERRAIN
)) {
1006 /* Try to use teleport away spell. */
1007 if (objects
[SPE_TELEPORT_AWAY
].oc_name_known
&& !Confusion
&& !costly
) {
1008 for (sp_no
= 0; sp_no
< MAXSPELL
; sp_no
++) {
1009 if (spl_book
[sp_no
].sp_id
== SPE_TELEPORT_AWAY
) {
1013 if (spl_book
[sp_no
].sp_id
== SPE_TELEPORT_SELF
) {
1024 You("don't know that spell.");
1025 else You("are not able to teleport at will.");
1033 reallycostly
= TRUE
; /* no trap, and using teleportitis at will */
1036 if ( (u
.uhunger
<= (costly
? 100 : 30)) || ACURR(A_STR
) < 6) {
1040 You("lack the strength %s.",
1041 castit
? "for a teleport spell" : "to teleport");
1048 energy
= objects
[SPE_TELEPORT_AWAY
].oc_level
* 5; /* even if it was teleport self (arbitrary) --Amy */
1049 if (costly
) energy
= 100;
1050 if (powerfulimplants() && uimplant
&& uimplant
->oartifact
== ART_KATRIN_S_SUDDEN_APPEARANCE
) energy
/= 2;
1051 if (uarmh
&& uarmh
->oartifact
== ART_TRIP_TERRAIN
) energy
/= 3;
1052 if (u
.uen
< energy
) {
1059 You("lack the energy %s. You need at least %d.",
1060 castit
? "for a teleport spell" : "to teleport", energy
);
1065 /* unless you have double teleportitis, which will be the case very rarely, you'll be unable to control it */
1066 if (reallycostly
&& !StrongTeleportation
) u
.uprops
[DEAC_TELEPORT_CONTROL
].intrinsic
++;
1069 "Your concentration falters from carrying so much."))
1073 exercise(A_WIS
, TRUE
);
1074 if (spelleffects(sp_no
, TRUE
))
1088 if (trap
&& trap
->once
) vault_tele();
1091 /* using teleportitis at will now stuns you, to reduce the player's ability to abuse it --Amy */
1092 if (reallycostly
) make_stunned(HStun
+ rn1(3,3), TRUE
);
1096 You("%s", shudder_for_moment
);
1099 if (!trap
) morehungry(costly
? 100 : 30);
1106 register int newlev
;
1108 const char *escape_by_flying
= 0; /* when surviving dest of -N */
1110 boolean force_dest
= FALSE
;
1111 if (iflags
.debug_fuzzer
) goto random_levtport
;
1113 if (((u
.uhave
.amulet
&& !u
.freeplaymode
) || CannotTeleport
|| In_endgame(&u
.uz
) || In_sokoban_real(&u
.uz
) || (Role_if(PM_CAMPERSTRIKER
) && In_quest(&u
.uz
)) || (u
.usteed
&& mon_has_amulet(u
.usteed
)) )
1118 You_feel("very disoriented for a moment.");
1120 if (uarmh
&& itemhasappearance(uarmh
, APP_WEEPING_HELMET
) ) {
1121 pline("Your helmet does not like the idea of blocked level teleportation!");
1122 losexp("weeping helmet", TRUE
, FALSE
);
1123 /* This ignores level-drain resistance (not a bug). --Amy */
1129 You_hear("faint weeping...");
1130 losexp("weeping", TRUE
, FALSE
);
1136 /* Skipping the quest via teleport control is lame. --Amy */
1137 if ((Teleport_control
&& !(In_quest(&u
.uz
)) && !(In_minotaurmaze(&u
.uz
)) && !Stunned
&& (!level
.flags
.has_insideroom
|| !rn2(5)) && rn2(StrongTeleport_control
? 10 : 3)) /* Teleport control might not always work. --Amy */
1139 || (wizard
&& yn_function("Invoke wizard-mode teleport control?", ynchars
, 'y') == 'y')
1145 strcpy(qbuf
, "To what level do you want to teleport?");
1147 if (++trycnt
== 2) {
1149 if (wizard
) strcat(qbuf
, " [type a number or ? for a menu]");
1152 strcat(qbuf
, " [type a number]");
1155 if (!strcmp(buf
,"\033")) { /* cancelled */
1156 if (Confusion
&& rnl(5)) {
1157 pline("Uh-oh..."); /* don't make player jump out of their seat with a fake panic message! --Amy */
1158 goto random_levtport
;
1161 } else if (!strcmp(buf
,"*")) {
1162 goto random_levtport
;
1163 } else if (Confusion
&& rnl(5)) {
1165 goto random_levtport
;
1168 if (wizard
&& !strcmp(buf
,"?")) {
1172 if ((newlev
= (int)print_dungeon(TRUE
, &destlev
, &destdnum
))) {
1173 newlevel
.dnum
= destdnum
;
1174 newlevel
.dlevel
= destlev
;
1175 if (In_endgame(&newlevel
) && !In_endgame(&u
.uz
)) {
1177 "Destination is earth level");
1178 if (!u
.uhave
.amulet
) {
1180 obj
= mksobj(AMULET_OF_YENDOR
, TRUE
, FALSE
, FALSE
);
1183 strcat(buf
, " with the amulet");
1186 assign_level(&newlevel
, &earth_level
);
1193 if ((newlev
= lev_by_name(buf
)) == 0) {
1197 /* if you're using wizard mode, you shouldn't really need
1198 * the game to interpret things like `mine town level' */
1200 if (wizard
&& (slev
= find_level(buf
))) {
1202 schedule_goto(&slev
->dlevel
, FALSE
, FALSE
, 0,
1203 (char *)0, (char *)0);
1209 } while (!newlev
&& !digit(buf
[0]) &&
1210 (buf
[0] != '-' || !digit(buf
[1])) &&
1213 /* no dungeon escape via this route */
1216 goto random_levtport
;
1217 if (ynq("Go to Nowhere. Are you sure?") != 'y') return;
1219 You("%s in agony as your body begins to warp...",
1220 is_silent(youmonst
.data
) ? "writhe" : "scream");
1221 display_nhwindow(WIN_MESSAGE
, FALSE
);
1222 You("cease to exist.");
1223 if (invent
) Your("possessions land on the %s with a thud.",
1224 surface(u
.ux
, u
.uy
));
1225 killer_format
= NO_KILLER_PREFIX
;
1226 killer
= "committed suicide";
1229 pline("An energized cloud of dust begins to coalesce.");
1230 Your("body rematerializes%s.", invent
?
1231 ", and you gather up all your possessions" : "");
1235 /* if in Knox and the requested level > 0, stay put.
1236 * we let negative values requests fall into the "heaven" loop.
1237 * [ALI] Add other single level dungeons entered via portal.
1239 if ((Is_knox(&u
.uz
) ||
1240 Is_blackmarket(&u
.uz
) ||
1241 Is_aligned_quest(&u
.uz
)) && newlev
> 0) {
1242 You("%s", shudder_for_moment
);
1245 /* if in Quest, the player sees "Home 1", etc., on the status
1246 * line, instead of the logical depth of the level. controlled
1247 * level teleport request is likely to be relativized to the
1248 * status line, and consequently it should be incremented to
1249 * the value of the logical depth of the target level.
1251 * we let negative values requests fall into the "heaven" loop.
1253 if (In_quest(&u
.uz
) && newlev
> 0)
1254 newlev
= newlev
+ dungeons
[u
.uz
.dnum
].depth_start
- 1;
1255 } else { /* involuntary level tele */
1257 newlev
= random_teleport_level();
1258 if (newlev
== depth(&u
.uz
)) {
1259 You("%s", shudder_for_moment
);
1265 You("%s", shudder_for_moment
);
1269 if (In_endgame(&u
.uz
)) { /* must already be wizard */
1270 int llimit
= dunlevs_in_dungeon(&u
.uz
);
1272 if (newlev
>= 0 || newlev
<= -llimit
) {
1273 You_cant("get there from here.");
1276 newlevel
.dnum
= u
.uz
.dnum
;
1277 newlevel
.dlevel
= llimit
+ newlev
;
1278 schedule_goto(&newlevel
, FALSE
, FALSE
, 0, (char *)0, (char *)0);
1282 killer
= 0; /* still alive, so far... */
1284 if (iflags
.debug_fuzzer
&& newlev
< 0) goto random_levtport
;
1286 if (newlev
< 0 && !force_dest
) {
1288 /* take unpaid inventory items off of shop bills */
1289 in_mklev
= TRUE
; /* suppress map update */
1290 u_left_shop(u
.ushops0
, TRUE
);
1291 /* you're now effectively out of the shop */
1292 *u
.ushops0
= *u
.ushops
= '\0';
1295 if (newlev
<= -10) {
1296 You("arrive in heaven.");
1297 verbalize("Thou art early, but we'll admit thee.");
1298 killer_format
= NO_KILLER_PREFIX
;
1299 killer
= "went to heaven prematurely";
1300 } else if (newlev
== -9) {
1301 You_feel("deliriously happy. ");
1302 pline("(In fact, you're on Cloud 9!) ");
1303 display_nhwindow(WIN_MESSAGE
, FALSE
);
1305 You("are now high above the clouds...");
1308 ; /* arrival in heaven is pending */
1309 } else if (Levitation
) {
1310 escape_by_flying
= "float gently down to earth";
1311 } else if (Flying
) {
1312 escape_by_flying
= "fly down to the ground";
1314 pline("Unfortunately, you don't know how to fly.");
1315 You("plummet a few thousand feet to your death.");
1317 "teleported out of the dungeon and fell to %s death",
1320 killer_format
= NO_KILLER_PREFIX
;
1324 if (killer
) { /* the chosen destination was not survivable */
1327 /* set specific death location; this also suppresses bones */
1328 lsav
= u
.uz
; /* save current level, see below */
1329 u
.uz
.dnum
= 0; /* main dungeon */
1330 u
.uz
.dlevel
= (newlev
<= -10) ? -10 : 0; /* heaven or surface */
1332 /* can only get here via life-saving (or declining to die in
1333 explore|debug mode); the hero has now left the dungeon... */
1334 escape_by_flying
= "find yourself back on the surface";
1335 u
.uz
= lsav
; /* restore u.uz so escape code works */
1338 /* calls done(ESCAPED) if newlevel==0 */
1339 if (escape_by_flying
) {
1340 You("%s.", escape_by_flying
);
1341 newlevel
.dnum
= 0; /* specify main dungeon */
1342 newlevel
.dlevel
= 0; /* escape the dungeon */
1343 /* [dlevel used to be set to 1, but it doesn't make sense to
1344 teleport out of the dungeon and float or fly down to the
1345 surface but then actually arrive back inside the dungeon] */
1346 } else if (u
.uz
.dnum
== medusa_level
.dnum
&&
1347 newlev
>= dungeons
[u
.uz
.dnum
].depth_start
+
1348 dunlevs_in_dungeon(&u
.uz
)) {
1350 if (!(wizard
&& force_dest
))
1352 find_hell(&newlevel
);
1354 /* if invocation did not yet occur, teleporting into
1355 * the last level of Gehennom is forbidden.
1360 if (In_gehennom(&u
.uz
) && !u
.uevent
.invoked
&&
1361 newlev
>= (dungeons
[u
.uz
.dnum
].depth_start
+
1362 dunlevs_in_dungeon(&u
.uz
) - 1)) {
1366 # endif /* WIZARD */
1367 newlev
= dungeons
[u
.uz
.dnum
].depth_start
+
1368 dunlevs_in_dungeon(&u
.uz
) - 2;
1372 # endif /* WIZARD */
1375 /* no teleporting out of quest dungeon */
1379 # endif /* WIZARD */
1380 (In_quest(&u
.uz
) && newlev
< depth(&qstart_level
))
1383 # endif /* WIZARD */
1384 newlev
= depth(&qstart_level
);
1385 /* the player thinks of levels purely in logical terms, so
1386 * we must translate newlev to a number relative to the
1390 if (!(wizard
&& force_dest
))
1392 get_level(&newlevel
, newlev
);
1394 u
.cnd_telelevelcount
++;
1396 schedule_goto(&newlevel
, FALSE
, FALSE
, 0, (char *)0, (char *)0);
1397 /* in case player just read a scroll and is about to be asked to
1398 call it something, we can't defer until the end of the turn */
1399 if (u
.utotype
&& !flags
.mon_moving
) deferred_goto();
1404 register struct trap
*ttmp
;
1406 struct d_level target_level
;
1409 You("%s", shudder_for_moment
);
1413 /* if landed from another portal, do nothing */
1414 /* problem: level teleport landing escapes the check */
1415 if (!on_level(&u
.uz
, &u
.uz0
)) return;
1417 You("activated a magic portal!");
1419 if (!rn2(isfriday
? 3 : 5)) u
.stairscumslowing
+= rn1(5,5);
1421 /* prevent the poor shnook, whose amulet was stolen while in
1422 * the endgame, from accidently triggering the portal to the
1423 * next level, and thus losing the game
1425 if (In_endgame(&u
.uz
) && !u
.uhave
.amulet
&& !u
.freeplaymode
) {
1426 You_feel("dizzy for a moment, but nothing happens...");
1430 target_level
= ttmp
->dst
;
1432 /* a slashem bug: it always panics if you're punished on water... should be fixed now --Amy */
1433 /*if (In_endgame(&u.uz) && Punished && Is_firelevel(&u.uz) ) {
1434 You_feel("the iron ball preventing you from proceeding...");
1438 schedule_goto(&target_level
, FALSE
, FALSE
, 1,
1439 "You feel dizzy for a moment, but the sensation passes.",
1448 if (In_endgame(&u
.uz
) || Antimagic
) {
1450 shieldeff(u
.ux
, u
.uy
);
1451 You_feel("a wrenching sensation.");
1452 } else if (!next_to_u()) {
1453 You("%s", shudder_for_moment
);
1454 } else if (trap
->once
) {
1455 You("%s onto a vault teleporter!",
1456 Levitation
? (const char *)"float" :
1457 locomotion(youmonst
.data
, "step"));
1458 if (flags
.moreforced
&& !MessagesSuppressed
) display_nhwindow(WIN_MESSAGE
, TRUE
); /* --More-- */
1460 newsym(u
.ux
,u
.uy
); /* get rid of trap symbol */
1463 You("%s onto a teleport trap!",
1464 Levitation
? (const char *)"float" :
1465 locomotion(youmonst
.data
, "step"));
1466 if (flags
.moreforced
&& !MessagesSuppressed
) display_nhwindow(WIN_MESSAGE
, TRUE
); /* --More-- */
1471 /* beamer trap - ignores magic resistance --Amy */
1476 if (In_endgame(&u
.uz
)) {
1477 You_feel("a wrenching sensation.");
1478 } else if (!next_to_u()) {
1479 You("%s", shudder_for_moment
);
1480 } else if (trap
->once
) {
1481 You("%s onto a vault teleporter!",
1482 Levitation
? (const char *)"float" :
1483 locomotion(youmonst
.data
, "step"));
1484 if (flags
.moreforced
&& !MessagesSuppressed
) display_nhwindow(WIN_MESSAGE
, TRUE
); /* --More-- */
1486 newsym(u
.ux
,u
.uy
); /* get rid of trap symbol */
1489 You("%s onto a teleport trap!",
1490 Levitation
? (const char *)"float" :
1491 locomotion(youmonst
.data
, "step"));
1492 if (flags
.moreforced
&& !MessagesSuppressed
) display_nhwindow(WIN_MESSAGE
, TRUE
); /* --More-- */
1498 level_tele_trap(trap
)
1501 You("%s onto a level teleport trap!",
1502 Levitation
? (const char *)"float" :
1503 locomotion(youmonst
.data
, "step"));
1505 shieldeff(u
.ux
, u
.uy
);
1507 if (Antimagic
|| In_endgame(&u
.uz
)) {
1508 You_feel("a wrenching sensation.");
1512 You("are momentarily blinded by a flash of light.");
1513 if (evilfriday
) make_blinded(Blinded
+rnz(100),FALSE
);
1515 You("are momentarily disoriented.");
1517 newsym(u
.ux
,u
.uy
); /* get rid of trap symbol */
1518 if (!playerlevelportdisabled()) level_tele();
1519 else pline("The trap doesn't seem to have any effect on you.");
1522 /* level beamer: ignores magic resistance --Amy */
1524 level_tele_trapX(trap
)
1527 You("%s onto a level teleport trap!",
1528 Levitation
? (const char *)"float" :
1529 locomotion(youmonst
.data
, "step"));
1530 if (In_endgame(&u
.uz
)) {
1531 You_feel("a wrenching sensation.");
1535 You("are momentarily blinded by a flash of light.");
1536 if (evilfriday
) make_blinded(Blinded
+rnz(100),FALSE
);
1538 You("are momentarily disoriented.");
1540 newsym(u
.ux
,u
.uy
); /* get rid of trap symbol */
1541 if (!playerlevelportdisabled()) level_tele();
1542 else pline("The trap doesn't seem to have any effect on you.");
1545 /* check whether monster can arrive at location <x,y> via Tport (or fall) */
1547 rloc_pos_ok(x
, y
, mtmp
)
1548 register int x
, y
; /* coordinates of candidate location */
1551 register int xx
, yy
;
1553 if (!goodpos(x
, y
, mtmp
, 0)) return FALSE
;
1555 * Check for restricted areas present in some special levels.
1557 * `xx' is current column; if 0, then `yy' will contain flag bits
1558 * rather than row: bit #0 set => moving upwards; bit #1 set =>
1559 * inside the Wizard's tower.
1564 /* no current location (migrating monster arrival) */
1565 if (dndest
.nlx
&& On_W_tower_level(&u
.uz
))
1566 return ((yy
& 2) != 0) ^ /* inside xor not within */
1567 !within_bounded_area(x
, y
, dndest
.nlx
, dndest
.nly
,
1568 dndest
.nhx
, dndest
.nhy
);
1569 if (updest
.lx
&& (yy
& 1) != 0) /* moving up */
1570 return (within_bounded_area(x
, y
, updest
.lx
, updest
.ly
,
1571 updest
.hx
, updest
.hy
) &&
1573 !within_bounded_area(x
, y
, updest
.nlx
, updest
.nly
,
1574 updest
.nhx
, updest
.nhy
)));
1575 if (dndest
.lx
&& (yy
& 1) == 0) /* moving down */
1576 return (within_bounded_area(x
, y
, dndest
.lx
, dndest
.ly
,
1577 dndest
.hx
, dndest
.hy
) &&
1579 !within_bounded_area(x
, y
, dndest
.nlx
, dndest
.nly
,
1580 dndest
.nhx
, dndest
.nhy
)));
1582 /* current location is <xx,yy> */
1583 if (!tele_jump_ok(xx
, yy
, x
, y
)) return FALSE
;
1592 * Pulls a monster from its current position and places a monster at
1593 * a new x and y. If oldx is 0, then the monster was not in the levels.monsters
1594 * array. However, if oldx is 0, oldy may still have a value because mtmp is a
1595 * migrating_mon. Worm tails are always placed randomly around the head of
1603 register int oldx
= mtmp
->mx
, oldy
= mtmp
->my
;
1604 boolean resident_shk
= mtmp
->isshk
&& inhishop(mtmp
);
1606 if (x
== mtmp
->mx
&& y
== mtmp
->my
) /* that was easy */
1609 if (oldx
) { /* "pick up" monster */
1613 remove_monster(oldx
, oldy
);
1614 newsym(oldx
, oldy
); /* update old location */
1618 place_monster(mtmp
, x
, y
); /* put monster down */
1619 update_monster_region(mtmp
);
1621 if (mtmp
->wormno
) /* now put down tail */
1622 place_worm_tail_randomly(mtmp
, x
, y
);
1624 if (u
.ustuck
== mtmp
) {
1629 } else setustuck(0);
1632 newsym(x
, y
); /* update new location */
1633 set_apparxy(mtmp
); /* orient monster */
1635 /* shopkeepers will only teleport if you zap them with a wand of
1636 teleportation or if they've been transformed into a jumpy monster;
1637 the latter only happens if you've attacked them with polymorph
1638 Amy edit: that is no longer the case, so this shouldn't make them hostile anymore! */
1639 if (resident_shk
&& issoviet
&& !inhishop(mtmp
)) {
1640 make_angry_shk(mtmp
, oldx
, oldy
);
1641 pline("VA-KHA-KHA-KHA, lavochnik ub'yet tebya.");
1645 /* place a monster at a random location, typically due to teleport */
1646 /* return TRUE if successful, FALSE if not */
1648 rloc(mtmp
, suppress_impossible
)
1649 struct monst
*mtmp
; /* mx==0 implies migrating monster arrival */
1650 boolean suppress_impossible
;
1652 register int x
, y
, trycount
;
1654 if (mtmp
== u
.usteed
) {
1659 if (mtmp
->iswiz
&& mtmp
->mx
) { /* Wizard, not just arriving */
1660 if (!In_W_tower(u
.ux
, u
.uy
, &u
.uz
))
1661 x
= xupstair
, y
= yupstair
;
1662 else if (!xdnladder
) /* bottom level of tower */
1663 x
= xupladder
, y
= yupladder
;
1665 x
= xdnladder
, y
= ydnladder
;
1666 /* if the wiz teleports away to heal, try the up staircase,
1667 to block the player's escaping before he's healed
1668 (deliberately use `goodpos' rather than `rloc_pos_ok' here) */
1669 if (goodpos(x
, y
, mtmp
, 0))
1677 if ((trycount
< 500) ? rloc_pos_ok(x
, y
, mtmp
)
1678 : goodpos(x
, y
, mtmp
, 0))
1680 } while (++trycount
< 1000);
1682 /* last ditch attempt to find a good place */
1683 for (x
= 2; x
< COLNO
- 1; x
++)
1684 for (y
= 0; y
< ROWNO
; y
++)
1685 if (goodpos(x
, y
, mtmp
, 0))
1688 /* level either full of monsters or somehow faulty */
1689 if (!suppress_impossible
)
1690 impossible("rloc(): couldn't relocate monster");
1694 rloc_to(mtmp
, x
, y
);
1702 register struct mkroom
*croom
= search_special(VAULT
);
1705 if (croom
&& somexy(croom
, &c
) &&
1706 goodpos(c
.x
, c
.y
, mtmp
, 0)) {
1707 rloc_to(mtmp
, c
.x
, c
.y
);
1710 (void) rloc(mtmp
, FALSE
);
1717 if (level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
) {
1719 pline("A mysterious force prevents %s from teleporting!",
1727 mtele_trap(mtmp
, trap
, in_sight
)
1734 if (tele_restrict(mtmp
)) return;
1735 if (teleport_pet(mtmp
, FALSE
)) {
1736 /* save name with pre-movement visibility */
1737 monname
= Monnam(mtmp
);
1739 /* Note: don't remove the trap if a vault. Other-
1740 * wise the monster will be stuck there, since
1741 * the guard isn't going to come for it...
1743 if (trap
->once
) mvault_tele(mtmp
);
1744 else (void) rloc(mtmp
, FALSE
);
1747 if (canseemon(mtmp
))
1748 pline("%s seems disoriented.", monname
);
1750 pline("%s suddenly disappears!", monname
);
1756 /* return 0 if still on level, 3 if not */
1758 mlevel_tele_trap(mtmp
, trap
, force_it
, in_sight
)
1764 int tt
= trap
->ttyp
;
1765 struct permonst
*mptr
= mtmp
->data
;
1767 if (mtmp
== u
.ustuck
) /* probably a vortex */
1768 return 0; /* temporary? kludge */
1769 if (teleport_pet(mtmp
, force_it
)) {
1771 int migrate_typ
= MIGR_RANDOM
;
1773 if ((tt
== HOLE
|| tt
== TRAPDOOR
|| tt
== SHAFT_TRAP
|| tt
== CURRENT_SHAFT
)) {
1774 if (Is_stronghold(&u
.uz
)) {
1775 assign_level(&tolevel
, &valley_level
);
1776 } else if (Is_botlevel(&u
.uz
)) {
1777 if (in_sight
&& trap
->tseen
)
1778 pline("%s avoids the %s.", Monnam(mtmp
),
1779 (tt
== HOLE
) ? "hole" : "trap");
1782 get_level(&tolevel
, depth(&u
.uz
) + 1);
1784 } else if (tt
== MAGIC_PORTAL
) {
1785 if (In_endgame(&u
.uz
) &&
1786 (mon_has_amulet(mtmp
) || is_home_elemental(mptr
))) {
1787 if (in_sight
&& mptr
->mlet
!= S_ELEMENTAL
) {
1788 pline("%s seems to shimmer for a moment.",
1794 else if (mtmp
->mtame
&&
1795 (Is_blackmarket(&trap
->dst
) || Is_blackmarket(&u
.uz
))) {
1797 pline("%s seems to shimmer for a moment.",
1804 assign_level(&tolevel
, &trap
->dst
);
1805 migrate_typ
= MIGR_PORTAL
;
1807 } else { /* (tt == LEVEL_TELEP) */
1810 if (mon_has_amulet(mtmp
) || In_endgame(&u
.uz
)) {
1812 pline("%s seems very disoriented for a moment.",
1816 nlev
= random_teleport_level();
1817 if (nlev
== depth(&u
.uz
)) {
1819 pline("%s shudders for a moment.", Monnam(mtmp
));
1822 get_level(&tolevel
, nlev
);
1826 pline("Suddenly, %s disappears out of sight.", mon_nam(mtmp
));
1829 migrate_to_level(mtmp
, ledger_no(&tolevel
),
1830 migrate_typ
, (coord
*)0);
1831 return 3; /* no longer on this level */
1839 register struct obj
*obj
;
1841 register xchar tx
, ty
, otx
, oty
;
1842 boolean restricted_fall
;
1843 int try_limit
= 4000;
1845 if (obj
->dynamitekaboom
) return;
1847 if (obj
->otyp
== CORPSE
&& is_rider(&mons
[obj
->corpsenm
])) {
1848 if (revive_corpse(obj
, FALSE
)) return;
1850 if (obj
->otyp
== CORPSE
&& is_deadlysin(&mons
[obj
->corpsenm
])) {
1851 if (revive_corpse(obj
, FALSE
)) return;
1854 obj_extract_self(obj
);
1857 restricted_fall
= (otx
== 0 && dndest
.lx
);
1859 tx
= rn1(COLNO
-3,2);
1861 if (!--try_limit
) break;
1862 } while (!goodpos(tx
, ty
, (struct monst
*)0, 0) ||
1863 /* bug: this lacks provision for handling the Wizard's tower */
1865 (!within_bounded_area(tx
, ty
, dndest
.lx
, dndest
.ly
,
1866 dndest
.hx
, dndest
.hy
) ||
1868 within_bounded_area(tx
, ty
, dndest
.nlx
, dndest
.nly
,
1869 dndest
.nhx
, dndest
.nhy
)))));
1871 if (flooreffects(obj
, tx
, ty
, "fall")) {
1873 } else if (otx
== 0 && oty
== 0) {
1874 ; /* fell through a trap door; no update of old loc needed */
1876 if (costly_spot(otx
, oty
)
1877 && (!costly_spot(tx
, ty
) ||
1878 !index(in_rooms(tx
, ty
, 0), *in_rooms(otx
, oty
, 0)))) {
1879 if (costly_spot(u
.ux
, u
.uy
) &&
1880 index(u
.urooms
, *in_rooms(otx
, oty
, 0)))
1881 addtobill(obj
, FALSE
, FALSE
, FALSE
);
1882 else (void)stolen_value(obj
, otx
, oty
, FALSE
, FALSE
, FALSE
);
1884 newsym(otx
, oty
); /* update old location */
1886 place_object(obj
, tx
, ty
);
1890 /* Returns an absolute depth */
1892 random_teleport_level()
1894 int nlev
, max_depth
, min_depth
,
1895 cur_depth
= (int)depth(&u
.uz
);
1897 if (!rn2(5) || Is_knox(&u
.uz
) ||
1898 Is_blackmarket(&u
.uz
) ||
1899 Is_aligned_quest(&u
.uz
))
1902 /* What I really want to do is as follows:
1903 * -- If in a dungeon that goes down, the new level is to be restricted
1904 * to [top of parent, bottom of current dungeon]
1905 * -- If in a dungeon that goes up, the new level is to be restricted
1906 * to [top of current dungeon, bottom of parent]
1907 * -- If in a quest dungeon or similar dungeon entered by portals,
1908 * the new level is to be restricted to [top of current dungeon,
1909 * bottom of current dungeon]
1910 * The current behavior is not as sophisticated as that ideal, but is
1911 * still better what we used to do, which was like this for players
1912 * but different for monsters for no obvious reason. Currently, we
1913 * must explicitly check for special dungeons. We check for Knox
1914 * above; endgame is handled in the caller due to its different
1915 * message ("disoriented").
1917 * [ALI] Also check for Sam's blackmarket and the three aligned quests
1919 * 3.4.2: explicitly handle quest here too, to fix the problem of
1920 * monsters sometimes level teleporting out of it into main dungeon.
1921 * Also prevent monsters reaching the Sanctum prior to invocation.
1923 min_depth
= In_quest(&u
.uz
) ? dungeons
[u
.uz
.dnum
].depth_start
: 1;
1924 max_depth
= dunlevs_in_dungeon(&u
.uz
) +
1925 (dungeons
[u
.uz
.dnum
].depth_start
- 1);
1927 /* can't reach the Sanctum if the invocation hasn't been performed */
1928 if (In_gehennom(&u
.uz
) && !u
.uevent
.invoked
) max_depth
-= 1;
1930 /* Get a random value relative to the current dungeon */
1931 /* Range is 1 to current+3, current not counting */
1932 /* Amy edit: somehow this is just stupid. Why not make it so that your deepest level counts? */
1933 if (In_quest(&u
.uz
)) nlev
= dungeons
[u
.uz
.dnum
].depth_start
+ rnd(6);
1934 else { nlev
= rn2(/*cur_depth*/deepest_lev_reached(TRUE
) + 3 - min_depth
) + min_depth
;
1935 if (nlev
>= cur_depth
) nlev
++;
1938 if (nlev
> max_depth
) {
1940 /* teleport up if already on bottom */
1941 if (Is_botlevel(&u
.uz
)) nlev
-= rnd(3);
1943 if (nlev
< min_depth
) {
1945 if (nlev
== cur_depth
) {
1947 if (nlev
> max_depth
)
1954 /* teleport player to a random branch --Amy */
1961 /* make sure you can't be branchported if you're supposed to be immune --Amy */
1962 if (((u
.uhave
.amulet
) && !u
.freeplaymode
) || CannotTeleport
|| (u
.usteed
&& mon_has_amulet(u
.usteed
))) {
1966 if (playerlevelportdisabled()) {
1970 dtmp
= random_branchport_level();
1972 schedule_goto(&dtmp
, FALSE
, FALSE
, 0, (char *)0, (char *)0);
1973 u
.cnd_branchportcount
++;
1977 /* Random branchport level decision */
1979 random_branchport_level()
1983 extern int n_dgns
; /* from dungeon.c */
1984 int duncounter
, num_ok_dungeons
, last_ok_dungeon
= 0;
1987 for (duncounter
= num_ok_dungeons
= 0; duncounter
< n_dgns
; duncounter
++) {
1988 if (!dungeons
[duncounter
].dunlev_ureached
) continue;
1989 if (flags
.wonderland
&& !achieve
.perform_invocation
) {
1990 if (!strcmp(dungeons
[duncounter
].dname
, "Yendorian Tower")) continue;
1991 if (!strcmp(dungeons
[duncounter
].dname
, "Forging Chamber")) continue;
1992 if (!strcmp(dungeons
[duncounter
].dname
, "Dead Grounds")) continue;
1993 if (!strcmp(dungeons
[duncounter
].dname
, "Ordered Chaos")) continue;
1994 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TA")) continue;
1995 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TB")) continue;
1996 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TC")) continue;
1997 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TD")) continue;
1998 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TE")) continue;
1999 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TF")) continue;
2000 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TG")) continue;
2001 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TH")) continue;
2002 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TI")) continue;
2003 if (!strcmp(dungeons
[duncounter
].dname
, "Resting Zone TJ")) continue;
2006 last_ok_dungeon
= duncounter
;
2009 if (num_ok_dungeons
> 1) randomnumber
= rnd(num_ok_dungeons
);
2010 else randomnumber
= 1;
2012 dtmp
.dnum
= u
.uz
.dnum
;
2014 while (randomnumber
> 0) {
2019 if (dtmp
.dnum
>= n_dgns
) dtmp
.dnum
= 0;
2021 while (!dungeons
[dtmp
.dnum
].dunlev_ureached
|| (flags
.wonderland
&& !achieve
.perform_invocation
&& ( !strcmp(dungeons
[dtmp
.dnum
].dname
, "Yendorian Tower") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Forging Chamber") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Dead Grounds") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Ordered Chaos") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TA") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TB") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TC") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TD") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TE") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TF") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TG") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TH") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TI") || !strcmp(dungeons
[dtmp
.dnum
].dname
, "Resting Zone TJ") ) ) ) {
2024 if (dtmp
.dnum
>= n_dgns
) dtmp
.dnum
= 0;
2029 if (dungeons
[dtmp
.dnum
].dunlev_ureached
> 1) dtmp
.dlevel
= rnd(dungeons
[dtmp
.dnum
].dunlev_ureached
);
2035 /* Levelport level decision */
2037 random_banish_level()
2039 int nlev
, max_depth
, min_depth
, cur_depth
= (int)depth(&u
.uz
);
2042 if (Is_knox(&u
.uz
) ||
2043 Is_blackmarket(&u
.uz
) ||
2044 Is_aligned_quest(&u
.uz
))
2047 min_depth
= In_quest(&u
.uz
) ? dungeons
[u
.uz
.dnum
].depth_start
: 1;
2048 max_depth
= dunlevs_in_dungeon(&u
.uz
) +
2049 (dungeons
[u
.uz
.dnum
].depth_start
- 1);
2050 /* can't reach the Sanctum, no matter if invocation or not */
2051 if (In_gehennom(&u
.uz
)) max_depth
-= 1;
2053 /* Get a random value relative to the current dungeon */
2055 nlev
= rn2(max_depth
- min_depth
) + min_depth
;
2057 if (nlev
> max_depth
) nlev
= max_depth
;
2059 if (nlev
< min_depth
) nlev
= min_depth
;
2064 /* Random branchport level decision */
2066 random_banishment_level()
2070 boolean minusworld
= FALSE
;
2072 dtmp
.dnum
= dname_to_dnum("The Dungeons of Doom"); /* fail safe in case something goes wrong */
2073 dtmp
.dlevel
= 1; /* ditto */
2076 dtmp
.dnum
= dname_to_dnum("Minus World");
2077 dtmp
.dlevel
= dunlevs_in_dungeon(&dtmp
);
2079 } else if (rn2(2)) {
2080 dtmp
.dnum
= dname_to_dnum("The Dungeons of Doom");
2081 } else if (rn2(2)) {
2082 dtmp
.dnum
= dname_to_dnum("Gehennom");
2083 } else if (rn2(4)) {
2087 dtmp
.dnum
= dname_to_dnum("The Gnomish Mines");
2090 dtmp
.dnum
= dname_to_dnum("The Quest");
2093 if (!u
.silverbellget
&& !u
.prematuresubquest
) goto upperdungeonagain
;
2094 dtmp
.dnum
= dname_to_dnum("The Subquest");
2097 if (!u
.silverbellget
&& !!u
.prematuresubquest
) goto upperdungeonagain
;
2098 dtmp
.dnum
= dname_to_dnum("Bell Caves");
2101 dtmp
.dnum
= dname_to_dnum("Lawful Quest");
2104 dtmp
.dnum
= dname_to_dnum("Neutral Quest");
2107 dtmp
.dnum
= dname_to_dnum("Chaotic Quest");
2110 dtmp
.dnum
= dname_to_dnum("Sokoban");
2113 dtmp
.dnum
= dname_to_dnum("Town");
2116 dtmp
.dnum
= dname_to_dnum("Grund's Stronghold");
2119 if (!u
.havebeeninludios
) goto upperdungeonagain
;
2120 dtmp
.dnum
= dname_to_dnum("Fort Ludios");
2123 dtmp
.dnum
= dname_to_dnum("The Wyrm Caves");
2126 dtmp
.dnum
= dname_to_dnum("One-eyed Sam's Market");
2129 dtmp
.dnum
= dname_to_dnum("The Lost Tomb");
2132 dtmp
.dnum
= dname_to_dnum("The Spider Caves");
2135 dtmp
.dnum
= dname_to_dnum("The Sunless Sea");
2138 dtmp
.dnum
= dname_to_dnum("The Temple of Moloch");
2141 dtmp
.dnum
= dname_to_dnum("Grue Challenge");
2144 dtmp
.dnum
= dname_to_dnum("Joust Challenge");
2147 dtmp
.dnum
= dname_to_dnum("Pacman Challenge");
2150 dtmp
.dnum
= dname_to_dnum("Pool Challenge");
2153 dtmp
.dnum
= dname_to_dnum("Digdug Challenge");
2156 dtmp
.dnum
= dname_to_dnum("Illusory Castle");
2159 dtmp
.dnum
= dname_to_dnum("Deep Mines");
2162 dtmp
.dnum
= dname_to_dnum("Space Base");
2164 case 26: /* not a bug that the space base subdungeons are always accessible --Amy */
2165 dtmp
.dnum
= dname_to_dnum("Sewer Plant");
2168 dtmp
.dnum
= dname_to_dnum("Gamma Caves");
2171 dtmp
.dnum
= dname_to_dnum("Mainframe");
2174 dtmp
.dnum
= dname_to_dnum("Minotaur Maze");
2177 if (!u
.greencrossopen
&& !Role_if(PM_PREVERSIONER
) && !Role_if(PM_SPACEWARS_FIGHTER
) && !Role_if(PM_CAMPERSTRIKER
) && !(Role_if(PM_GANG_SCHOLAR
) && u
.greencrosschance
< 5) && !(Role_if(PM_WALSCHOLAR
) && u
.greencrosschance
< 5) && !(u
.greencrosschance
< 2) ) goto upperdungeonagain
;
2178 dtmp
.dnum
= dname_to_dnum("Green Cross");
2181 dtmp
.dnum
= dname_to_dnum("The Giant Caverns");
2184 dtmp
.dnum
= dname_to_dnum("The Ice Queen's Realm");
2191 if (!u
.silverbellget
) goto lowerdungeonagain
;
2192 dtmp
.dnum
= dname_to_dnum("Rival Quest");
2195 dtmp
.dnum
= dname_to_dnum("Void");
2198 dtmp
.dnum
= dname_to_dnum("Nether Realm");
2201 dtmp
.dnum
= dname_to_dnum("Angmar");
2204 dtmp
.dnum
= dname_to_dnum("Emyn Luin");
2207 dtmp
.dnum
= dname_to_dnum("Swimming Pool");
2210 dtmp
.dnum
= dname_to_dnum("Hell's Bathroom");
2213 dtmp
.dnum
= dname_to_dnum("Frankenstein's Lab");
2216 dtmp
.dnum
= dname_to_dnum("Sheol");
2219 dtmp
.dnum
= dname_to_dnum("Vlad's Tower");
2222 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2223 dtmp
.dnum
= dname_to_dnum("Yendorian Tower");
2226 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2227 dtmp
.dnum
= dname_to_dnum("Forging Chamber");
2230 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2231 dtmp
.dnum
= dname_to_dnum("Dead Grounds");
2234 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2235 dtmp
.dnum
= dname_to_dnum("Ordered Chaos");
2238 dtmp
.dnum
= dname_to_dnum("Resting Zone GA");
2241 dtmp
.dnum
= dname_to_dnum("Resting Zone GB");
2244 dtmp
.dnum
= dname_to_dnum("Resting Zone GC");
2247 dtmp
.dnum
= dname_to_dnum("Resting Zone GD");
2250 dtmp
.dnum
= dname_to_dnum("Resting Zone GE");
2253 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2254 dtmp
.dnum
= dname_to_dnum("Resting Zone TA");
2257 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2258 dtmp
.dnum
= dname_to_dnum("Resting Zone TB");
2261 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2262 dtmp
.dnum
= dname_to_dnum("Resting Zone TC");
2265 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2266 dtmp
.dnum
= dname_to_dnum("Resting Zone TD");
2269 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2270 dtmp
.dnum
= dname_to_dnum("Resting Zone TE");
2273 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2274 dtmp
.dnum
= dname_to_dnum("Resting Zone TF");
2277 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2278 dtmp
.dnum
= dname_to_dnum("Resting Zone TG");
2281 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2282 dtmp
.dnum
= dname_to_dnum("Resting Zone TH");
2285 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2286 dtmp
.dnum
= dname_to_dnum("Resting Zone TI");
2289 if (!achieve
.get_amulet
&& !u
.prematureyendortower
) goto lowerdungeonagain
;
2290 dtmp
.dnum
= dname_to_dnum("Resting Zone TJ");
2293 dtmp
.dnum
= dname_to_dnum("Resting Zone A");
2296 dtmp
.dnum
= dname_to_dnum("Resting Zone S");
2299 if (!u
.freeplaymode
|| !u
.freeplayplanes
) goto lowerdungeonagain
;
2300 dtmp
.dnum
= dname_to_dnum("The Elemental Planes");
2303 dtmp
.dnum
= dname_to_dnum("Resting Zone E");
2309 dtmp
.dlevel
= rnd(dunlevs_in_dungeon(&dtmp
));
2310 if (!strcmp(dungeons
[dtmp
.dnum
].dname
, "Gehennom") && !u
.uevent
.invoked
) dtmp
.dlevel
= rnd((dunlevs_in_dungeon(&dtmp
)) - 1);
2316 /* the player is being banished to a random level. Usually it'll be in the upper dungeons, sometimes in Gehennom,
2317 * but occasionally also some branch, which should be accessible so as to prevent sequence breaking.
2318 * 1% chance to end up in Minus World, which is initially the only way to get there. --Amy */
2324 /* make sure you can't be banished if you're supposed to be immune --Amy */
2325 if (((u
.uhave
.amulet
) && !u
.freeplaymode
) || CannotTeleport
|| (u
.usteed
&& mon_has_amulet(u
.usteed
))) {
2329 if (playerlevelportdisabled()) {
2333 dtmp
= random_banishment_level();
2336 nomovemsg
= "You feel a little dizzy.";
2337 nomul(-1, "being banished", FALSE
); /* because it's not called until you get another turn... */
2340 schedule_goto(&dtmp
, FALSE
, FALSE
, 0, (char *)0, (char *)0);
2341 u
.cnd_banishmentcount
++;
2345 /* you teleport a monster (via wand, spell, or poly'd q.mechanic attack);
2346 return false iff the attempt fails */
2348 u_teleport_mon(mtmp
, give_feedback
)
2350 boolean give_feedback
;
2354 if (evilfriday
&& (level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
)) {
2356 pline("Ha ha ha, the wand destruction patch made it so that your wand of teleportation does jack diddly on a no-teleport level. You just wasted a charge, sucker!");
2358 } else if (mtmp
->ispriest
&& *in_rooms(mtmp
->mx
, mtmp
->my
, TEMPLE
)) {
2360 pline("%s resists your magic!", Monnam(mtmp
));
2362 } else if ((level
.flags
.noteleport
|| Race_if(PM_STABILISATOR
) || u
.antitelespelltimeout
) && u
.uswallow
&& mtmp
== u
.ustuck
) {
2364 You("are no longer inside %s!", mon_nam(mtmp
));
2367 } else if (is_rider(mtmp
->data
) && rn2(13) &&
2368 enexto(&cc
, u
.ux
, u
.uy
, mtmp
->data
))
2369 rloc_to(mtmp
, cc
.x
, cc
.y
);
2370 else if (is_deadlysin(mtmp
->data
) && rn2(13) &&
2371 enexto(&cc
, u
.ux
, u
.uy
, mtmp
->data
))
2372 rloc_to(mtmp
, cc
.x
, cc
.y
);
2375 if (wizard
&& Teleport_control
)
2378 * [ALI] This code will only allow monsters to be
2379 * teleported to positions acceptable to rloc_pos_ok().
2380 * We could use goodpos() which would allow more
2381 * locations but, in my view, is less informative.
2384 pline("To what position do you want %s to be teleported?",
2386 cc
.x
= omx
= mtmp
->mx
;
2387 cc
.y
= omy
= mtmp
->my
;
2388 if (getpos(&cc
, TRUE
, "the desired position") < 0)
2389 rloc(mtmp
, FALSE
); /* abort */
2390 else if (rloc_pos_ok(cc
.x
, cc
.y
, mtmp
)) {
2391 rloc_to(mtmp
, cc
.x
, cc
.y
);
2393 if (mtmp
->isshk
&& !inhishop(mtmp
) && issoviet
) {
2394 make_angry_shk(mtmp
, omx
, omy
);
2395 pline("VA-KHA-KHA-KHA, lavochnik ub'yet tebya.");
2408 /* monster is levelported */
2410 u_teleport_monB(mtmp
, give_feedback
)
2412 boolean give_feedback
;
2414 boolean ball_active
= (Punished
&& uball
->where
!= OBJ_FREE
);
2416 if (mtmp
->data
== &mons
[PM_BAN_EVADING_TROLL
]) return FALSE
;
2418 if (mtmp
->isshk
&& issoviet
) {
2419 make_angry_shk(mtmp
, 0, 0);
2420 pline("VA-KHA-KHA-KHA, lavochnik ub'yet tebya.");
2426 if (mon_has_amulet(mtmp
) || In_endgame(&u
.uz
)) {
2427 if (give_feedback
) pline("%s seems very disoriented for a moment.", Monnam(mtmp
));
2430 nlev
= random_banish_level();
2431 if (nlev
== depth(&u
.uz
)) {
2432 if (give_feedback
) pline("%s shudders for a moment.", Monnam(mtmp
));
2437 u
.uswldtim
= u
.uswallow
= 0;
2438 if (Punished
&& !ball_active
) {
2439 /* ensure ball placement, like unstuck */
2445 get_level(&flev
, nlev
);
2446 migrate_to_level(mtmp
, ledger_no(&flev
), MIGR_RANDOM
, (coord
*)0);
2451 /* monster is branchported */
2453 u_teleport_monC(mtmp
, give_feedback
)
2455 boolean give_feedback
;
2457 boolean ball_active
= (Punished
&& uball
->where
!= OBJ_FREE
);
2459 if (mtmp
->data
== &mons
[PM_BAN_EVADING_TROLL
]) return FALSE
;
2461 if (mtmp
->isshk
&& issoviet
) {
2462 make_angry_shk(mtmp
, 0, 0);
2463 pline("VA-KHA-KHA-KHA, lavochnik ub'yet tebya.");
2469 if (mon_has_amulet(mtmp
) || In_endgame(&u
.uz
)) {
2470 if (give_feedback
) pline("%s seems very disoriented for a moment.", Monnam(mtmp
));
2474 flev
= random_branchport_level();
2475 migrate_to_level(mtmp
, ledger_no(&flev
), MIGR_RANDOM
, (coord
*)0);
2480 /* monster is banished */
2482 u_teleport_monD(mtmp
, give_feedback
)
2484 boolean give_feedback
;
2486 boolean ball_active
= (Punished
&& uball
->where
!= OBJ_FREE
);
2488 if (mtmp
->data
== &mons
[PM_BAN_EVADING_TROLL
]) return FALSE
;
2490 if (mtmp
->isshk
&& issoviet
) {
2491 make_angry_shk(mtmp
, 0, 0);
2492 pline("VA-KHA-KHA-KHA, lavochnik ub'yet tebya.");
2498 if (mon_has_amulet(mtmp
) || In_endgame(&u
.uz
)) {
2499 if (give_feedback
) pline("%s seems very disoriented for a moment.", Monnam(mtmp
));
2503 flev
= random_banishment_level();
2504 migrate_to_level(mtmp
, ledger_no(&flev
), MIGR_RANDOM
, (coord
*)0);
2509 /* scroll/spell/whatever of return: lets the player choose a previously visited dungeon level to return to --Amy */
2511 setupreturn(returntype
)
2512 int returntype
; /* 0 = let player choose, 1 = let RNG choose, 2 = destination is specifically level 62 of Gehennom */
2514 int possiblechoices
= 1;
2516 /* have the amulet? then sorry, pal, it won't work at all --Amy */
2517 if ((u
.uhave
.amulet
&& !u
.freeplaymode
) || (u
.usteed
&& mon_has_amulet(u
.usteed
))) {
2518 pline_The("charge is absorbed by the amulet!");
2521 /* otherwise, set up return; if you're prevented from levelporting at the time it triggers, it fizzles */
2523 u
.returntimer
= rn1(50, 50);
2524 u
.returndestination
= 1; /* fail safe: Dungeons of Doom, level 1 */
2526 if (returntype
== 2) {
2527 u
.returndestination
= 99999; /* Gehennom, level 62, eevn if you've not been there yet */
2528 You_feel("the air change around you..."); /* intentionally says "change", not "charge" */
2532 if (returntype
== 0) pline("Pick a destination to return to. The prompt will loop until you actually make a choice.");
2534 if (returntype
== 0) {
2536 if (yn("Dungeons of Doom, level 1?") == 'y') {
2537 u
.returndestination
= 1;
2539 } else if (u
.returndest_dod14
&& yn("Dungeons of Doom, level 14?") == 'y') {
2540 u
.returndestination
= 2;
2542 } else if (u
.returndest_dod29
&& yn("Dungeons of Doom, level 29?") == 'y') {
2543 u
.returndestination
= 3;
2545 } else if (u
.returndest_dod44
&& yn("Dungeons of Doom, level 44?") == 'y') {
2546 u
.returndestination
= 4;
2548 } else if (u
.returndest_geh53
&& yn("Gehennom, level 53?") == 'y') {
2549 u
.returndestination
= 5;
2551 } else if (u
.returndest_geh74
&& yn("Gehennom, level 74?") == 'y') {
2552 u
.returndestination
= 6;
2554 } else if (u
.returndest_geh95
&& yn("Gehennom, level 95?") == 'y') {
2555 u
.returndestination
= 7;
2557 } else if (u
.returndest_min7
&& yn("Gnomish Mines, level 7?") == 'y') {
2558 u
.returndestination
= 8;
2560 } else if (u
.returndest_ill11
&& yn("Illusory Castle, level 11?") == 'y') {
2561 u
.returndestination
= 9;
2563 } else if (u
.returndest_ill22
&& yn("Illusory Castle, level 22?") == 'y') {
2564 u
.returndestination
= 10;
2566 } else if (u
.returndest_dee11
&& yn("Deep Mines, level 11?") == 'y') {
2567 u
.returndestination
= 11;
2569 } else if (u
.returndest_dee22
&& yn("Deep Mines, level 22?") == 'y') {
2570 u
.returndestination
= 12;
2572 } else if (u
.returndest_spa6
&& yn("Space Base, level 6?") == 'y') {
2573 u
.returndestination
= 13;
2575 } else if (u
.returndest_ang7
&& yn("Angmar, level 7?") == 'y') {
2576 u
.returndestination
= 14;
2578 } else if (u
.returndest_emy10
&& yn("Emyn Luin, level 10?") == 'y') {
2579 u
.returndestination
= 15;
2581 } else if (u
.returndest_swi8
&& yn("Swimming Pools, level 8?") == 'y') {
2582 u
.returndestination
= 16;
2584 } else if (u
.returndest_gre13
&& yn("Green Cross, level 13?") == 'y') {
2585 u
.returndestination
= 17;
2587 } else if (u
.returndest_gre28
&& yn("Green Cross, level 28?") == 'y') {
2588 u
.returndestination
= 18;
2590 } else if (u
.returndest_gre43
&& yn("Green Cross, level 43?") == 'y') {
2591 u
.returndestination
= 19;
2593 } else if (u
.returndest_gre58
&& yn("Green Cross, level 58?") == 'y') {
2594 u
.returndestination
= 20;
2596 } else if (u
.returndest_maz11
&& yn("Minotaur Maze, level 11?") == 'y') {
2597 u
.returndestination
= 21;
2599 } else if (u
.returndest_maz22
&& yn("Minotaur Maze, level 22?") == 'y') {
2600 u
.returndestination
= 22;
2602 } else if (u
.returndest_she5
&& yn("Sheol, level 5?") == 'y') {
2603 u
.returndestination
= 23;
2605 } else if (u
.returndest_she20
&& yn("Sheol, level 20?") == 'y') {
2606 u
.returndestination
= 24;
2608 } else if (u
.returndest_yen4
&& yn("Yendorian Tower, level 4?") == 'y') {
2609 u
.returndestination
= 25;
2611 } else if (u
.returndest_yen19
&& yn("Yendorian Tower, level 19?") == 'y') {
2612 u
.returndestination
= 26;
2614 } else if (u
.returndest_yen34
&& yn("Yendorian Tower, level 34?") == 'y') {
2615 u
.returndestination
= 27;
2617 } else if (u
.returndest_yen49
&& yn("Yendorian Tower, level 49?") == 'y') {
2618 u
.returndestination
= 28;
2620 } else if (u
.returndest_yen64
&& yn("Yendorian Tower, level 64?") == 'y') {
2621 u
.returndestination
= 29;
2623 } else if (u
.returndest_yen79
&& yn("Yendorian Tower, level 79?") == 'y') {
2624 u
.returndestination
= 30;
2626 } else if (u
.returndest_yen94
&& yn("Yendorian Tower, level 94?") == 'y') {
2627 u
.returndestination
= 31;
2629 } else goto returnagain
;
2632 if (returntype
== 1) {
2633 u
.returndestination
= 1;
2634 possiblechoices
= 1;
2635 if (u
.returndest_dod14
) {
2637 if (!rn2(possiblechoices
)) u
.returndestination
= 2;
2639 if (u
.returndest_dod29
) {
2641 if (!rn2(possiblechoices
)) u
.returndestination
= 3;
2643 if (u
.returndest_dod44
) {
2645 if (!rn2(possiblechoices
)) u
.returndestination
= 4;
2647 if (u
.returndest_geh53
) {
2649 if (!rn2(possiblechoices
)) u
.returndestination
= 5;
2651 if (u
.returndest_geh74
) {
2653 if (!rn2(possiblechoices
)) u
.returndestination
= 6;
2655 if (u
.returndest_geh95
) {
2657 if (!rn2(possiblechoices
)) u
.returndestination
= 7;
2659 if (u
.returndest_min7
) {
2661 if (!rn2(possiblechoices
)) u
.returndestination
= 8;
2663 if (u
.returndest_ill11
) {
2665 if (!rn2(possiblechoices
)) u
.returndestination
= 9;
2667 if (u
.returndest_ill22
) {
2669 if (!rn2(possiblechoices
)) u
.returndestination
= 10;
2671 if (u
.returndest_dee11
) {
2673 if (!rn2(possiblechoices
)) u
.returndestination
= 11;
2675 if (u
.returndest_dee22
) {
2677 if (!rn2(possiblechoices
)) u
.returndestination
= 12;
2679 if (u
.returndest_spa6
) {
2681 if (!rn2(possiblechoices
)) u
.returndestination
= 13;
2683 if (u
.returndest_ang7
) {
2685 if (!rn2(possiblechoices
)) u
.returndestination
= 14;
2687 if (u
.returndest_emy10
) {
2689 if (!rn2(possiblechoices
)) u
.returndestination
= 15;
2691 if (u
.returndest_swi8
) {
2693 if (!rn2(possiblechoices
)) u
.returndestination
= 16;
2695 if (u
.returndest_gre13
) {
2697 if (!rn2(possiblechoices
)) u
.returndestination
= 17;
2699 if (u
.returndest_gre28
) {
2701 if (!rn2(possiblechoices
)) u
.returndestination
= 18;
2703 if (u
.returndest_gre43
) {
2705 if (!rn2(possiblechoices
)) u
.returndestination
= 19;
2707 if (u
.returndest_gre58
) {
2709 if (!rn2(possiblechoices
)) u
.returndestination
= 20;
2711 if (u
.returndest_maz11
) {
2713 if (!rn2(possiblechoices
)) u
.returndestination
= 21;
2715 if (u
.returndest_maz22
) {
2717 if (!rn2(possiblechoices
)) u
.returndestination
= 22;
2719 if (u
.returndest_she5
) {
2721 if (!rn2(possiblechoices
)) u
.returndestination
= 23;
2723 if (u
.returndest_she20
) {
2725 if (!rn2(possiblechoices
)) u
.returndestination
= 24;
2727 if (u
.returndest_yen4
) {
2729 if (!rn2(possiblechoices
)) u
.returndestination
= 25;
2731 if (u
.returndest_yen19
) {
2733 if (!rn2(possiblechoices
)) u
.returndestination
= 26;
2735 if (u
.returndest_yen34
) {
2737 if (!rn2(possiblechoices
)) u
.returndestination
= 27;
2739 if (u
.returndest_yen49
) {
2741 if (!rn2(possiblechoices
)) u
.returndestination
= 28;
2743 if (u
.returndest_yen64
) {
2745 if (!rn2(possiblechoices
)) u
.returndestination
= 29;
2747 if (u
.returndest_yen79
) {
2749 if (!rn2(possiblechoices
)) u
.returndestination
= 30;
2751 if (u
.returndest_yen94
) {
2753 if (!rn2(possiblechoices
)) u
.returndestination
= 31;
2758 You_feel("the air charge around you..."); /* here it says "charge", this isn't a mistake */
2763 /* A function that pushes the player around, mainly to be used by ranged attackers so they can get a shot. --Amy
2764 * "allowtrap" should be FALSE if it's the result of a monster attack, because otherwise we could get segfaults
2765 * and bus errors when the trap moves you off the level before the monster's attack routine is finished! */
2767 pushplayer(allowtrap
)
2771 int direction
, pushwidth
, trycnt
;
2772 register struct obj
*otmp
;
2775 if (uwep
&& uwep
->oartifact
== ART_IMHULLU
&& rn2(10)) return;
2776 if (uarmf
&& uarmf
->oartifact
== ART_STAND_TALL_IN_THE_STORM
) return;
2781 if (!rn2(2)) pushwidth
+= rnd(2);
2782 if (uarmf
&& itemhasappearance(uarmf
, APP_ZERO_DROP_SHOES
)) {
2784 if (!rn2(2)) pushwidth
+= rnd(3);
2789 while (pushwidth
--) {
2790 if (direction
== 1 || direction
== 5) ccc
.x
+= 1;
2791 else if (direction
== 2 || direction
== 6) ccc
.x
-= 1;
2792 else if (direction
== 3 || direction
== 7) ccc
.y
+= 1;
2793 else if (direction
== 4 || direction
== 8) ccc
.y
-= 1;
2795 if (direction
== 5) ccc
.y
+= 1;
2796 else if (direction
== 6) ccc
.y
-= 1;
2797 else if (direction
== 7) ccc
.x
-= 1;
2798 else if (direction
== 8) ccc
.x
+= 1;
2800 if (!isok(ccc
.x
, ccc
.y
)) break; /* otherwise the game could segfault! */
2802 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2803 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0 ) break;
2806 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2807 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (t_at(ccc
.x
, ccc
.y
) && !allowtrap
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0) {
2808 if (trycnt
< 50) {trycnt
++; goto newtry
;}
2809 return; /* more than 50 tries */
2812 if (!isok(ccc
.x
, ccc
.y
)) return; /* otherwise the game could segfault! */
2814 pline("You're pushed back!");
2816 teleds(ccc
.x
, ccc
.y
, allowtrap
);
2818 if (!(InterfaceScrewed
|| u
.uprops
[INTERFACE_SCREW
].extrinsic
|| have_interfacescrewstone())) doredraw();
2823 pushplayerfar(allowtrap
, distance
)
2828 int direction
, pushwidth
, trycnt
;
2829 register struct obj
*otmp
;
2832 if (uwep
&& uwep
->oartifact
== ART_IMHULLU
&& rn2(10)) return;
2833 if (uarmf
&& uarmf
->oartifact
== ART_STAND_TALL_IN_THE_STORM
) return;
2837 pushwidth
= rnd(distance
);
2841 while (pushwidth
--) {
2842 if (direction
== 1 || direction
== 5) ccc
.x
+= 1;
2843 else if (direction
== 2 || direction
== 6) ccc
.x
-= 1;
2844 else if (direction
== 3 || direction
== 7) ccc
.y
+= 1;
2845 else if (direction
== 4 || direction
== 8) ccc
.y
-= 1;
2847 if (direction
== 5) ccc
.y
+= 1;
2848 else if (direction
== 6) ccc
.y
-= 1;
2849 else if (direction
== 7) ccc
.x
-= 1;
2850 else if (direction
== 8) ccc
.x
+= 1;
2852 if (!isok(ccc
.x
, ccc
.y
)) break; /* otherwise the game could segfault! */
2854 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2855 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0 ) break;
2858 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2859 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (t_at(ccc
.x
, ccc
.y
) && !allowtrap
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0) {
2860 if (trycnt
< 5000) {trycnt
++; goto newtry
;}
2861 return; /* more than 5000 tries */
2864 if (!isok(ccc
.x
, ccc
.y
)) return; /* otherwise the game could segfault! */
2866 pline("You're pushed back!");
2868 teleds(ccc
.x
, ccc
.y
, allowtrap
);
2870 if (!(InterfaceScrewed
|| u
.uprops
[INTERFACE_SCREW
].extrinsic
|| have_interfacescrewstone())) doredraw();
2875 pushplayersilently(allowtrap
)
2879 int direction
, pushwidth
, trycnt
;
2880 register struct obj
*otmp
;
2883 if (uwep
&& uwep
->oartifact
== ART_IMHULLU
&& rn2(10)) return;
2884 if (uarmf
&& uarmf
->oartifact
== ART_STAND_TALL_IN_THE_STORM
) return;
2889 if (!rn2(2)) pushwidth
+= rnd(2);
2890 if (uarmf
&& itemhasappearance(uarmf
, APP_ZERO_DROP_SHOES
)) {
2892 if (!rn2(2)) pushwidth
+= rnd(3);
2897 while (pushwidth
--) {
2898 if (direction
== 1 || direction
== 5) ccc
.x
+= 1;
2899 else if (direction
== 2 || direction
== 6) ccc
.x
-= 1;
2900 else if (direction
== 3 || direction
== 7) ccc
.y
+= 1;
2901 else if (direction
== 4 || direction
== 8) ccc
.y
-= 1;
2903 if (direction
== 5) ccc
.y
+= 1;
2904 else if (direction
== 6) ccc
.y
-= 1;
2905 else if (direction
== 7) ccc
.x
-= 1;
2906 else if (direction
== 8) ccc
.x
+= 1;
2908 if (!isok(ccc
.x
, ccc
.y
)) break; /* otherwise the game could segfault! */
2910 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2911 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0 ) break;
2914 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2915 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (t_at(ccc
.x
, ccc
.y
) && !allowtrap
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0) {
2916 if (trycnt
< 50) {trycnt
++; goto newtry
;}
2917 return; /* more than 50 tries */
2920 if (!isok(ccc
.x
, ccc
.y
)) return; /* otherwise the game could segfault! */
2922 teleds(ccc
.x
, ccc
.y
, allowtrap
);
2924 if (!(InterfaceScrewed
|| u
.uprops
[INTERFACE_SCREW
].extrinsic
|| have_interfacescrewstone())) doredraw();
2933 int direction
, pushwidth
, trycnt
;
2934 register struct obj
*otmp
;
2938 impossible("pushmonster() called with no monster.");
2942 if (!isok(mtmp
->mx
, mtmp
->my
)) {
2943 impossible("monster coordinates for pushmonster() out of range: %d, %d", mtmp
->mx
, mtmp
->my
);
2950 if (!rn2(2)) pushwidth
+= rnd(2);
2954 while (pushwidth
--) {
2955 if (direction
== 1 || direction
== 5) ccc
.x
+= 1;
2956 else if (direction
== 2 || direction
== 6) ccc
.x
-= 1;
2957 else if (direction
== 3 || direction
== 7) ccc
.y
+= 1;
2958 else if (direction
== 4 || direction
== 8) ccc
.y
-= 1;
2960 if (direction
== 5) ccc
.y
+= 1;
2961 else if (direction
== 6) ccc
.y
-= 1;
2962 else if (direction
== 7) ccc
.x
-= 1;
2963 else if (direction
== 8) ccc
.x
+= 1;
2965 if (!isok(ccc
.x
, ccc
.y
)) break; /* otherwise the game could segfault! */
2967 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2968 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0 ) break;
2971 if ((levl
[ccc
.x
][ccc
.y
].typ
!= ROOM
&& levl
[ccc
.x
][ccc
.y
].typ
!= AIR
&& levl
[ccc
.x
][ccc
.y
].typ
!= STAIRS
&& levl
[ccc
.x
][ccc
.y
].typ
!= LADDER
&& levl
[ccc
.x
][ccc
.y
].typ
!= FOUNTAIN
&& levl
[ccc
.x
][ccc
.y
].typ
!= THRONE
&& levl
[ccc
.x
][ccc
.y
].typ
!= SINK
&& levl
[ccc
.x
][ccc
.y
].typ
!= TOILET
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRAVE
&& levl
[ccc
.x
][ccc
.y
].typ
!= ALTAR
&& levl
[ccc
.x
][ccc
.y
].typ
!= ICE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CLOUD
&& levl
[ccc
.x
][ccc
.y
].typ
!= SNOW
&& levl
[ccc
.x
][ccc
.y
].typ
!= ASH
&& levl
[ccc
.x
][ccc
.y
].typ
!= SAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= PAVEDFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= HIGHWAY
&& levl
[ccc
.x
][ccc
.y
].typ
!= GRASSLAND
&& levl
[ccc
.x
][ccc
.y
].typ
!= NETHERMIST
&& levl
[ccc
.x
][ccc
.y
].typ
!= STALACTITE
&& levl
[ccc
.x
][ccc
.y
].typ
!= CRYPTFLOOR
&& levl
[ccc
.x
][ccc
.y
].typ
!= BUBBLES
&& levl
[ccc
.x
][ccc
.y
].typ
!= RAINCLOUD
&&
2972 levl
[ccc
.x
][ccc
.y
].typ
!= CORR
) || MON_AT(ccc
.x
, ccc
.y
) || (otmp
= sobj_at(BOULDER
, ccc
.x
, ccc
.y
)) != 0) {
2973 if (trycnt
< 50) {trycnt
++; goto newtry
;}
2974 return 0; /* more than 50 tries */
2977 if (!isok(ccc
.x
, ccc
.y
)) return 0; /* otherwise the game could segfault! */
2979 remove_monster(mtmp
->mx
, mtmp
->my
);
2980 place_monster(mtmp
, ccc
.x
, ccc
.y
);