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