1    | /*	SCCS Id: @(#)teleport.c	3.3	2000/03/03	*/
2    | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | #include "hack.h"
6    | 
7    | STATIC_DCL boolean FDECL(tele_jump_ok, (int,int,int,int));
8    | STATIC_DCL boolean FDECL(teleok, (int,int,BOOLEAN_P));
9    | STATIC_DCL void NDECL(vault_tele);
10   | STATIC_DCL boolean FDECL(rloc_pos_ok, (int,int,struct monst *));
11   | STATIC_DCL void FDECL(mvault_tele, (struct monst *));
12   | 
13   | /*
14   |  * Is (x,y) a good position of mtmp?  If mtmp is NULL, then is (x,y) good
15   |  * for an object?
16   |  *
17   |  * This function will only look at mtmp->mdat, so makemon, mplayer, etc can
18   |  * call it to generate new monster positions with fake monster structures.
19   |  */
20   | boolean
21   | goodpos(x, y, mtmp)
22   | int x,y;
23   | struct monst *mtmp;
24   | {
25   | 	struct permonst *mdat = NULL;
26   | 
27   | 	if (!isok(x, y)) return FALSE;
28   | 
29   | 	/* in many cases, we're trying to create a new monster, which
30   | 	 * can't go on top of the player or any existing monster.
31   | 	 * however, occasionally we are relocating engravings or objects,
32   | 	 * which could be co-located and thus get restricted a bit too much.
33   | 	 * oh well.
34   | 	 */
35   | 	if (mtmp != &youmonst && x == u.ux && y == u.uy
36   | #ifdef STEED
37   | 			&& (!u.usteed || mtmp != u.usteed)
38   | #endif
39   | 			)
40   | 		return FALSE;
41   | 
42   | 	if (mtmp) {
43   | 	    struct monst *mtmp2 = m_at(x,y);
44   | 
45   | 	    if (mtmp2 && mtmp2 != mtmp)
46   | 		return FALSE;
47   | 
48   | 	    mdat = mtmp->data;
49   | 	    if (is_pool(x,y)) {
50   | 		if (mtmp == &youmonst)
51   | 			return !!(HLevitation || Flying || Wwalking ||
52   | 					Swimming || Amphibious);
53   | 		else	return (is_flyer(mdat) || is_swimmer(mdat) ||
54   | 							is_clinger(mdat));
55   | 	    } else if (mdat->mlet == S_EEL && rn2(13)) {
56   | 		return FALSE;
57   | 	    } else if (is_lava(x,y)) {
58   | 		if (mtmp == &youmonst)
59   | 		    return !!HLevitation;
60   | 		else
61   | 		    return (is_flyer(mdat) || likes_lava(mdat));
62   | 	    }
63   | 	    if (passes_walls(mdat) && may_passwall(x,y)) return TRUE;
64   | 	}
65   | 	if (!ACCESSIBLE(levl[x][y].typ)) return FALSE;
66   | 	if (closed_door(x, y) && (!mdat || !amorphous(mdat)))
67   | 		return FALSE;
68   | 	if (sobj_at(BOULDER, x, y) && (!mdat || !throws_rocks(mdat)))
69   | 		return FALSE;
70   | 	return TRUE;
71   | }
72   | 
73   | /*
74   |  * "entity next to"
75   |  *
76   |  * Attempt to find a good place for the given monster type in the closest
77   |  * position to (xx,yy).  Do so in successive square rings around (xx,yy).
78   |  * If there is more than one valid positon in the ring, choose one randomly.
79   |  * Return TRUE and the position chosen when successful, FALSE otherwise.
80   |  */
81   | boolean
82   | enexto(cc, xx, yy, mdat)
83   | coord *cc;
84   | register xchar xx, yy;
85   | struct permonst *mdat;
86   | {
87   | #define MAX_GOOD 15
88   |     coord good[MAX_GOOD], *good_ptr;
89   |     int x, y, range, i;
90   |     int xmin, xmax, ymin, ymax;
91   |     struct monst fakemon;	/* dummy monster */
92   | 
93   |     if (!mdat) {
94   | #ifdef DEBUG
95   | 	pline("enexto() called with mdat==0");
96   | #endif
97   | 	/* default to player's original monster type */
98   | 	mdat = &mons[u.umonster];
99   |     }
100  |     fakemon.data = mdat;	/* set up for goodpos */
101  |     good_ptr = good;
102  |     range = 1;
103  |     /*
104  |      * Walk around the border of the square with center (xx,yy) and
105  |      * radius range.  Stop when we find at least one valid position.
106  |      */
107  |     do {
108  | 	xmin = max(1, xx-range);
109  | 	xmax = min(COLNO-1, xx+range);
110  | 	ymin = max(0, yy-range);
111  | 	ymax = min(ROWNO-1, yy+range);
112  | 
113  | 	for (x = xmin; x <= xmax; x++)
114  | 	    if (goodpos(x, ymin, &fakemon)) {
115  | 		good_ptr->x = x;
116  | 		good_ptr->y = ymin ;
117  | 		/* beware of accessing beyond segment boundaries.. */
118  | 		if (good_ptr++ == &good[MAX_GOOD-1]) goto full;
119  | 	    }
120  | 	for (x = xmin; x <= xmax; x++)
121  | 	    if (goodpos(x, ymax, &fakemon)) {
122  | 		good_ptr->x = x;
123  | 		good_ptr->y = ymax ;
124  | 		/* beware of accessing beyond segment boundaries.. */
125  | 		if (good_ptr++ == &good[MAX_GOOD-1]) goto full;
126  | 	    }
127  | 	for (y = ymin+1; y < ymax; y++)
128  | 	    if (goodpos(xmin, y, &fakemon)) {
129  | 		good_ptr->x = xmin;
130  | 		good_ptr-> y = y ;
131  | 		/* beware of accessing beyond segment boundaries.. */
132  | 		if (good_ptr++ == &good[MAX_GOOD-1]) goto full;
133  | 	    }
134  | 	for (y = ymin+1; y < ymax; y++)
135  | 	    if (goodpos(xmax, y, &fakemon)) {
136  | 		good_ptr->x = xmax;
137  | 		good_ptr->y = y ;
138  | 		/* beware of accessing beyond segment boundaries.. */
139  | 		if (good_ptr++ == &good[MAX_GOOD-1]) goto full;
140  | 	    }
141  | 	range++;
142  | 
143  | 	/* return if we've grown too big (nothing is valid) */
144  | 	if (range > ROWNO && range > COLNO) return FALSE;
145  |     } while (good_ptr == good);
146  | 
147  | full:
148  |     i = rn2((int)(good_ptr - good));
149  |     cc->x = good[i].x;
150  |     cc->y = good[i].y;
151  |     return TRUE;
152  | }
153  | 
154  | /*
155  |  * Check for restricted areas present in some special levels.  (This might
156  |  * need to be augmented to allow deliberate passage in wizard mode, but
157  |  * only for explicitly chosen destinations.)
158  |  */
159  | STATIC_OVL boolean
160  | tele_jump_ok(x1, y1, x2, y2)
161  | int x1, y1, x2, y2;
162  | {
163  | 	if (dndest.nlx > 0) {
164  | 	    /* if inside a restricted region, can't teleport outside */
165  | 	    if (within_bounded_area(x1, y1, dndest.nlx, dndest.nly,
166  | 						dndest.nhx, dndest.nhy) &&
167  | 		!within_bounded_area(x2, y2, dndest.nlx, dndest.nly,
168  | 						dndest.nhx, dndest.nhy))
169  | 		return FALSE;
170  | 	    /* and if outside, can't teleport inside */
171  | 	    if (!within_bounded_area(x1, y1, dndest.nlx, dndest.nly,
172  | 						dndest.nhx, dndest.nhy) &&
173  | 		within_bounded_area(x2, y2, dndest.nlx, dndest.nly,
174  | 						dndest.nhx, dndest.nhy))
175  | 		return FALSE;
176  | 	}
177  | 	if (updest.nlx > 0) {		/* ditto */
178  | 	    if (within_bounded_area(x1, y1, updest.nlx, updest.nly,
179  | 						updest.nhx, updest.nhy) &&
180  | 		!within_bounded_area(x2, y2, updest.nlx, updest.nly,
181  | 						updest.nhx, updest.nhy))
182  | 		return FALSE;
183  | 	    if (!within_bounded_area(x1, y1, updest.nlx, updest.nly,
184  | 						updest.nhx, updest.nhy) &&
185  | 		within_bounded_area(x2, y2, updest.nlx, updest.nly,
186  | 						updest.nhx, updest.nhy))
187  | 		return FALSE;
188  | 	}
189  | 	return TRUE;
190  | }
191  | 
192  | STATIC_OVL boolean
193  | teleok(x, y, trapok)
194  | register int x, y;
195  | boolean trapok;
196  | {
197  | 	if (!trapok && t_at(x, y)) return FALSE;
198  | 	if (!goodpos(x, y, &youmonst)) return FALSE;
199  | 	if (!tele_jump_ok(u.ux, u.uy, x, y)) return FALSE;
200  | 	return TRUE;
201  | }
202  | 
203  | void
204  | teleds(nux, nuy)
205  | register int nux,nuy;
206  | {
207  | 	if (Punished) unplacebc();
208  | 	u.utrap = 0;
209  | 	u.ustuck = 0;
210  | 	u.ux0 = u.ux;
211  | 	u.uy0 = u.uy;
212  | 	u.ux = nux;
213  | 	u.uy = nuy;
214  | 	fill_pit(u.ux0, u.uy0); /* do this now so that cansee() is correct */
215  | 
216  | 	if (hides_under(youmonst.data))
217  | 		u.uundetected = OBJ_AT(nux, nuy);
218  | 	else if (youmonst.data->mlet == S_EEL)
219  | 		u.uundetected = is_pool(u.ux, u.uy);
220  | 	else {
221  | 		u.uundetected = 0;
222  | 		/* mimics stop being unnoticed */
223  | 		if (youmonst.data->mlet == S_MIMIC)
224  | 		    youmonst.m_ap_type = M_AP_NOTHING;
225  | 	}
226  | 
227  | 	if (u.uswallow) {
228  | 		u.uswldtim = u.uswallow = 0;
229  | 		docrt();
230  | 	}
231  | 	if (Punished) placebc();
232  | 	initrack(); /* teleports mess up tracking monsters without this */
233  | 	update_player_regions();
234  | #ifdef STEED
235  | 	/* Move your steed, too */
236  | 	if (u.usteed) {
237  | 		u.usteed->mx = nux;
238  | 		u.usteed->my = nuy;
239  | 	}
240  | #endif
241  | 	/*
242  | 	 *  Make sure the hero disappears from the old location.  This will
243  | 	 *  not happen if she is teleported within sight of her previous
244  | 	 *  location.  Force a full vision recalculation because the hero
245  | 	 *  is now in a new location.
246  | 	 */
247  | 	newsym(u.ux0,u.uy0);
248  | 	see_monsters();
249  | 	vision_full_recalc = 1;
250  | 	nomul(0);
251  | 	spoteffects(TRUE);
252  | 	invocation_message();
253  | }
254  | 
255  | boolean
256  | safe_teleds()
257  | {
258  | 	register int nux, nuy, tcnt = 0;
259  | 
260  | 	do {
261  | 		nux = rnd(COLNO-1);
262  | 		nuy = rn2(ROWNO);
263  | 	} while (!teleok(nux, nuy, (boolean)(tcnt > 200)) && ++tcnt <= 400);
264  | 
265  | 	if (tcnt <= 400) {
266  | 		teleds(nux, nuy);
267  | 		return TRUE;
268  | 	} else
269  | 		return FALSE;
270  | }
271  | 
272  | STATIC_OVL void
273  | vault_tele()
274  | {
275  | 	register struct mkroom *croom = search_special(VAULT);
276  | 	coord c;
277  | 
278  | 	if (croom && somexy(croom, &c) && teleok(c.x,c.y,FALSE)) {
279  | 		teleds(c.x,c.y);
280  | 		return;
281  | 	}
282  | 	tele();
283  | }
284  | 
285  | boolean
286  | teleport_pet(mtmp, force_it)
287  | register struct monst *mtmp;
288  | boolean force_it;
289  | {
290  | 	register struct obj *otmp;
291  | 
292  | #ifdef STEED
293  | 	if (mtmp == u.usteed)
294  | 		return (FALSE);
295  | #endif
296  | 
297  | 	if (mtmp->mleashed) {
298  | 	    otmp = get_mleash(mtmp);
299  | 	    if (!otmp) {
300  | 		impossible("%s is leashed, without a leash.", Monnam(mtmp));
301  | 		goto release_it;
302  | 	    }
303  | 	    if (otmp->cursed && !force_it) {
304  | 		yelp(mtmp);
305  | 		return FALSE;
306  | 	    } else {
307  | 		Your("leash goes slack.");
308  |  release_it:
309  | 		m_unleash(mtmp);
310  | 		return TRUE;
311  | 	    }
312  | 	}
313  | 	return TRUE;
314  | }
315  | 
316  | void
317  | tele()
318  | {
319  | 	coord cc;
320  | 
321  | 	/* Disable teleportation in stronghold && Vlad's Tower */
322  | 	if (level.flags.noteleport) {
323  | #ifdef WIZARD
324  | 		if (!wizard) {
325  | #endif
326  | 		    pline("A mysterious force prevents you from teleporting!");
327  | 		    return;
328  | #ifdef WIZARD
329  | 		}
330  | #endif
331  | 	}
332  | 
333  | 	/* don't show trap if "Sorry..." */
334  | 	if (!Blinded) make_blinded(0L,FALSE);
335  | 
336  | 	if ((u.uhave.amulet || On_W_tower_level(&u.uz)) && !rn2(3)) {
337  | 	    You_feel("disoriented for a moment.");
338  | 	    return;
339  | 	}
340  | 	if (Teleport_control
341  | #ifdef WIZARD
342  | 			    || wizard
343  | #endif
344  | 					) {
345  | 	    if (unconscious()) {
346  | 		pline("Being unconscious, you cannot control your teleport.");
347  | 	    } else {
348  | #ifdef STEED
349  | 		    char buf[BUFSZ];
350  | 		    if (u.usteed) Sprintf(buf," and %s", mon_nam(u.usteed));
351  | #endif
352  | 		    pline("To what position do you%s want to be teleported?",
353  | #ifdef STEED
354  | 				u.usteed ? buf :
355  | #endif
356  | 			   "");
357  | 		    cc.x = u.ux;
358  | 		    cc.y = u.uy;
359  | 		    if (getpos(&cc, TRUE, "the desired position") < 0)
360  | 			return;	/* abort */
361  | 		    /* possible extensions: introduce a small error if
362  | 		       magic power is low; allow transfer to solid rock */
363  | 		    if (teleok(cc.x, cc.y, FALSE)) {
364  | 			teleds(cc.x, cc.y);
365  | 			return;
366  | 		    }
367  | 		    pline("Sorry...");
368  | 		}
369  | 	}
370  | 
371  | 	(void) safe_teleds();
372  | }
373  | 
374  | int
375  | dotele()
376  | {
377  | 	struct trap *trap;
378  | 
379  | 	trap = t_at(u.ux, u.uy);
380  | 	if (trap && (!trap->tseen || trap->ttyp != TELEP_TRAP))
381  | 		trap = 0;
382  | 
383  | 	if (trap) {
384  | 		if (trap->once) {
385  | 			pline("This is a vault teleport, usable once only.");
386  | 			if (yn("Jump in?") == 'n')
387  | 				trap = 0;
388  | 			else {
389  | 				deltrap(trap);
390  | 				newsym(u.ux, u.uy);
391  | 			}
392  | 		}
393  | 		if (trap)
394  | 			You("%s onto the teleportation trap.",
395  | 			    locomotion(youmonst.data, "jump"));
396  | 	}
397  | 	if (!trap) {
398  | 	    boolean castit = FALSE;
399  | 	    register int sp_no = 0, energy = 0;
400  | 
401  | 	    if (!Teleportation || (u.ulevel < (Role_if(PM_WIZARD) ? 8 : 12)
402  | 					&& !can_teleport(youmonst.data))) {
403  | 		/* Try to use teleport away spell. */
404  | 		if (objects[SPE_TELEPORT_AWAY].oc_name_known && !Confusion)
405  | 		    for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
406  | 			if (spl_book[sp_no].sp_id == SPE_TELEPORT_AWAY) {
407  | 				castit = TRUE;
408  | 				break;
409  | 			}
410  | #ifdef WIZARD
411  | 		if (!wizard) {
412  | #endif
413  | 		    if (!castit) {
414  | 			if (!Teleportation)
415  | 			    You("don't know that spell.");
416  | 			else You("are not able to teleport at will.");
417  | 			return(0);
418  | 		    }
419  | #ifdef WIZARD
420  | 		}
421  | #endif
422  | 	    }
423  | 
424  | 	    if (u.uhunger <= 100 || ACURR(A_STR) < 6) {
425  | #ifdef WIZARD
426  | 		if (!wizard) {
427  | #endif
428  | 			You("lack the strength %s.",
429  | 			    castit ? "for a teleport spell" : "to teleport");
430  | 			return 1;
431  | #ifdef WIZARD
432  | 		}
433  | #endif
434  | 	    }
435  | 
436  | 	    energy = objects[SPE_TELEPORT_AWAY].oc_level * 7 / 2 - 2;
437  | 	    if (u.uen <= energy) {
438  | #ifdef WIZARD
439  | 		if (wizard)
440  | 			energy = u.uen;
441  | 		else
442  | #endif
443  | 		{
444  | 			You("lack the energy %s.",
445  | 			    castit ? "for a teleport spell" : "to teleport");
446  | 			return 1;
447  | 		}
448  | 	    }
449  | 
450  | 	    if (check_capacity(
451  | 			"Your concentration falters from carrying so much."))
452  | 		return 1;
453  | 
454  | 	    if (castit) {
455  | 		exercise(A_WIS, TRUE);
456  | 		if (spelleffects(sp_no, TRUE))
457  | 			return(1);
458  | 		else
459  | #ifdef WIZARD
460  | 		    if (!wizard)
461  | #endif
462  | 			return(0);
463  | 	    } else {
464  | 		u.uen -= energy;
465  | 		flags.botl = 1;
466  | 	    }
467  | 	}
468  | 
469  | 	if (next_to_u()) {
470  | 		if (trap && trap->once) vault_tele();
471  | 		else tele();
472  | 		(void) next_to_u();
473  | 	} else {
474  | 		You(shudder_for_moment);
475  | 		return(0);
476  | 	}
477  | 	if (!trap) morehungry(100);
478  | 	return(1);
479  | }
480  | 
481  | 
482  | void
483  | level_tele()
484  | {
485  | 	register int newlev;
486  | 	d_level newlevel;
487  | 	const char *escape_by_flying = 0;	/* when surviving dest of -N */
488  | 
489  | 	if ((u.uhave.amulet || In_endgame(&u.uz) || In_sokoban(&u.uz))
490  | #ifdef WIZARD
491  | 						&& !wizard
492  | #endif
493  | 							) {
494  | 	    You_feel("very disoriented for a moment.");
495  | 	    return;
496  | 	}
497  | 	if (Teleport_control
498  | #ifdef WIZARD
499  | 	   || wizard
500  | #endif
501  | 		) {
502  | 	    char buf[BUFSZ], qbuf[BUFSZ];
503  | 	    int trycnt = 0;
504  | 
505  | 	    Strcpy(qbuf, "To what level do you want to teleport?");
506  | 	    do {
507  | 		if (++trycnt == 2) Strcat(qbuf, " [type a number]");
508  | 		getlin(qbuf, buf);
509  | 		if (!strcmp(buf,"\033"))	/* cancelled */
510  | 		    return;
511  | 		else if (!strcmp(buf,"*"))
512  | 		    goto random_levtport;
513  | 		if ((newlev = lev_by_name(buf)) == 0) newlev = atoi(buf);
514  | 	    } while (!newlev && !digit(buf[0]) &&
515  | 		     (buf[0] != '-' || !digit(buf[1])) &&
516  | 		     trycnt < 10);
517  | 
518  | 	    /* no dungeon escape via this route */
519  | 	    if (newlev == 0) {
520  | 		if (trycnt >= 10)
521  | 		    goto random_levtport;
522  | 		if (ynq("Go to Nowhere.  Are you sure?") != 'y') return;
523  | 		You("scream in agony as your body begins to warp...");
524  | 		display_nhwindow(WIN_MESSAGE, FALSE);
525  | 		You("cease to exist.");
526  | 		killer_format = NO_KILLER_PREFIX;
527  | 		killer = "committed suicide";
528  | 		done(DIED);
529  | 		return;
530  | 	    }
531  | 
532  | 	    /* if in Knox and the requested level > 0, stay put.
533  | 	     * we let negative values requests fall into the "heaven" loop.
534  | 	     */
535  | 	    if (Is_knox(&u.uz) && newlev > 0) {
536  | 		You(shudder_for_moment);
537  | 		return;
538  | 	    }
539  | 	    /* if in Quest, the player sees "Home 1", etc., on the status
540  | 	     * line, instead of the logical depth of the level.  controlled
541  | 	     * level teleport request is likely to be relativized to the
542  | 	     * status line, and consequently it should be incremented to
543  | 	     * the value of the logical depth of the target level.
544  | 	     *
545  | 	     * we let negative values requests fall into the "heaven" loop.
546  | 	     */
547  | 	    if (In_quest(&u.uz) && newlev > 0)
548  | 		newlev = newlev + dungeons[u.uz.dnum].depth_start - 1;
549  | 	} else { /* involuntary level tele */
550  |  random_levtport:
551  | 	    newlev = random_teleport_level();
552  | 	    if (newlev == depth(&u.uz)) {
553  | 		You(shudder_for_moment);
554  | 		return;
555  | 	    }
556  | 	}
557  | 
558  | 	if (!next_to_u()) {
559  | 		You(shudder_for_moment);
560  | 		return;
561  | 	}
562  | #ifdef WIZARD
563  | 	if (In_endgame(&u.uz)) {	/* must already be wizard */
564  | 	    int llimit = dunlevs_in_dungeon(&u.uz);
565  | 
566  | 	    if (newlev >= 0 || newlev <= -llimit) {
567  | 		You_cant("get there from here.");
568  | 		return;
569  | 	    }
570  | 	    newlevel.dnum = u.uz.dnum;
571  | 	    newlevel.dlevel = llimit + newlev;
572  | 	    schedule_goto(&newlevel, FALSE, FALSE, 0, (char *)0, (char *)0);
573  | 	    return;
574  | 	}
575  | #endif
576  | 
577  | 	killer = 0;		/* still alive, so far... */
578  | 
579  | 	if (newlev < 0) {
580  | 		if (newlev <= -10) {
581  | 			You("arrive in heaven.");
582  | 			verbalize("Thou art early, but we'll admit thee.");
583  | 			killer_format = NO_KILLER_PREFIX;
584  | 			killer = "went to heaven prematurely";
585  | 		} else if (newlev == -9) {
586  | 			You_feel("deliriously happy. ");
587  | 			pline("(In fact, you're on Cloud 9!) ");
588  | 			display_nhwindow(WIN_MESSAGE, FALSE);
589  | 		} else
590  | 			You("are now high above the clouds...");
591  | 
592  | 		if (killer) {
593  | 		    ;		/* arrival in heaven is pending */
594  | 		} else if (Levitation) {
595  | 		    escape_by_flying = "float gently down to earth";
596  | 		} else if (Flying) {
597  | 		    escape_by_flying = "fly down to the ground";
598  | 		} else {
599  | 		    pline("Unfortunately, you don't know how to fly.");
600  | 		    You("plummet a few thousand feet to your death.");
601  | 		    killer_format = NO_KILLER_PREFIX;
602  | 		    killer =
603  |     self_pronoun("teleported out of the dungeon and fell to %s death","his");
604  | 		}
605  | 	}
606  | 
607  | 	if (killer) {	/* the chosen destination was not survivable */
608  | 	    d_level lsav;
609  | 
610  | 	    /* set specific death location; this also suppresses bones */
611  | 	    lsav = u.uz;	/* save current level, see below */
612  | 	    u.uz.dnum = 0;	/* main dungeon */
613  | 	    u.uz.dlevel = (newlev <= -10) ? -10 : 0;	/* heaven or surface */
614  | 	    done(DIED);
615  | 	    /* can only get here via life-saving (or declining to die in
616  | 	       explore|debug mode); the hero has now left the dungeon... */
617  | 	    escape_by_flying = "find yourself back on the surface";
618  | 	    u.uz = lsav;	/* restore u.uz so escape code works */
619  | 	}
620  | 
621  | 	/* calls done(ESCAPED) if newlevel==0 */
622  | 	if (escape_by_flying) {
623  | 	    You("%s.", escape_by_flying);
624  | 	    newlevel.dnum = 0;		/* specify main dungeon */
625  | 	    newlevel.dlevel = 0;	/* escape the dungeon */
626  | 	    /* [dlevel used to be set to 1, but it doesn't make sense to
627  | 		teleport out of the dungeon and float or fly down to the
628  | 		surface but then actually arrive back inside the dungeon] */
629  | 	} else if (u.uz.dnum == medusa_level.dnum &&
630  | 	    newlev >= dungeons[u.uz.dnum].depth_start +
631  | 						dunlevs_in_dungeon(&u.uz)) {
632  | 	    find_hell(&newlevel);
633  | 	} else {
634  | 	    /* if invocation did not yet occur, teleporting into
635  | 	     * the last level of Gehennom is forbidden.
636  | 	     */
637  | #ifdef WIZARD
638  | 		if (!wizard)
639  | #endif
640  | 	    if (Inhell && !u.uevent.invoked &&
641  | 			newlev >= (dungeons[u.uz.dnum].depth_start +
642  | 					dunlevs_in_dungeon(&u.uz) - 1)) {
643  | 		newlev = dungeons[u.uz.dnum].depth_start +
644  | 					dunlevs_in_dungeon(&u.uz) - 2;
645  | 		pline("Sorry...");
646  | 	    }
647  | 	    /* no teleporting out of quest dungeon */
648  | 	    if (In_quest(&u.uz) && newlev < depth(&qstart_level))
649  | 		newlev = depth(&qstart_level);
650  | 	    /* the player thinks of levels purely in logical terms, so
651  | 	     * we must translate newlev to a number relative to the
652  | 	     * current dungeon.
653  | 	     */
654  | 	    get_level(&newlevel, newlev);
655  | 	}
656  | 	schedule_goto(&newlevel, FALSE, FALSE, 0, (char *)0, (char *)0);
657  | 	/* in case player just read a scroll and is about to be asked to
658  | 	   call it something, we can't defer until the end of the turn */
659  | 	if (u.utotype && !flags.mon_moving) deferred_goto();
660  | }
661  | 
662  | void
663  | domagicportal(ttmp)
664  | register struct trap *ttmp;
665  | {
666  | 	struct d_level target_level;
667  | 
668  | 	if (!next_to_u()) {
669  | 		You(shudder_for_moment);
670  | 		return;
671  | 	}
672  | 
673  | 	/* if landed from another portal, do nothing */
674  | 	/* problem: level teleport landing escapes the check */
675  | 	if (!on_level(&u.uz, &u.uz0)) return;
676  | 
677  | 	You("activated a magic portal!");
678  | 
679  | 	/* prevent the poor shnook, whose amulet was stolen while in
680  | 	 * the endgame, from accidently triggering the portal to the
681  | 	 * next level, and thus losing the game
682  | 	 */
683  | 	if (In_endgame(&u.uz) && !u.uhave.amulet) {
684  | 	    You_feel("dizzy for a moment, but nothing happens...");
685  | 	    return;
686  | 	}
687  | 
688  | 	target_level = ttmp->dst;
689  | 	schedule_goto(&target_level, FALSE, FALSE, 1,
690  | 		      "You feel dizzy for a moment, but the sensation passes.",
691  | 		      (char *)0);
692  | }
693  | 
694  | void
695  | tele_trap(trap)
696  | struct trap *trap;
697  | {
698  | 	if (In_endgame(&u.uz) || Antimagic) {
699  | 		if (Antimagic)
700  | 			shieldeff(u.ux, u.uy);
701  | 		You_feel("a wrenching sensation.");
702  | 	} else if (!next_to_u()) {
703  | 		You(shudder_for_moment);
704  | 	} else if (trap->once) {
705  | 		deltrap(trap);
706  | 		newsym(u.ux,u.uy);	/* get rid of trap symbol */
707  | 		vault_tele();
708  | 	} else
709  | 		tele();
710  | }
711  | 
712  | void
713  | level_tele_trap(trap)
714  | struct trap *trap;
715  | {
716  | 	You("%s onto a level teleport trap!",
717  | 		      Levitation ? (const char *)"float" :
718  | 				  locomotion(youmonst.data, "step"));
719  | 	if (Antimagic) {
720  | 	    shieldeff(u.ux, u.uy);
721  | 	}
722  | 	if (Antimagic || In_endgame(&u.uz)) {
723  | 	    You_feel("a wrenching sensation.");
724  | 	    return;
725  | 	}
726  | 	if (!Blind)
727  | 	    You("are momentarily blinded by a flash of light.");
728  | 	else
729  | 	    You("are momentarily disoriented.");
730  | 	deltrap(trap);
731  | 	newsym(u.ux,u.uy);	/* get rid of trap symbol */
732  | 	level_tele();
733  | }
734  | 
735  | /* check whether monster can arrive at location <x,y> via Tport (or fall) */
736  | STATIC_OVL boolean
737  | rloc_pos_ok(x, y, mtmp)
738  | register int x, y;		/* coordinates of candidate location */
739  | struct monst *mtmp;
740  | {
741  | 	register int xx, yy;
742  | 
743  | 	if (!goodpos(x, y, mtmp)) return FALSE;
744  | 	/*
745  | 	 * Check for restricted areas present in some special levels.
746  | 	 *
747  | 	 * `xx' is current column; if 0, then `yy' will contain flag bits
748  | 	 * rather than row:  bit #0 set => moving upwards; bit #1 set =>
749  | 	 * inside the Wizard's tower.
750  | 	 */
751  | 	xx = mtmp->mx;
752  | 	yy = mtmp->my;
753  | 	if (!xx) {
754  | 	    /* no current location (migrating monster arrival) */
755  | 	    if (dndest.nlx && On_W_tower_level(&u.uz))
756  | 		return ((yy & 2) != 0) ^	/* inside xor not within */
757  | 		       !within_bounded_area(x, y, dndest.nlx, dndest.nly,
758  | 						  dndest.nhx, dndest.nhy);
759  | 	    if (updest.lx && (yy & 1) != 0)	/* moving up */
760  | 		return (within_bounded_area(x, y, updest.lx, updest.ly,
761  | 						  updest.hx, updest.hy) &&
762  | 		       (!updest.nlx ||
763  | 			!within_bounded_area(x, y, updest.nlx, updest.nly,
764  | 						   updest.nhx, updest.nhy)));
765  | 	    if (dndest.lx && (yy & 1) == 0)	/* moving down */
766  | 		return (within_bounded_area(x, y, dndest.lx, dndest.ly,
767  | 						  dndest.hx, dndest.hy) &&
768  | 		       (!dndest.nlx ||
769  | 			!within_bounded_area(x, y, dndest.nlx, dndest.nly,
770  | 						   dndest.nhx, dndest.nhy)));
771  | 	} else {
772  | 	    /* current location is <xx,yy> */
773  | 	    if (!tele_jump_ok(xx, yy, x, y)) return FALSE;
774  | 	}
775  | 	/* <x,y> is ok */
776  | 	return TRUE;
777  | }
778  | 
779  | /*
780  |  * rloc_to()
781  |  *
782  |  * Pulls a monster from its current position and places a monster at
783  |  * a new x and y.  If oldx is 0, then the monster was not in the levels.monsters
784  |  * array.  However, if oldx is 0, oldy may still have a value because mtmp is a
785  |  * migrating_mon.  Worm tails are always placed randomly around the head of
786  |  * the worm.
787  |  */
788  | void
789  | rloc_to(mtmp, x, y)
790  | struct monst *mtmp;
791  | register int x, y;
792  | {
793  | 	register int oldx = mtmp->mx, oldy = mtmp->my;
794  | 
795  | 	if (x == mtmp->mx && y == mtmp->my)	/* that was easy */
796  | 		return;
797  | 
798  | 	if (oldx) {				/* "pick up" monster */
799  | 	    if (mtmp->wormno)
800  | 		remove_worm(mtmp);
801  | 	    else {
802  | 		remove_monster(oldx, oldy);
803  | 		newsym(oldx, oldy);		/* update old location */
804  | 	    }
805  | 	}
806  | 
807  | 	place_monster(mtmp, x, y);		/* put monster down */
808  | 	update_monster_region(mtmp);
809  | 
810  | 	if (mtmp->wormno)			/* now put down tail */
811  | 		place_worm_tail_randomly(mtmp, x, y);
812  | 
813  | 	if (u.ustuck == mtmp) {
814  | 		if (u.uswallow) {
815  | 			u.ux = x;
816  | 			u.uy = y;
817  | 			docrt();
818  | 		} else	u.ustuck = 0;
819  | 	}
820  | 
821  | 	newsym(x, y);				/* update new location */
822  | 	set_apparxy(mtmp);			/* orient monster */
823  | }
824  | 
825  | /* place a monster at a random location, typically due to teleport */
826  | void
827  | rloc(mtmp)
828  | struct monst *mtmp;	/* mx==0 implies migrating monster arrival */
829  | {
830  | 	register int x, y, trycount;
831  | 	xchar omx = mtmp->mx, omy = mtmp->my;
832  | 
833  | #ifdef STEED
834  | 	if (mtmp == u.usteed) {
835  | 	    tele();
836  | 	    return;
837  | 	}
838  | #endif
839  | 
840  | 	if (mtmp->iswiz && omx) {	/* Wizard, not just arriving */
841  | 	    if (!In_W_tower(u.ux, u.uy, &u.uz))
842  | 		x = xupstair,  y = yupstair;
843  | 	    else if (!xdnladder)	/* bottom level of tower */
844  | 		x = xupladder,  y = yupladder;
845  | 	    else
846  | 		x = xdnladder,  y = ydnladder;
847  | 	    /* if the wiz teleports away to heal, try the up staircase,
848  | 	       to block the player's escaping before he's healed
849  | 	       (deliberately use `goodpos' rather than `rloc_pos_ok' here) */
850  | 	    if (goodpos(x, y, mtmp))
851  | 		goto found_xy;
852  | 	}
853  | 
854  | 	trycount = 0;
855  | 	do {
856  | 	    x = rn1(COLNO-3,2);
857  | 	    y = rn2(ROWNO);
858  | 	    if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp)
859  | 				 : goodpos(x, y, mtmp))
860  | 		goto found_xy;
861  | 	} while (++trycount < 1000);
862  | 
863  | 	/* last ditch attempt to find a good place */
864  | 	for (x = 2; x < COLNO - 1; x++)
865  | 	    for (y = 0; y < ROWNO; y++)
866  | 		if (goodpos(x, y, mtmp))
867  | 		    goto found_xy;
868  | 
869  | 	/* level either full of monsters or somehow faulty */
870  | 	impossible("rloc(): couldn't relocate monster");
871  | 	return;
872  | 
873  |  found_xy:
874  | 	rloc_to(mtmp, x, y);
875  | 	/* shopkeepers will only teleport if you zap them with a wand of
876  | 	   teleportation or if they've been transformed into a jumpy monster;
877  | 	   the latter only happens if you've attacked them with polymorph */
878  | 	if (mtmp->isshk && !inhishop(mtmp)) make_angry_shk(mtmp, omx, omy);
879  | }
880  | 
881  | STATIC_OVL void
882  | mvault_tele(mtmp)
883  | struct monst *mtmp;
884  | {
885  | 	register struct mkroom *croom = search_special(VAULT);
886  | 	coord c;
887  | 
888  | 	if (croom && somexy(croom, &c) &&
889  | 				goodpos(c.x, c.y, mtmp)) {
890  | 		rloc_to(mtmp, c.x, c.y);
891  | 		return;
892  | 	}
893  | 	rloc(mtmp);
894  | }
895  | 
896  | boolean
897  | tele_restrict(mon)
898  | struct monst *mon;
899  | {
900  | 	if (level.flags.noteleport) {
901  | 		if (canseemon(mon))
902  | 		    pline("A mysterious force prevents %s from teleporting!",
903  | 			mon_nam(mon));
904  | 		return TRUE;
905  | 	}
906  | 	return FALSE;
907  | }
908  | 
909  | void
910  | mtele_trap(mtmp, trap, in_sight)
911  | struct monst *mtmp;
912  | struct trap *trap;
913  | int in_sight;
914  | {
915  | 	char *monname;
916  | 
917  | 	if (tele_restrict(mtmp)) return;
918  | 	if (teleport_pet(mtmp, FALSE)) {
919  | 	    /* save name with pre-movement visibility */
920  | 	    monname = Monnam(mtmp);
921  | 
922  | 	    /* Note: don't remove the trap if a vault.  Other-
923  | 	     * wise the monster will be stuck there, since
924  | 	     * the guard isn't going to come for it...
925  | 	     */
926  | 	    if (trap->once) mvault_tele(mtmp);
927  | 	    else rloc(mtmp);
928  | 
929  | 	    if (in_sight) {
930  | 		if (canseemon(mtmp))
931  | 		    pline("%s seems disoriented.", monname);
932  | 		else
933  | 		    pline("%s suddenly disappears!", monname);
934  | 		seetrap(trap);
935  | 	    }
936  | 	}
937  | }
938  | 
939  | /* return 0 if still on level, 3 if not */
940  | int
941  | mlevel_tele_trap(mtmp, trap, force_it, in_sight)
942  | struct monst *mtmp;
943  | struct trap *trap;
944  | boolean force_it;
945  | int in_sight;
946  | {
947  | 	int tt = trap->ttyp;
948  | 	struct permonst *mptr = mtmp->data;
949  | 
950  | 	if (mtmp == u.ustuck)	/* probably a vortex */
951  | 	    return 0;		/* temporary? kludge */
952  | 	if (teleport_pet(mtmp, force_it)) {
953  | 	    d_level tolevel;
954  | 	    int migrate_typ = MIGR_RANDOM;
955  | 
956  | 	    if ((tt == HOLE || tt == TRAPDOOR)) {
957  | 		if (Is_stronghold(&u.uz)) {
958  | 		    assign_level(&tolevel, &valley_level);
959  | 		} else if (Is_botlevel(&u.uz)) {
960  | 		    if (in_sight && trap->tseen)
961  | 			pline("%s avoids the %s.", Monnam(mtmp),
962  | 			(tt == HOLE) ? "hole" : "trap");
963  | 		    return 0;
964  | 		} else {
965  | 		    get_level(&tolevel, depth(&u.uz) + 1);
966  | 		}
967  | 	    } else if (tt == MAGIC_PORTAL) {
968  | 		if (In_endgame(&u.uz) &&
969  | 		    (mon_has_amulet(mtmp) || is_home_elemental(mptr))) {
970  | 		    if (in_sight && mptr->mlet != S_ELEMENTAL) {
971  | 			pline("%s seems to shimmer for a moment.",
972  | 							Monnam(mtmp));
973  | 			seetrap(trap);
974  | 		    }
975  | 		    return 0;
976  | 		} else {
977  | 		    assign_level(&tolevel, &trap->dst);
978  | 		    migrate_typ = MIGR_PORTAL;
979  | 		}
980  | 	    } else { /* (tt == LEVEL_TELEP) */
981  | 		int nlev;
982  | 
983  | 		if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) {
984  | 		    if (in_sight)
985  | 			pline("%s seems very disoriented for a moment.",
986  | 				Monnam(mtmp));
987  | 		    return 0;
988  | 		}
989  | 		nlev = random_teleport_level();
990  | 		if (nlev == depth(&u.uz)) {
991  | 		    if (in_sight)
992  | 			pline("%s shudders for a moment.", Monnam(mtmp));
993  | 		    return 0;
994  | 		}
995  | 		get_level(&tolevel, nlev);
996  | 	    }
997  | 
998  | 	    if (in_sight) {
999  | 		pline("Suddenly, %s disappears out of sight.", mon_nam(mtmp));
1000 | 		seetrap(trap);
1001 | 	    }
1002 | 	    migrate_to_level(mtmp, ledger_no(&tolevel),
1003 | 			     migrate_typ, (coord *)0);
1004 | 	    return 3;	/* no longer on this level */
1005 | 	}
1006 | 	return 0;
1007 | }
1008 | 
1009 | 
1010 | void
1011 | rloco(obj)
1012 | register struct obj *obj;
1013 | {
1014 | 	register xchar tx, ty, otx, oty;
1015 | 	boolean restricted_fall;
1016 | 	int try_limit = 4000;
1017 | 
1018 | 	if (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm])) {
1019 | 	    if (revive_corpse(obj)) return;
1020 | 	}
1021 | 
1022 | 	obj_extract_self(obj);
1023 | 	otx = obj->ox;
1024 | 	oty = obj->oy;
1025 | 	restricted_fall = (otx == 0 && dndest.lx);
1026 | 	do {
1027 | 	    tx = rn1(COLNO-3,2);
1028 | 	    ty = rn2(ROWNO);
1029 | 	    if (!--try_limit) break;
1030 | 	} while (!goodpos(tx, ty, (struct monst *)0) ||
1031 | 		/* bug: this lacks provision for handling the Wizard's tower */
1032 | 		 (restricted_fall &&
1033 | 		  (!within_bounded_area(tx, ty, dndest.lx, dndest.ly,
1034 | 						dndest.hx, dndest.hy) ||
1035 | 		   (dndest.nlx &&
1036 | 		    within_bounded_area(tx, ty, dndest.nlx, dndest.nly,
1037 | 						dndest.nhx, dndest.nhy)))));
1038 | 
1039 | 	if (flooreffects(obj, tx, ty, "fall")) {
1040 | 	    return;
1041 | 	} else if (otx == 0 && oty == 0) {
1042 | 	    ;	/* fell through a trap door; no update of old loc needed */
1043 | 	} else {
1044 | 	    if (costly_spot(otx, oty)
1045 | 	      && (!costly_spot(tx, ty) ||
1046 | 		  !index(in_rooms(tx, ty, 0), *in_rooms(otx, oty, 0)))) {
1047 | 		if (costly_spot(u.ux, u.uy) &&
1048 | 			    index(u.urooms, *in_rooms(otx, oty, 0)))
1049 | 		    addtobill(obj, FALSE, FALSE, FALSE);
1050 | 		else (void)stolen_value(obj, otx, oty, FALSE, FALSE);
1051 | 	    }
1052 | 	    newsym(otx, oty);	/* update old location */
1053 | 	}
1054 | 	place_object(obj, tx, ty);
1055 | 	newsym(tx, ty);
1056 | }
1057 | 
1058 | /* Returns an absolute depth */
1059 | int
1060 | random_teleport_level()
1061 | {
1062 | 	int nlev, max_depth, min_depth;
1063 | 
1064 | 	if (!rn2(5) || Is_knox(&u.uz))
1065 | 		return (int)depth(&u.uz);
1066 | 
1067 | 	/* Get a random value relative to the current dungeon */
1068 | 	/* Range is 1 to current+3, current not counting */
1069 | 	nlev = rnd((int)depth(&u.uz) + 2);
1070 | 	if (nlev >= (int)depth(&u.uz)) nlev++;
1071 | 
1072 | 	/* What I really want to do is as follows:
1073 | 	 * -- If in a dungeon that goes down, the new level is to be restricted
1074 | 	 *    to [top of parent, bottom of current dungeon]
1075 | 	 * -- If in a dungeon that goes up, the new level is to be restricted
1076 | 	 *    to [top of current dungeon, bottom of parent]
1077 | 	 * -- If in a quest dungeon or similar dungeon entered by portals,
1078 | 	 *    the new level is to be restricted to [top of current dungeon,
1079 | 	 *    bottom of current dungeon]
1080 | 	 * The current behavior is not as sophisticated as that ideal, but is
1081 | 	 * still better what we used to do, which was like this for players
1082 | 	 * but different for monsters for no obvious reason.  Currently, we
1083 | 	 * must explicitly check for special dungeons.  We check for Knox
1084 | 	 * above; endgame is handled in the caller due to its different
1085 | 	 * message ("disoriented").
1086 | 	 * --KAA
1087 | 	 */
1088 | 	min_depth = 1;
1089 | 	max_depth = dunlevs_in_dungeon(&u.uz) +
1090 | 			(dungeons[u.uz.dnum].depth_start - 1);
1091 | 
1092 | 	if (nlev > max_depth) {
1093 | 	    nlev = max_depth;
1094 | 	    /* teleport up if already on bottom */
1095 | 	    if (Is_botlevel(&u.uz)) nlev -= rnd(3);
1096 | 	}
1097 | 	if (nlev < min_depth) {
1098 | 	    nlev = min_depth;
1099 | 	    if ((int)depth(&u.uz) == min_depth) {
1100 | 		nlev += rnd(3);
1101 | 		if (nlev > max_depth)
1102 | 		    nlev = max_depth;
1103 | 	    }
1104 | 	}
1105 | 	return nlev;
1106 | }
1107 | 
1108 | /* you teleport a monster (via wand, spell, or poly'd q.mechanic attack);
1109 |    return false iff the attempt fails */
1110 | boolean
1111 | u_teleport_mon(mtmp, give_feedback)
1112 | struct monst *mtmp;
1113 | boolean give_feedback;
1114 | {
1115 | 	coord cc;
1116 | 
1117 | 	if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) {
1118 | 	    if (give_feedback)
1119 | 		pline("%s resists your magic!", Monnam(mtmp));
1120 | 	    return FALSE;
1121 | 	} else {
1122 | 	    if (is_rider(mtmp->data) && rn2(13) &&
1123 | 		    enexto(&cc, u.ux, u.uy, mtmp->data))
1124 | 		rloc_to(mtmp, cc.x, cc.y);
1125 | 	    else
1126 | 		rloc(mtmp);
1127 | 	    return TRUE;
1128 | 	}
1129 | }
1130 | 
1131 | /*teleport.c*/