1 /* NetHack 3.6	hack.c	$NHDT-Date: 1576638500 2019/12/18 03:08:20 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.220 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Derek S. Ray, 2015. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 /* #define DEBUG */ /* uncomment for debugging */
9 
10 STATIC_DCL void NDECL(maybe_wail);
11 STATIC_DCL int NDECL(moverock);
12 STATIC_DCL int FDECL(still_chewing, (XCHAR_P, XCHAR_P));
13 STATIC_DCL void NDECL(dosinkfall);
14 STATIC_DCL boolean FDECL(findtravelpath, (int));
15 STATIC_DCL boolean FDECL(trapmove, (int, int, struct trap *));
16 STATIC_DCL struct monst *FDECL(monstinroom, (struct permonst *, int));
17 STATIC_DCL boolean FDECL(doorless_door, (int, int));
18 STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
19 STATIC_DCL void FDECL(maybe_smudge_engr, (int, int, int, int));
20 STATIC_DCL void NDECL(domove_core);
21 
22 #define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE)
23 
24 /* mode values for findtravelpath() */
25 #define TRAVP_TRAVEL 0
26 #define TRAVP_GUESS  1
27 #define TRAVP_VALID  2
28 
29 static anything tmp_anything;
30 
31 anything *
uint_to_any(ui)32 uint_to_any(ui)
33 unsigned ui;
34 {
35     tmp_anything = zeroany;
36     tmp_anything.a_uint = ui;
37     return &tmp_anything;
38 }
39 
40 anything *
long_to_any(lng)41 long_to_any(lng)
42 long lng;
43 {
44     tmp_anything = zeroany;
45     tmp_anything.a_long = lng;
46     return &tmp_anything;
47 }
48 
49 anything *
monst_to_any(mtmp)50 monst_to_any(mtmp)
51 struct monst *mtmp;
52 {
53     tmp_anything = zeroany;
54     tmp_anything.a_monst = mtmp;
55     return &tmp_anything;
56 }
57 
58 anything *
obj_to_any(obj)59 obj_to_any(obj)
60 struct obj *obj;
61 {
62     tmp_anything = zeroany;
63     tmp_anything.a_obj = obj;
64     return &tmp_anything;
65 }
66 
67 boolean
revive_nasty(x,y,msg)68 revive_nasty(x, y, msg)
69 int x, y;
70 const char *msg;
71 {
72     register struct obj *otmp, *otmp2;
73     struct monst *mtmp;
74     coord cc;
75     boolean revived = FALSE;
76 
77     for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
78         otmp2 = otmp->nexthere;
79         if (otmp->otyp == CORPSE
80             && (is_rider(&mons[otmp->corpsenm])
81                 || otmp->corpsenm == PM_WIZARD_OF_YENDOR)) {
82             /* move any living monster already at that location */
83             if ((mtmp = m_at(x, y)) && enexto(&cc, x, y, mtmp->data))
84                 rloc_to(mtmp, cc.x, cc.y);
85             if (msg)
86                 Norep("%s", msg);
87             revived = revive_corpse(otmp);
88         }
89     }
90 
91     /* this location might not be safe, if not, move revived monster */
92     if (revived) {
93         mtmp = m_at(x, y);
94         if (mtmp && !goodpos(x, y, mtmp, 0)
95             && enexto(&cc, x, y, mtmp->data)) {
96             rloc_to(mtmp, cc.x, cc.y);
97         }
98         /* else impossible? */
99     }
100 
101     return revived;
102 }
103 
104 STATIC_OVL int
moverock()105 moverock()
106 {
107     register xchar rx, ry, sx, sy;
108     register struct obj *otmp;
109     register struct trap *ttmp;
110     register struct monst *mtmp;
111 
112     sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */
113     while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
114         /* make sure that this boulder is visible as the top object */
115         if (otmp != level.objects[sx][sy])
116             movobj(otmp, sx, sy);
117 
118         rx = u.ux + 2 * u.dx; /* boulder destination position */
119         ry = u.uy + 2 * u.dy;
120         nomul(0);
121         if (Levitation || Is_airlevel(&u.uz)) {
122             if (Blind)
123                 feel_location(sx, sy);
124             You("don't have enough leverage to push %s.", the(xname(otmp)));
125             /* Give them a chance to climb over it? */
126             return -1;
127         }
128         if (verysmall(youmonst.data) && !u.usteed) {
129             if (Blind)
130                 feel_location(sx, sy);
131             pline("You're too small to push that %s.", xname(otmp));
132             goto cannot_push;
133         }
134         if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ)
135             && levl[rx][ry].typ != IRONBARS
136             && (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy)
137                 || doorless_door(rx, ry)) && !sobj_at(BOULDER, rx, ry)) {
138             ttmp = t_at(rx, ry);
139             mtmp = m_at(rx, ry);
140 
141             /* KMH -- Sokoban doesn't let you push boulders diagonally */
142             if (Sokoban && u.dx && u.dy) {
143                 if (Blind)
144                     feel_location(sx, sy);
145                 pline("%s won't roll diagonally on this %s.",
146                       The(xname(otmp)), surface(sx, sy));
147                 goto cannot_push;
148             }
149 
150             if (revive_nasty(rx, ry, "You sense movement on the other side."))
151                 return -1;
152 
153             if (mtmp && !noncorporeal(mtmp->data)
154                 && (!mtmp->mtrapped
155                     || !(ttmp && is_pit(ttmp->ttyp)))) {
156                 boolean deliver_part1 = FALSE;
157 
158                 if (Blind)
159                     feel_location(sx, sy);
160                 if (canspotmon(mtmp)) {
161                     pline("There's %s on the other side.", a_monnam(mtmp));
162                     deliver_part1 = TRUE;
163                 } else {
164                     You_hear("a monster behind %s.", the(xname(otmp)));
165                     if (!Deaf)
166                         deliver_part1 = TRUE;
167                     map_invisible(rx, ry);
168                 }
169                 if (flags.verbose) {
170                     char you_or_steed[BUFSZ];
171 
172                     Strcpy(you_or_steed,
173                            u.usteed ? y_monnam(u.usteed) : "you");
174                     pline("%s%s cannot move %s.",
175                           deliver_part1
176                               ? "Perhaps that's why "
177                               : "",
178                           deliver_part1
179                               ? you_or_steed
180                               : upstart(you_or_steed),
181                           deliver_part1
182                               ? "it"
183                               : the(xname(otmp)));
184                 }
185                 goto cannot_push;
186             }
187 
188             if (ttmp) {
189                 /* if a trap operates on the boulder, don't attempt
190                    to move any others at this location; return -1
191                    if another boulder is in hero's way, or 0 if he
192                    should advance to the vacated boulder position */
193                 switch (ttmp->ttyp) {
194                 case LANDMINE:
195                     if (rn2(10)) {
196                         obj_extract_self(otmp);
197                         place_object(otmp, rx, ry);
198                         newsym(sx, sy);
199                         pline("KAABLAMM!!!  %s %s land mine.",
200                               Tobjnam(otmp, "trigger"),
201                               ttmp->madeby_u ? "your" : "a");
202                         blow_up_landmine(ttmp);
203                         /* if the boulder remains, it should fill the pit */
204                         fill_pit(u.ux, u.uy);
205                         if (cansee(rx, ry))
206                             newsym(rx, ry);
207                         return sobj_at(BOULDER, sx, sy) ? -1 : 0;
208                     }
209                     break;
210                 case SPIKED_PIT:
211                 case PIT:
212                     obj_extract_self(otmp);
213                     /* vision kludge to get messages right;
214                        the pit will temporarily be seen even
215                        if this is one among multiple boulders */
216                     if (!Blind)
217                         viz_array[ry][rx] |= IN_SIGHT;
218                     if (!flooreffects(otmp, rx, ry, "fall")) {
219                         place_object(otmp, rx, ry);
220                     }
221                     if (mtmp && !Blind)
222                         newsym(rx, ry);
223                     return sobj_at(BOULDER, sx, sy) ? -1 : 0;
224                 case HOLE:
225                 case TRAPDOOR:
226                     if (Blind)
227                         pline("Kerplunk!  You no longer feel %s.",
228                               the(xname(otmp)));
229                     else
230                         pline("%s%s and %s a %s in the %s!",
231                               Tobjnam(otmp, (ttmp->ttyp == TRAPDOOR)
232                                                 ? "trigger"
233                                                 : "fall"),
234                               (ttmp->ttyp == TRAPDOOR) ? "" : " into",
235                               otense(otmp, "plug"),
236                               (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole",
237                               surface(rx, ry));
238                     deltrap(ttmp);
239                     delobj(otmp);
240                     bury_objs(rx, ry);
241                     levl[rx][ry].wall_info &= ~W_NONDIGGABLE;
242                     levl[rx][ry].candig = 1;
243                     if (cansee(rx, ry))
244                         newsym(rx, ry);
245                     return sobj_at(BOULDER, sx, sy) ? -1 : 0;
246                 case LEVEL_TELEP:
247                 case TELEP_TRAP: {
248                     int newlev = 0; /* lint suppression */
249                     d_level dest;
250 
251                     if (ttmp->ttyp == LEVEL_TELEP) {
252                         newlev = random_teleport_level();
253                         if (newlev == depth(&u.uz) || In_endgame(&u.uz))
254                             /* trap didn't work; skip "disappears" message */
255                             goto dopush;
256                     }
257                     if (u.usteed)
258                         pline("%s pushes %s and suddenly it disappears!",
259                               upstart(y_monnam(u.usteed)), the(xname(otmp)));
260                     else
261                         You("push %s and suddenly it disappears!",
262                             the(xname(otmp)));
263                     if (ttmp->ttyp == TELEP_TRAP) {
264                         (void) rloco(otmp);
265                     } else {
266                         obj_extract_self(otmp);
267                         add_to_migration(otmp);
268                         get_level(&dest, newlev);
269                         otmp->ox = dest.dnum;
270                         otmp->oy = dest.dlevel;
271                         otmp->owornmask = (long) MIGR_RANDOM;
272                     }
273                     seetrap(ttmp);
274                     return sobj_at(BOULDER, sx, sy) ? -1 : 0;
275                 }
276                 default:
277                     break; /* boulder not affected by this trap */
278                 }
279             }
280 
281             if (closed_door(rx, ry))
282                 goto nopushmsg;
283             if (boulder_hits_pool(otmp, rx, ry, TRUE))
284                 continue;
285             /*
286              * Re-link at top of fobj chain so that pile order is preserved
287              * when level is restored.
288              */
289             if (otmp != fobj) {
290                 remove_object(otmp);
291                 place_object(otmp, otmp->ox, otmp->oy);
292             }
293 
294             {
295 #ifdef LINT /* static long lastmovetime; */
296                 long lastmovetime;
297                 lastmovetime = 0;
298 #else
299                 /* note: reset to zero after save/restore cycle */
300                 static NEARDATA long lastmovetime;
301 #endif
302  dopush:
303                 if (!u.usteed) {
304                     if (moves > lastmovetime + 2 || moves < lastmovetime)
305                         pline("With %s effort you move %s.",
306                               throws_rocks(youmonst.data) ? "little"
307                                                           : "great",
308                               the(xname(otmp)));
309                     exercise(A_STR, TRUE);
310                 } else
311                     pline("%s moves %s.", upstart(y_monnam(u.usteed)),
312                           the(xname(otmp)));
313                 lastmovetime = moves;
314             }
315 
316             /* Move the boulder *after* the message. */
317             if (glyph_is_invisible(levl[rx][ry].glyph))
318                 unmap_object(rx, ry);
319             movobj(otmp, rx, ry); /* does newsym(rx,ry) */
320             if (Blind) {
321                 feel_location(rx, ry);
322                 feel_location(sx, sy);
323             } else {
324                 newsym(sx, sy);
325             }
326         } else {
327  nopushmsg:
328             if (u.usteed)
329                 pline("%s tries to move %s, but cannot.",
330                       upstart(y_monnam(u.usteed)), the(xname(otmp)));
331             else
332                 You("try to move %s, but in vain.", the(xname(otmp)));
333             if (Blind)
334                 feel_location(sx, sy);
335  cannot_push:
336             if (throws_rocks(youmonst.data)) {
337                 boolean
338                     canpickup = (!Sokoban
339                                  /* similar exception as in can_lift():
340                                     when poly'd into a giant, you can
341                                     pick up a boulder if you have a free
342                                     slot or into the overflow ('#') slot
343                                     unless already carrying at least one */
344                               && (inv_cnt(FALSE) < 52 || !carrying(BOULDER))),
345                     willpickup = (canpickup && autopick_testobj(otmp, TRUE));
346 
347                 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
348                     You("aren't skilled enough to %s %s from %s.",
349                         willpickup ? "pick up" : "push aside",
350                         the(xname(otmp)), y_monnam(u.usteed));
351                 } else {
352                     /*
353                      * willpickup:  you easily pick it up
354                      * canpickup:   you could easily pick it up
355                      * otherwise:   you easily push it aside
356                      */
357                     pline("However, you %seasily %s.",
358                           (willpickup || !canpickup) ? "" : "could ",
359                           (willpickup || canpickup) ? "pick it up"
360                                                     : "push it aside");
361                     sokoban_guilt();
362                     break;
363                 }
364                 break;
365             }
366 
367             if (!u.usteed
368                 && (((!invent || inv_weight() <= -850)
369                      && (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ)
370                                             && IS_ROCK(levl[sx][u.uy].typ))))
371                     || verysmall(youmonst.data))) {
372                 pline(
373                    "However, you can squeeze yourself into a small opening.");
374                 sokoban_guilt();
375                 break;
376             } else
377                 return -1;
378         }
379     }
380     return 0;
381 }
382 
383 /*
384  *  still_chewing()
385  *
386  *  Chew on a wall, door, or boulder.  [What about statues?]
387  *  Returns TRUE if still eating, FALSE when done.
388  */
389 STATIC_OVL int
still_chewing(x,y)390 still_chewing(x, y)
391 xchar x, y;
392 {
393     struct rm *lev = &levl[x][y];
394     struct obj *boulder = sobj_at(BOULDER, x, y);
395     const char *digtxt = (char *) 0, *dmgtxt = (char *) 0;
396 
397     if (context.digging.down) /* not continuing previous dig (w/ pick-axe) */
398         (void) memset((genericptr_t) &context.digging, 0,
399                       sizeof (struct dig_info));
400 
401     if (!boulder
402         && ((IS_ROCK(lev->typ) && !may_dig(x, y))
403             /* may_dig() checks W_NONDIGGABLE but doesn't handle iron bars */
404             || (lev->typ == IRONBARS && (lev->wall_info & W_NONDIGGABLE)))) {
405         You("hurt your teeth on the %s.",
406             (lev->typ == IRONBARS)
407                 ? "bars"
408                 : IS_TREE(lev->typ)
409                     ? "tree"
410                     : "hard stone");
411         nomul(0);
412         return 1;
413     } else if (context.digging.pos.x != x || context.digging.pos.y != y
414                || !on_level(&context.digging.level, &u.uz)) {
415         context.digging.down = FALSE;
416         context.digging.chew = TRUE;
417         context.digging.warned = FALSE;
418         context.digging.pos.x = x;
419         context.digging.pos.y = y;
420         assign_level(&context.digging.level, &u.uz);
421         /* solid rock takes more work & time to dig through */
422         context.digging.effort =
423             (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc;
424         You("start chewing %s %s.",
425             (boulder || IS_TREE(lev->typ) || lev->typ == IRONBARS)
426                 ? "on a"
427                 : "a hole in the",
428             boulder
429                 ? "boulder"
430                 : IS_TREE(lev->typ)
431                     ? "tree"
432                     : IS_ROCK(lev->typ)
433                         ? "rock"
434                         : (lev->typ == IRONBARS)
435                             ? "bar"
436                             : "door");
437         watch_dig((struct monst *) 0, x, y, FALSE);
438         return 1;
439     } else if ((context.digging.effort += (30 + u.udaminc)) <= 100) {
440         if (flags.verbose)
441             You("%s chewing on the %s.",
442                 context.digging.chew ? "continue" : "begin",
443                 boulder
444                     ? "boulder"
445                     : IS_TREE(lev->typ)
446                         ? "tree"
447                         : IS_ROCK(lev->typ)
448                             ? "rock"
449                             : (lev->typ == IRONBARS)
450                                 ? "bars"
451                                 : "door");
452         context.digging.chew = TRUE;
453         watch_dig((struct monst *) 0, x, y, FALSE);
454         return 1;
455     }
456 
457     /* Okay, you've chewed through something */
458     u.uconduct.food++;
459     u.uhunger += rnd(20);
460 
461     if (boulder) {
462         delobj(boulder);         /* boulder goes bye-bye */
463         You("eat the boulder."); /* yum */
464 
465         /*
466          *  The location could still block because of
467          *      1. More than one boulder
468          *      2. Boulder stuck in a wall/stone/door.
469          *
470          *  [perhaps use does_block() below (from vision.c)]
471          */
472         if (IS_ROCK(lev->typ) || closed_door(x, y)
473             || sobj_at(BOULDER, x, y)) {
474             block_point(x, y); /* delobj will unblock the point */
475             /* reset dig state */
476             (void) memset((genericptr_t) &context.digging, 0,
477                           sizeof (struct dig_info));
478             return 1;
479         }
480 
481     } else if (IS_WALL(lev->typ)) {
482         if (*in_rooms(x, y, SHOPBASE)) {
483             add_damage(x, y, SHOP_WALL_DMG);
484             dmgtxt = "damage";
485         }
486         digtxt = "chew a hole in the wall.";
487         if (level.flags.is_maze_lev) {
488             lev->typ = ROOM;
489         } else if (level.flags.is_cavernous_lev && !in_town(x, y)) {
490             lev->typ = CORR;
491         } else {
492             lev->typ = DOOR;
493             lev->doormask = D_NODOOR;
494         }
495     } else if (IS_TREE(lev->typ)) {
496         digtxt = "chew through the tree.";
497         lev->typ = ROOM;
498     } else if (lev->typ == IRONBARS) {
499         digtxt = "eat through the bars.";
500         dissolve_bars(x, y);
501     } else if (lev->typ == SDOOR) {
502         if (lev->doormask & D_TRAPPED) {
503             lev->doormask = D_NODOOR;
504             b_trapped("secret door", 0);
505         } else {
506             digtxt = "chew through the secret door.";
507             lev->doormask = D_BROKEN;
508         }
509         lev->typ = DOOR;
510 
511     } else if (IS_DOOR(lev->typ)) {
512         if (*in_rooms(x, y, SHOPBASE)) {
513             add_damage(x, y, SHOP_DOOR_COST);
514             dmgtxt = "break";
515         }
516         if (lev->doormask & D_TRAPPED) {
517             lev->doormask = D_NODOOR;
518             b_trapped("door", 0);
519         } else {
520             digtxt = "chew through the door.";
521             lev->doormask = D_BROKEN;
522         }
523 
524     } else { /* STONE or SCORR */
525         digtxt = "chew a passage through the rock.";
526         lev->typ = CORR;
527     }
528 
529     unblock_point(x, y); /* vision */
530     newsym(x, y);
531     if (digtxt)
532         You1(digtxt); /* after newsym */
533     if (dmgtxt)
534         pay_for_damage(dmgtxt, FALSE);
535     (void) memset((genericptr_t) &context.digging, 0,
536                   sizeof (struct dig_info));
537     return 0;
538 }
539 
540 void
movobj(obj,ox,oy)541 movobj(obj, ox, oy)
542 register struct obj *obj;
543 register xchar ox, oy;
544 {
545     /* optimize by leaving on the fobj chain? */
546     remove_object(obj);
547     newsym(obj->ox, obj->oy);
548     place_object(obj, ox, oy);
549     newsym(ox, oy);
550 }
551 
552 static NEARDATA const char fell_on_sink[] = "fell onto a sink";
553 
554 STATIC_OVL void
dosinkfall()555 dosinkfall()
556 {
557     register struct obj *obj;
558     int dmg;
559     boolean lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS),
560             innate_lev = ((HLevitation & (FROMOUTSIDE | FROMFORM)) != 0L),
561             /* to handle being chained to buried iron ball, trying to
562                levitate but being blocked, then moving onto adjacent sink;
563                no need to worry about being blocked by terrain because we
564                couldn't be over a sink at the same time */
565             blockd_lev = (BLevitation == I_SPECIAL),
566             ufall = (!innate_lev && !blockd_lev
567                      && !(HFlying || EFlying)); /* BFlying */
568 
569     if (!ufall) {
570         You((innate_lev || blockd_lev) ? "wobble unsteadily for a moment."
571                                        : "gain control of your flight.");
572     } else {
573         long save_ELev = ELevitation, save_HLev = HLevitation;
574 
575         /* fake removal of levitation in advance so that final
576            disclosure will be right in case this turns out to
577            be fatal; fortunately the fact that rings and boots
578            are really still worn has no effect on bones data */
579         ELevitation = HLevitation = 0L;
580         You("crash to the floor!");
581         dmg = rn1(8, 25 - (int) ACURR(A_CON));
582         losehp(Maybe_Half_Phys(dmg), fell_on_sink, NO_KILLER_PREFIX);
583         exercise(A_DEX, FALSE);
584         selftouch("Falling, you");
585         for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
586             if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) {
587                 You("fell on %s.", doname(obj));
588                 losehp(Maybe_Half_Phys(rnd(3)), fell_on_sink,
589                        NO_KILLER_PREFIX);
590                 exercise(A_CON, FALSE);
591             }
592         ELevitation = save_ELev;
593         HLevitation = save_HLev;
594     }
595 
596     /*
597      * Interrupt multi-turn putting on/taking off of armor (in which
598      * case we reached the sink due to being teleported while busy;
599      * in 3.4.3, Boots_on()/Boots_off() [called via (*afternmv)() when
600      * 'multi' reaches 0] triggered a crash if we were donning/doffing
601      * levitation boots [because the Boots_off() below causes 'uarmf'
602      * to be null by the time 'afternmv' gets called]).
603      *
604      * Interrupt donning/doffing if we fall onto the sink, or if the
605      * code below is going to remove levitation boots even when we
606      * haven't fallen (innate floating or flying becoming unblocked).
607      */
608     if (ufall || lev_boots) {
609         (void) stop_donning(lev_boots ? uarmf : (struct obj *) 0);
610         /* recalculate in case uarmf just got set to null */
611         lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS);
612     }
613 
614     /* remove worn levitation items */
615     ELevitation &= ~W_ARTI;
616     HLevitation &= ~(I_SPECIAL | TIMEOUT);
617     HLevitation++;
618     if (uleft && uleft->otyp == RIN_LEVITATION) {
619         obj = uleft;
620         Ring_off(obj);
621         off_msg(obj);
622     }
623     if (uright && uright->otyp == RIN_LEVITATION) {
624         obj = uright;
625         Ring_off(obj);
626         off_msg(obj);
627     }
628     if (lev_boots) {
629         obj = uarmf;
630         (void) Boots_off();
631         off_msg(obj);
632     }
633     HLevitation--;
634     /* probably moot; we're either still levitating or went
635        through float_down(), but make sure BFlying is up to date */
636     float_vs_flight();
637 }
638 
639 /* intended to be called only on ROCKs or TREEs */
640 boolean
may_dig(x,y)641 may_dig(x, y)
642 register xchar x, y;
643 {
644     struct rm *lev = &levl[x][y];
645 
646     return (boolean) !((IS_STWALL(lev->typ) || IS_TREE(lev->typ))
647                        && (lev->wall_info & W_NONDIGGABLE));
648 }
649 
650 boolean
may_passwall(x,y)651 may_passwall(x, y)
652 register xchar x, y;
653 {
654     return (boolean) !(IS_STWALL(levl[x][y].typ)
655                        && (levl[x][y].wall_info & W_NONPASSWALL));
656 }
657 
658 boolean
bad_rock(mdat,x,y)659 bad_rock(mdat, x, y)
660 struct permonst *mdat;
661 register xchar x, y;
662 {
663     return (boolean) ((Sokoban && sobj_at(BOULDER, x, y))
664                       || (IS_ROCK(levl[x][y].typ)
665                           && (!tunnels(mdat) || needspick(mdat)
666                               || !may_dig(x, y))
667                           && !(passes_walls(mdat) && may_passwall(x, y))));
668 }
669 
670 /* caller has already decided that it's a tight diagonal; check whether a
671    monster--who might be the hero--can fit through, and if not then return
672    the reason why:  1: can't fit, 2: possessions won't fit, 3: sokoban
673    returns 0 if we can squeeze through */
674 int
cant_squeeze_thru(mon)675 cant_squeeze_thru(mon)
676 struct monst *mon;
677 {
678     int amt;
679     struct permonst *ptr = mon->data;
680 
681     /* too big? */
682     if (bigmonst(ptr)
683         && !(amorphous(ptr) || is_whirly(ptr) || noncorporeal(ptr)
684              || slithy(ptr) || can_fog(mon)))
685         return 1;
686 
687     /* lugging too much junk? */
688     amt = (mon == &youmonst) ? inv_weight() + weight_cap()
689                              : curr_mon_load(mon);
690     if (amt > 600)
691         return 2;
692 
693     /* Sokoban restriction applies to hero only */
694     if (mon == &youmonst && Sokoban)
695         return 3;
696 
697     /* can squeeze through */
698     return 0;
699 }
700 
701 boolean
invocation_pos(x,y)702 invocation_pos(x, y)
703 xchar x, y;
704 {
705     return (boolean) (Invocation_lev(&u.uz)
706                       && x == inv_pos.x && y == inv_pos.y);
707 }
708 
709 /* return TRUE if (dx,dy) is an OK place to move
710  * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP
711  */
712 boolean
test_move(ux,uy,dx,dy,mode)713 test_move(ux, uy, dx, dy, mode)
714 int ux, uy, dx, dy;
715 int mode;
716 {
717     int x = ux + dx;
718     int y = uy + dy;
719     register struct rm *tmpr = &levl[x][y];
720     register struct rm *ust;
721 
722     context.door_opened = FALSE;
723     /*
724      *  Check for physical obstacles.  First, the place we are going.
725      */
726     if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
727         if (Blind && mode == DO_MOVE)
728             feel_location(x, y);
729         if (Passes_walls && may_passwall(x, y)) {
730             ; /* do nothing */
731         } else if (Underwater) {
732             /* note: if water_friction() changes direction due to
733                turbulence, new target destination will always be water,
734                so we won't get here, hence don't need to worry about
735                "there" being somewhere the player isn't sure of */
736             if (mode == DO_MOVE)
737                 pline("There is an obstacle there.");
738             return FALSE;
739         } else if (tmpr->typ == IRONBARS) {
740             if ((dmgtype(youmonst.data, AD_RUST)
741                  || dmgtype(youmonst.data, AD_CORR)) && mode == DO_MOVE
742                 && still_chewing(x, y)) {
743                 return FALSE;
744             }
745             if (!(Passes_walls || passes_bars(youmonst.data))) {
746                 if (mode == DO_MOVE && iflags.mention_walls)
747                     You("cannot pass through the bars.");
748                 return FALSE;
749             }
750         } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
751             /* Eat the rock. */
752             if (mode == DO_MOVE && still_chewing(x, y))
753                 return FALSE;
754         } else if (flags.autodig && !context.run && !context.nopick && uwep
755                    && is_pick(uwep)) {
756             /* MRKR: Automatic digging when wielding the appropriate tool */
757             if (mode == DO_MOVE)
758                 (void) use_pick_axe2(uwep);
759             return FALSE;
760         } else {
761             if (mode == DO_MOVE) {
762                 if (is_db_wall(x, y))
763                     pline("That drawbridge is up!");
764                 /* sokoban restriction stays even after puzzle is solved */
765                 else if (Passes_walls && !may_passwall(x, y)
766                          && In_sokoban(&u.uz))
767                     pline_The("Sokoban walls resist your ability.");
768                 else if (iflags.mention_walls)
769                     pline("It's %s.",
770                           (IS_WALL(tmpr->typ) || tmpr->typ == SDOOR) ? "a wall"
771                           : IS_TREE(tmpr->typ) ? "a tree"
772                           : "solid stone");
773             }
774             return FALSE;
775         }
776     } else if (IS_DOOR(tmpr->typ)) {
777         if (closed_door(x, y)) {
778             if (Blind && mode == DO_MOVE)
779                 feel_location(x, y);
780             if (Passes_walls) {
781                 ; /* do nothing */
782             } else if (can_ooze(&youmonst)) {
783                 if (mode == DO_MOVE)
784                     You("ooze under the door.");
785             } else if (Underwater) {
786                 if (mode == DO_MOVE)
787                     pline("There is an obstacle there.");
788                 return FALSE;
789             } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
790                 /* Eat the door. */
791                 if (mode == DO_MOVE && still_chewing(x, y))
792                     return FALSE;
793             } else {
794                 if (mode == DO_MOVE) {
795                     if (amorphous(youmonst.data))
796                         You(
797    "try to ooze under the door, but can't squeeze your possessions through.");
798                     if (flags.autoopen && !context.run && !Confusion
799                         && !Stunned && !Fumbling) {
800                         context.door_opened = context.move =
801                             doopen_indir(x, y);
802                     } else if (x == ux || y == uy) {
803                         if (Blind || Stunned || ACURR(A_DEX) < 10
804                             || Fumbling) {
805                             if (u.usteed) {
806                                 You_cant("lead %s through that closed door.",
807                                          y_monnam(u.usteed));
808                             } else {
809                                 pline("Ouch!  You bump into a door.");
810                                 exercise(A_DEX, FALSE);
811                             }
812                         } else
813                             pline("That door is closed.");
814                     }
815                 } else if (mode == TEST_TRAV || mode == TEST_TRAP)
816                     goto testdiag;
817                 return FALSE;
818             }
819         } else {
820  testdiag:
821             if (dx && dy && !Passes_walls
822                 && (!doorless_door(x, y) || block_door(x, y))) {
823                 /* Diagonal moves into a door are not allowed. */
824                 if (mode == DO_MOVE) {
825                     if (Blind)
826                         feel_location(x, y);
827                     if (Underwater || iflags.mention_walls)
828                         You_cant("move diagonally into an intact doorway.");
829                 }
830                 return FALSE;
831             }
832         }
833     }
834     if (dx && dy && bad_rock(youmonst.data, ux, y)
835         && bad_rock(youmonst.data, x, uy)) {
836         /* Move at a diagonal. */
837         switch (cant_squeeze_thru(&youmonst)) {
838         case 3:
839             if (mode == DO_MOVE)
840                 You("cannot pass that way.");
841             return FALSE;
842         case 2:
843             if (mode == DO_MOVE)
844                 You("are carrying too much to get through.");
845             return FALSE;
846         case 1:
847             if (mode == DO_MOVE)
848                 Your("body is too large to fit through.");
849             return FALSE;
850         default:
851             break; /* can squeeze through */
852         }
853     } else if (dx && dy && worm_cross(ux, uy, x, y)) {
854         /* consecutive long worm segments are at <ux,y> and <x,uy> */
855         if (mode == DO_MOVE)
856             pline("%s is in your way.", Monnam(m_at(ux, y)));
857         return FALSE;
858     }
859     /* Pick travel path that does not require crossing a trap.
860      * Avoid water and lava using the usual running rules.
861      * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
862     if (context.run == 8 && (mode != DO_MOVE)
863         && (x != u.ux || y != u.uy)) {
864         struct trap *t = t_at(x, y);
865 
866         if ((t && t->tseen)
867             || (!Levitation && !Flying && !is_clinger(youmonst.data)
868                 && is_pool_or_lava(x, y) && levl[x][y].seenv))
869             return (mode == TEST_TRAP);
870     }
871 
872     if (mode == TEST_TRAP)
873         return FALSE; /* do not move through traps */
874 
875     ust = &levl[ux][uy];
876 
877     /* Now see if other things block our way . . */
878     if (dx && dy && !Passes_walls && IS_DOOR(ust->typ)
879         && (!doorless_door(ux, uy) || block_entry(x, y))) {
880         /* Can't move at a diagonal out of a doorway with door. */
881         if (mode == DO_MOVE && iflags.mention_walls)
882             You_cant("move diagonally out of an intact doorway.");
883         return FALSE;
884     }
885 
886     if (sobj_at(BOULDER, x, y) && (Sokoban || !Passes_walls)) {
887         if (!(Blind || Hallucination) && (context.run >= 2)
888             && mode != TEST_TRAV) {
889             if (mode == DO_MOVE && iflags.mention_walls)
890                 pline("A boulder blocks your path.");
891             return FALSE;
892         }
893         if (mode == DO_MOVE) {
894             /* tunneling monsters will chew before pushing */
895             if (tunnels(youmonst.data) && !needspick(youmonst.data)
896                 && !Sokoban) {
897                 if (still_chewing(x, y))
898                     return FALSE;
899             } else if (moverock() < 0)
900                 return FALSE;
901         } else if (mode == TEST_TRAV) {
902             struct obj *obj;
903 
904             /* never travel through boulders in Sokoban */
905             if (Sokoban)
906                 return FALSE;
907 
908             /* don't pick two boulders in a row, unless there's a way thru */
909             if (sobj_at(BOULDER, ux, uy) && !Sokoban) {
910                 if (!Passes_walls
911                     && !(tunnels(youmonst.data) && !needspick(youmonst.data))
912                     && !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK)
913                     && !((obj = carrying(WAN_DIGGING))
914                          && !objects[obj->otyp].oc_name_known))
915                     return FALSE;
916             }
917         }
918         /* assume you'll be able to push it when you get there... */
919     }
920 
921     /* OK, it is a legal place to move. */
922     return TRUE;
923 }
924 
925 /*
926  * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
927  * A shortest path is returned.  If guess is TRUE, consider various
928  * inaccessible locations as valid intermediate path points.
929  * Returns TRUE if a path was found.
930  */
931 STATIC_OVL boolean
findtravelpath(mode)932 findtravelpath(mode)
933 int mode;
934 {
935     /* if travel to adjacent, reachable location, use normal movement rules */
936     if ((mode == TRAVP_TRAVEL || mode == TRAVP_VALID) && context.travel1
937         && distmin(u.ux, u.uy, u.tx, u.ty) == 1
938         && !(u.ux != u.tx && u.uy != u.ty && NODIAG(u.umonnum))) {
939         context.run = 0;
940         if (test_move(u.ux, u.uy, u.tx - u.ux, u.ty - u.uy, TEST_MOVE)) {
941             if (mode == TRAVP_TRAVEL) {
942                 u.dx = u.tx - u.ux;
943                 u.dy = u.ty - u.uy;
944                 nomul(0);
945                 iflags.travelcc.x = iflags.travelcc.y = 0;
946             }
947             return TRUE;
948         }
949         if (mode == TRAVP_TRAVEL)
950             context.run = 8;
951     }
952     if (u.tx != u.ux || u.ty != u.uy) {
953         xchar travel[COLNO][ROWNO];
954         xchar travelstepx[2][COLNO * ROWNO];
955         xchar travelstepy[2][COLNO * ROWNO];
956         xchar tx, ty, ux, uy;
957         int n = 1;      /* max offset in travelsteps */
958         int set = 0;    /* two sets current and previous */
959         int radius = 1; /* search radius */
960         int i;
961 
962         /* If guessing, first find an "obvious" goal location.  The obvious
963          * goal is the position the player knows of, or might figure out
964          * (couldsee) that is closest to the target on a straight path.
965          */
966         if (mode == TRAVP_GUESS || mode == TRAVP_VALID) {
967             tx = u.ux;
968             ty = u.uy;
969             ux = u.tx;
970             uy = u.ty;
971         } else {
972             tx = u.tx;
973             ty = u.ty;
974             ux = u.ux;
975             uy = u.uy;
976         }
977 
978  noguess:
979         (void) memset((genericptr_t) travel, 0, sizeof travel);
980         travelstepx[0][0] = tx;
981         travelstepy[0][0] = ty;
982 
983         while (n != 0) {
984             int nn = 0;
985 
986             for (i = 0; i < n; i++) {
987                 int dir;
988                 int x = travelstepx[set][i];
989                 int y = travelstepy[set][i];
990                 static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
991                 /* no diagonal movement for grid bugs */
992                 int dirmax = NODIAG(u.umonnum) ? 4 : 8;
993                 boolean alreadyrepeated = FALSE;
994 
995                 for (dir = 0; dir < dirmax; ++dir) {
996                     int nx = x + xdir[ordered[dir]];
997                     int ny = y + ydir[ordered[dir]];
998 
999                     /*
1000                      * When guessing and trying to travel as close as possible
1001                      * to an unreachable target space, don't include spaces
1002                      * that would never be picked as a guessed target in the
1003                      * travel matrix describing hero-reachable spaces.
1004                      * This stops travel from getting confused and moving
1005                      * the hero back and forth in certain degenerate
1006                      * configurations of sight-blocking obstacles, e.g.
1007                      *
1008                      *  T         1. Dig this out and carry enough to not be
1009                      *   ####       able to squeeze through diagonal gaps.
1010                      *   #--.---    Stand at @ and target travel at space T.
1011                      *    @.....
1012                      *    |.....
1013                      *
1014                      *  T         2. couldsee() marks spaces marked a and x
1015                      *   ####       as eligible guess spaces to move the hero
1016                      *   a--.---    towards.  Space a is closest to T, so it
1017                      *    @xxxxx    gets chosen.  Travel system moves @ right
1018                      *    |xxxxx    to travel to space a.
1019                      *
1020                      *  T         3. couldsee() marks spaces marked b, c and x
1021                      *   ####       as eligible guess spaces to move the hero
1022                      *   a--c---    towards.  Since findtravelpath() is called
1023                      *    b@xxxx    repeatedly during travel, it doesn't
1024                      *    |xxxxx    remember that it wanted to go to space a,
1025                      *              so in comparing spaces b and c, b is
1026                      *              chosen, since it seems like the closest
1027                      *              eligible space to T. Travel system moves @
1028                      *              left to go to space b.
1029                      *
1030                      *            4. Go to 2.
1031                      *
1032                      * By limiting the travel matrix here, space a in the
1033                      * example above is never included in it, preventing
1034                      * the cycle.
1035                      */
1036                     if (!isok(nx, ny)
1037                         || ((mode == TRAVP_GUESS) && !couldsee(nx, ny)))
1038                         continue;
1039                     if ((!Passes_walls && !can_ooze(&youmonst)
1040                          && closed_door(x, y)) || sobj_at(BOULDER, x, y)
1041                         || test_move(x, y, nx-x, ny-y, TEST_TRAP)) {
1042                         /* closed doors and boulders usually
1043                          * cause a delay, so prefer another path */
1044                         if (travel[x][y] > radius - 3) {
1045                             if (!alreadyrepeated) {
1046                                 travelstepx[1 - set][nn] = x;
1047                                 travelstepy[1 - set][nn] = y;
1048                                 /* don't change travel matrix! */
1049                                 nn++;
1050                                 alreadyrepeated = TRUE;
1051                             }
1052                             continue;
1053                         }
1054                     }
1055                     if (test_move(x, y, nx - x, ny - y, TEST_TRAV)
1056                         && (levl[nx][ny].seenv
1057                             || (!Blind && couldsee(nx, ny)))) {
1058                         if (nx == ux && ny == uy) {
1059                             if (mode == TRAVP_TRAVEL || mode == TRAVP_VALID) {
1060                                 u.dx = x - ux;
1061                                 u.dy = y - uy;
1062                                 if (mode == TRAVP_TRAVEL
1063                                     && x == u.tx && y == u.ty) {
1064                                     nomul(0);
1065                                     /* reset run so domove run checks work */
1066                                     context.run = 8;
1067                                     iflags.travelcc.x = iflags.travelcc.y = 0;
1068                                 }
1069                                 return TRUE;
1070                             }
1071                         } else if (!travel[nx][ny]) {
1072                             travelstepx[1 - set][nn] = nx;
1073                             travelstepy[1 - set][nn] = ny;
1074                             travel[nx][ny] = radius;
1075                             nn++;
1076                         }
1077                     }
1078                 }
1079             }
1080 
1081 #ifdef DEBUG
1082             if (iflags.trav_debug) {
1083                 /* Use of warning glyph is arbitrary. It stands out. */
1084                 tmp_at(DISP_ALL, warning_to_glyph(1));
1085                 for (i = 0; i < nn; ++i) {
1086                     tmp_at(travelstepx[1 - set][i], travelstepy[1 - set][i]);
1087                 }
1088                 delay_output();
1089                 if (flags.runmode == RUN_CRAWL) {
1090                     delay_output();
1091                     delay_output();
1092                 }
1093                 tmp_at(DISP_END, 0);
1094             }
1095 #endif /* DEBUG */
1096 
1097             n = nn;
1098             set = 1 - set;
1099             radius++;
1100         }
1101 
1102         /* if guessing, find best location in travel matrix and go there */
1103         if (mode == TRAVP_GUESS) {
1104             int px = tx, py = ty; /* pick location */
1105             int dist, nxtdist, d2, nd2;
1106 
1107             dist = distmin(ux, uy, tx, ty);
1108             d2 = dist2(ux, uy, tx, ty);
1109             for (tx = 1; tx < COLNO; ++tx)
1110                 for (ty = 0; ty < ROWNO; ++ty)
1111                     if (travel[tx][ty]) {
1112                         nxtdist = distmin(ux, uy, tx, ty);
1113                         if (nxtdist == dist && couldsee(tx, ty)) {
1114                             nd2 = dist2(ux, uy, tx, ty);
1115                             if (nd2 < d2) {
1116                                 /* prefer non-zigzag path */
1117                                 px = tx;
1118                                 py = ty;
1119                                 d2 = nd2;
1120                             }
1121                         } else if (nxtdist < dist && couldsee(tx, ty)) {
1122                             px = tx;
1123                             py = ty;
1124                             dist = nxtdist;
1125                             d2 = dist2(ux, uy, tx, ty);
1126                         }
1127                     }
1128 
1129             if (px == u.ux && py == u.uy) {
1130                 /* no guesses, just go in the general direction */
1131                 u.dx = sgn(u.tx - u.ux);
1132                 u.dy = sgn(u.ty - u.uy);
1133                 if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE))
1134                     return TRUE;
1135                 goto found;
1136             }
1137 #ifdef DEBUG
1138             if (iflags.trav_debug) {
1139                 /* Use of warning glyph is arbitrary. It stands out. */
1140                 tmp_at(DISP_ALL, warning_to_glyph(2));
1141                 tmp_at(px, py);
1142                 delay_output();
1143                 if (flags.runmode == RUN_CRAWL) {
1144                     delay_output();
1145                     delay_output();
1146                     delay_output();
1147                     delay_output();
1148                 }
1149                 tmp_at(DISP_END, 0);
1150             }
1151 #endif /* DEBUG */
1152             tx = px;
1153             ty = py;
1154             ux = u.ux;
1155             uy = u.uy;
1156             set = 0;
1157             n = radius = 1;
1158             mode = TRAVP_TRAVEL;
1159             goto noguess;
1160         }
1161         return FALSE;
1162     }
1163 
1164  found:
1165     u.dx = 0;
1166     u.dy = 0;
1167     nomul(0);
1168     return FALSE;
1169 }
1170 
1171 boolean
is_valid_travelpt(x,y)1172 is_valid_travelpt(x,y)
1173 int x,y;
1174 {
1175     int tx = u.tx;
1176     int ty = u.ty;
1177     boolean ret;
1178     int g = glyph_at(x,y);
1179 
1180     if (x == u.ux && y == u.uy)
1181         return TRUE;
1182     if (isok(x,y) && glyph_is_cmap(g) && S_stone == glyph_to_cmap(g)
1183         && !levl[x][y].seenv)
1184         return FALSE;
1185     u.tx = x;
1186     u.ty = y;
1187     ret = findtravelpath(TRAVP_VALID);
1188     u.tx = tx;
1189     u.ty = ty;
1190     return ret;
1191 }
1192 
1193 /* try to escape being stuck in a trapped state by walking out of it;
1194    return true iff moving should continue to intended destination
1195    (all failures and most successful escapes leave hero at original spot) */
1196 STATIC_OVL boolean
trapmove(x,y,desttrap)1197 trapmove(x, y, desttrap)
1198 int x, y;              /* targetted destination, <u.ux+u.dx,u.uy+u.dy> */
1199 struct trap *desttrap; /* nonnull if another trap at <x,y> */
1200 {
1201     boolean anchored = FALSE;
1202     const char *predicament, *culprit;
1203     char *steedname = !u.usteed ? (char *) 0 : y_monnam(u.usteed);
1204 
1205     if (!u.utrap)
1206         return TRUE; /* sanity check */
1207 
1208     /*
1209      * Note: caller should call reset_utrap() when we set u.utrap to 0.
1210      */
1211 
1212     switch (u.utraptype) {
1213     case TT_BEARTRAP:
1214         if (flags.verbose) {
1215             predicament = "caught in a bear trap";
1216             if (u.usteed)
1217                 Norep("%s is %s.", upstart(steedname), predicament);
1218             else
1219                 Norep("You are %s.", predicament);
1220         }
1221         /* [why does diagonal movement give quickest escape?] */
1222         if ((u.dx && u.dy) || !rn2(5))
1223             u.utrap--;
1224         if (!u.utrap)
1225             goto wriggle_free;
1226         break;
1227     case TT_PIT:
1228         if (desttrap && desttrap->tseen
1229             && is_pit(desttrap->ttyp))
1230             return TRUE; /* move into adjacent pit */
1231         /* try to escape; position stays same regardless of success */
1232         climb_pit();
1233         break;
1234     case TT_WEB:
1235         if (uwep && uwep->oartifact == ART_STING) {
1236             /* escape trap but don't move and don't destroy it */
1237             u.utrap = 0; /* caller will call reset_utrap() */
1238             pline("Sting cuts through the web!");
1239             break;
1240         }
1241         if (--u.utrap) {
1242             if (flags.verbose) {
1243                 predicament = "stuck to the web";
1244                 if (u.usteed)
1245                     Norep("%s is %s.", upstart(steedname), predicament);
1246                 else
1247                     Norep("You are %s.", predicament);
1248             }
1249         } else {
1250             if (u.usteed)
1251                 pline("%s breaks out of the web.", upstart(steedname));
1252             else
1253                 You("disentangle yourself.");
1254         }
1255         break;
1256     case TT_LAVA:
1257         if (flags.verbose) {
1258             predicament = "stuck in the lava";
1259             if (u.usteed)
1260                 Norep("%s is %s.", upstart(steedname), predicament);
1261             else
1262                 Norep("You are %s.", predicament);
1263         }
1264         if (!is_lava(x, y)) {
1265             u.utrap--;
1266             if ((u.utrap & 0xff) == 0) {
1267                 u.utrap = 0;
1268                 if (u.usteed)
1269                     You("lead %s to the edge of the %s.", steedname,
1270                         hliquid("lava"));
1271                 else
1272                     You("pull yourself to the edge of the %s.",
1273                         hliquid("lava"));
1274             }
1275         }
1276         u.umoved = TRUE;
1277         break;
1278     case TT_INFLOOR:
1279     case TT_BURIEDBALL:
1280         anchored = (u.utraptype == TT_BURIEDBALL);
1281         if (anchored) {
1282             coord cc;
1283 
1284             cc.x = u.ux, cc.y = u.uy;
1285             /* can move normally within radius 1 of buried ball */
1286             if (buried_ball(&cc) && dist2(x, y, cc.x, cc.y) <= 2) {
1287                 /* ugly hack: we need to issue some message here
1288                    in case "you are chained to the buried ball"
1289                    was the most recent message given, otherwise
1290                    our next attempt to move out of tether range
1291                    after this successful move would have its
1292                    can't-do-that message suppressed by Norep */
1293                 if (flags.verbose)
1294                     Norep("You move within the chain's reach.");
1295                 return TRUE;
1296             }
1297         }
1298         if (--u.utrap) {
1299             if (flags.verbose) {
1300                 if (anchored) {
1301                     predicament = "chained to the";
1302                     culprit = "buried ball";
1303                 } else {
1304                     predicament = "stuck in the";
1305                     culprit = surface(u.ux, u.uy);
1306                 }
1307                 if (u.usteed) {
1308                     if (anchored)
1309                         Norep("You and %s are %s %s.", steedname, predicament,
1310                               culprit);
1311                     else
1312                         Norep("%s is %s %s.", upstart(steedname), predicament,
1313                               culprit);
1314                 } else
1315                     Norep("You are %s %s.", predicament, culprit);
1316             }
1317         } else {
1318  wriggle_free:
1319             if (u.usteed)
1320                 pline("%s finally %s free.", upstart(steedname),
1321                       !anchored ? "lurches" : "wrenches the ball");
1322             else
1323                 You("finally %s free.",
1324                     !anchored ? "wriggle" : "wrench the ball");
1325             if (anchored)
1326                 buried_ball_to_punishment();
1327         }
1328         break;
1329     default:
1330         impossible("trapmove: stuck in unknown trap? (%d)",
1331                    (int) u.utraptype);
1332         break;
1333     }
1334     return FALSE;
1335 }
1336 
1337 boolean
u_rooted()1338 u_rooted()
1339 {
1340     if (!youmonst.data->mmove) {
1341         You("are rooted %s.",
1342             Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
1343                 ? "in place"
1344                 : "to the ground");
1345         nomul(0);
1346         return TRUE;
1347     }
1348     return FALSE;
1349 }
1350 
1351 void
domove()1352 domove()
1353 {
1354         int ux1 = u.ux, uy1 = u.uy;
1355 
1356         domove_succeeded = 0L;
1357         domove_core();
1358         /* domove_succeeded is available for making assessments now */
1359         if ((domove_succeeded & (DOMOVE_RUSH | DOMOVE_WALK)) != 0)
1360             maybe_smudge_engr(ux1, uy1, u.ux, u.uy);
1361         domove_attempting = 0L;
1362 }
1363 
1364 STATIC_OVL void
domove_core()1365 domove_core()
1366 {
1367     register struct monst *mtmp;
1368     register struct rm *tmpr;
1369     register xchar x, y;
1370     struct trap *trap = NULL;
1371     int wtcap;
1372     boolean on_ice;
1373     xchar chainx = 0, chainy = 0,
1374           ballx = 0, bally = 0;         /* ball&chain new positions */
1375     int bc_control = 0;                 /* control for ball&chain */
1376     boolean cause_delay = FALSE,        /* dragging ball will skip a move */
1377             u_with_boulder = (sobj_at(BOULDER, u.ux, u.uy) != 0);
1378 
1379     if (context.travel) {
1380         if (!findtravelpath(FALSE))
1381             (void) findtravelpath(TRUE);
1382         context.travel1 = 0;
1383     }
1384 
1385     if (((wtcap = near_capacity()) >= OVERLOADED
1386          || (wtcap > SLT_ENCUMBER
1387              && (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
1388                         : (u.uhp < 10 && u.uhp != u.uhpmax))))
1389         && !Is_airlevel(&u.uz)) {
1390         if (wtcap < OVERLOADED) {
1391             You("don't have enough stamina to move.");
1392             exercise(A_CON, FALSE);
1393         } else
1394             You("collapse under your load.");
1395         nomul(0);
1396         return;
1397     }
1398     if (u.uswallow) {
1399         u.dx = u.dy = 0;
1400         u.ux = x = u.ustuck->mx;
1401         u.uy = y = u.ustuck->my;
1402         mtmp = u.ustuck;
1403     } else {
1404         if (Is_airlevel(&u.uz) && rn2(4) && !Levitation && !Flying) {
1405             switch (rn2(3)) {
1406             case 0:
1407                 You("tumble in place.");
1408                 exercise(A_DEX, FALSE);
1409                 break;
1410             case 1:
1411                 You_cant("control your movements very well.");
1412                 break;
1413             case 2:
1414                 pline("It's hard to walk in thin air.");
1415                 exercise(A_DEX, TRUE);
1416                 break;
1417             }
1418             return;
1419         }
1420 
1421         /* check slippery ice */
1422         on_ice = !Levitation && is_ice(u.ux, u.uy);
1423         if (on_ice) {
1424             static int skates = 0;
1425 
1426             if (!skates)
1427                 skates = find_skates();
1428             if ((uarmf && uarmf->otyp == skates) || resists_cold(&youmonst)
1429                 || Flying || is_floater(youmonst.data)
1430                 || is_clinger(youmonst.data) || is_whirly(youmonst.data)) {
1431                 on_ice = FALSE;
1432             } else if (!rn2(Cold_resistance ? 3 : 2)) {
1433                 HFumbling |= FROMOUTSIDE;
1434                 HFumbling &= ~TIMEOUT;
1435                 HFumbling += 1; /* slip on next move */
1436             }
1437         }
1438         if (!on_ice && (HFumbling & FROMOUTSIDE))
1439             HFumbling &= ~FROMOUTSIDE;
1440 
1441         x = u.ux + u.dx;
1442         y = u.uy + u.dy;
1443         if (Stunned || (Confusion && !rn2(5))) {
1444             register int tries = 0;
1445 
1446             do {
1447                 if (tries++ > 50) {
1448                     nomul(0);
1449                     return;
1450                 }
1451                 confdir();
1452                 x = u.ux + u.dx;
1453                 y = u.uy + u.dy;
1454             } while (!isok(x, y) || bad_rock(youmonst.data, x, y));
1455         }
1456         /* turbulence might alter your actual destination */
1457         if (u.uinwater) {
1458             water_friction();
1459             if (!u.dx && !u.dy) {
1460                 nomul(0);
1461                 return;
1462             }
1463             x = u.ux + u.dx;
1464             y = u.uy + u.dy;
1465 
1466             /* are we trying to move out of water while carrying too much? */
1467             if (isok(x, y) && !is_pool(x, y) && !Is_waterlevel(&u.uz)
1468                 && wtcap > (Swimming ? MOD_ENCUMBER : SLT_ENCUMBER)) {
1469                 /* when escaping from drowning you need to be unencumbered
1470                    in order to crawl out of water, but when not drowning,
1471                    doing so while encumbered is feasible; if in an aquatic
1472                    form, stressed or less is allowed; otherwise (magical
1473                    breathing), only burdened is allowed */
1474                 You("are carrying too much to climb out of the water.");
1475                 nomul(0);
1476                 return;
1477             }
1478         }
1479         if (!isok(x, y)) {
1480             if (iflags.mention_walls) {
1481                 int dx = u.dx, dy = u.dy;
1482 
1483                 if (dx && dy) { /* diagonal */
1484                     /* only as far as possible diagonally if in very
1485                        corner; otherwise just report whichever of the
1486                        cardinal directions has reached its limit */
1487                     if (isok(x, u.uy))
1488                         dx = 0;
1489                     else if (isok(u.ux, y))
1490                         dy = 0;
1491                 }
1492                 You("have already gone as far %s as possible.",
1493                     directionname(xytod(dx, dy)));
1494             }
1495             nomul(0);
1496             return;
1497         }
1498         if (((trap = t_at(x, y)) && trap->tseen)
1499             || (Blind && !Levitation && !Flying && !is_clinger(youmonst.data)
1500                 && is_pool_or_lava(x, y) && levl[x][y].seenv)) {
1501             if (context.run >= 2) {
1502                 if (iflags.mention_walls) {
1503                     if (trap && trap->tseen) {
1504                         int tt = what_trap(trap->ttyp, rn2_on_display_rng);
1505 
1506                         You("stop in front of %s.",
1507                             an(defsyms[trap_to_defsym(tt)].explanation));
1508                     } else if (is_pool_or_lava(x,y) && levl[x][y].seenv) {
1509                         You("stop at the edge of the %s.",
1510                             hliquid(is_pool(x,y) ? "water" : "lava"));
1511                     }
1512                 }
1513                 nomul(0);
1514                 context.move = 0;
1515                 return;
1516             } else
1517                 nomul(0);
1518         }
1519 
1520         if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) {
1521             if (distu(u.ustuck->mx, u.ustuck->my) > 2) {
1522                 /* perhaps it fled (or was teleported or ... ) */
1523                 u.ustuck = 0;
1524             } else if (sticks(youmonst.data)) {
1525                 /* When polymorphed into a sticking monster,
1526                  * u.ustuck means it's stuck to you, not you to it.
1527                  */
1528                 You("release %s.", mon_nam(u.ustuck));
1529                 u.ustuck = 0;
1530             } else {
1531                 /* If holder is asleep or paralyzed:
1532                  *      37.5% chance of getting away,
1533                  *      12.5% chance of waking/releasing it;
1534                  * otherwise:
1535                  *       7.5% chance of getting away.
1536                  * [strength ought to be a factor]
1537                  * If holder is tame and there is no conflict,
1538                  * guaranteed escape.
1539                  */
1540                 switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
1541                 case 0:
1542                 case 1:
1543                 case 2:
1544  pull_free:
1545                     You("pull free from %s.", mon_nam(u.ustuck));
1546                     u.ustuck = 0;
1547                     break;
1548                 case 3:
1549                     if (!u.ustuck->mcanmove) {
1550                         /* it's free to move on next turn */
1551                         u.ustuck->mfrozen = 1;
1552                         u.ustuck->msleeping = 0;
1553                     }
1554                 /*FALLTHRU*/
1555                 default:
1556                     if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf)
1557                         goto pull_free;
1558                     You("cannot escape from %s!", mon_nam(u.ustuck));
1559                     nomul(0);
1560                     return;
1561                 }
1562             }
1563         }
1564 
1565         mtmp = m_at(x, y);
1566         if (mtmp && !is_safepet(mtmp)) {
1567             /* Don't attack if you're running, and can see it */
1568             /* It's fine to displace pets, though */
1569             /* We should never get here if forcefight */
1570             if (context.run && ((!Blind && mon_visible(mtmp)
1571                                  && ((M_AP_TYPE(mtmp) != M_AP_FURNITURE
1572                                       && M_AP_TYPE(mtmp) != M_AP_OBJECT)
1573                                      || Protection_from_shape_changers))
1574                                 || sensemon(mtmp))) {
1575                 nomul(0);
1576                 context.move = 0;
1577                 return;
1578             }
1579         }
1580     }
1581 
1582     u.ux0 = u.ux;
1583     u.uy0 = u.uy;
1584     bhitpos.x = x;
1585     bhitpos.y = y;
1586     tmpr = &levl[x][y];
1587 
1588     /* attack monster */
1589     if (mtmp) {
1590         /* don't stop travel when displacing pets; if the
1591            displace fails for some reason, attack() in uhitm.c
1592            will stop travel rather than domove */
1593         if (!is_safepet(mtmp) || context.forcefight)
1594             nomul(0);
1595         /* only attack if we know it's there */
1596         /* or if we used the 'F' command to fight blindly */
1597         /* or if it hides_under, in which case we call attack() to print
1598          * the Wait! message.
1599          * This is different from ceiling hiders, who aren't handled in
1600          * attack().
1601          */
1602 
1603         /* If they used a 'm' command, trying to move onto a monster
1604          * prints the below message and wastes a turn.  The exception is
1605          * if the monster is unseen and the player doesn't remember an
1606          * invisible monster--then, we fall through to attack() and
1607          * attack_check(), which still wastes a turn, but prints a
1608          * different message and makes the player remember the monster.
1609          */
1610         if (context.nopick && !context.travel
1611             && (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))) {
1612             if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers
1613                 && !sensemon(mtmp))
1614                 stumble_onto_mimic(mtmp);
1615             else if (mtmp->mpeaceful && !Hallucination)
1616                 /* m_monnam(): "dog" or "Fido", no "invisible dog" or "it" */
1617                 pline("Pardon me, %s.", m_monnam(mtmp));
1618             else
1619                 You("move right into %s.", mon_nam(mtmp));
1620             return;
1621         }
1622         if (context.forcefight || !mtmp->mundetected || sensemon(mtmp)
1623             || ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)
1624                 && !is_safepet(mtmp))) {
1625             /* try to attack; note that it might evade */
1626             /* also, we don't attack tame when _safepet_ */
1627             if (attack(mtmp))
1628                 return;
1629         }
1630     }
1631 
1632     if (context.forcefight && levl[x][y].typ == IRONBARS && uwep) {
1633         struct obj *obj = uwep;
1634 
1635         if (breaktest(obj)) {
1636             if (obj->quan > 1L)
1637                 obj = splitobj(obj, 1L);
1638             else
1639                 setuwep((struct obj *)0);
1640             freeinv(obj);
1641         }
1642         hit_bars(&obj, u.ux, u.uy, x, y, TRUE, TRUE);
1643         return;
1644     }
1645 
1646     /* specifying 'F' with no monster wastes a turn */
1647     if (context.forcefight
1648         /* remembered an 'I' && didn't use a move command */
1649         || (glyph_is_invisible(levl[x][y].glyph) && !context.nopick)) {
1650         struct obj *boulder = 0;
1651         boolean explo = (Upolyd && attacktype(youmonst.data, AT_EXPL)),
1652                 solid = !accessible(x, y);
1653         int glyph = glyph_at(x, y); /* might be monster */
1654         char buf[BUFSZ];
1655 
1656         if (!Underwater) {
1657             boulder = sobj_at(BOULDER, x, y);
1658             /* if a statue is displayed at the target location,
1659                player is attempting to attack it [and boulder
1660                handling below is suitable for handling that] */
1661             if (glyph_is_statue(glyph)
1662                 || (Hallucination && glyph_is_monster(glyph)))
1663                 boulder = sobj_at(STATUE, x, y);
1664 
1665             /* force fight at boulder/statue or wall/door while wielding
1666                pick:  start digging to break the boulder or wall */
1667             if (context.forcefight
1668                 /* can we dig? */
1669                 && uwep && dig_typ(uwep, x, y)
1670                 /* should we dig? */
1671                 && !glyph_is_invisible(glyph) && !glyph_is_monster(glyph)) {
1672                 (void) use_pick_axe2(uwep);
1673                 return;
1674             }
1675         }
1676 
1677         /* about to become known empty -- remove 'I' if present */
1678         unmap_object(x, y);
1679         if (boulder)
1680             map_object(boulder, TRUE);
1681         newsym(x, y);
1682         glyph = glyph_at(x, y); /* might have just changed */
1683 
1684         if (boulder) {
1685             Strcpy(buf, ansimpleoname(boulder));
1686         } else if (Underwater && !is_pool(x, y)) {
1687             /* Underwater, targetting non-water; the map just shows blank
1688                because you don't see remembered terrain while underwater;
1689                although the hero can attack an adjacent monster this way,
1690                assume he can't reach out far enough to distinguish terrain */
1691             Sprintf(buf, (Is_waterlevel(&u.uz) && levl[x][y].typ == AIR)
1692                              ? "an air bubble"
1693                              : "nothing");
1694         } else if (solid) {
1695             /* glyph might indicate unseen terrain if hero is blind;
1696                unlike searching, this won't reveal what that terrain is
1697                (except for solid rock, where the glyph would otherwise
1698                yield ludicrous "dark part of a room") */
1699             Strcpy(buf, (levl[x][y].typ == STONE) ? "solid rock"
1700                          : glyph_is_cmap(glyph)
1701                             ? the(defsyms[glyph_to_cmap(glyph)].explanation)
1702                             : (const char *) "an unknown obstacle");
1703             /* note: 'solid' is misleadingly named and catches pools
1704                of water and lava as well as rock and walls */
1705         } else {
1706             Strcpy(buf, "thin air");
1707         }
1708         You("%s%s %s.",
1709             !(boulder || solid) ? "" : !explo ? "harmlessly " : "futilely ",
1710             explo ? "explode at" : "attack", buf);
1711 
1712         nomul(0);
1713         if (explo) {
1714             wake_nearby();
1715             u.mh = -1; /* dead in the current form */
1716             rehumanize();
1717         }
1718         return;
1719     }
1720     (void) unmap_invisible(x, y);
1721     /* not attacking an animal, so we try to move */
1722     if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) {
1723         nomul(0);
1724         return;
1725     }
1726 
1727     if (u_rooted())
1728         return;
1729 
1730     if (u.utrap) {
1731         boolean moved = trapmove(x, y, trap);
1732 
1733         if (!u.utrap)
1734             reset_utrap(TRUE); /* might resume levitation or flight */
1735         /* might not have escaped, or did escape but remain in same spot */
1736         if (!moved)
1737             return;
1738     }
1739 
1740     if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) {
1741         if (!context.door_opened) {
1742             context.move = 0;
1743             nomul(0);
1744         }
1745         return;
1746     }
1747 
1748     /* Move ball and chain.  */
1749     if (Punished)
1750         if (!drag_ball(x, y, &bc_control, &ballx, &bally, &chainx, &chainy,
1751                        &cause_delay, TRUE))
1752             return;
1753 
1754     /* Check regions entering/leaving */
1755     if (!in_out_region(x, y))
1756         return;
1757 
1758     /* now move the hero */
1759     mtmp = m_at(x, y);
1760     u.ux += u.dx;
1761     u.uy += u.dy;
1762     /* Move your steed, too */
1763     if (u.usteed) {
1764         u.usteed->mx = u.ux;
1765         u.usteed->my = u.uy;
1766         exercise_steed();
1767     }
1768 
1769     /*
1770      * If safepet at destination then move the pet to the hero's
1771      * previous location using the same conditions as in attack().
1772      * there are special extenuating circumstances:
1773      * (1) if the pet dies then your god angers,
1774      * (2) if the pet gets trapped then your god may disapprove,
1775      * (3) if the pet was already trapped and you attempt to free it
1776      * not only do you encounter the trap but you may frighten your
1777      * pet causing it to go wild!  moral: don't abuse this privilege.
1778      *
1779      * Ceiling-hiding pets are skipped by this section of code, to
1780      * be caught by the normal falling-monster code.
1781      */
1782     if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) {
1783         /* if trapped, there's a chance the pet goes wild */
1784         if (mtmp->mtrapped) {
1785             if (!rn2(mtmp->mtame)) {
1786                 mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
1787                 if (mtmp->mleashed)
1788                     m_unleash(mtmp, TRUE);
1789                 growl(mtmp);
1790             } else {
1791                 yelp(mtmp);
1792             }
1793         }
1794 
1795         /* seemimic/newsym should be done before moving hero, otherwise
1796            the display code will draw the hero here before we possibly
1797            cancel the swap below (we can ignore steed mx,my here) */
1798         u.ux = u.ux0, u.uy = u.uy0;
1799         mtmp->mundetected = 0;
1800         if (M_AP_TYPE(mtmp))
1801             seemimic(mtmp);
1802         else if (!mtmp->mtame)
1803             newsym(mtmp->mx, mtmp->my);
1804         u.ux = mtmp->mx, u.uy = mtmp->my; /* resume swapping positions */
1805 
1806         if (mtmp->mtrapped && (trap = t_at(mtmp->mx, mtmp->my)) != 0
1807             && is_pit(trap->ttyp)
1808             && sobj_at(BOULDER, trap->tx, trap->ty)) {
1809             /* can't swap places with pet pinned in a pit by a boulder */
1810             u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1811             if (u.usteed)
1812                 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1813         } else if (u.ux0 != x && u.uy0 != y && NODIAG(mtmp->data - mons)) {
1814             /* can't swap places when pet can't move to your spot */
1815             u.ux = u.ux0, u.uy = u.uy0;
1816             if (u.usteed)
1817                 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1818             You("stop.  %s can't move diagonally.", upstart(y_monnam(mtmp)));
1819         } else if (u_with_boulder
1820                     && !(verysmall(mtmp->data)
1821                          && (!mtmp->minvent || (curr_mon_load(mtmp) <= 600)))) {
1822             /* can't swap places when pet won't fit there with the boulder */
1823             u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1824             if (u.usteed)
1825                 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1826             You("stop.  %s won't fit into the same spot that you're at.",
1827                  upstart(y_monnam(mtmp)));
1828         } else if (u.ux0 != x && u.uy0 != y && bad_rock(mtmp->data, x, u.uy0)
1829                    && bad_rock(mtmp->data, u.ux0, y)
1830                    && (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) {
1831             /* can't swap places when pet won't fit thru the opening */
1832             u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1833             if (u.usteed)
1834                 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1835             You("stop.  %s won't fit through.", upstart(y_monnam(mtmp)));
1836         } else {
1837             char pnambuf[BUFSZ];
1838 
1839             /* save its current description in case of polymorph */
1840             Strcpy(pnambuf, y_monnam(mtmp));
1841             mtmp->mtrapped = 0;
1842             remove_monster(x, y);
1843             place_monster(mtmp, u.ux0, u.uy0);
1844             newsym(x, y);
1845             newsym(u.ux0, u.uy0);
1846 
1847             You("%s %s.", mtmp->mtame ? "swap places with" : "frighten",
1848                 pnambuf);
1849 
1850             /* check for displacing it into pools and traps */
1851             switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) {
1852             case 0:
1853                 break;
1854             case 1: /* trapped */
1855             case 3: /* changed levels */
1856                 /* there's already been a trap message, reinforce it */
1857                 abuse_dog(mtmp);
1858                 adjalign(-3);
1859                 break;
1860             case 2:
1861                 /* drowned or died...
1862                  * you killed your pet by direct action, so get experience
1863                  * and possibly penalties;
1864                  * we want the level gain message, if it happens, to occur
1865                  * before the guilt message below
1866                  */
1867                 {
1868                     /* minliquid() and mintrap() call mondead() rather than
1869                        killed() so we duplicate some of the latter here */
1870                     int tmp, mndx;
1871 
1872                     u.uconduct.killer++;
1873                     mndx = monsndx(mtmp->data);
1874                     tmp = experience(mtmp, (int) mvitals[mndx].died);
1875                     more_experienced(tmp, 0);
1876                     newexplevel(); /* will decide if you go up */
1877                 }
1878                 /* That's no way to treat a pet!  Your god gets angry.
1879                  *
1880                  * [This has always been pretty iffy.  Why does your
1881                  * patron deity care at all, let alone enough to get mad?]
1882                  */
1883                 if (rn2(4)) {
1884                     You_feel("guilty about losing your pet like this.");
1885                     u.ugangr++;
1886                     adjalign(-15);
1887                 }
1888                 break;
1889             default:
1890                 pline("that's strange, unknown mintrap result!");
1891                 break;
1892             }
1893         }
1894     }
1895 
1896     reset_occupations();
1897     if (context.run) {
1898         if (context.run < 8)
1899             if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ)
1900                 || IS_FURNITURE(tmpr->typ))
1901                 nomul(0);
1902     }
1903 
1904     if (hides_under(youmonst.data) || youmonst.data->mlet == S_EEL
1905         || u.dx || u.dy)
1906         (void) hideunder(&youmonst);
1907 
1908     /*
1909      * Mimics (or whatever) become noticeable if they move and are
1910      * imitating something that doesn't move.  We could extend this
1911      * to non-moving monsters...
1912      */
1913     if ((u.dx || u.dy) && (U_AP_TYPE == M_AP_OBJECT
1914                            || U_AP_TYPE == M_AP_FURNITURE))
1915         youmonst.m_ap_type = M_AP_NOTHING;
1916 
1917     check_leash(u.ux0, u.uy0);
1918 
1919     if (u.ux0 != u.ux || u.uy0 != u.uy) {
1920         /* let caller know so that an evaluation may take place */
1921         domove_succeeded |= (domove_attempting & (DOMOVE_RUSH | DOMOVE_WALK));
1922         u.umoved = TRUE;
1923         /* Clean old position -- vision_recalc() will print our new one. */
1924         newsym(u.ux0, u.uy0);
1925         /* Since the hero has moved, adjust what can be seen/unseen. */
1926         vision_recalc(1); /* Do the work now in the recover time. */
1927         invocation_message();
1928     }
1929 
1930     if (Punished) /* put back ball and chain */
1931         move_bc(0, bc_control, ballx, bally, chainx, chainy);
1932 
1933     if (u.umoved)
1934         spoteffects(TRUE);
1935 
1936     /* delay next move because of ball dragging */
1937     /* must come after we finished picking up, in spoteffects() */
1938     if (cause_delay) {
1939         nomul(-2);
1940         multi_reason = "dragging an iron ball";
1941         nomovemsg = "";
1942     }
1943 
1944     if (context.run && flags.runmode != RUN_TPORT) {
1945         /* display every step or every 7th step depending upon mode */
1946         if (flags.runmode != RUN_LEAP || !(moves % 7L)) {
1947             if (flags.time)
1948                 context.botl = 1;
1949             curs_on_u();
1950             delay_output();
1951             if (flags.runmode == RUN_CRAWL) {
1952                 delay_output();
1953                 delay_output();
1954                 delay_output();
1955                 delay_output();
1956             }
1957         }
1958     }
1959 }
1960 
1961 STATIC_OVL void
maybe_smudge_engr(x1,y1,x2,y2)1962 maybe_smudge_engr(x1,y1,x2,y2)
1963 int x1, y1, x2, y2;
1964 {
1965     struct engr *ep;
1966 
1967     if (can_reach_floor(TRUE)) {
1968         if ((ep = engr_at(x1, y1)) && ep->engr_type != HEADSTONE)
1969             wipe_engr_at(x1, y1, rnd(5), FALSE);
1970         if ((x2 != x1 || y2 != y1)
1971                 && (ep = engr_at(x2, y2)) && ep->engr_type != HEADSTONE)
1972             wipe_engr_at(x2, y2, rnd(5), FALSE);
1973     }
1974 }
1975 
1976 /* combat increases metabolism */
1977 boolean
overexertion()1978 overexertion()
1979 {
1980     /* this used to be part of domove() when moving to a monster's
1981        position, but is now called by attack() so that it doesn't
1982        execute if you decline to attack a peaceful monster */
1983     gethungry();
1984     if ((moves % 3L) != 0L && near_capacity() >= HVY_ENCUMBER) {
1985         int *hp = (!Upolyd ? &u.uhp : &u.mh);
1986 
1987         if (*hp > 1) {
1988             *hp -= 1;
1989         } else {
1990             You("pass out from exertion!");
1991             exercise(A_CON, FALSE);
1992             fall_asleep(-10, FALSE);
1993         }
1994     }
1995     return (boolean) (multi < 0); /* might have fainted (forced to sleep) */
1996 }
1997 
1998 void
invocation_message()1999 invocation_message()
2000 {
2001     /* a special clue-msg when on the Invocation position */
2002     if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
2003         char buf[BUFSZ];
2004         struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION);
2005 
2006         nomul(0); /* stop running or travelling */
2007         if (u.usteed)
2008             Sprintf(buf, "beneath %s", y_monnam(u.usteed));
2009         else if (Levitation || Flying)
2010             Strcpy(buf, "beneath you");
2011         else
2012             Sprintf(buf, "under your %s", makeplural(body_part(FOOT)));
2013 
2014         You_feel("a strange vibration %s.", buf);
2015         u.uevent.uvibrated = 1;
2016         if (otmp && otmp->spe == 7 && otmp->lamplit)
2017             pline("%s %s!", The(xname(otmp)),
2018                   Blind ? "throbs palpably" : "glows with a strange light");
2019     }
2020 }
2021 
2022 /* moving onto different terrain;
2023    might be going into solid rock, inhibiting levitation or flight,
2024    or coming back out of such, reinstating levitation/flying */
2025 void
switch_terrain()2026 switch_terrain()
2027 {
2028     struct rm *lev = &levl[u.ux][u.uy];
2029     boolean blocklev = (IS_ROCK(lev->typ) || closed_door(u.ux, u.uy)
2030                         || (Is_waterlevel(&u.uz) && lev->typ == WATER)),
2031             was_levitating = !!Levitation, was_flying = !!Flying;
2032 
2033     if (blocklev) {
2034         /* called from spoteffects(), stop levitating but skip float_down() */
2035         if (Levitation)
2036             You_cant("levitate in here.");
2037         BLevitation |= FROMOUTSIDE;
2038     } else if (BLevitation) {
2039         BLevitation &= ~FROMOUTSIDE;
2040         /* we're probably levitating now; if not, we must be chained
2041            to a buried iron ball so get float_up() feedback for that */
2042         if (Levitation || BLevitation)
2043             float_up();
2044     }
2045     /* the same terrain that blocks levitation also blocks flight */
2046     if (blocklev) {
2047         if (Flying)
2048             You_cant("fly in here.");
2049         BFlying |= FROMOUTSIDE;
2050     } else if (BFlying) {
2051         BFlying &= ~FROMOUTSIDE;
2052         float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
2053         /* [minor bug: we don't know whether this is beginning flight or
2054            resuming it; that could be tracked so that this message could
2055            be adjusted to "resume flying", but isn't worth the effort...] */
2056         if (Flying)
2057             You("start flying.");
2058     }
2059     if ((!Levitation ^ was_levitating) || (!Flying ^ was_flying))
2060         context.botl = TRUE; /* update Lev/Fly status condition */
2061 }
2062 
2063 /* extracted from spoteffects; called by spoteffects to check for entering or
2064    leaving a pool of water/lava, and by moveloop to check for staying on one;
2065    returns true to skip rest of spoteffects */
2066 boolean
pooleffects(newspot)2067 pooleffects(newspot)
2068 boolean newspot;             /* true if called by spoteffects */
2069 {
2070     /* check for leaving water */
2071     if (u.uinwater) {
2072         boolean still_inwater = FALSE; /* assume we're getting out */
2073 
2074         if (!is_pool(u.ux, u.uy)) {
2075             if (Is_waterlevel(&u.uz))
2076                 You("pop into an air bubble.");
2077             else if (is_lava(u.ux, u.uy))
2078                 You("leave the %s...", hliquid("water")); /* oops! */
2079             else
2080                 You("are on solid %s again.",
2081                     is_ice(u.ux, u.uy) ? "ice" : "land");
2082         } else if (Is_waterlevel(&u.uz)) {
2083             still_inwater = TRUE;
2084         } else if (Levitation) {
2085             You("pop out of the %s like a cork!", hliquid("water"));
2086         } else if (Flying) {
2087             You("fly out of the %s.", hliquid("water"));
2088         } else if (Wwalking) {
2089             You("slowly rise above the surface.");
2090         } else {
2091             still_inwater = TRUE;
2092         }
2093         if (!still_inwater) {
2094             boolean was_underwater = (Underwater && !Is_waterlevel(&u.uz));
2095 
2096             u.uinwater = 0;       /* leave the water */
2097             if (was_underwater) { /* restore vision */
2098                 docrt();
2099                 vision_full_recalc = 1;
2100             }
2101         }
2102     }
2103 
2104     /* check for entering water or lava */
2105     if (!u.ustuck && !Levitation && !Flying && is_pool_or_lava(u.ux, u.uy)) {
2106         if (u.usteed
2107             && (is_flyer(u.usteed->data) || is_floater(u.usteed->data)
2108                 || is_clinger(u.usteed->data))) {
2109             /* floating or clinging steed keeps hero safe (is_flyer() test
2110                is redundant; it can't be true since Flying yielded false) */
2111             return FALSE;
2112         } else if (u.usteed) {
2113             /* steed enters pool */
2114             dismount_steed(Underwater ? DISMOUNT_FELL : DISMOUNT_GENERIC);
2115             /* dismount_steed() -> float_down() -> pickup()
2116                (float_down doesn't do autopickup on Air or Water) */
2117             if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
2118                 return FALSE;
2119             /* even if we actually end up at same location, float_down()
2120                has already done spoteffect()'s trap and pickup actions */
2121             if (newspot)
2122                 check_special_room(FALSE); /* spoteffects */
2123             return TRUE;
2124         }
2125         /* not mounted */
2126 
2127         /* if hiding on ceiling then don't automatically enter pool */
2128         if (Upolyd && ceiling_hider(&mons[u.umonnum]) && u.uundetected)
2129             return FALSE;
2130 
2131         /* drown(),lava_effects() return true if hero changes
2132            location while surviving the problem */
2133         if (is_lava(u.ux, u.uy)) {
2134             if (lava_effects())
2135                 return TRUE;
2136         } else if (!Wwalking
2137                    && (newspot || !u.uinwater || !(Swimming || Amphibious))) {
2138             if (drown())
2139                 return TRUE;
2140         }
2141     }
2142     return FALSE;
2143 }
2144 
2145 void
spoteffects(pick)2146 spoteffects(pick)
2147 boolean pick;
2148 {
2149     static int inspoteffects = 0;
2150     static coord spotloc;
2151     static int spotterrain;
2152     static struct trap *spottrap = (struct trap *) 0;
2153     static unsigned spottraptyp = NO_TRAP;
2154 
2155     struct monst *mtmp;
2156     struct trap *trap = t_at(u.ux, u.uy);
2157     int trapflag = iflags.failing_untrap ? FAILEDUNTRAP : 0;
2158 
2159     /* prevent recursion from affecting the hero all over again
2160        [hero poly'd to iron golem enters water here, drown() inflicts
2161        damage that triggers rehumanize() which calls spoteffects()...] */
2162     if (inspoteffects && u.ux == spotloc.x && u.uy == spotloc.y
2163         /* except when reason is transformed terrain (ice -> water) */
2164         && spotterrain == levl[u.ux][u.uy].typ
2165         /* or transformed trap (land mine -> pit) */
2166         && (!spottrap || !trap || trap->ttyp == spottraptyp))
2167         return;
2168 
2169     ++inspoteffects;
2170     spotterrain = levl[u.ux][u.uy].typ;
2171     spotloc.x = u.ux, spotloc.y = u.uy;
2172 
2173     /* moving onto different terrain might cause Lev or Fly to toggle */
2174     if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0))
2175         switch_terrain();
2176 
2177     if (pooleffects(TRUE))
2178         goto spotdone;
2179 
2180     check_special_room(FALSE);
2181     if (IS_SINK(levl[u.ux][u.uy].typ) && Levitation)
2182         dosinkfall();
2183     if (!in_steed_dismounting) { /* if dismounting, we'll check again later */
2184         boolean pit;
2185 
2186         /* if levitation is due to time out at the end of this
2187            turn, allowing it to do so could give the perception
2188            that a trap here is being triggered twice, so adjust
2189            the timeout to prevent that */
2190         if (trap && (HLevitation & TIMEOUT) == 1L
2191             && !(ELevitation || (HLevitation & ~(I_SPECIAL | TIMEOUT)))) {
2192             if (rn2(2)) { /* defer timeout */
2193                 incr_itimeout(&HLevitation, 1L);
2194             } else { /* timeout early */
2195                 if (float_down(I_SPECIAL | TIMEOUT, 0L)) {
2196                     /* levitation has ended; we've already triggered
2197                        any trap and [usually] performed autopickup */
2198                     trap = 0;
2199                     pick = FALSE;
2200                 }
2201             }
2202         }
2203         /*
2204          * If not a pit, pickup before triggering trap.
2205          * If pit, trigger trap before pickup.
2206          */
2207         pit = (trap && is_pit(trap->ttyp));
2208         if (pick && !pit)
2209             (void) pickup(1);
2210 
2211         if (trap) {
2212             /*
2213              * dotrap on a fire trap calls melt_ice() which triggers
2214              * spoteffects() (again) which can trigger the same fire
2215              * trap (again). Use static spottrap to prevent that.
2216              * We track spottraptyp because some traps morph
2217              * (landmine to pit) and any new trap type
2218              * should get triggered.
2219              */
2220             if (!spottrap || spottraptyp != trap->ttyp) {
2221                 spottrap = trap;
2222                 spottraptyp = trap->ttyp;
2223                 dotrap(trap, trapflag); /* fall into arrow trap, etc. */
2224                 spottrap = (struct trap *) 0;
2225                 spottraptyp = NO_TRAP;
2226             }
2227         }
2228         if (pick && pit)
2229             (void) pickup(1);
2230     }
2231     /* Warning alerts you to ice danger */
2232     if (Warning && is_ice(u.ux, u.uy)) {
2233         static const char *const icewarnings[] = {
2234             "The ice seems very soft and slushy.",
2235             "You feel the ice shift beneath you!",
2236             "The ice, is gonna BREAK!", /* The Dead Zone */
2237         };
2238         long time_left = spot_time_left(u.ux, u.uy, MELT_ICE_AWAY);
2239 
2240         if (time_left && time_left < 15L)
2241             pline("%s", icewarnings[(time_left < 5L) ? 2
2242                                     : (time_left < 10L) ? 1
2243                                       : 0]);
2244     }
2245     if ((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
2246         mtmp->mundetected = mtmp->msleeping = 0;
2247         switch (mtmp->data->mlet) {
2248         case S_PIERCER:
2249             pline("%s suddenly drops from the %s!", Amonnam(mtmp),
2250                   ceiling(u.ux, u.uy));
2251             if (mtmp->mtame) { /* jumps to greet you, not attack */
2252                 ;
2253             } else if (uarmh && is_metallic(uarmh)) {
2254                 pline("Its blow glances off your %s.",
2255                       helm_simple_name(uarmh));
2256             } else if (u.uac + 3 <= rnd(20)) {
2257                 You("are almost hit by %s!",
2258                     x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2259             } else {
2260                 int dmg;
2261 
2262                 You("are hit by %s!",
2263                     x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2264                 dmg = d(4, 6);
2265                 if (Half_physical_damage)
2266                     dmg = (dmg + 1) / 2;
2267                 mdamageu(mtmp, dmg);
2268             }
2269             break;
2270         default: /* monster surprises you. */
2271             if (mtmp->mtame)
2272                 pline("%s jumps near you from the %s.", Amonnam(mtmp),
2273                       ceiling(u.ux, u.uy));
2274             else if (mtmp->mpeaceful) {
2275                 You("surprise %s!",
2276                     Blind && !sensemon(mtmp) ? something : a_monnam(mtmp));
2277                 mtmp->mpeaceful = 0;
2278             } else
2279                 pline("%s attacks you by surprise!", Amonnam(mtmp));
2280             break;
2281         }
2282         mnexto(mtmp); /* have to move the monster */
2283     }
2284  spotdone:
2285     if (!--inspoteffects) {
2286         spotterrain = STONE; /* 0 */
2287         spotloc.x = spotloc.y = 0;
2288     }
2289     return;
2290 }
2291 
2292 /* returns first matching monster */
2293 STATIC_OVL struct monst *
monstinroom(mdat,roomno)2294 monstinroom(mdat, roomno)
2295 struct permonst *mdat;
2296 int roomno;
2297 {
2298     register struct monst *mtmp;
2299 
2300     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2301         if (DEADMONSTER(mtmp))
2302             continue;
2303         if (mtmp->data == mdat
2304             && index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET))
2305             return mtmp;
2306     }
2307     return (struct monst *) 0;
2308 }
2309 
2310 char *
in_rooms(x,y,typewanted)2311 in_rooms(x, y, typewanted)
2312 register xchar x, y;
2313 register int typewanted;
2314 {
2315     static char buf[5];
2316     char rno, *ptr = &buf[4];
2317     int typefound, min_x, min_y, max_x, max_y_offset, step;
2318     register struct rm *lev;
2319 
2320 #define goodtype(rno) \
2321     (!typewanted                                                   \
2322      || (typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted  \
2323      || (typewanted == SHOPBASE && typefound > SHOPBASE))
2324 
2325     switch (rno = levl[x][y].roomno) {
2326     case NO_ROOM:
2327         return ptr;
2328     case SHARED:
2329         step = 2;
2330         break;
2331     case SHARED_PLUS:
2332         step = 1;
2333         break;
2334     default: /* i.e. a regular room # */
2335         if (goodtype(rno))
2336             *(--ptr) = rno;
2337         return ptr;
2338     }
2339 
2340     min_x = x - 1;
2341     max_x = x + 1;
2342     if (x < 1)
2343         min_x += step;
2344     else if (x >= COLNO)
2345         max_x -= step;
2346 
2347     min_y = y - 1;
2348     max_y_offset = 2;
2349     if (min_y < 0) {
2350         min_y += step;
2351         max_y_offset -= step;
2352     } else if ((min_y + max_y_offset) >= ROWNO)
2353         max_y_offset -= step;
2354 
2355     for (x = min_x; x <= max_x; x += step) {
2356         lev = &levl[x][min_y];
2357         y = 0;
2358         if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2359             && goodtype(rno))
2360             *(--ptr) = rno;
2361         y += step;
2362         if (y > max_y_offset)
2363             continue;
2364         if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2365             && goodtype(rno))
2366             *(--ptr) = rno;
2367         y += step;
2368         if (y > max_y_offset)
2369             continue;
2370         if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2371             && goodtype(rno))
2372             *(--ptr) = rno;
2373     }
2374     return ptr;
2375 }
2376 
2377 /* is (x,y) in a town? */
2378 boolean
in_town(x,y)2379 in_town(x, y)
2380 register int x, y;
2381 {
2382     s_level *slev = Is_special(&u.uz);
2383     register struct mkroom *sroom;
2384     boolean has_subrooms = FALSE;
2385 
2386     if (!slev || !slev->flags.town)
2387         return FALSE;
2388 
2389     /*
2390      * See if (x,y) is in a room with subrooms, if so, assume it's the
2391      * town.  If there are no subrooms, the whole level is in town.
2392      */
2393     for (sroom = &rooms[0]; sroom->hx > 0; sroom++) {
2394         if (sroom->nsubrooms > 0) {
2395             has_subrooms = TRUE;
2396             if (inside_room(sroom, x, y))
2397                 return TRUE;
2398         }
2399     }
2400 
2401     return !has_subrooms;
2402 }
2403 
2404 STATIC_OVL void
move_update(newlev)2405 move_update(newlev)
2406 register boolean newlev;
2407 {
2408     char *ptr1, *ptr2, *ptr3, *ptr4;
2409 
2410     Strcpy(u.urooms0, u.urooms);
2411     Strcpy(u.ushops0, u.ushops);
2412     if (newlev) {
2413         u.urooms[0] = '\0';
2414         u.uentered[0] = '\0';
2415         u.ushops[0] = '\0';
2416         u.ushops_entered[0] = '\0';
2417         Strcpy(u.ushops_left, u.ushops0);
2418         return;
2419     }
2420     Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0));
2421 
2422     for (ptr1 = &u.urooms[0], ptr2 = &u.uentered[0], ptr3 = &u.ushops[0],
2423          ptr4 = &u.ushops_entered[0];
2424          *ptr1; ptr1++) {
2425         if (!index(u.urooms0, *ptr1))
2426             *(ptr2++) = *ptr1;
2427         if (IS_SHOP(*ptr1 - ROOMOFFSET)) {
2428             *(ptr3++) = *ptr1;
2429             if (!index(u.ushops0, *ptr1))
2430                 *(ptr4++) = *ptr1;
2431         }
2432     }
2433     *ptr2 = '\0';
2434     *ptr3 = '\0';
2435     *ptr4 = '\0';
2436 
2437     /* filter u.ushops0 -> u.ushops_left */
2438     for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++)
2439         if (!index(u.ushops, *ptr1))
2440             *(ptr2++) = *ptr1;
2441     *ptr2 = '\0';
2442 }
2443 
2444 /* possibly deliver a one-time room entry message */
2445 void
check_special_room(newlev)2446 check_special_room(newlev)
2447 register boolean newlev;
2448 {
2449     register struct monst *mtmp;
2450     char *ptr;
2451 
2452     move_update(newlev);
2453 
2454     if (*u.ushops0)
2455         u_left_shop(u.ushops_left, newlev);
2456 
2457     if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */
2458         return; /* no entrance messages necessary */
2459 
2460     /* Did we just enter a shop? */
2461     if (*u.ushops_entered)
2462         u_entered_shop(u.ushops_entered);
2463 
2464     for (ptr = &u.uentered[0]; *ptr; ptr++) {
2465         int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype;
2466         boolean msg_given = TRUE;
2467 
2468         /* Did we just enter some other special room? */
2469         /* vault.c insists that a vault remain a VAULT,
2470          * and temples should remain TEMPLEs,
2471          * but everything else gives a message only the first time */
2472         switch (rt) {
2473         case ZOO:
2474             pline("Welcome to David's treasure zoo!");
2475             break;
2476         case SWAMP:
2477             pline("It %s rather %s down here.", Blind ? "feels" : "looks",
2478                   Blind ? "humid" : "muddy");
2479             break;
2480         case COURT:
2481             You("enter an opulent throne room!");
2482             break;
2483         case LEPREHALL:
2484             You("enter a leprechaun hall!");
2485             break;
2486         case MORGUE:
2487             if (midnight()) {
2488                 const char *run = locomotion(youmonst.data, "Run");
2489                 pline("%s away!  %s away!", run, run);
2490             } else
2491                 You("have an uncanny feeling...");
2492             break;
2493         case BEEHIVE:
2494             You("enter a giant beehive!");
2495             break;
2496         case COCKNEST:
2497             You("enter a disgusting nest!");
2498             break;
2499         case ANTHOLE:
2500             You("enter an anthole!");
2501             break;
2502         case BARRACKS:
2503             if (monstinroom(&mons[PM_SOLDIER], roomno)
2504                 || monstinroom(&mons[PM_SERGEANT], roomno)
2505                 || monstinroom(&mons[PM_LIEUTENANT], roomno)
2506                 || monstinroom(&mons[PM_CAPTAIN], roomno))
2507                 You("enter a military barracks!");
2508             else
2509                 You("enter an abandoned barracks.");
2510             break;
2511         case DELPHI: {
2512             struct monst *oracle = monstinroom(&mons[PM_ORACLE], roomno);
2513             if (oracle) {
2514                 if (!oracle->mpeaceful)
2515                     verbalize("You're in Delphi, %s.", plname);
2516                 else
2517                     verbalize("%s, %s, welcome to Delphi!",
2518                               Hello((struct monst *) 0), plname);
2519             } else
2520                 msg_given = FALSE;
2521             break;
2522         }
2523         case TEMPLE:
2524             intemple(roomno + ROOMOFFSET);
2525         /*FALLTHRU*/
2526         default:
2527             msg_given = (rt == TEMPLE);
2528             rt = 0;
2529             break;
2530         }
2531         if (msg_given)
2532             room_discovered(roomno);
2533 
2534         if (rt != 0) {
2535             rooms[roomno].rtype = OROOM;
2536             if (!search_special(rt)) {
2537                 /* No more room of that type */
2538                 switch (rt) {
2539                 case COURT:
2540                     level.flags.has_court = 0;
2541                     break;
2542                 case SWAMP:
2543                     level.flags.has_swamp = 0;
2544                     break;
2545                 case MORGUE:
2546                     level.flags.has_morgue = 0;
2547                     break;
2548                 case ZOO:
2549                     level.flags.has_zoo = 0;
2550                     break;
2551                 case BARRACKS:
2552                     level.flags.has_barracks = 0;
2553                     break;
2554                 case TEMPLE:
2555                     level.flags.has_temple = 0;
2556                     break;
2557                 case BEEHIVE:
2558                     level.flags.has_beehive = 0;
2559                     break;
2560                 }
2561             }
2562             if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO)
2563                 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2564                     if (DEADMONSTER(mtmp))
2565                         continue;
2566                     if (!Stealth && !rn2(3))
2567                         mtmp->msleeping = 0;
2568                 }
2569         }
2570     }
2571 
2572     return;
2573 }
2574 
2575 /* returns
2576    1 = cannot pickup, time taken
2577    0 = cannot pickup, no time taken
2578   -1 = do normal pickup
2579   -2 = loot the monster */
2580 int
pickup_checks()2581 pickup_checks()
2582 {
2583     /* uswallow case added by GAN 01/29/87 */
2584     if (u.uswallow) {
2585         if (!u.ustuck->minvent) {
2586             if (is_animal(u.ustuck->data)) {
2587                 You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
2588                 pline("But it's kind of slimy, so you drop it.");
2589             } else
2590                 You("don't %s anything in here to pick up.",
2591                     Blind ? "feel" : "see");
2592             return 1;
2593         } else {
2594             return -2; /* loot the monster inventory */
2595         }
2596     }
2597     if (is_pool(u.ux, u.uy)) {
2598         if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2599             || (Flying && !Breathless)) {
2600             You("cannot dive into the %s to pick things up.",
2601                 hliquid("water"));
2602             return 0;
2603         } else if (!Underwater) {
2604             You_cant("even see the bottom, let alone pick up %s.", something);
2605             return 0;
2606         }
2607     }
2608     if (is_lava(u.ux, u.uy)) {
2609         if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2610             || (Flying && !Breathless)) {
2611             You_cant("reach the bottom to pick things up.");
2612             return 0;
2613         } else if (!likes_lava(youmonst.data)) {
2614             You("would burn to a crisp trying to pick things up.");
2615             return 0;
2616         }
2617     }
2618     if (!OBJ_AT(u.ux, u.uy)) {
2619         register struct rm *lev = &levl[u.ux][u.uy];
2620 
2621         if (IS_THRONE(lev->typ))
2622             pline("It must weigh%s a ton!", lev->looted ? " almost" : "");
2623         else if (IS_SINK(lev->typ))
2624             pline_The("plumbing connects it to the floor.");
2625         else if (IS_GRAVE(lev->typ))
2626             You("don't need a gravestone.  Yet.");
2627         else if (IS_FOUNTAIN(lev->typ))
2628             You("could drink the %s...", hliquid("water"));
2629         else if (IS_DOOR(lev->typ) && (lev->doormask & D_ISOPEN))
2630             pline("It won't come off the hinges.");
2631         else if (IS_ALTAR(lev->typ))
2632             pline("Moving the altar would be a very bad idea.");
2633         else if (lev->typ == STAIRS)
2634             pline_The("stairs are solidly fixed to the %s.",
2635                       surface(u.ux, u.uy));
2636         else
2637             There("is nothing here to pick up.");
2638         return 0;
2639     }
2640     if (!can_reach_floor(TRUE)) {
2641         struct trap *traphere = t_at(u.ux, u.uy);
2642         if (traphere
2643             && (uteetering_at_seen_pit(traphere) || uescaped_shaft(traphere)))
2644             You("cannot reach the bottom of the %s.",
2645                 is_pit(traphere->ttyp) ? "pit" : "abyss");
2646         else if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
2647             rider_cant_reach();
2648         else if (Blind && !can_reach_floor(TRUE))
2649             You("cannot reach anything here.");
2650         else
2651             You("cannot reach the %s.", surface(u.ux, u.uy));
2652         return 0;
2653     }
2654     return -1; /* can do normal pickup */
2655 }
2656 
2657 /* the ',' command */
2658 int
dopickup(VOID_ARGS)2659 dopickup(VOID_ARGS)
2660 {
2661     int count, tmpcount, ret;
2662 
2663     /* awful kludge to work around parse()'s pre-decrement */
2664     count = (multi || (save_cm && *save_cm == cmd_from_func(dopickup)))
2665               ? multi + 1 : 0;
2666     multi = 0; /* always reset */
2667 
2668     if ((ret = pickup_checks()) >= 0) {
2669         return ret;
2670     } else if (ret == -2) {
2671         tmpcount = -count;
2672         return loot_mon(u.ustuck, &tmpcount, (boolean *) 0);
2673     } /* else ret == -1 */
2674 
2675     return pickup(-count);
2676 }
2677 
2678 /* stop running if we see something interesting */
2679 /* turn around a corner if that is the only way we can proceed */
2680 /* do not turn left or right twice */
2681 void
lookaround()2682 lookaround()
2683 {
2684     register int x, y;
2685     int i, x0 = 0, y0 = 0, m0 = 1, i0 = 9;
2686     int corrct = 0, noturn = 0;
2687     struct monst *mtmp;
2688     struct trap *trap;
2689 
2690     /* Grid bugs stop if trying to move diagonal, even if blind.  Maybe */
2691     /* they polymorphed while in the middle of a long move. */
2692     if (NODIAG(u.umonnum) && u.dx && u.dy) {
2693         You("cannot move diagonally.");
2694         nomul(0);
2695         return;
2696     }
2697 
2698     if (Blind || context.run == 0)
2699         return;
2700     for (x = u.ux - 1; x <= u.ux + 1; x++)
2701         for (y = u.uy - 1; y <= u.uy + 1; y++) {
2702             if (!isok(x, y) || (x == u.ux && y == u.uy))
2703                 continue;
2704             if (NODIAG(u.umonnum) && x != u.ux && y != u.uy)
2705                 continue;
2706 
2707             if ((mtmp = m_at(x, y)) != 0
2708                 && M_AP_TYPE(mtmp) != M_AP_FURNITURE
2709                 && M_AP_TYPE(mtmp) != M_AP_OBJECT
2710                 && (!mtmp->minvis || See_invisible) && !mtmp->mundetected) {
2711                 if ((context.run != 1 && !mtmp->mtame)
2712                     || (x == u.ux + u.dx && y == u.uy + u.dy
2713                         && !context.travel)) {
2714                     if (iflags.mention_walls)
2715                         pline("%s blocks your path.", upstart(a_monnam(mtmp)));
2716                     goto stop;
2717                 }
2718             }
2719 
2720             if (levl[x][y].typ == STONE)
2721                 continue;
2722             if (x == u.ux - u.dx && y == u.uy - u.dy)
2723                 continue;
2724 
2725             if (IS_ROCK(levl[x][y].typ) || levl[x][y].typ == ROOM
2726                 || IS_AIR(levl[x][y].typ)) {
2727                 continue;
2728             } else if (closed_door(x, y) || (mtmp && is_door_mappear(mtmp))) {
2729                 if (x != u.ux && y != u.uy)
2730                     continue;
2731                 if (context.run != 1) {
2732                     if (iflags.mention_walls)
2733                         You("stop in front of the door.");
2734                     goto stop;
2735                 }
2736                 goto bcorr;
2737             } else if (levl[x][y].typ == CORR) {
2738  bcorr:
2739                 if (levl[u.ux][u.uy].typ != ROOM) {
2740                     if (context.run == 1 || context.run == 3
2741                         || context.run == 8) {
2742                         i = dist2(x, y, u.ux + u.dx, u.uy + u.dy);
2743                         if (i > 2)
2744                             continue;
2745                         if (corrct == 1 && dist2(x, y, x0, y0) != 1)
2746                             noturn = 1;
2747                         if (i < i0) {
2748                             i0 = i;
2749                             x0 = x;
2750                             y0 = y;
2751                             m0 = mtmp ? 1 : 0;
2752                         }
2753                     }
2754                     corrct++;
2755                 }
2756                 continue;
2757             } else if ((trap = t_at(x, y)) && trap->tseen) {
2758                 if (context.run == 1)
2759                     goto bcorr; /* if you must */
2760                 if (x == u.ux + u.dx && y == u.uy + u.dy) {
2761                     if (iflags.mention_walls) {
2762                         int tt = what_trap(trap->ttyp, rn2_on_display_rng);
2763 
2764                         You("stop in front of %s.",
2765                             an(defsyms[trap_to_defsym(tt)].explanation));
2766                     }
2767                     goto stop;
2768                 }
2769                 continue;
2770             } else if (is_pool_or_lava(x, y)) {
2771                 /* water and lava only stop you if directly in front, and stop
2772                  * you even if you are running
2773                  */
2774                 if (!Levitation && !Flying && !is_clinger(youmonst.data)
2775                     && x == u.ux + u.dx && y == u.uy + u.dy) {
2776                     /* No Wwalking check; otherwise they'd be able
2777                      * to test boots by trying to SHIFT-direction
2778                      * into a pool and seeing if the game allowed it
2779                      */
2780                     if (iflags.mention_walls)
2781                         You("stop at the edge of the %s.",
2782                             hliquid(is_pool(x,y) ? "water" : "lava"));
2783                     goto stop;
2784                 }
2785                 continue;
2786             } else { /* e.g. objects or trap or stairs */
2787                 if (context.run == 1)
2788                     goto bcorr;
2789                 if (context.run == 8)
2790                     continue;
2791                 if (mtmp)
2792                     continue; /* d */
2793                 if (((x == u.ux - u.dx) && (y != u.uy + u.dy))
2794                     || ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
2795                     continue;
2796             }
2797  stop:
2798             nomul(0);
2799             return;
2800         } /* end for loops */
2801 
2802     if (corrct > 1 && context.run == 2) {
2803         if (iflags.mention_walls)
2804             pline_The("corridor widens here.");
2805         goto stop;
2806     }
2807     if ((context.run == 1 || context.run == 3 || context.run == 8) && !noturn
2808         && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) {
2809         /* make sure that we do not turn too far */
2810         if (i0 == 2) {
2811             if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
2812                 i = 2; /* straight turn right */
2813             else
2814                 i = -2; /* straight turn left */
2815         } else if (u.dx && u.dy) {
2816             if ((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy))
2817                 i = -1; /* half turn left */
2818             else
2819                 i = 1; /* half turn right */
2820         } else {
2821             if ((x0 - u.ux == y0 - u.uy && !u.dy)
2822                 || (x0 - u.ux != y0 - u.uy && u.dy))
2823                 i = 1; /* half turn right */
2824             else
2825                 i = -1; /* half turn left */
2826         }
2827 
2828         i += u.last_str_turn;
2829         if (i <= 2 && i >= -2) {
2830             u.last_str_turn = i;
2831             u.dx = x0 - u.ux;
2832             u.dy = y0 - u.uy;
2833         }
2834     }
2835 }
2836 
2837 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2838 STATIC_OVL boolean
doorless_door(x,y)2839 doorless_door(x, y)
2840 int x, y;
2841 {
2842     struct rm *lev_p = &levl[x][y];
2843 
2844     if (!IS_DOOR(lev_p->typ))
2845         return FALSE;
2846     /* all rogue level doors are doorless but disallow diagonal access, so
2847        we treat them as if their non-existent doors were actually present */
2848     if (Is_rogue_level(&u.uz))
2849         return FALSE;
2850     return !(lev_p->doormask & ~(D_NODOOR | D_BROKEN));
2851 }
2852 
2853 /* used by drown() to check whether hero can crawl from water to <x,y> */
2854 boolean
crawl_destination(x,y)2855 crawl_destination(x, y)
2856 int x, y;
2857 {
2858     /* is location ok in general? */
2859     if (!goodpos(x, y, &youmonst, 0))
2860         return FALSE;
2861 
2862     /* orthogonal movement is unrestricted when destination is ok */
2863     if (x == u.ux || y == u.uy)
2864         return TRUE;
2865 
2866     /* diagonal movement has some restrictions */
2867     if (NODIAG(u.umonnum))
2868         return FALSE; /* poly'd into a grid bug... */
2869     if (Passes_walls)
2870         return TRUE; /* or a xorn... */
2871     /* pool could be next to a door, conceivably even inside a shop */
2872     if (IS_DOOR(levl[x][y].typ) && (!doorless_door(x, y) || block_door(x, y)))
2873         return FALSE;
2874     /* finally, are we trying to squeeze through a too-narrow gap? */
2875     return !(bad_rock(youmonst.data, u.ux, y)
2876              && bad_rock(youmonst.data, x, u.uy));
2877 }
2878 
2879 /* something like lookaround, but we are not running */
2880 /* react only to monsters that might hit us */
2881 int
monster_nearby()2882 monster_nearby()
2883 {
2884     register int x, y;
2885     register struct monst *mtmp;
2886 
2887     /* Also see the similar check in dochugw() in monmove.c */
2888     for (x = u.ux - 1; x <= u.ux + 1; x++)
2889         for (y = u.uy - 1; y <= u.uy + 1; y++) {
2890             if (!isok(x, y) || (x == u.ux && y == u.uy))
2891                 continue;
2892             if ((mtmp = m_at(x, y)) && M_AP_TYPE(mtmp) != M_AP_FURNITURE
2893                 && M_AP_TYPE(mtmp) != M_AP_OBJECT
2894                 && (!mtmp->mpeaceful || Hallucination)
2895                 && (!is_hider(mtmp->data) || !mtmp->mundetected)
2896                 && !noattacks(mtmp->data) && mtmp->mcanmove
2897                 && !mtmp->msleeping  /* aplvax!jcn */
2898                 && !onscary(u.ux, u.uy, mtmp) && canspotmon(mtmp))
2899                 return 1;
2900         }
2901     return 0;
2902 }
2903 
2904 void
nomul(nval)2905 nomul(nval)
2906 register int nval;
2907 {
2908     if (multi < nval)
2909         return;              /* This is a bug fix by ab@unido */
2910     u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */
2911     u.usleep = 0;
2912     multi = nval;
2913     if (nval == 0)
2914         multi_reason = NULL;
2915     context.travel = context.travel1 = context.mv = context.run = 0;
2916 }
2917 
2918 /* called when a non-movement, multi-turn action has completed */
2919 void
unmul(msg_override)2920 unmul(msg_override)
2921 const char *msg_override;
2922 {
2923     multi = 0; /* caller will usually have done this already */
2924     if (msg_override)
2925         nomovemsg = msg_override;
2926     else if (!nomovemsg)
2927         nomovemsg = You_can_move_again;
2928     if (*nomovemsg) {
2929         pline("%s", nomovemsg);
2930         /* follow "you survived that attempt on your life" with a message
2931            about current form if it's not the default; primarily for
2932            life-saving while turning into green slime but is also a reminder
2933            if life-saved while poly'd and Unchanging (explore or wizard mode
2934            declining to die since can't be both Unchanging and Lifesaved) */
2935         if (Upolyd && !strncmpi(nomovemsg, "You survived that ", 18))
2936             You("are %s.", an(mons[u.umonnum].mname)); /* (ignore Hallu) */
2937     }
2938     nomovemsg = 0;
2939     u.usleep = 0;
2940     multi_reason = NULL;
2941     if (afternmv) {
2942         int NDECL((*f)) = afternmv;
2943 
2944         /* clear afternmv before calling it (to override the
2945            encumbrance hack for levitation--see weight_cap()) */
2946         afternmv = (int NDECL((*))) 0;
2947         (void) (*f)();
2948         /* for finishing Armor/Boots/&c_on() */
2949         update_inventory();
2950     }
2951 }
2952 
2953 STATIC_OVL void
maybe_wail()2954 maybe_wail()
2955 {
2956     static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
2957                               SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES,
2958                               TELEPORT_CONTROL, STEALTH, FAST, INVIS };
2959 
2960     if (moves <= wailmsg + 50)
2961         return;
2962 
2963     wailmsg = moves;
2964     if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) {
2965         const char *who;
2966         int i, powercnt;
2967 
2968         who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? urole.name.m
2969                                                            : "Elf";
2970         if (u.uhp == 1) {
2971             pline("%s is about to die.", who);
2972         } else {
2973             for (i = 0, powercnt = 0; i < SIZE(powers); ++i)
2974                 if (u.uprops[powers[i]].intrinsic & INTRINSIC)
2975                     ++powercnt;
2976 
2977             pline((powercnt >= 4) ? "%s, all your powers will be lost..."
2978                                   : "%s, your life force is running out.",
2979                   who);
2980         }
2981     } else {
2982         You_hear(u.uhp == 1 ? "the wailing of the Banshee..."
2983                             : "the howling of the CwnAnnwn...");
2984     }
2985 }
2986 
2987 void
losehp(n,knam,k_format)2988 losehp(n, knam, k_format)
2989 register int n;
2990 register const char *knam;
2991 boolean k_format;
2992 {
2993     if (Upolyd) {
2994         u.mh -= n;
2995         if (u.mhmax < u.mh)
2996             u.mhmax = u.mh;
2997         context.botl = 1;
2998         if (u.mh < 1)
2999             rehumanize();
3000         else if (n > 0 && u.mh * 10 < u.mhmax && Unchanging)
3001             maybe_wail();
3002         return;
3003     }
3004 
3005     u.uhp -= n;
3006     if (u.uhp > u.uhpmax)
3007         u.uhpmax = u.uhp; /* perhaps n was negative */
3008     else
3009         context.travel = context.travel1 = context.mv = context.run = 0;
3010     context.botl = 1;
3011     if (u.uhp < 1) {
3012         killer.format = k_format;
3013         if (killer.name != knam) /* the thing that killed you */
3014             Strcpy(killer.name, knam ? knam : "");
3015         You("die...");
3016         done(DIED);
3017     } else if (n > 0 && u.uhp * 10 < u.uhpmax) {
3018         maybe_wail();
3019     }
3020 }
3021 
3022 int
weight_cap()3023 weight_cap()
3024 {
3025     long carrcap, save_ELev = ELevitation, save_BLev = BLevitation;
3026 
3027     /* boots take multiple turns to wear but any properties they
3028        confer are enabled at the start rather than the end; that
3029        causes message sequencing issues for boots of levitation
3030        so defer their encumbrance benefit until they're fully worn */
3031     if (afternmv == Boots_on && (ELevitation & W_ARMF) != 0L) {
3032         ELevitation &= ~W_ARMF;
3033         float_vs_flight(); /* in case Levitation is blocking Flying */
3034     }
3035     /* levitation is blocked by being trapped in the floor, but it still
3036        functions enough in that situation to enhance carrying capacity */
3037     BLevitation &= ~I_SPECIAL;
3038 
3039     carrcap = 25 * (ACURRSTR + ACURR(A_CON)) + 50;
3040     if (Upolyd) {
3041         /* consistent with can_carry() in mon.c */
3042         if (youmonst.data->mlet == S_NYMPH)
3043             carrcap = MAX_CARR_CAP;
3044         else if (!youmonst.data->cwt)
3045             carrcap = (carrcap * (long) youmonst.data->msize) / MZ_HUMAN;
3046         else if (!strongmonst(youmonst.data)
3047                  || (strongmonst(youmonst.data)
3048                      && (youmonst.data->cwt > WT_HUMAN)))
3049             carrcap = (carrcap * (long) youmonst.data->cwt / WT_HUMAN);
3050     }
3051 
3052     if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */
3053         || (u.usteed && strongmonst(u.usteed->data))) {
3054         carrcap = MAX_CARR_CAP;
3055     } else {
3056         if (carrcap > MAX_CARR_CAP)
3057             carrcap = MAX_CARR_CAP;
3058         if (!Flying) {
3059             if (EWounded_legs & LEFT_SIDE)
3060                 carrcap -= 100;
3061             if (EWounded_legs & RIGHT_SIDE)
3062                 carrcap -= 100;
3063         }
3064         if (carrcap < 0)
3065             carrcap = 0;
3066     }
3067 
3068     if (ELevitation != save_ELev || BLevitation != save_BLev) {
3069         ELevitation = save_ELev;
3070         BLevitation = save_BLev;
3071         float_vs_flight();
3072     }
3073 
3074     return (int) carrcap;
3075 }
3076 
3077 static int wc; /* current weight_cap(); valid after call to inv_weight() */
3078 
3079 /* returns how far beyond the normal capacity the player is currently. */
3080 /* inv_weight() is negative if the player is below normal capacity. */
3081 int
inv_weight()3082 inv_weight()
3083 {
3084     register struct obj *otmp = invent;
3085     register int wt = 0;
3086 
3087     while (otmp) {
3088         if (otmp->oclass == COIN_CLASS)
3089             wt += (int) (((long) otmp->quan + 50L) / 100L);
3090         else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
3091             wt += otmp->owt;
3092         otmp = otmp->nobj;
3093     }
3094     wc = weight_cap();
3095     return (wt - wc);
3096 }
3097 
3098 /*
3099  * Returns 0 if below normal capacity, or the number of "capacity units"
3100  * over the normal capacity the player is loaded.  Max is 5.
3101  */
3102 int
calc_capacity(xtra_wt)3103 calc_capacity(xtra_wt)
3104 int xtra_wt;
3105 {
3106     int cap, wt = inv_weight() + xtra_wt;
3107 
3108     if (wt <= 0)
3109         return UNENCUMBERED;
3110     if (wc <= 1)
3111         return OVERLOADED;
3112     cap = (wt * 2 / wc) + 1;
3113     return min(cap, OVERLOADED);
3114 }
3115 
3116 int
near_capacity()3117 near_capacity()
3118 {
3119     return calc_capacity(0);
3120 }
3121 
3122 int
max_capacity()3123 max_capacity()
3124 {
3125     int wt = inv_weight();
3126 
3127     return (wt - (2 * wc));
3128 }
3129 
3130 boolean
check_capacity(str)3131 check_capacity(str)
3132 const char *str;
3133 {
3134     if (near_capacity() >= EXT_ENCUMBER) {
3135         if (str)
3136             pline1(str);
3137         else
3138             You_cant("do that while carrying so much stuff.");
3139         return 1;
3140     }
3141     return 0;
3142 }
3143 
3144 int
inv_cnt(incl_gold)3145 inv_cnt(incl_gold)
3146 boolean incl_gold;
3147 {
3148     register struct obj *otmp = invent;
3149     register int ct = 0;
3150 
3151     while (otmp) {
3152         if (incl_gold || otmp->invlet != GOLD_SYM)
3153             ct++;
3154         otmp = otmp->nobj;
3155     }
3156     return ct;
3157 }
3158 
3159 /* Counts the money in an object chain. */
3160 /* Intended use is for your or some monster's inventory, */
3161 /* now that u.gold/m.gold is gone.*/
3162 /* Counting money in a container might be possible too. */
3163 long
money_cnt(otmp)3164 money_cnt(otmp)
3165 struct obj *otmp;
3166 {
3167     while (otmp) {
3168         /* Must change when silver & copper is implemented: */
3169         if (otmp->oclass == COIN_CLASS)
3170             return otmp->quan;
3171         otmp = otmp->nobj;
3172     }
3173     return 0L;
3174 }
3175 
3176 /*hack.c*/
3177