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