Move generic usernames to sysconf
[aNetHack.git] / src / hack.c
blobe62b8d333db099bde2983b34fe2567e6c366118f
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, (BOOLEAN_P));
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 static anything tmp_anything;
24 anything *
25 uint_to_any(ui)
26 unsigned ui;
28 tmp_anything = zeroany;
29 tmp_anything.a_uint = ui;
30 return &tmp_anything;
33 anything *
34 long_to_any(lng)
35 long lng;
37 tmp_anything = zeroany;
38 tmp_anything.a_long = lng;
39 return &tmp_anything;
42 anything *
43 monst_to_any(mtmp)
44 struct monst *mtmp;
46 tmp_anything = zeroany;
47 tmp_anything.a_monst = mtmp;
48 return &tmp_anything;
51 anything *
52 obj_to_any(obj)
53 struct obj *obj;
55 tmp_anything = zeroany;
56 tmp_anything.a_obj = obj;
57 return &tmp_anything;
60 boolean
61 revive_nasty(x, y, msg)
62 int x, y;
63 const char *msg;
65 register struct obj *otmp, *otmp2;
66 struct monst *mtmp;
67 coord cc;
68 boolean revived = FALSE;
70 for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
71 otmp2 = otmp->nexthere;
72 if (otmp->otyp == CORPSE
73 && (is_rider(&mons[otmp->corpsenm])
74 || otmp->corpsenm == PM_WIZARD_OF_YENDOR)) {
75 /* move any living monster already at that location */
76 if ((mtmp = m_at(x, y)) && enexto(&cc, x, y, mtmp->data))
77 rloc_to(mtmp, cc.x, cc.y);
78 if (msg)
79 Norep("%s", msg);
80 revived = revive_corpse(otmp);
84 /* this location might not be safe, if not, move revived monster */
85 if (revived) {
86 mtmp = m_at(x, y);
87 if (mtmp && !goodpos(x, y, mtmp, 0)
88 && enexto(&cc, x, y, mtmp->data)) {
89 rloc_to(mtmp, cc.x, cc.y);
91 /* else impossible? */
94 return revived;
97 STATIC_OVL int
98 moverock()
100 register xchar rx, ry, sx, sy;
101 register struct obj *otmp;
102 register struct trap *ttmp;
103 register struct monst *mtmp;
105 sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */
106 while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
107 /* make sure that this boulder is visible as the top object */
108 if (otmp != level.objects[sx][sy])
109 movobj(otmp, sx, sy);
111 rx = u.ux + 2 * u.dx; /* boulder destination position */
112 ry = u.uy + 2 * u.dy;
113 nomul(0);
114 if (Levitation || Is_airlevel(&u.uz)) {
115 if (Blind)
116 feel_location(sx, sy);
117 You("don't have enough leverage to push %s.", the(xname(otmp)));
118 /* Give them a chance to climb over it? */
119 return -1;
121 if (verysmall(youmonst.data) && !u.usteed) {
122 if (Blind)
123 feel_location(sx, sy);
124 pline("You're too small to push that %s.", xname(otmp));
125 goto cannot_push;
127 if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ)
128 && levl[rx][ry].typ != IRONBARS
129 && (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy)
130 || doorless_door(rx, ry)) && !sobj_at(BOULDER, rx, ry)) {
131 ttmp = t_at(rx, ry);
132 mtmp = m_at(rx, ry);
134 /* KMH -- Sokoban doesn't let you push boulders diagonally */
135 if (Sokoban && u.dx && u.dy) {
136 if (Blind)
137 feel_location(sx, sy);
138 pline("%s won't roll diagonally on this %s.",
139 The(xname(otmp)), surface(sx, sy));
140 goto cannot_push;
143 if (revive_nasty(rx, ry, "You sense movement on the other side."))
144 return -1;
146 if (mtmp && !noncorporeal(mtmp->data)
147 && (!mtmp->mtrapped
148 || !(ttmp && ((ttmp->ttyp == PIT)
149 || (ttmp->ttyp == SPIKED_PIT))))) {
150 if (Blind)
151 feel_location(sx, sy);
152 if (canspotmon(mtmp)) {
153 pline("There's %s on the other side.", a_monnam(mtmp));
154 } else {
155 You_hear("a monster behind %s.", the(xname(otmp)));
156 map_invisible(rx, ry);
158 if (flags.verbose)
159 pline("Perhaps that's why %s cannot move it.",
160 u.usteed ? y_monnam(u.usteed) : "you");
161 goto cannot_push;
164 if (ttmp) {
165 /* if a trap operates on the boulder, don't attempt
166 to move any others at this location; return -1
167 if another boulder is in hero's way, or 0 if he
168 should advance to the vacated boulder position */
169 switch (ttmp->ttyp) {
170 case LANDMINE:
171 if (rn2(10)) {
172 obj_extract_self(otmp);
173 place_object(otmp, rx, ry);
174 newsym(sx, sy);
175 pline("KAABLAMM!!! %s %s land mine.",
176 Tobjnam(otmp, "trigger"),
177 ttmp->madeby_u ? "your" : "a");
178 blow_up_landmine(ttmp);
179 /* if the boulder remains, it should fill the pit */
180 fill_pit(u.ux, u.uy);
181 if (cansee(rx, ry))
182 newsym(rx, ry);
183 return sobj_at(BOULDER, sx, sy) ? -1 : 0;
185 break;
186 case SPIKED_PIT:
187 case PIT:
188 obj_extract_self(otmp);
189 /* vision kludge to get messages right;
190 the pit will temporarily be seen even
191 if this is one among multiple boulders */
192 if (!Blind)
193 viz_array[ry][rx] |= IN_SIGHT;
194 if (!flooreffects(otmp, rx, ry, "fall")) {
195 place_object(otmp, rx, ry);
197 if (mtmp && !Blind)
198 newsym(rx, ry);
199 return sobj_at(BOULDER, sx, sy) ? -1 : 0;
200 case HOLE:
201 case TRAPDOOR:
202 if (Blind)
203 pline("Kerplunk! You no longer feel %s.",
204 the(xname(otmp)));
205 else
206 pline("%s%s and %s a %s in the %s!",
207 Tobjnam(otmp, (ttmp->ttyp == TRAPDOOR)
208 ? "trigger"
209 : "fall"),
210 (ttmp->ttyp == TRAPDOOR) ? "" : " into",
211 otense(otmp, "plug"),
212 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole",
213 surface(rx, ry));
214 deltrap(ttmp);
215 delobj(otmp);
216 bury_objs(rx, ry);
217 levl[rx][ry].wall_info &= ~W_NONDIGGABLE;
218 levl[rx][ry].candig = 1;
219 if (cansee(rx, ry))
220 newsym(rx, ry);
221 return sobj_at(BOULDER, sx, sy) ? -1 : 0;
222 case LEVEL_TELEP:
223 case TELEP_TRAP: {
224 int newlev = 0; /* lint suppression */
225 d_level dest;
227 if (ttmp->ttyp == LEVEL_TELEP) {
228 newlev = random_teleport_level();
229 if (newlev == depth(&u.uz) || In_endgame(&u.uz))
230 /* trap didn't work; skip "disappears" message */
231 goto dopush;
233 if (u.usteed)
234 pline("%s pushes %s and suddenly it disappears!",
235 upstart(y_monnam(u.usteed)), the(xname(otmp)));
236 else
237 You("push %s and suddenly it disappears!",
238 the(xname(otmp)));
239 if (ttmp->ttyp == TELEP_TRAP) {
240 (void) rloco(otmp);
241 } else {
242 obj_extract_self(otmp);
243 add_to_migration(otmp);
244 get_level(&dest, newlev);
245 otmp->ox = dest.dnum;
246 otmp->oy = dest.dlevel;
247 otmp->owornmask = (long) MIGR_RANDOM;
249 seetrap(ttmp);
250 return sobj_at(BOULDER, sx, sy) ? -1 : 0;
252 default:
253 break; /* boulder not affected by this trap */
257 if (closed_door(rx, ry))
258 goto nopushmsg;
259 if (boulder_hits_pool(otmp, rx, ry, TRUE))
260 continue;
262 * Re-link at top of fobj chain so that pile order is preserved
263 * when level is restored.
265 if (otmp != fobj) {
266 remove_object(otmp);
267 place_object(otmp, otmp->ox, otmp->oy);
271 #ifdef LINT /* static long lastmovetime; */
272 long lastmovetime;
273 lastmovetime = 0;
274 #else
275 /* note: reset to zero after save/restore cycle */
276 static NEARDATA long lastmovetime;
277 #endif
278 dopush:
279 if (!u.usteed) {
280 if (moves > lastmovetime + 2 || moves < lastmovetime)
281 pline("With %s effort you move %s.",
282 throws_rocks(youmonst.data) ? "little"
283 : "great",
284 the(xname(otmp)));
285 exercise(A_STR, TRUE);
286 } else
287 pline("%s moves %s.", upstart(y_monnam(u.usteed)),
288 the(xname(otmp)));
289 lastmovetime = moves;
292 /* Move the boulder *after* the message. */
293 if (glyph_is_invisible(levl[rx][ry].glyph))
294 unmap_object(rx, ry);
295 movobj(otmp, rx, ry); /* does newsym(rx,ry) */
296 if (Blind) {
297 feel_location(rx, ry);
298 feel_location(sx, sy);
299 } else {
300 newsym(sx, sy);
302 } else {
303 nopushmsg:
304 if (u.usteed)
305 pline("%s tries to move %s, but cannot.",
306 upstart(y_monnam(u.usteed)), the(xname(otmp)));
307 else
308 You("try to move %s, but in vain.", the(xname(otmp)));
309 if (Blind)
310 feel_location(sx, sy);
311 cannot_push:
312 if (throws_rocks(youmonst.data)) {
313 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
314 You("aren't skilled enough to %s %s from %s.",
315 (flags.pickup && !Sokoban) ? "pick up" : "push aside",
316 the(xname(otmp)), y_monnam(u.usteed));
317 } else {
318 pline("However, you can easily %s.",
319 (flags.pickup && !Sokoban) ? "pick it up"
320 : "push it aside");
321 sokoban_guilt();
322 break;
324 break;
327 if (!u.usteed
328 && (((!invent || inv_weight() <= -850)
329 && (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ)
330 && IS_ROCK(levl[sx][u.uy].typ))))
331 || verysmall(youmonst.data))) {
332 pline(
333 "However, you can squeeze yourself into a small opening.");
334 sokoban_guilt();
335 break;
336 } else
337 return -1;
340 return 0;
344 * still_chewing()
346 * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE
347 * when done.
349 STATIC_OVL int
350 still_chewing(x, y)
351 xchar x, y;
353 struct rm *lev = &levl[x][y];
354 struct obj *boulder = sobj_at(BOULDER, x, y);
355 const char *digtxt = (char *) 0, *dmgtxt = (char *) 0;
357 if (context.digging.down) /* not continuing previous dig (w/ pick-axe) */
358 (void) memset((genericptr_t) &context.digging, 0,
359 sizeof (struct dig_info));
361 if (!boulder && IS_ROCK(lev->typ) && !may_dig(x, y)) {
362 You("hurt your teeth on the %s.",
363 (lev->typ == IRONBARS)
364 ? "bars"
365 : IS_TREE(lev->typ)
366 ? "tree"
367 : "hard stone");
368 nomul(0);
369 return 1;
370 } else if (context.digging.pos.x != x || context.digging.pos.y != y
371 || !on_level(&context.digging.level, &u.uz)) {
372 context.digging.down = FALSE;
373 context.digging.chew = TRUE;
374 context.digging.warned = FALSE;
375 context.digging.pos.x = x;
376 context.digging.pos.y = y;
377 assign_level(&context.digging.level, &u.uz);
378 /* solid rock takes more work & time to dig through */
379 context.digging.effort =
380 (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc;
381 You("start chewing %s %s.",
382 (boulder || IS_TREE(lev->typ) || lev->typ == IRONBARS)
383 ? "on a"
384 : "a hole in the",
385 boulder
386 ? "boulder"
387 : IS_TREE(lev->typ)
388 ? "tree"
389 : IS_ROCK(lev->typ)
390 ? "rock"
391 : (lev->typ == IRONBARS)
392 ? "bar"
393 : "door");
394 watch_dig((struct monst *) 0, x, y, FALSE);
395 return 1;
396 } else if ((context.digging.effort += (30 + u.udaminc)) <= 100) {
397 if (flags.verbose)
398 You("%s chewing on the %s.",
399 context.digging.chew ? "continue" : "begin",
400 boulder
401 ? "boulder"
402 : IS_TREE(lev->typ)
403 ? "tree"
404 : IS_ROCK(lev->typ)
405 ? "rock"
406 : (lev->typ == IRONBARS)
407 ? "bars"
408 : "door");
409 context.digging.chew = TRUE;
410 watch_dig((struct monst *) 0, x, y, FALSE);
411 return 1;
414 /* Okay, you've chewed through something */
415 u.uconduct.food++;
416 u.uhunger += rnd(20);
418 if (boulder) {
419 delobj(boulder); /* boulder goes bye-bye */
420 You("eat the boulder."); /* yum */
423 * The location could still block because of
424 * 1. More than one boulder
425 * 2. Boulder stuck in a wall/stone/door.
427 * [perhaps use does_block() below (from vision.c)]
429 if (IS_ROCK(lev->typ) || closed_door(x, y)
430 || sobj_at(BOULDER, x, y)) {
431 block_point(x, y); /* delobj will unblock the point */
432 /* reset dig state */
433 (void) memset((genericptr_t) &context.digging, 0,
434 sizeof (struct dig_info));
435 return 1;
438 } else if (IS_WALL(lev->typ)) {
439 if (*in_rooms(x, y, SHOPBASE)) {
440 add_damage(x, y, 10L * ACURRSTR);
441 dmgtxt = "damage";
443 digtxt = "chew a hole in the wall.";
444 if (level.flags.is_maze_lev) {
445 lev->typ = ROOM;
446 } else if (level.flags.is_cavernous_lev && !in_town(x, y)) {
447 lev->typ = CORR;
448 } else {
449 lev->typ = DOOR;
450 lev->doormask = D_NODOOR;
452 } else if (IS_TREE(lev->typ)) {
453 digtxt = "chew through the tree.";
454 lev->typ = ROOM;
455 } else if (lev->typ == IRONBARS) {
456 digtxt = "eat through the bars.";
457 dissolve_bars(x, y);
458 } else if (lev->typ == SDOOR) {
459 if (lev->doormask & D_TRAPPED) {
460 lev->doormask = D_NODOOR;
461 b_trapped("secret door", 0);
462 } else {
463 digtxt = "chew through the secret door.";
464 lev->doormask = D_BROKEN;
466 lev->typ = DOOR;
468 } else if (IS_DOOR(lev->typ)) {
469 if (*in_rooms(x, y, SHOPBASE)) {
470 add_damage(x, y, 400L);
471 dmgtxt = "break";
473 if (lev->doormask & D_TRAPPED) {
474 lev->doormask = D_NODOOR;
475 b_trapped("door", 0);
476 } else {
477 digtxt = "chew through the door.";
478 lev->doormask = D_BROKEN;
481 } else { /* STONE or SCORR */
482 digtxt = "chew a passage through the rock.";
483 lev->typ = CORR;
486 unblock_point(x, y); /* vision */
487 newsym(x, y);
488 if (digtxt)
489 You1(digtxt); /* after newsym */
490 if (dmgtxt)
491 pay_for_damage(dmgtxt, FALSE);
492 (void) memset((genericptr_t) &context.digging, 0,
493 sizeof (struct dig_info));
494 return 0;
497 void
498 movobj(obj, ox, oy)
499 register struct obj *obj;
500 register xchar ox, oy;
502 /* optimize by leaving on the fobj chain? */
503 remove_object(obj);
504 newsym(obj->ox, obj->oy);
505 place_object(obj, ox, oy);
506 newsym(ox, oy);
509 static NEARDATA const char fell_on_sink[] = "fell onto a sink";
511 STATIC_OVL void
512 dosinkfall()
514 register struct obj *obj;
515 int dmg;
516 boolean lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS),
517 innate_lev = ((HLevitation & (FROMOUTSIDE | FROMFORM)) != 0L),
518 ufall = (!innate_lev && !(HFlying || EFlying)); /* BFlying */
520 if (!ufall) {
521 You(innate_lev ? "wobble unsteadily for a moment."
522 : "gain control of your flight.");
523 } else {
524 long save_ELev = ELevitation, save_HLev = HLevitation;
526 /* fake removal of levitation in advance so that final
527 disclosure will be right in case this turns out to
528 be fatal; fortunately the fact that rings and boots
529 are really still worn has no effect on bones data */
530 ELevitation = HLevitation = 0L;
531 You("crash to the floor!");
532 dmg = rn1(8, 25 - (int) ACURR(A_CON));
533 losehp(Maybe_Half_Phys(dmg), fell_on_sink, NO_KILLER_PREFIX);
534 exercise(A_DEX, FALSE);
535 selftouch("Falling, you");
536 for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
537 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) {
538 You("fell on %s.", doname(obj));
539 losehp(Maybe_Half_Phys(rnd(3)), fell_on_sink,
540 NO_KILLER_PREFIX);
541 exercise(A_CON, FALSE);
543 ELevitation = save_ELev;
544 HLevitation = save_HLev;
548 * Interrupt multi-turn putting on/taking off of armor (in which
549 * case we reached the sink due to being teleported while busy;
550 * in 3.4.3, Boots_on()/Boots_off() [called via (*aftermv)() when
551 * 'multi' reaches 0] triggered a crash if we were donning/doffing
552 * levitation boots [because the Boots_off() below causes 'uarmf'
553 * to be null by the time 'aftermv' gets called]).
555 * Interrupt donning/doffing if we fall onto the sink, or if the
556 * code below is going to remove levitation boots even when we
557 * haven't fallen (innate floating or flying becoming unblocked).
559 if (ufall || lev_boots) {
560 (void) stop_donning(lev_boots ? uarmf : (struct obj *) 0);
561 /* recalculate in case uarmf just got set to null */
562 lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS);
565 /* remove worn levitation items */
566 ELevitation &= ~W_ARTI;
567 HLevitation &= ~(I_SPECIAL | TIMEOUT);
568 HLevitation++;
569 if (uleft && uleft->otyp == RIN_LEVITATION) {
570 obj = uleft;
571 Ring_off(obj);
572 off_msg(obj);
574 if (uright && uright->otyp == RIN_LEVITATION) {
575 obj = uright;
576 Ring_off(obj);
577 off_msg(obj);
579 if (lev_boots) {
580 obj = uarmf;
581 (void) Boots_off();
582 off_msg(obj);
584 HLevitation--;
585 /* probably moot; we're either still levitating or went
586 through float_down(), but make sure BFlying is up to date */
587 float_vs_flight();
590 /* intended to be called only on ROCKs or TREEs */
591 boolean
592 may_dig(x, y)
593 register xchar x, y;
595 struct rm *lev = &levl[x][y];
597 return (boolean) !((IS_STWALL(lev->typ) || IS_TREE(lev->typ))
598 && (lev->wall_info & W_NONDIGGABLE));
601 boolean
602 may_passwall(x, y)
603 register xchar x, y;
605 return (boolean) !(IS_STWALL(levl[x][y].typ)
606 && (levl[x][y].wall_info & W_NONPASSWALL));
609 boolean
610 bad_rock(mdat, x, y)
611 struct permonst *mdat;
612 register xchar x, y;
614 return (boolean) ((Sokoban && sobj_at(BOULDER, x, y))
615 || (IS_ROCK(levl[x][y].typ)
616 && (!tunnels(mdat) || needspick(mdat)
617 || !may_dig(x, y))
618 && !(passes_walls(mdat) && may_passwall(x, y))));
621 /* caller has already decided that it's a tight diagonal; check whether a
622 monster--who might be the hero--can fit through, and if not then return
623 the reason why: 1: can't fit, 2: possessions won't fit, 3: sokoban
624 returns 0 if we can squeeze through */
626 cant_squeeze_thru(mon)
627 struct monst *mon;
629 int amt;
630 struct permonst *ptr = mon->data;
632 /* too big? */
633 if (bigmonst(ptr)
634 && !(amorphous(ptr) || is_whirly(ptr) || noncorporeal(ptr)
635 || slithy(ptr) || can_fog(mon)))
636 return 1;
638 /* lugging too much junk? */
639 amt = (mon == &youmonst) ? inv_weight() + weight_cap()
640 : curr_mon_load(mon);
641 if (amt > 600)
642 return 2;
644 /* Sokoban restriction applies to hero only */
645 if (mon == &youmonst && Sokoban)
646 return 3;
648 /* can squeeze through */
649 return 0;
652 boolean
653 invocation_pos(x, y)
654 xchar x, y;
656 return (boolean) (Invocation_lev(&u.uz)
657 && x == inv_pos.x && y == inv_pos.y);
660 /* return TRUE if (dx,dy) is an OK place to move
661 * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP
663 boolean
664 test_move(ux, uy, dx, dy, mode)
665 int ux, uy, dx, dy;
666 int mode;
668 int x = ux + dx;
669 int y = uy + dy;
670 register struct rm *tmpr = &levl[x][y];
671 register struct rm *ust;
673 context.door_opened = FALSE;
675 * Check for physical obstacles. First, the place we are going.
677 if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
678 if (Blind && mode == DO_MOVE)
679 feel_location(x, y);
680 if (Passes_walls && may_passwall(x, y)) {
681 ; /* do nothing */
682 } else if (tmpr->typ == IRONBARS) {
683 if ((dmgtype(youmonst.data, AD_RUST)
684 || dmgtype(youmonst.data, AD_CORR)) && mode == DO_MOVE
685 && still_chewing(x, y)) {
686 return FALSE;
688 if (!(Passes_walls || passes_bars(youmonst.data))) {
689 if (iflags.mention_walls)
690 You("cannot pass through the bars.");
691 return FALSE;
693 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
694 /* Eat the rock. */
695 if (mode == DO_MOVE && still_chewing(x, y))
696 return FALSE;
697 } else if (flags.autodig && !context.run && !context.nopick && uwep
698 && is_pick(uwep)) {
699 /* MRKR: Automatic digging when wielding the appropriate tool */
700 if (mode == DO_MOVE)
701 (void) use_pick_axe2(uwep);
702 return FALSE;
703 } else {
704 if (mode == DO_MOVE) {
705 if (Is_stronghold(&u.uz) && is_db_wall(x, y))
706 pline_The("drawbridge is up!");
707 /* sokoban restriction stays even after puzzle is solved */
708 else if (Passes_walls && !may_passwall(x, y)
709 && In_sokoban(&u.uz))
710 pline_The("Sokoban walls resist your ability.");
711 else if (iflags.mention_walls)
712 pline("It's a wall.");
714 return FALSE;
716 } else if (IS_DOOR(tmpr->typ)) {
717 if (closed_door(x, y)) {
718 if (Blind && mode == DO_MOVE)
719 feel_location(x, y);
720 if (Passes_walls) {
721 ; /* do nothing */
722 } else if (can_ooze(&youmonst)) {
723 if (mode == DO_MOVE)
724 You("ooze under the door.");
725 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
726 /* Eat the door. */
727 if (mode == DO_MOVE && still_chewing(x, y))
728 return FALSE;
729 } else {
730 if (mode == DO_MOVE) {
731 if (amorphous(youmonst.data))
732 You(
733 "try to ooze under the door, but can't squeeze your possessions through.");
734 if (flags.autoopen && !context.run && !Confusion
735 && !Stunned && !Fumbling) {
736 context.door_opened = context.move =
737 doopen_indir(x, y);
738 } else if (x == ux || y == uy) {
739 if (Blind || Stunned || ACURR(A_DEX) < 10
740 || Fumbling) {
741 if (u.usteed) {
742 You_cant("lead %s through that closed door.",
743 y_monnam(u.usteed));
744 } else {
745 pline("Ouch! You bump into a door.");
746 exercise(A_DEX, FALSE);
748 } else
749 pline("That door is closed.");
751 } else if (mode == TEST_TRAV || mode == TEST_TRAP)
752 goto testdiag;
753 return FALSE;
755 } else {
756 testdiag:
757 if (dx && dy && !Passes_walls
758 && (!doorless_door(x, y) || block_door(x, y))) {
759 /* Diagonal moves into a door are not allowed. */
760 if (Blind && mode == DO_MOVE)
761 feel_location(x, y);
762 return FALSE;
766 if (dx && dy && bad_rock(youmonst.data, ux, y)
767 && bad_rock(youmonst.data, x, uy)) {
768 /* Move at a diagonal. */
769 switch (cant_squeeze_thru(&youmonst)) {
770 case 3:
771 if (mode == DO_MOVE)
772 You("cannot pass that way.");
773 return FALSE;
774 case 2:
775 if (mode == DO_MOVE)
776 You("are carrying too much to get through.");
777 return FALSE;
778 case 1:
779 if (mode == DO_MOVE)
780 Your("body is too large to fit through.");
781 return FALSE;
782 default:
783 break; /* can squeeze through */
785 } else if (dx && dy && worm_cross(ux, uy, x, y)) {
786 /* consecutive long worm segments are at <ux,y> and <x,uy> */
787 if (mode == DO_MOVE)
788 pline("%s is in your way.", Monnam(m_at(ux, y)));
789 return FALSE;
791 /* Pick travel path that does not require crossing a trap.
792 * Avoid water and lava using the usual running rules.
793 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
794 if (context.run == 8 && (mode != DO_MOVE)
795 && (x != u.ux || y != u.uy)) {
796 struct trap *t = t_at(x, y);
798 if ((t && t->tseen)
799 || (!Levitation && !Flying && !is_clinger(youmonst.data)
800 && is_pool_or_lava(x, y) && levl[x][y].seenv))
801 return (mode == TEST_TRAP);
804 if (mode == TEST_TRAP)
805 return FALSE; /* do not move through traps */
807 ust = &levl[ux][uy];
809 /* Now see if other things block our way . . */
810 if (dx && dy && !Passes_walls && IS_DOOR(ust->typ)
811 && (!doorless_door(ux, uy) || block_entry(x, y))) {
812 /* Can't move at a diagonal out of a doorway with door. */
813 return FALSE;
816 if (sobj_at(BOULDER, x, y) && (Sokoban || !Passes_walls)) {
817 if (!(Blind || Hallucination) && (context.run >= 2)
818 && mode != TEST_TRAV)
819 return FALSE;
820 if (mode == DO_MOVE) {
821 /* tunneling monsters will chew before pushing */
822 if (tunnels(youmonst.data) && !needspick(youmonst.data)
823 && !Sokoban) {
824 if (still_chewing(x, y))
825 return FALSE;
826 } else if (moverock() < 0)
827 return FALSE;
828 } else if (mode == TEST_TRAV) {
829 struct obj *obj;
831 /* never travel through boulders in Sokoban */
832 if (Sokoban)
833 return FALSE;
835 /* don't pick two boulders in a row, unless there's a way thru */
836 if (sobj_at(BOULDER, ux, uy) && !Sokoban) {
837 if (!Passes_walls
838 && !(tunnels(youmonst.data) && !needspick(youmonst.data))
839 && !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK)
840 && !((obj = carrying(WAN_DIGGING))
841 && !objects[obj->otyp].oc_name_known))
842 return FALSE;
845 /* assume you'll be able to push it when you get there... */
848 /* OK, it is a legal place to move. */
849 return TRUE;
852 #ifdef DEBUG
853 static boolean trav_debug = FALSE;
855 /* in this case, toggle display of travel debug info */
856 int wiz_debug_cmd_traveldisplay()
858 trav_debug = !trav_debug;
859 return 0;
861 #endif /* DEBUG */
864 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
865 * A shortest path is returned. If guess is TRUE, consider various
866 * inaccessible locations as valid intermediate path points.
867 * Returns TRUE if a path was found.
869 STATIC_OVL boolean
870 findtravelpath(guess)
871 boolean guess;
873 /* if travel to adjacent, reachable location, use normal movement rules */
874 if (!guess && context.travel1 && distmin(u.ux, u.uy, u.tx, u.ty) == 1
875 && !(u.ux != u.tx && u.uy != u.ty && NODIAG(u.umonnum))) {
876 context.run = 0;
877 if (test_move(u.ux, u.uy, u.tx - u.ux, u.ty - u.uy, TEST_MOVE)) {
878 u.dx = u.tx - u.ux;
879 u.dy = u.ty - u.uy;
880 nomul(0);
881 iflags.travelcc.x = iflags.travelcc.y = -1;
882 return TRUE;
884 context.run = 8;
886 if (u.tx != u.ux || u.ty != u.uy) {
887 xchar travel[COLNO][ROWNO];
888 xchar travelstepx[2][COLNO * ROWNO];
889 xchar travelstepy[2][COLNO * ROWNO];
890 xchar tx, ty, ux, uy;
891 int n = 1; /* max offset in travelsteps */
892 int set = 0; /* two sets current and previous */
893 int radius = 1; /* search radius */
894 int i;
896 /* If guessing, first find an "obvious" goal location. The obvious
897 * goal is the position the player knows of, or might figure out
898 * (couldsee) that is closest to the target on a straight path.
900 if (guess) {
901 tx = u.ux;
902 ty = u.uy;
903 ux = u.tx;
904 uy = u.ty;
905 } else {
906 tx = u.tx;
907 ty = u.ty;
908 ux = u.ux;
909 uy = u.uy;
912 noguess:
913 (void) memset((genericptr_t) travel, 0, sizeof(travel));
914 travelstepx[0][0] = tx;
915 travelstepy[0][0] = ty;
917 while (n != 0) {
918 int nn = 0;
920 for (i = 0; i < n; i++) {
921 int dir;
922 int x = travelstepx[set][i];
923 int y = travelstepy[set][i];
924 static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
925 /* no diagonal movement for grid bugs */
926 int dirmax = NODIAG(u.umonnum) ? 4 : 8;
927 boolean alreadyrepeated = FALSE;
929 for (dir = 0; dir < dirmax; ++dir) {
930 int nx = x + xdir[ordered[dir]];
931 int ny = y + ydir[ordered[dir]];
934 * When guessing and trying to travel as close as possible
935 * to an unreachable target space, don't include spaces
936 * that would never be picked as a guessed target in the
937 * travel matrix describing hero-reachable spaces.
938 * This stops travel from getting confused and moving
939 * the hero back and forth in certain degenerate
940 * configurations of sight-blocking obstacles, e.g.
942 * T 1. Dig this out and carry enough to not be
943 * #### able to squeeze through diagonal gaps.
944 * #--.--- Stand at @ and target travel at space T.
945 * @.....
946 * |.....
948 * T 2. couldsee() marks spaces marked a and x
949 * #### as eligible guess spaces to move the hero
950 * a--.--- towards. Space a is closest to T, so it
951 * @xxxxx gets chosen. Travel system moves @ right
952 * |xxxxx to travel to space a.
954 * T 3. couldsee() marks spaces marked b, c and x
955 * #### as eligible guess spaces to move the hero
956 * a--c--- towards. Since findtravelpath() is called
957 * b@xxxx repeatedly during travel, it doesn't
958 * |xxxxx remember that it wanted to go to space a,
959 * so in comparing spaces b and c, b is
960 * chosen, since it seems like the closest
961 * eligible space to T. Travel system moves @
962 * left to go to space b.
964 * 4. Go to 2.
966 * By limiting the travel matrix here, space a in the
967 * example above is never included in it, preventing
968 * the cycle.
970 if (!isok(nx, ny) || (guess && !couldsee(nx, ny)))
971 continue;
972 if ((!Passes_walls && !can_ooze(&youmonst)
973 && closed_door(x, y)) || sobj_at(BOULDER, x, y)
974 || test_move(x, y, nx-x, ny-y, TEST_TRAP)) {
975 /* closed doors and boulders usually
976 * cause a delay, so prefer another path */
977 if (travel[x][y] > radius - 3) {
978 if (!alreadyrepeated) {
979 travelstepx[1 - set][nn] = x;
980 travelstepy[1 - set][nn] = y;
981 /* don't change travel matrix! */
982 nn++;
983 alreadyrepeated = TRUE;
985 continue;
988 if (test_move(x, y, nx - x, ny - y, TEST_TRAV)
989 && (levl[nx][ny].seenv
990 || (!Blind && couldsee(nx, ny)))) {
991 if (nx == ux && ny == uy) {
992 if (!guess) {
993 u.dx = x - ux;
994 u.dy = y - uy;
995 if (x == u.tx && y == u.ty) {
996 nomul(0);
997 /* reset run so domove run checks work */
998 context.run = 8;
999 iflags.travelcc.x = iflags.travelcc.y = -1;
1001 return TRUE;
1003 } else if (!travel[nx][ny]) {
1004 travelstepx[1 - set][nn] = nx;
1005 travelstepy[1 - set][nn] = ny;
1006 travel[nx][ny] = radius;
1007 nn++;
1013 #ifdef DEBUG
1014 if (trav_debug) {
1015 /* Use of warning glyph is arbitrary. It stands out. */
1016 tmp_at(DISP_ALL, warning_to_glyph(1));
1017 for (i = 0; i < nn; ++i) {
1018 tmp_at(travelstepx[1 - set][i], travelstepy[1 - set][i]);
1020 delay_output();
1021 if (flags.runmode == RUN_CRAWL) {
1022 delay_output();
1023 delay_output();
1025 tmp_at(DISP_END, 0);
1027 #endif /* DEBUG */
1029 n = nn;
1030 set = 1 - set;
1031 radius++;
1034 /* if guessing, find best location in travel matrix and go there */
1035 if (guess) {
1036 int px = tx, py = ty; /* pick location */
1037 int dist, nxtdist, d2, nd2;
1039 dist = distmin(ux, uy, tx, ty);
1040 d2 = dist2(ux, uy, tx, ty);
1041 for (tx = 1; tx < COLNO; ++tx)
1042 for (ty = 0; ty < ROWNO; ++ty)
1043 if (travel[tx][ty]) {
1044 nxtdist = distmin(ux, uy, tx, ty);
1045 if (nxtdist == dist && couldsee(tx, ty)) {
1046 nd2 = dist2(ux, uy, tx, ty);
1047 if (nd2 < d2) {
1048 /* prefer non-zigzag path */
1049 px = tx;
1050 py = ty;
1051 d2 = nd2;
1053 } else if (nxtdist < dist && couldsee(tx, ty)) {
1054 px = tx;
1055 py = ty;
1056 dist = nxtdist;
1057 d2 = dist2(ux, uy, tx, ty);
1061 if (px == u.ux && py == u.uy) {
1062 /* no guesses, just go in the general direction */
1063 u.dx = sgn(u.tx - u.ux);
1064 u.dy = sgn(u.ty - u.uy);
1065 if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE))
1066 return TRUE;
1067 goto found;
1069 #ifdef DEBUG
1070 if (trav_debug) {
1071 /* Use of warning glyph is arbitrary. It stands out. */
1072 tmp_at(DISP_ALL, warning_to_glyph(2));
1073 tmp_at(px, py);
1074 delay_output();
1075 if (flags.runmode == RUN_CRAWL) {
1076 delay_output();
1077 delay_output();
1078 delay_output();
1079 delay_output();
1081 tmp_at(DISP_END, 0);
1083 #endif /* DEBUG */
1084 tx = px;
1085 ty = py;
1086 ux = u.ux;
1087 uy = u.uy;
1088 set = 0;
1089 n = radius = 1;
1090 guess = FALSE;
1091 goto noguess;
1093 return FALSE;
1096 found:
1097 u.dx = 0;
1098 u.dy = 0;
1099 nomul(0);
1100 return FALSE;
1103 /* try to escape being stuck in a trapped state by walking out of it;
1104 return true iff moving should continue to intended destination
1105 (all failures and most successful escapes leave hero at original spot) */
1106 STATIC_OVL boolean
1107 trapmove(x, y, desttrap)
1108 int x, y; /* targetted destination, <u.ux+u.dx,u.uy+u.dy> */
1109 struct trap *desttrap; /* nonnull if another trap at <x,y> */
1111 boolean anchored;
1112 const char *predicament, *culprit;
1113 char *steedname = !u.usteed ? (char *) 0 : y_monnam(u.usteed);
1115 if (!u.utrap)
1116 return TRUE; /* sanity check */
1118 switch (u.utraptype) {
1119 case TT_BEARTRAP:
1120 if (flags.verbose) {
1121 predicament = "caught in a bear trap";
1122 if (u.usteed)
1123 Norep("%s is %s.", upstart(steedname), predicament);
1124 else
1125 Norep("You are %s.", predicament);
1127 /* [why does diagonal movement give quickest escape?] */
1128 if ((u.dx && u.dy) || !rn2(5))
1129 u.utrap--;
1130 break;
1131 case TT_PIT:
1132 if (desttrap && desttrap->tseen
1133 && (desttrap->ttyp == PIT || desttrap->ttyp == SPIKED_PIT))
1134 return TRUE; /* move into adjacent pit */
1135 /* try to escape; position stays same regardless of success */
1136 climb_pit();
1137 break;
1138 case TT_WEB:
1139 if (uwep && uwep->oartifact == ART_STING) {
1140 u.utrap = 0;
1141 pline("Sting cuts through the web!");
1142 break; /* escape trap but don't move */
1144 if (--u.utrap) {
1145 if (flags.verbose) {
1146 predicament = "stuck to the web";
1147 if (u.usteed)
1148 Norep("%s is %s.", upstart(steedname), predicament);
1149 else
1150 Norep("You are %s.", predicament);
1152 } else {
1153 if (u.usteed)
1154 pline("%s breaks out of the web.", upstart(steedname));
1155 else
1156 You("disentangle yourself.");
1158 break;
1159 case TT_LAVA:
1160 if (flags.verbose) {
1161 predicament = "stuck in the lava";
1162 if (u.usteed)
1163 Norep("%s is %s.", upstart(steedname), predicament);
1164 else
1165 Norep("You are %s.", predicament);
1167 if (!is_lava(x, y)) {
1168 u.utrap--;
1169 if ((u.utrap & 0xff) == 0) {
1170 u.utrap = 0;
1171 if (u.usteed)
1172 You("lead %s to the edge of the %s.", steedname,
1173 hliquid("lava"));
1174 else
1175 You("pull yourself to the edge of the %s.",
1176 hliquid("lava"));
1179 u.umoved = TRUE;
1180 break;
1181 case TT_INFLOOR:
1182 case TT_BURIEDBALL:
1183 anchored = (u.utraptype == TT_BURIEDBALL);
1184 if (anchored) {
1185 coord cc;
1187 cc.x = u.ux, cc.y = u.uy;
1188 /* can move normally within radius 1 of buried ball */
1189 if (buried_ball(&cc) && dist2(x, y, cc.x, cc.y) <= 2) {
1190 /* ugly hack: we need to issue some message here
1191 in case "you are chained to the buried ball"
1192 was the most recent message given, otherwise
1193 our next attempt to move out of tether range
1194 after this successful move would have its
1195 can't-do-that message suppressed by Norep */
1196 if (flags.verbose)
1197 Norep("You move within the chain's reach.");
1198 return TRUE;
1201 if (--u.utrap) {
1202 if (flags.verbose) {
1203 if (anchored) {
1204 predicament = "chained to the";
1205 culprit = "buried ball";
1206 } else {
1207 predicament = "stuck in the";
1208 culprit = surface(u.ux, u.uy);
1210 if (u.usteed) {
1211 if (anchored)
1212 Norep("You and %s are %s %s.", steedname, predicament,
1213 culprit);
1214 else
1215 Norep("%s is %s %s.", upstart(steedname), predicament,
1216 culprit);
1217 } else
1218 Norep("You are %s %s.", predicament, culprit);
1220 } else {
1221 if (u.usteed)
1222 pline("%s finally %s free.", upstart(steedname),
1223 !anchored ? "lurches" : "wrenches the ball");
1224 else
1225 You("finally %s free.",
1226 !anchored ? "wriggle" : "wrench the ball");
1227 if (anchored)
1228 buried_ball_to_punishment();
1230 break;
1231 default:
1232 impossible("trapmove: stuck in unknown trap? (%d)",
1233 (int) u.utraptype);
1234 break;
1236 return FALSE;
1239 boolean
1240 u_rooted()
1242 if (!youmonst.data->mmove) {
1243 You("are rooted %s.",
1244 Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
1245 ? "in place"
1246 : "to the ground");
1247 nomul(0);
1248 return TRUE;
1250 return FALSE;
1253 void
1254 domove()
1256 register struct monst *mtmp;
1257 register struct rm *tmpr;
1258 register xchar x, y;
1259 struct trap *trap = NULL;
1260 int wtcap;
1261 boolean on_ice;
1262 xchar chainx = 0, chainy = 0,
1263 ballx = 0, bally = 0; /* ball&chain new positions */
1264 int bc_control = 0; /* control for ball&chain */
1265 boolean cause_delay = FALSE; /* dragging ball will skip a move */
1267 u_wipe_engr(rnd(5));
1269 if (context.travel) {
1270 if (!findtravelpath(FALSE))
1271 (void) findtravelpath(TRUE);
1272 context.travel1 = 0;
1275 if (((wtcap = near_capacity()) >= OVERLOADED
1276 || (wtcap > SLT_ENCUMBER
1277 && (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
1278 : (u.uhp < 10 && u.uhp != u.uhpmax))))
1279 && !Is_airlevel(&u.uz)) {
1280 if (wtcap < OVERLOADED) {
1281 You("don't have enough stamina to move.");
1282 exercise(A_CON, FALSE);
1283 } else
1284 You("collapse under your load.");
1285 nomul(0);
1286 return;
1288 if (u.uswallow) {
1289 u.dx = u.dy = 0;
1290 u.ux = x = u.ustuck->mx;
1291 u.uy = y = u.ustuck->my;
1292 mtmp = u.ustuck;
1293 } else {
1294 if (Is_airlevel(&u.uz) && rn2(4) && !Levitation && !Flying) {
1295 switch (rn2(3)) {
1296 case 0:
1297 You("tumble in place.");
1298 exercise(A_DEX, FALSE);
1299 break;
1300 case 1:
1301 You_cant("control your movements very well.");
1302 break;
1303 case 2:
1304 pline("It's hard to walk in thin air.");
1305 exercise(A_DEX, TRUE);
1306 break;
1308 return;
1311 /* check slippery ice */
1312 on_ice = !Levitation && is_ice(u.ux, u.uy);
1313 if (on_ice) {
1314 static int skates = 0;
1316 if (!skates)
1317 skates = find_skates();
1318 if ((uarmf && uarmf->otyp == skates) || resists_cold(&youmonst)
1319 || Flying || is_floater(youmonst.data)
1320 || is_clinger(youmonst.data) || is_whirly(youmonst.data)) {
1321 on_ice = FALSE;
1322 } else if (!rn2(Cold_resistance ? 3 : 2)) {
1323 HFumbling |= FROMOUTSIDE;
1324 HFumbling &= ~TIMEOUT;
1325 HFumbling += 1; /* slip on next move */
1328 if (!on_ice && (HFumbling & FROMOUTSIDE))
1329 HFumbling &= ~FROMOUTSIDE;
1331 x = u.ux + u.dx;
1332 y = u.uy + u.dy;
1333 if (Stunned || (Confusion && !rn2(5))) {
1334 register int tries = 0;
1336 do {
1337 if (tries++ > 50) {
1338 nomul(0);
1339 return;
1341 confdir();
1342 x = u.ux + u.dx;
1343 y = u.uy + u.dy;
1344 } while (!isok(x, y) || bad_rock(youmonst.data, x, y));
1346 /* turbulence might alter your actual destination */
1347 if (u.uinwater) {
1348 water_friction();
1349 if (!u.dx && !u.dy) {
1350 nomul(0);
1351 return;
1353 x = u.ux + u.dx;
1354 y = u.uy + u.dy;
1356 if (!isok(x, y)) {
1357 nomul(0);
1358 return;
1360 if (((trap = t_at(x, y)) && trap->tseen)
1361 || (Blind && !Levitation && !Flying && !is_clinger(youmonst.data)
1362 && is_pool_or_lava(x, y) && levl[x][y].seenv)) {
1363 if (context.run >= 2) {
1364 nomul(0);
1365 context.move = 0;
1366 return;
1367 } else
1368 nomul(0);
1371 if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) {
1372 if (distu(u.ustuck->mx, u.ustuck->my) > 2) {
1373 /* perhaps it fled (or was teleported or ... ) */
1374 u.ustuck = 0;
1375 } else if (sticks(youmonst.data)) {
1376 /* When polymorphed into a sticking monster,
1377 * u.ustuck means it's stuck to you, not you to it.
1379 You("release %s.", mon_nam(u.ustuck));
1380 u.ustuck = 0;
1381 } else {
1382 /* If holder is asleep or paralyzed:
1383 * 37.5% chance of getting away,
1384 * 12.5% chance of waking/releasing it;
1385 * otherwise:
1386 * 7.5% chance of getting away.
1387 * [strength ought to be a factor]
1388 * If holder is tame and there is no conflict,
1389 * guaranteed escape.
1391 switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
1392 case 0:
1393 case 1:
1394 case 2:
1395 pull_free:
1396 You("pull free from %s.", mon_nam(u.ustuck));
1397 u.ustuck = 0;
1398 break;
1399 case 3:
1400 if (!u.ustuck->mcanmove) {
1401 /* it's free to move on next turn */
1402 u.ustuck->mfrozen = 1;
1403 u.ustuck->msleeping = 0;
1405 /*FALLTHRU*/
1406 default:
1407 if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf)
1408 goto pull_free;
1409 You("cannot escape from %s!", mon_nam(u.ustuck));
1410 nomul(0);
1411 return;
1416 mtmp = m_at(x, y);
1417 if (mtmp && !is_safepet(mtmp)) {
1418 /* Don't attack if you're running, and can see it */
1419 /* It's fine to displace pets, though */
1420 /* We should never get here if forcefight */
1421 if (context.run && ((!Blind && mon_visible(mtmp)
1422 && ((mtmp->m_ap_type != M_AP_FURNITURE
1423 && mtmp->m_ap_type != M_AP_OBJECT)
1424 || Protection_from_shape_changers))
1425 || sensemon(mtmp))) {
1426 nomul(0);
1427 context.move = 0;
1428 return;
1433 u.ux0 = u.ux;
1434 u.uy0 = u.uy;
1435 bhitpos.x = x;
1436 bhitpos.y = y;
1437 tmpr = &levl[x][y];
1439 /* attack monster */
1440 if (mtmp) {
1441 /* don't stop travel when displacing pets; if the
1442 displace fails for some reason, attack() in uhitm.c
1443 will stop travel rather than domove */
1444 if (!is_safepet(mtmp) || context.forcefight)
1445 nomul(0);
1446 /* only attack if we know it's there */
1447 /* or if we used the 'F' command to fight blindly */
1448 /* or if it hides_under, in which case we call attack() to print
1449 * the Wait! message.
1450 * This is different from ceiling hiders, who aren't handled in
1451 * attack().
1454 /* If they used a 'm' command, trying to move onto a monster
1455 * prints the below message and wastes a turn. The exception is
1456 * if the monster is unseen and the player doesn't remember an
1457 * invisible monster--then, we fall through to attack() and
1458 * attack_check(), which still wastes a turn, but prints a
1459 * different message and makes the player remember the monster.
1461 if (context.nopick && !context.travel
1462 && (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))) {
1463 if (mtmp->m_ap_type && !Protection_from_shape_changers
1464 && !sensemon(mtmp))
1465 stumble_onto_mimic(mtmp);
1466 else if (mtmp->mpeaceful && !Hallucination)
1467 pline("Pardon me, %s.", m_monnam(mtmp));
1468 else
1469 You("move right into %s.", mon_nam(mtmp));
1470 return;
1472 if (context.forcefight || !mtmp->mundetected || sensemon(mtmp)
1473 || ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)
1474 && !is_safepet(mtmp))) {
1475 /* try to attack; note that it might evade */
1476 /* also, we don't attack tame when _safepet_ */
1477 if (attack(mtmp))
1478 return;
1482 if (context.forcefight && levl[x][y].typ == IRONBARS && uwep) {
1483 struct obj *obj = uwep;
1485 if (breaktest(obj)) {
1486 if (obj->quan > 1L)
1487 obj = splitobj(obj, 1L);
1488 else
1489 setuwep((struct obj *)0);
1490 freeinv(obj);
1492 hit_bars(&obj, u.ux, u.uy, x, y, TRUE, TRUE);
1493 return;
1496 /* specifying 'F' with no monster wastes a turn */
1497 if (context.forcefight
1498 /* remembered an 'I' && didn't use a move command */
1499 || (glyph_is_invisible(levl[x][y].glyph) && !context.nopick)) {
1500 struct obj *boulder = 0;
1501 boolean explo = (Upolyd && attacktype(youmonst.data, AT_EXPL)),
1502 solid = !accessible(x, y);
1503 int glyph = glyph_at(x, y); /* might be monster */
1504 char buf[BUFSZ];
1506 if (!Underwater) {
1507 boulder = sobj_at(BOULDER, x, y);
1508 /* if a statue is displayed at the target location,
1509 player is attempting to attack it [and boulder
1510 handling below is suitable for handling that] */
1511 if (glyph_is_statue(glyph)
1512 || (Hallucination && glyph_is_monster(glyph)))
1513 boulder = sobj_at(STATUE, x, y);
1515 /* force fight at boulder/statue or wall/door while wielding
1516 pick: start digging to break the boulder or wall */
1517 if (context.forcefight
1518 /* can we dig? */
1519 && uwep && dig_typ(uwep, x, y)
1520 /* should we dig? */
1521 && !glyph_is_invisible(glyph) && !glyph_is_monster(glyph)) {
1522 (void) use_pick_axe2(uwep);
1523 return;
1527 /* about to become known empty -- remove 'I' if present */
1528 unmap_object(x, y);
1529 if (boulder)
1530 map_object(boulder, TRUE);
1531 newsym(x, y);
1532 glyph = glyph_at(x, y); /* might have just changed */
1534 if (boulder)
1535 Strcpy(buf, ansimpleoname(boulder));
1536 else if (Underwater && !is_pool(x, y))
1537 /* Underwater, targetting non-water; the map just shows blank
1538 because you don't see remembered terrain while underwater;
1539 although the hero can attack an adjacent monster this way,
1540 assume he can't reach out far enough to distinguish terrain */
1541 Sprintf(buf, (Is_waterlevel(&u.uz) && levl[x][y].typ == AIR)
1542 ? "an air bubble"
1543 : "nothing");
1544 else if (solid)
1545 /* glyph might indicate unseen terrain if hero is blind;
1546 unlike searching, this won't reveal what that terrain is
1547 (except for solid rock, where the glyph would otherwise
1548 yield ludicrous "dark part of a room") */
1549 Strcpy(buf,
1550 (levl[x][y].typ == STONE)
1551 ? "solid rock"
1552 : glyph_is_cmap(glyph)
1553 ? the(defsyms[glyph_to_cmap(glyph)].explanation)
1554 : (const char *) "an unknown obstacle");
1555 /* note: 'solid' is misleadingly named and catches pools
1556 of water and lava as well as rock and walls */
1557 else
1558 Strcpy(buf, "thin air");
1559 You("%s%s %s.",
1560 !(boulder || solid) ? "" : !explo ? "harmlessly " : "futilely ",
1561 explo ? "explode at" : "attack", buf);
1563 nomul(0);
1564 if (explo) {
1565 wake_nearby();
1566 u.mh = -1; /* dead in the current form */
1567 rehumanize();
1569 return;
1571 if (glyph_is_invisible(levl[x][y].glyph)) {
1572 unmap_object(x, y);
1573 newsym(x, y);
1575 /* not attacking an animal, so we try to move */
1576 if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) {
1577 nomul(0);
1578 return;
1581 if (u_rooted())
1582 return;
1584 if (u.utrap) {
1585 if (!trapmove(x, y, trap))
1586 return;
1589 if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) {
1590 if (!context.door_opened) {
1591 context.move = 0;
1592 nomul(0);
1594 return;
1597 /* Move ball and chain. */
1598 if (Punished)
1599 if (!drag_ball(x, y, &bc_control, &ballx, &bally, &chainx, &chainy,
1600 &cause_delay, TRUE))
1601 return;
1603 /* Check regions entering/leaving */
1604 if (!in_out_region(x, y))
1605 return;
1607 /* now move the hero */
1608 mtmp = m_at(x, y);
1609 u.ux += u.dx;
1610 u.uy += u.dy;
1611 /* Move your steed, too */
1612 if (u.usteed) {
1613 u.usteed->mx = u.ux;
1614 u.usteed->my = u.uy;
1615 exercise_steed();
1619 * If safepet at destination then move the pet to the hero's
1620 * previous location using the same conditions as in attack().
1621 * there are special extenuating circumstances:
1622 * (1) if the pet dies then your god angers,
1623 * (2) if the pet gets trapped then your god may disapprove,
1624 * (3) if the pet was already trapped and you attempt to free it
1625 * not only do you encounter the trap but you may frighten your
1626 * pet causing it to go wild! moral: don't abuse this privilege.
1628 * Ceiling-hiding pets are skipped by this section of code, to
1629 * be caught by the normal falling-monster code.
1631 if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) {
1632 /* if trapped, there's a chance the pet goes wild */
1633 if (mtmp->mtrapped) {
1634 if (!rn2(mtmp->mtame)) {
1635 mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
1636 if (mtmp->mleashed)
1637 m_unleash(mtmp, TRUE);
1638 growl(mtmp);
1639 } else {
1640 yelp(mtmp);
1643 mtmp->mundetected = 0;
1644 if (mtmp->m_ap_type)
1645 seemimic(mtmp);
1646 else if (!mtmp->mtame)
1647 newsym(mtmp->mx, mtmp->my);
1649 if (mtmp->mtrapped && (trap = t_at(mtmp->mx, mtmp->my)) != 0
1650 && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)
1651 && sobj_at(BOULDER, trap->tx, trap->ty)) {
1652 /* can't swap places with pet pinned in a pit by a boulder */
1653 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1654 } else if (u.ux0 != x && u.uy0 != y && NODIAG(mtmp->data - mons)) {
1655 /* can't swap places when pet can't move to your spot */
1656 u.ux = u.ux0, u.uy = u.uy0;
1657 You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp)));
1658 } else if (u.ux0 != x && u.uy0 != y && bad_rock(mtmp->data, x, u.uy0)
1659 && bad_rock(mtmp->data, u.ux0, y)
1660 && (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) {
1661 /* can't swap places when pet won't fit thru the opening */
1662 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1663 You("stop. %s won't fit through.", upstart(y_monnam(mtmp)));
1664 } else {
1665 char pnambuf[BUFSZ];
1667 /* save its current description in case of polymorph */
1668 Strcpy(pnambuf, y_monnam(mtmp));
1669 mtmp->mtrapped = 0;
1670 remove_monster(x, y);
1671 place_monster(mtmp, u.ux0, u.uy0);
1672 newsym(x, y);
1673 newsym(u.ux0, u.uy0);
1675 You("%s %s.", mtmp->mtame ? "swap places with" : "frighten",
1676 pnambuf);
1678 /* check for displacing it into pools and traps */
1679 switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) {
1680 case 0:
1681 break;
1682 case 1: /* trapped */
1683 case 3: /* changed levels */
1684 /* there's already been a trap message, reinforce it */
1685 abuse_dog(mtmp);
1686 adjalign(-3);
1687 break;
1688 case 2:
1689 /* drowned or died...
1690 * you killed your pet by direct action, so get experience
1691 * and possibly penalties;
1692 * we want the level gain message, if it happens, to occur
1693 * before the guilt message below
1696 /* minliquid() and mintrap() call mondead() rather than
1697 killed() so we duplicate some of the latter here */
1698 int tmp, mndx;
1700 u.uconduct.killer++;
1701 mndx = monsndx(mtmp->data);
1702 tmp = experience(mtmp, (int) mvitals[mndx].died);
1703 more_experienced(tmp, 0);
1704 newexplevel(); /* will decide if you go up */
1706 /* That's no way to treat a pet! Your god gets angry.
1708 * [This has always been pretty iffy. Why does your
1709 * patron deity care at all, let alone enough to get mad?]
1711 if (rn2(4)) {
1712 You_feel("guilty about losing your pet like this.");
1713 u.ugangr++;
1714 adjalign(-15);
1716 break;
1717 default:
1718 pline("that's strange, unknown mintrap result!");
1719 break;
1724 reset_occupations();
1725 if (context.run) {
1726 if (context.run < 8)
1727 if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ)
1728 || IS_FURNITURE(tmpr->typ))
1729 nomul(0);
1732 if (hides_under(youmonst.data) || youmonst.data->mlet == S_EEL
1733 || u.dx || u.dy)
1734 (void) hideunder(&youmonst);
1737 * Mimics (or whatever) become noticeable if they move and are
1738 * imitating something that doesn't move. We could extend this
1739 * to non-moving monsters...
1741 if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT
1742 || youmonst.m_ap_type == M_AP_FURNITURE))
1743 youmonst.m_ap_type = M_AP_NOTHING;
1745 check_leash(u.ux0, u.uy0);
1747 if (u.ux0 != u.ux || u.uy0 != u.uy) {
1748 u.umoved = TRUE;
1749 /* Clean old position -- vision_recalc() will print our new one. */
1750 newsym(u.ux0, u.uy0);
1751 /* Since the hero has moved, adjust what can be seen/unseen. */
1752 vision_recalc(1); /* Do the work now in the recover time. */
1753 invocation_message();
1756 if (Punished) /* put back ball and chain */
1757 move_bc(0, bc_control, ballx, bally, chainx, chainy);
1759 spoteffects(TRUE);
1761 /* delay next move because of ball dragging */
1762 /* must come after we finished picking up, in spoteffects() */
1763 if (cause_delay) {
1764 nomul(-2);
1765 multi_reason = "dragging an iron ball";
1766 nomovemsg = "";
1769 if (context.run && flags.runmode != RUN_TPORT) {
1770 /* display every step or every 7th step depending upon mode */
1771 if (flags.runmode != RUN_LEAP || !(moves % 7L)) {
1772 if (flags.time)
1773 context.botl = 1;
1774 curs_on_u();
1775 delay_output();
1776 if (flags.runmode == RUN_CRAWL) {
1777 delay_output();
1778 delay_output();
1779 delay_output();
1780 delay_output();
1786 /* combat increases metabolism */
1787 boolean
1788 overexertion()
1790 /* this used to be part of domove() when moving to a monster's
1791 position, but is now called by attack() so that it doesn't
1792 execute if you decline to attack a peaceful monster */
1793 gethungry();
1794 if ((moves % 3L) != 0L && near_capacity() >= HVY_ENCUMBER) {
1795 int *hp = (!Upolyd ? &u.uhp : &u.mh);
1797 if (*hp > 1) {
1798 *hp -= 1;
1799 } else {
1800 You("pass out from exertion!");
1801 exercise(A_CON, FALSE);
1802 fall_asleep(-10, FALSE);
1805 return (boolean) (multi < 0); /* might have fainted (forced to sleep) */
1808 void
1809 invocation_message()
1811 /* a special clue-msg when on the Invocation position */
1812 if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
1813 char buf[BUFSZ];
1814 struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION);
1816 nomul(0); /* stop running or travelling */
1817 if (u.usteed)
1818 Sprintf(buf, "beneath %s", y_monnam(u.usteed));
1819 else if (Levitation || Flying)
1820 Strcpy(buf, "beneath you");
1821 else
1822 Sprintf(buf, "under your %s", makeplural(body_part(FOOT)));
1824 You_feel("a strange vibration %s.", buf);
1825 u.uevent.uvibrated = 1;
1826 if (otmp && otmp->spe == 7 && otmp->lamplit)
1827 pline("%s %s!", The(xname(otmp)),
1828 Blind ? "throbs palpably" : "glows with a strange light");
1832 /* moving onto different terrain;
1833 might be going into solid rock, inhibiting levitation or flight,
1834 or coming back out of such, reinstating levitation/flying */
1835 STATIC_OVL void
1836 switch_terrain()
1838 struct rm *lev = &levl[u.ux][u.uy];
1839 boolean blocklev = (IS_ROCK(lev->typ) || closed_door(u.ux, u.uy)
1840 || (Is_waterlevel(&u.uz) && lev->typ == WATER));
1842 if (blocklev) {
1843 /* called from spoteffects(), skip float_down() */
1844 if (Levitation)
1845 You_cant("levitate in here.");
1846 BLevitation |= FROMOUTSIDE;
1847 } else if (BLevitation) {
1848 BLevitation &= ~FROMOUTSIDE;
1849 if (Levitation)
1850 float_up();
1852 /* the same terrain that blocks levitation also blocks flight */
1853 if (blocklev) {
1854 if (Flying)
1855 You_cant("fly in here.");
1856 BFlying |= FROMOUTSIDE;
1857 } else if (BFlying) {
1858 BFlying &= ~FROMOUTSIDE;
1859 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
1860 /* [minor bug: we don't know whether this is beginning flight or
1861 resuming it; that could be tracked so that this message could
1862 be adjusted to "resume flying", but isn't worth the effort...] */
1863 if (Flying)
1864 You("start flying.");
1868 /* extracted from spoteffects; called by spoteffects to check for entering or
1869 leaving a pool of water/lava, and by moveloop to check for staying on one;
1870 returns true to skip rest of spoteffects */
1871 boolean
1872 pooleffects(newspot)
1873 boolean newspot; /* true if called by spoteffects */
1875 /* check for leaving water */
1876 if (u.uinwater) {
1877 boolean still_inwater = FALSE; /* assume we're getting out */
1879 if (!is_pool(u.ux, u.uy)) {
1880 if (Is_waterlevel(&u.uz))
1881 You("pop into an air bubble.");
1882 else if (is_lava(u.ux, u.uy))
1883 You("leave the %s...", hliquid("water")); /* oops! */
1884 else
1885 You("are on solid %s again.",
1886 is_ice(u.ux, u.uy) ? "ice" : "land");
1887 } else if (Is_waterlevel(&u.uz)) {
1888 still_inwater = TRUE;
1889 } else if (Levitation) {
1890 You("pop out of the %s like a cork!", hliquid("water"));
1891 } else if (Flying) {
1892 You("fly out of the %s.", hliquid("water"));
1893 } else if (Wwalking) {
1894 You("slowly rise above the surface.");
1895 } else {
1896 still_inwater = TRUE;
1898 if (!still_inwater) {
1899 boolean was_underwater = (Underwater && !Is_waterlevel(&u.uz));
1901 u.uinwater = 0; /* leave the water */
1902 if (was_underwater) { /* restore vision */
1903 docrt();
1904 vision_full_recalc = 1;
1909 /* check for entering water or lava */
1910 if (!u.ustuck && !Levitation && !Flying && is_pool_or_lava(u.ux, u.uy)) {
1911 if (u.usteed
1912 && (is_flyer(u.usteed->data) || is_floater(u.usteed->data)
1913 || is_clinger(u.usteed->data))) {
1914 /* floating or clinging steed keeps hero safe (is_flyer() test
1915 is redundant; it can't be true since Flying yielded false) */
1916 return FALSE;
1917 } else if (u.usteed) {
1918 /* steed enters pool */
1919 dismount_steed(Underwater ? DISMOUNT_FELL : DISMOUNT_GENERIC);
1920 /* dismount_steed() -> float_down() -> pickup()
1921 (float_down doesn't do autopickup on Air or Water) */
1922 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
1923 return FALSE;
1924 /* even if we actually end up at same location, float_down()
1925 has already done spoteffect()'s trap and pickup actions */
1926 if (newspot)
1927 check_special_room(FALSE); /* spoteffects */
1928 return TRUE;
1930 /* not mounted */
1932 /* drown(),lava_effects() return true if hero changes
1933 location while surviving the problem */
1934 if (is_lava(u.ux, u.uy)) {
1935 if (lava_effects())
1936 return TRUE;
1937 } else if (!Wwalking
1938 && (newspot || !u.uinwater || !(Swimming || Amphibious))) {
1939 if (drown())
1940 return TRUE;
1943 return FALSE;
1946 void
1947 spoteffects(pick)
1948 boolean pick;
1950 static int inspoteffects = 0;
1951 static coord spotloc;
1952 static int spotterrain;
1953 static struct trap *spottrap = (struct trap *) 0;
1954 static unsigned spottraptyp = NO_TRAP;
1956 struct monst *mtmp;
1957 struct trap *trap = t_at(u.ux, u.uy);
1959 /* prevent recursion from affecting the hero all over again
1960 [hero poly'd to iron golem enters water here, drown() inflicts
1961 damage that triggers rehumanize() which calls spoteffects()...] */
1962 if (inspoteffects && u.ux == spotloc.x && u.uy == spotloc.y
1963 /* except when reason is transformed terrain (ice -> water) */
1964 && spotterrain == levl[u.ux][u.uy].typ
1965 /* or transformed trap (land mine -> pit) */
1966 && (!spottrap || !trap || trap->ttyp == spottraptyp))
1967 return;
1969 ++inspoteffects;
1970 spotterrain = levl[u.ux][u.uy].typ;
1971 spotloc.x = u.ux, spotloc.y = u.uy;
1973 /* moving onto different terrain might cause Levitation to toggle */
1974 if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0))
1975 switch_terrain();
1977 if (pooleffects(TRUE))
1978 goto spotdone;
1980 check_special_room(FALSE);
1981 if (IS_SINK(levl[u.ux][u.uy].typ) && Levitation)
1982 dosinkfall();
1983 if (!in_steed_dismounting) { /* if dismounting, we'll check again later */
1984 boolean pit;
1986 /* if levitation is due to time out at the end of this
1987 turn, allowing it to do so could give the perception
1988 that a trap here is being triggered twice, so adjust
1989 the timeout to prevent that */
1990 if (trap && (HLevitation & TIMEOUT) == 1L
1991 && !(ELevitation || (HLevitation & ~(I_SPECIAL | TIMEOUT)))) {
1992 if (rn2(2)) { /* defer timeout */
1993 incr_itimeout(&HLevitation, 1L);
1994 } else { /* timeout early */
1995 if (float_down(I_SPECIAL | TIMEOUT, 0L)) {
1996 /* levitation has ended; we've already triggered
1997 any trap and [usually] performed autopickup */
1998 trap = 0;
1999 pick = FALSE;
2004 * If not a pit, pickup before triggering trap.
2005 * If pit, trigger trap before pickup.
2007 pit = (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT));
2008 if (pick && !pit)
2009 (void) pickup(1);
2011 if (trap) {
2013 * dotrap on a fire trap calls melt_ice() which triggers
2014 * spoteffects() (again) which can trigger the same fire
2015 * trap (again). Use static spottrap to prevent that.
2016 * We track spottraptyp because some traps morph
2017 * (landmine to pit) and any new trap type
2018 * should get triggered.
2020 if (!spottrap || spottraptyp != trap->ttyp) {
2021 spottrap = trap;
2022 spottraptyp = trap->ttyp;
2023 dotrap(trap, 0); /* fall into arrow trap, etc. */
2024 spottrap = (struct trap *) 0;
2025 spottraptyp = NO_TRAP;
2028 if (pick && pit)
2029 (void) pickup(1);
2031 /* Warning alerts you to ice danger */
2032 if (Warning && is_ice(u.ux, u.uy)) {
2033 static const char *const icewarnings[] = {
2034 "The ice seems very soft and slushy.",
2035 "You feel the ice shift beneath you!",
2036 "The ice, is gonna BREAK!", /* The Dead Zone */
2038 long time_left = spot_time_left(u.ux, u.uy, MELT_ICE_AWAY);
2040 if (time_left && time_left < 15L)
2041 pline("%s", icewarnings[(time_left < 5L) ? 2
2042 : (time_left < 10L) ? 1
2043 : 0]);
2045 if ((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
2046 mtmp->mundetected = mtmp->msleeping = 0;
2047 switch (mtmp->data->mlet) {
2048 case S_PIERCER:
2049 pline("%s suddenly drops from the %s!", Amonnam(mtmp),
2050 ceiling(u.ux, u.uy));
2051 if (mtmp->mtame) { /* jumps to greet you, not attack */
2053 } else if (uarmh && is_metallic(uarmh)) {
2054 pline("Its blow glances off your %s.",
2055 helm_simple_name(uarmh));
2056 } else if (u.uac + 3 <= rnd(20)) {
2057 You("are almost hit by %s!",
2058 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2059 } else {
2060 int dmg;
2062 You("are hit by %s!",
2063 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2064 dmg = d(4, 6);
2065 if (Half_physical_damage)
2066 dmg = (dmg + 1) / 2;
2067 mdamageu(mtmp, dmg);
2069 break;
2070 default: /* monster surprises you. */
2071 if (mtmp->mtame)
2072 pline("%s jumps near you from the %s.", Amonnam(mtmp),
2073 ceiling(u.ux, u.uy));
2074 else if (mtmp->mpeaceful) {
2075 You("surprise %s!",
2076 Blind && !sensemon(mtmp) ? something : a_monnam(mtmp));
2077 mtmp->mpeaceful = 0;
2078 } else
2079 pline("%s attacks you by surprise!", Amonnam(mtmp));
2080 break;
2082 mnexto(mtmp); /* have to move the monster */
2084 spotdone:
2085 if (!--inspoteffects) {
2086 spotterrain = STONE; /* 0 */
2087 spotloc.x = spotloc.y = 0;
2089 return;
2092 /* returns first matching monster */
2093 STATIC_OVL struct monst *
2094 monstinroom(mdat, roomno)
2095 struct permonst *mdat;
2096 int roomno;
2098 register struct monst *mtmp;
2100 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2101 if (DEADMONSTER(mtmp))
2102 continue;
2103 if (mtmp->data == mdat
2104 && index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET))
2105 return mtmp;
2107 return (struct monst *) 0;
2110 char *
2111 in_rooms(x, y, typewanted)
2112 register xchar x, y;
2113 register int typewanted;
2115 static char buf[5];
2116 char rno, *ptr = &buf[4];
2117 int typefound, min_x, min_y, max_x, max_y_offset, step;
2118 register struct rm *lev;
2120 #define goodtype(rno) \
2121 (!typewanted \
2122 || (typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted \
2123 || (typewanted == SHOPBASE && typefound > SHOPBASE))
2125 switch (rno = levl[x][y].roomno) {
2126 case NO_ROOM:
2127 return ptr;
2128 case SHARED:
2129 step = 2;
2130 break;
2131 case SHARED_PLUS:
2132 step = 1;
2133 break;
2134 default: /* i.e. a regular room # */
2135 if (goodtype(rno))
2136 *(--ptr) = rno;
2137 return ptr;
2140 min_x = x - 1;
2141 max_x = x + 1;
2142 if (x < 1)
2143 min_x += step;
2144 else if (x >= COLNO)
2145 max_x -= step;
2147 min_y = y - 1;
2148 max_y_offset = 2;
2149 if (min_y < 0) {
2150 min_y += step;
2151 max_y_offset -= step;
2152 } else if ((min_y + max_y_offset) >= ROWNO)
2153 max_y_offset -= step;
2155 for (x = min_x; x <= max_x; x += step) {
2156 lev = &levl[x][min_y];
2157 y = 0;
2158 if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2159 && goodtype(rno))
2160 *(--ptr) = rno;
2161 y += step;
2162 if (y > max_y_offset)
2163 continue;
2164 if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2165 && goodtype(rno))
2166 *(--ptr) = rno;
2167 y += step;
2168 if (y > max_y_offset)
2169 continue;
2170 if ((rno = lev[y].roomno) >= ROOMOFFSET && !index(ptr, rno)
2171 && goodtype(rno))
2172 *(--ptr) = rno;
2174 return ptr;
2177 /* is (x,y) in a town? */
2178 boolean
2179 in_town(x, y)
2180 register int x, y;
2182 s_level *slev = Is_special(&u.uz);
2183 register struct mkroom *sroom;
2184 boolean has_subrooms = FALSE;
2186 if (!slev || !slev->flags.town)
2187 return FALSE;
2190 * See if (x,y) is in a room with subrooms, if so, assume it's the
2191 * town. If there are no subrooms, the whole level is in town.
2193 for (sroom = &rooms[0]; sroom->hx > 0; sroom++) {
2194 if (sroom->nsubrooms > 0) {
2195 has_subrooms = TRUE;
2196 if (inside_room(sroom, x, y))
2197 return TRUE;
2201 return !has_subrooms;
2204 STATIC_OVL void
2205 move_update(newlev)
2206 register boolean newlev;
2208 char *ptr1, *ptr2, *ptr3, *ptr4;
2210 Strcpy(u.urooms0, u.urooms);
2211 Strcpy(u.ushops0, u.ushops);
2212 if (newlev) {
2213 u.urooms[0] = '\0';
2214 u.uentered[0] = '\0';
2215 u.ushops[0] = '\0';
2216 u.ushops_entered[0] = '\0';
2217 Strcpy(u.ushops_left, u.ushops0);
2218 return;
2220 Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0));
2222 for (ptr1 = &u.urooms[0], ptr2 = &u.uentered[0], ptr3 = &u.ushops[0],
2223 ptr4 = &u.ushops_entered[0];
2224 *ptr1; ptr1++) {
2225 if (!index(u.urooms0, *ptr1))
2226 *(ptr2++) = *ptr1;
2227 if (IS_SHOP(*ptr1 - ROOMOFFSET)) {
2228 *(ptr3++) = *ptr1;
2229 if (!index(u.ushops0, *ptr1))
2230 *(ptr4++) = *ptr1;
2233 *ptr2 = '\0';
2234 *ptr3 = '\0';
2235 *ptr4 = '\0';
2237 /* filter u.ushops0 -> u.ushops_left */
2238 for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++)
2239 if (!index(u.ushops, *ptr1))
2240 *(ptr2++) = *ptr1;
2241 *ptr2 = '\0';
2244 /* possibly deliver a one-time room entry message */
2245 void
2246 check_special_room(newlev)
2247 register boolean newlev;
2249 register struct monst *mtmp;
2250 char *ptr;
2252 move_update(newlev);
2254 if (*u.ushops0)
2255 u_left_shop(u.ushops_left, newlev);
2257 if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */
2258 return; /* no entrance messages necessary */
2260 /* Did we just enter a shop? */
2261 if (*u.ushops_entered)
2262 u_entered_shop(u.ushops_entered);
2264 for (ptr = &u.uentered[0]; *ptr; ptr++) {
2265 int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype;
2266 boolean msg_given = TRUE;
2268 /* Did we just enter some other special room? */
2269 /* vault.c insists that a vault remain a VAULT,
2270 * and temples should remain TEMPLEs,
2271 * but everything else gives a message only the first time */
2272 switch (rt) {
2273 case ZOO:
2274 pline("Welcome to David's treasure zoo!");
2275 break;
2276 case SWAMP:
2277 pline("It %s rather %s down here.", Blind ? "feels" : "looks",
2278 Blind ? "humid" : "muddy");
2279 break;
2280 case COURT:
2281 You("enter an opulent throne room!");
2282 break;
2283 case LEPREHALL:
2284 You("enter a leprechaun hall!");
2285 break;
2286 case MORGUE:
2287 if (midnight()) {
2288 const char *run = locomotion(youmonst.data, "Run");
2289 pline("%s away! %s away!", run, run);
2290 } else
2291 You("have an uncanny feeling...");
2292 break;
2293 case BEEHIVE:
2294 You("enter a giant beehive!");
2295 break;
2296 case COCKNEST:
2297 You("enter a disgusting nest!");
2298 break;
2299 case ANTHOLE:
2300 You("enter an anthole!");
2301 break;
2302 case BARRACKS:
2303 if (monstinroom(&mons[PM_SOLDIER], roomno)
2304 || monstinroom(&mons[PM_SERGEANT], roomno)
2305 || monstinroom(&mons[PM_LIEUTENANT], roomno)
2306 || monstinroom(&mons[PM_CAPTAIN], roomno))
2307 You("enter a military barracks!");
2308 else
2309 You("enter an abandoned barracks.");
2310 break;
2311 case DELPHI: {
2312 struct monst *oracle = monstinroom(&mons[PM_ORACLE], roomno);
2313 if (oracle) {
2314 if (!oracle->mpeaceful)
2315 verbalize("You're in Delphi, %s.", plname);
2316 else
2317 verbalize("%s, %s, welcome to Delphi!",
2318 Hello((struct monst *) 0), plname);
2319 } else
2320 msg_given = FALSE;
2321 break;
2323 case TEMPLE:
2324 intemple(roomno + ROOMOFFSET);
2325 /*FALLTHRU*/
2326 default:
2327 msg_given = (rt == TEMPLE);
2328 rt = 0;
2329 break;
2331 if (msg_given)
2332 room_discovered(roomno);
2334 if (rt != 0) {
2335 rooms[roomno].rtype = OROOM;
2336 if (!search_special(rt)) {
2337 /* No more room of that type */
2338 switch (rt) {
2339 case COURT:
2340 level.flags.has_court = 0;
2341 break;
2342 case SWAMP:
2343 level.flags.has_swamp = 0;
2344 break;
2345 case MORGUE:
2346 level.flags.has_morgue = 0;
2347 break;
2348 case ZOO:
2349 level.flags.has_zoo = 0;
2350 break;
2351 case BARRACKS:
2352 level.flags.has_barracks = 0;
2353 break;
2354 case TEMPLE:
2355 level.flags.has_temple = 0;
2356 break;
2357 case BEEHIVE:
2358 level.flags.has_beehive = 0;
2359 break;
2362 if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO)
2363 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2364 if (DEADMONSTER(mtmp))
2365 continue;
2366 if (!Stealth && !rn2(3))
2367 mtmp->msleeping = 0;
2372 return;
2375 /* the ',' command */
2377 dopickup()
2379 int count, tmpcount;
2380 struct trap *traphere = t_at(u.ux, u.uy);
2382 /* awful kludge to work around parse()'s pre-decrement */
2383 count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0;
2384 multi = 0; /* always reset */
2385 /* uswallow case added by GAN 01/29/87 */
2386 if (u.uswallow) {
2387 if (!u.ustuck->minvent) {
2388 if (is_animal(u.ustuck->data)) {
2389 You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
2390 pline("But it's kind of slimy, so you drop it.");
2391 } else
2392 You("don't %s anything in here to pick up.",
2393 Blind ? "feel" : "see");
2394 return 1;
2395 } else {
2396 tmpcount = -count;
2397 return loot_mon(u.ustuck, &tmpcount, (boolean *) 0);
2400 if (is_pool(u.ux, u.uy)) {
2401 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2402 || (Flying && !Breathless)) {
2403 You("cannot dive into the %s to pick things up.",
2404 hliquid("water"));
2405 return 0;
2406 } else if (!Underwater) {
2407 You_cant("even see the bottom, let alone pick up %s.", something);
2408 return 0;
2411 if (is_lava(u.ux, u.uy)) {
2412 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2413 || (Flying && !Breathless)) {
2414 You_cant("reach the bottom to pick things up.");
2415 return 0;
2416 } else if (!likes_lava(youmonst.data)) {
2417 You("would burn to a crisp trying to pick things up.");
2418 return 0;
2421 if (!OBJ_AT(u.ux, u.uy)) {
2422 register struct rm *lev = &levl[u.ux][u.uy];
2424 if (IS_THRONE(lev->typ))
2425 pline("It must weigh%s a ton!", lev->looted ? " almost" : "");
2426 else if (IS_SINK(lev->typ))
2427 pline_The("plumbing connects it to the floor.");
2428 else if (IS_GRAVE(lev->typ))
2429 You("don't need a gravestone. Yet.");
2430 else if (IS_FOUNTAIN(lev->typ))
2431 You("could drink the %s...", hliquid("water"));
2432 else if (IS_DOOR(lev->typ) && (lev->doormask & D_ISOPEN))
2433 pline("It won't come off the hinges.");
2434 else
2435 There("is nothing here to pick up.");
2436 return 0;
2438 if (!can_reach_floor(TRUE)) {
2439 if (traphere && uteetering_at_seen_pit(traphere))
2440 You("cannot reach the bottom of the pit.");
2441 else if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
2442 rider_cant_reach();
2443 else if (Blind && !can_reach_floor(TRUE))
2444 You("cannot reach anything here.");
2445 else
2446 You("cannot reach the %s.", surface(u.ux, u.uy));
2447 return 0;
2450 return pickup(-count);
2453 /* stop running if we see something interesting */
2454 /* turn around a corner if that is the only way we can proceed */
2455 /* do not turn left or right twice */
2456 void
2457 lookaround()
2459 register int x, y;
2460 int i, x0 = 0, y0 = 0, m0 = 1, i0 = 9;
2461 int corrct = 0, noturn = 0;
2462 struct monst *mtmp;
2463 struct trap *trap;
2465 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
2466 /* they polymorphed while in the middle of a long move. */
2467 if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
2468 nomul(0);
2469 return;
2472 if (Blind || context.run == 0)
2473 return;
2474 for (x = u.ux - 1; x <= u.ux + 1; x++)
2475 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2476 if (!isok(x, y) || (x == u.ux && y == u.uy))
2477 continue;
2478 if (NODIAG(u.umonnum) && x != u.ux && y != u.uy)
2479 continue;
2481 if ((mtmp = m_at(x, y)) != 0
2482 && mtmp->m_ap_type != M_AP_FURNITURE
2483 && mtmp->m_ap_type != M_AP_OBJECT
2484 && (!mtmp->minvis || See_invisible) && !mtmp->mundetected) {
2485 if ((context.run != 1 && !mtmp->mtame)
2486 || (x == u.ux + u.dx && y == u.uy + u.dy
2487 && !context.travel))
2488 goto stop;
2491 if (levl[x][y].typ == STONE)
2492 continue;
2493 if (x == u.ux - u.dx && y == u.uy - u.dy)
2494 continue;
2496 if (IS_ROCK(levl[x][y].typ) || levl[x][y].typ == ROOM
2497 || IS_AIR(levl[x][y].typ)) {
2498 continue;
2499 } else if (closed_door(x, y) || (mtmp && is_door_mappear(mtmp))) {
2500 if (x != u.ux && y != u.uy)
2501 continue;
2502 if (context.run != 1)
2503 goto stop;
2504 goto bcorr;
2505 } else if (levl[x][y].typ == CORR) {
2506 bcorr:
2507 if (levl[u.ux][u.uy].typ != ROOM) {
2508 if (context.run == 1 || context.run == 3
2509 || context.run == 8) {
2510 i = dist2(x, y, u.ux + u.dx, u.uy + u.dy);
2511 if (i > 2)
2512 continue;
2513 if (corrct == 1 && dist2(x, y, x0, y0) != 1)
2514 noturn = 1;
2515 if (i < i0) {
2516 i0 = i;
2517 x0 = x;
2518 y0 = y;
2519 m0 = mtmp ? 1 : 0;
2522 corrct++;
2524 continue;
2525 } else if ((trap = t_at(x, y)) && trap->tseen) {
2526 if (context.run == 1)
2527 goto bcorr; /* if you must */
2528 if (x == u.ux + u.dx && y == u.uy + u.dy)
2529 goto stop;
2530 continue;
2531 } else if (is_pool_or_lava(x, y)) {
2532 /* water and lava only stop you if directly in front, and stop
2533 * you even if you are running
2535 if (!Levitation && !Flying && !is_clinger(youmonst.data)
2536 && x == u.ux + u.dx && y == u.uy + u.dy)
2537 /* No Wwalking check; otherwise they'd be able
2538 * to test boots by trying to SHIFT-direction
2539 * into a pool and seeing if the game allowed it
2541 goto stop;
2542 continue;
2543 } else { /* e.g. objects or trap or stairs */
2544 if (context.run == 1)
2545 goto bcorr;
2546 if (context.run == 8)
2547 continue;
2548 if (mtmp)
2549 continue; /* d */
2550 if (((x == u.ux - u.dx) && (y != u.uy + u.dy))
2551 || ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
2552 continue;
2554 stop:
2555 nomul(0);
2556 return;
2557 } /* end for loops */
2559 if (corrct > 1 && context.run == 2)
2560 goto stop;
2561 if ((context.run == 1 || context.run == 3 || context.run == 8) && !noturn
2562 && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) {
2563 /* make sure that we do not turn too far */
2564 if (i0 == 2) {
2565 if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
2566 i = 2; /* straight turn right */
2567 else
2568 i = -2; /* straight turn left */
2569 } else if (u.dx && u.dy) {
2570 if ((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy))
2571 i = -1; /* half turn left */
2572 else
2573 i = 1; /* half turn right */
2574 } else {
2575 if ((x0 - u.ux == y0 - u.uy && !u.dy)
2576 || (x0 - u.ux != y0 - u.uy && u.dy))
2577 i = 1; /* half turn right */
2578 else
2579 i = -1; /* half turn left */
2582 i += u.last_str_turn;
2583 if (i <= 2 && i >= -2) {
2584 u.last_str_turn = i;
2585 u.dx = x0 - u.ux;
2586 u.dy = y0 - u.uy;
2591 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2592 STATIC_OVL boolean
2593 doorless_door(x, y)
2594 int x, y;
2596 struct rm *lev_p = &levl[x][y];
2598 if (!IS_DOOR(lev_p->typ))
2599 return FALSE;
2600 /* all rogue level doors are doorless but disallow diagonal access, so
2601 we treat them as if their non-existant doors were actually present */
2602 if (Is_rogue_level(&u.uz))
2603 return FALSE;
2604 return !(lev_p->doormask & ~(D_NODOOR | D_BROKEN));
2607 /* used by drown() to check whether hero can crawl from water to <x,y> */
2608 boolean
2609 crawl_destination(x, y)
2610 int x, y;
2612 /* is location ok in general? */
2613 if (!goodpos(x, y, &youmonst, 0))
2614 return FALSE;
2616 /* orthogonal movement is unrestricted when destination is ok */
2617 if (x == u.ux || y == u.uy)
2618 return TRUE;
2620 /* diagonal movement has some restrictions */
2621 if (NODIAG(u.umonnum))
2622 return FALSE; /* poly'd into a grid bug... */
2623 if (Passes_walls)
2624 return TRUE; /* or a xorn... */
2625 /* pool could be next to a door, conceivably even inside a shop */
2626 if (IS_DOOR(levl[x][y].typ) && (!doorless_door(x, y) || block_door(x, y)))
2627 return FALSE;
2628 /* finally, are we trying to squeeze through a too-narrow gap? */
2629 return !(bad_rock(youmonst.data, u.ux, y)
2630 && bad_rock(youmonst.data, x, u.uy));
2633 /* something like lookaround, but we are not running */
2634 /* react only to monsters that might hit us */
2636 monster_nearby()
2638 register int x, y;
2639 register struct monst *mtmp;
2641 /* Also see the similar check in dochugw() in monmove.c */
2642 for (x = u.ux - 1; x <= u.ux + 1; x++)
2643 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2644 if (!isok(x, y) || (x == u.ux && y == u.uy))
2645 continue;
2646 if ((mtmp = m_at(x, y)) && mtmp->m_ap_type != M_AP_FURNITURE
2647 && mtmp->m_ap_type != M_AP_OBJECT
2648 && (!mtmp->mpeaceful || Hallucination)
2649 && (!is_hider(mtmp->data) || !mtmp->mundetected)
2650 && !noattacks(mtmp->data) && mtmp->mcanmove
2651 && !mtmp->msleeping /* aplvax!jcn */
2652 && !onscary(u.ux, u.uy, mtmp) && canspotmon(mtmp))
2653 return 1;
2655 return 0;
2658 void
2659 nomul(nval)
2660 register int nval;
2662 if (multi < nval)
2663 return; /* This is a bug fix by ab@unido */
2664 u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */
2665 u.usleep = 0;
2666 multi = nval;
2667 if (nval == 0)
2668 multi_reason = NULL;
2669 context.travel = context.travel1 = context.mv = context.run = 0;
2672 /* called when a non-movement, multi-turn action has completed */
2673 void
2674 unmul(msg_override)
2675 const char *msg_override;
2677 multi = 0; /* caller will usually have done this already */
2678 if (msg_override)
2679 nomovemsg = msg_override;
2680 else if (!nomovemsg)
2681 nomovemsg = You_can_move_again;
2682 if (*nomovemsg)
2683 pline1(nomovemsg);
2684 nomovemsg = 0;
2685 u.usleep = 0;
2686 multi_reason = NULL;
2687 if (afternmv)
2688 (*afternmv)();
2689 afternmv = 0;
2692 STATIC_OVL void
2693 maybe_wail()
2695 static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
2696 SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES,
2697 TELEPORT_CONTROL, STEALTH, FAST, INVIS };
2699 if (moves <= wailmsg + 50)
2700 return;
2702 wailmsg = moves;
2703 if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) {
2704 const char *who;
2705 int i, powercnt;
2707 who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? urole.name.m
2708 : "Elf";
2709 if (u.uhp == 1) {
2710 pline("%s is about to die.", who);
2711 } else {
2712 for (i = 0, powercnt = 0; i < SIZE(powers); ++i)
2713 if (u.uprops[powers[i]].intrinsic & INTRINSIC)
2714 ++powercnt;
2716 pline((powercnt >= 4) ? "%s, all your powers will be lost..."
2717 : "%s, your life force is running out.",
2718 who);
2720 } else {
2721 You_hear(u.uhp == 1 ? "the wailing of the Banshee..."
2722 : "the howling of the CwnAnnwn...");
2726 void
2727 losehp(n, knam, k_format)
2728 register int n;
2729 register const char *knam;
2730 boolean k_format;
2732 if (Upolyd) {
2733 u.mh -= n;
2734 if (u.mhmax < u.mh)
2735 u.mhmax = u.mh;
2736 context.botl = 1;
2737 if (u.mh < 1)
2738 rehumanize();
2739 else if (n > 0 && u.mh * 10 < u.mhmax && Unchanging)
2740 maybe_wail();
2741 return;
2744 u.uhp -= n;
2745 if (u.uhp > u.uhpmax)
2746 u.uhpmax = u.uhp; /* perhaps n was negative */
2747 else
2748 context.travel = context.travel1 = context.mv = context.run = 0;
2749 context.botl = 1;
2750 if (u.uhp < 1) {
2751 killer.format = k_format;
2752 if (killer.name != knam) /* the thing that killed you */
2753 Strcpy(killer.name, knam ? knam : "");
2754 You("die...");
2755 done(DIED);
2756 } else if (n > 0 && u.uhp * 10 < u.uhpmax) {
2757 maybe_wail();
2762 weight_cap()
2764 register long carrcap;
2766 carrcap = 25 * (ACURRSTR + ACURR(A_CON)) + 50;
2767 if (Upolyd) {
2768 /* consistent with can_carry() in mon.c */
2769 if (youmonst.data->mlet == S_NYMPH)
2770 carrcap = MAX_CARR_CAP;
2771 else if (!youmonst.data->cwt)
2772 carrcap = (carrcap * (long) youmonst.data->msize) / MZ_HUMAN;
2773 else if (!strongmonst(youmonst.data)
2774 || (strongmonst(youmonst.data)
2775 && (youmonst.data->cwt > WT_HUMAN)))
2776 carrcap = (carrcap * (long) youmonst.data->cwt / WT_HUMAN);
2779 if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */
2780 || (u.usteed && strongmonst(u.usteed->data))) {
2781 carrcap = MAX_CARR_CAP;
2782 } else {
2783 if (carrcap > MAX_CARR_CAP)
2784 carrcap = MAX_CARR_CAP;
2785 if (!Flying) {
2786 if (EWounded_legs & LEFT_SIDE)
2787 carrcap -= 100;
2788 if (EWounded_legs & RIGHT_SIDE)
2789 carrcap -= 100;
2791 if (carrcap < 0)
2792 carrcap = 0;
2794 return (int) carrcap;
2797 static int wc; /* current weight_cap(); valid after call to inv_weight() */
2799 /* returns how far beyond the normal capacity the player is currently. */
2800 /* inv_weight() is negative if the player is below normal capacity. */
2802 inv_weight()
2804 register struct obj *otmp = invent;
2805 register int wt = 0;
2807 while (otmp) {
2808 if (otmp->oclass == COIN_CLASS)
2809 wt += (int) (((long) otmp->quan + 50L) / 100L);
2810 else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
2811 wt += otmp->owt;
2812 otmp = otmp->nobj;
2814 wc = weight_cap();
2815 return (wt - wc);
2819 * Returns 0 if below normal capacity, or the number of "capacity units"
2820 * over the normal capacity the player is loaded. Max is 5.
2823 calc_capacity(xtra_wt)
2824 int xtra_wt;
2826 int cap, wt = inv_weight() + xtra_wt;
2828 if (wt <= 0)
2829 return UNENCUMBERED;
2830 if (wc <= 1)
2831 return OVERLOADED;
2832 cap = (wt * 2 / wc) + 1;
2833 return min(cap, OVERLOADED);
2837 near_capacity()
2839 return calc_capacity(0);
2843 max_capacity()
2845 int wt = inv_weight();
2847 return (wt - (2 * wc));
2850 boolean
2851 check_capacity(str)
2852 const char *str;
2854 if (near_capacity() >= EXT_ENCUMBER) {
2855 if (str)
2856 pline1(str);
2857 else
2858 You_cant("do that while carrying so much stuff.");
2859 return 1;
2861 return 0;
2865 inv_cnt(incl_gold)
2866 boolean incl_gold;
2868 register struct obj *otmp = invent;
2869 register int ct = 0;
2871 while (otmp) {
2872 if (incl_gold || otmp->invlet != GOLD_SYM)
2873 ct++;
2874 otmp = otmp->nobj;
2876 return ct;
2879 /* Counts the money in an object chain. */
2880 /* Intended use is for your or some monster's inventory, */
2881 /* now that u.gold/m.gold is gone.*/
2882 /* Counting money in a container might be possible too. */
2883 long
2884 money_cnt(otmp)
2885 struct obj *otmp;
2887 while (otmp) {
2888 /* Must change when silver & copper is implemented: */
2889 if (otmp->oclass == COIN_CLASS)
2890 return otmp->quan;
2891 otmp = otmp->nobj;
2893 return 0L;
2896 /*hack.c*/