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