Use define for iron ball weight increment
[aNetHack.git] / src / hack.c
blob098546579148c8ed31ee19a30e1d511d75697bed
1 /* NetHack 3.6 hack.c $NHDT-Date: 1446604111 2015/11/04 02:28:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.155 $ */
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 int /* returns 0 if we can squeeze through */
625 cant_squeeze_thru(mon)
626 struct monst *mon;
628 int amt;
629 struct permonst *ptr = mon->data;
631 /* too big? */
632 if (bigmonst(ptr)
633 && !(amorphous(ptr) || is_whirly(ptr) || noncorporeal(ptr)
634 || slithy(ptr) || can_fog(mon)))
635 return 1;
637 /* lugging too much junk? */
638 amt =
639 (mon == &youmonst) ? inv_weight() + weight_cap() : curr_mon_load(mon);
640 if (amt > 600)
641 return 2;
643 /* Sokoban restriction applies to hero only */
644 if (mon == &youmonst && Sokoban)
645 return 3;
647 /* can squeeze through */
648 return 0;
651 boolean
652 invocation_pos(x, y)
653 xchar x, y;
655 return (boolean) (Invocation_lev(&u.uz)
656 && x == inv_pos.x && y == inv_pos.y);
659 /* return TRUE if (dx,dy) is an OK place to move
660 * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP
662 boolean
663 test_move(ux, uy, dx, dy, mode)
664 int ux, uy, dx, dy;
665 int mode;
667 int x = ux + dx;
668 int y = uy + dy;
669 register struct rm *tmpr = &levl[x][y];
670 register struct rm *ust;
672 context.door_opened = FALSE;
674 * Check for physical obstacles. First, the place we are going.
676 if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
677 if (Blind && mode == DO_MOVE)
678 feel_location(x, y);
679 if (Passes_walls && may_passwall(x, y)) {
680 ; /* do nothing */
681 } else if (tmpr->typ == IRONBARS) {
682 if ((dmgtype(youmonst.data, AD_RUST)
683 || dmgtype(youmonst.data, AD_CORR)) && mode == DO_MOVE
684 && still_chewing(x, y)) {
685 return FALSE;
687 if (!(Passes_walls || passes_bars(youmonst.data))) {
688 if (iflags.mention_walls)
689 You("cannot pass through the bars.");
690 return FALSE;
692 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
693 /* Eat the rock. */
694 if (mode == DO_MOVE && still_chewing(x, y))
695 return FALSE;
696 } else if (flags.autodig && !context.run && !context.nopick && uwep
697 && is_pick(uwep)) {
698 /* MRKR: Automatic digging when wielding the appropriate tool */
699 if (mode == DO_MOVE)
700 (void) use_pick_axe2(uwep);
701 return FALSE;
702 } else {
703 if (mode == DO_MOVE) {
704 if (Is_stronghold(&u.uz) && is_db_wall(x, y))
705 pline_The("drawbridge is up!");
706 /* sokoban restriction stays even after puzzle is solved */
707 else if (Passes_walls && !may_passwall(x, y)
708 && In_sokoban(&u.uz))
709 pline_The("Sokoban walls resist your ability.");
710 else if (iflags.mention_walls)
711 pline("It's a wall.");
713 return FALSE;
715 } else if (IS_DOOR(tmpr->typ)) {
716 if (closed_door(x, y)) {
717 if (Blind && mode == DO_MOVE)
718 feel_location(x, y);
719 if (Passes_walls)
720 ; /* do nothing */
721 else if (can_ooze(&youmonst)) {
722 if (mode == DO_MOVE)
723 You("ooze under the door.");
724 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
725 /* Eat the door. */
726 if (mode == DO_MOVE && still_chewing(x, y))
727 return FALSE;
728 } else {
729 if (mode == DO_MOVE) {
730 if (amorphous(youmonst.data))
731 You(
732 "try to ooze under the door, but can't squeeze your possessions through.");
733 if (flags.autoopen && !context.run && !Confusion
734 && !Stunned && !Fumbling) {
735 context.door_opened = context.move =
736 doopen_indir(x, y);
737 } else if (x == ux || y == uy) {
738 if (Blind || Stunned || ACURR(A_DEX) < 10
739 || Fumbling) {
740 if (u.usteed) {
741 You_cant("lead %s through that closed door.",
742 y_monnam(u.usteed));
743 } else {
744 pline("Ouch! You bump into a door.");
745 exercise(A_DEX, FALSE);
747 } else
748 pline("That door is closed.");
750 } else if (mode == TEST_TRAV || mode == TEST_TRAP)
751 goto testdiag;
752 return FALSE;
754 } else {
755 testdiag:
756 if (dx && dy && !Passes_walls
757 && (!doorless_door(x, y) || block_door(x, y))) {
758 /* Diagonal moves into a door are not allowed. */
759 if (Blind && mode == DO_MOVE)
760 feel_location(x, y);
761 return FALSE;
765 if (dx && dy && bad_rock(youmonst.data, ux, y)
766 && bad_rock(youmonst.data, x, uy)) {
767 /* Move at a diagonal. */
768 switch (cant_squeeze_thru(&youmonst)) {
769 case 3:
770 if (mode == DO_MOVE)
771 You("cannot pass that way.");
772 return FALSE;
773 case 2:
774 if (mode == DO_MOVE)
775 You("are carrying too much to get through.");
776 return FALSE;
777 case 1:
778 if (mode == DO_MOVE)
779 Your("body is too large to fit through.");
780 return FALSE;
781 default:
782 break; /* can squeeze through */
784 } else if (dx && dy && worm_cross(ux, uy, x, y)) {
785 /* consecutive long worm segments are at <ux,y> and <x,uy> */
786 if (mode == DO_MOVE)
787 pline("%s is in your way.", Monnam(m_at(ux, y)));
788 return FALSE;
790 /* Pick travel path that does not require crossing a trap.
791 * Avoid water and lava using the usual running rules.
792 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
793 if (context.run == 8
794 && (mode == TEST_MOVE || mode == TEST_TRAP)
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) return FALSE; /* do not move through traps */
806 ust = &levl[ux][uy];
808 /* Now see if other things block our way . . */
809 if (dx && dy && !Passes_walls && IS_DOOR(ust->typ)
810 && (!doorless_door(ux, uy) || block_entry(x, y))) {
811 /* Can't move at a diagonal out of a doorway with door. */
812 return FALSE;
815 if (sobj_at(BOULDER, x, y) && (Sokoban || !Passes_walls)) {
816 if (!(Blind || Hallucination) && (context.run >= 2)
817 && mode != TEST_TRAV)
818 return FALSE;
819 if (mode == DO_MOVE) {
820 /* tunneling monsters will chew before pushing */
821 if (tunnels(youmonst.data) && !needspick(youmonst.data)
822 && !Sokoban) {
823 if (still_chewing(x, y))
824 return FALSE;
825 } else if (moverock() < 0)
826 return FALSE;
827 } else if (mode == TEST_TRAV) {
828 struct obj *obj;
830 /* never travel through boulders in Sokoban */
831 if (Sokoban) return FALSE;
833 /* don't pick two boulders in a row, unless there's a way thru */
834 if (sobj_at(BOULDER, ux, uy) && !Sokoban) {
835 if (!Passes_walls
836 && !(tunnels(youmonst.data) && !needspick(youmonst.data))
837 && !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK)
838 && !((obj = carrying(WAN_DIGGING))
839 && !objects[obj->otyp].oc_name_known))
840 return FALSE;
843 /* assume you'll be able to push it when you get there... */
846 /* OK, it is a legal place to move. */
847 return TRUE;
850 #ifdef DEBUG
851 static boolean trav_debug = FALSE;
853 /* in this case, toggle display of travel debug info */
854 int wiz_debug_cmd_traveldisplay()
856 trav_debug = !trav_debug;
857 return 0;
859 #endif /* DEBUG */
862 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
863 * A shortest path is returned. If guess is TRUE, consider various
864 * inaccessible locations as valid intermediate path points.
865 * Returns TRUE if a path was found.
867 STATIC_OVL boolean
868 findtravelpath(guess)
869 boolean guess;
871 /* if travel to adjacent, reachable location, use normal movement rules */
872 if (!guess && context.travel1 && distmin(u.ux, u.uy, u.tx, u.ty) == 1
873 && !(u.ux != u.tx && u.uy != u.ty && NODIAG(u.umonnum))) {
874 context.run = 0;
875 if (test_move(u.ux, u.uy, u.tx - u.ux, u.ty - u.uy, TEST_MOVE)) {
876 u.dx = u.tx - u.ux;
877 u.dy = u.ty - u.uy;
878 nomul(0);
879 iflags.travelcc.x = iflags.travelcc.y = -1;
880 return TRUE;
882 context.run = 8;
884 if (u.tx != u.ux || u.ty != u.uy) {
885 xchar travel[COLNO][ROWNO];
886 xchar travelstepx[2][COLNO * ROWNO];
887 xchar travelstepy[2][COLNO * ROWNO];
888 xchar tx, ty, ux, uy;
889 int n = 1; /* max offset in travelsteps */
890 int set = 0; /* two sets current and previous */
891 int radius = 1; /* search radius */
892 int i;
894 /* If guessing, first find an "obvious" goal location. The obvious
895 * goal is the position the player knows of, or might figure out
896 * (couldsee) that is closest to the target on a straight path.
898 if (guess) {
899 tx = u.ux;
900 ty = u.uy;
901 ux = u.tx;
902 uy = u.ty;
903 } else {
904 tx = u.tx;
905 ty = u.ty;
906 ux = u.ux;
907 uy = u.uy;
910 noguess:
911 (void) memset((genericptr_t) travel, 0, sizeof(travel));
912 travelstepx[0][0] = tx;
913 travelstepy[0][0] = ty;
915 while (n != 0) {
916 int nn = 0;
918 for (i = 0; i < n; i++) {
919 int dir;
920 int x = travelstepx[set][i];
921 int y = travelstepy[set][i];
922 static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
923 /* no diagonal movement for grid bugs */
924 int dirmax = NODIAG(u.umonnum) ? 4 : 8;
925 boolean alreadyrepeated = FALSE;
927 for (dir = 0; dir < dirmax; ++dir) {
928 int nx = x + xdir[ordered[dir]];
929 int ny = y + ydir[ordered[dir]];
931 if (!isok(nx, ny))
932 continue;
933 if ((!Passes_walls && !can_ooze(&youmonst)
934 && closed_door(x, y)) || sobj_at(BOULDER, x, y)
935 || test_move(x, y, nx-x, ny-y, TEST_TRAP)) {
936 /* closed doors and boulders usually
937 * cause a delay, so prefer another path */
938 if (travel[x][y] > radius - 3) {
939 if (!alreadyrepeated) {
940 travelstepx[1 - set][nn] = x;
941 travelstepy[1 - set][nn] = y;
942 /* don't change travel matrix! */
943 nn++;
944 alreadyrepeated = TRUE;
946 continue;
949 if (test_move(x, y, nx - x, ny - y, TEST_TRAV)
950 && (levl[nx][ny].seenv
951 || (!Blind && couldsee(nx, ny)))) {
952 if (nx == ux && ny == uy) {
953 if (!guess) {
954 u.dx = x - ux;
955 u.dy = y - uy;
956 if (x == u.tx && y == u.ty) {
957 nomul(0);
958 /* reset run so domove run checks work */
959 context.run = 8;
960 iflags.travelcc.x = iflags.travelcc.y =
963 return TRUE;
965 } else if (!travel[nx][ny]) {
966 travelstepx[1 - set][nn] = nx;
967 travelstepy[1 - set][nn] = ny;
968 travel[nx][ny] = radius;
969 nn++;
975 #ifdef DEBUG
976 if (trav_debug) {
977 /* Use of warning glyph is arbitrary. It stands out. */
978 tmp_at(DISP_ALL, warning_to_glyph(1));
979 for (i = 0; i < nn; ++i) {
980 tmp_at(travelstepx[1 - set][i], travelstepy[1 - set][i]);
982 delay_output();
983 if (flags.runmode == RUN_CRAWL) {
984 delay_output();
985 delay_output();
987 tmp_at(DISP_END, 0);
989 #endif /* DEBUG */
991 n = nn;
992 set = 1 - set;
993 radius++;
996 /* if guessing, find best location in travel matrix and go there */
997 if (guess) {
998 int px = tx, py = ty; /* pick location */
999 int dist, nxtdist, d2, nd2;
1001 dist = distmin(ux, uy, tx, ty);
1002 d2 = dist2(ux, uy, tx, ty);
1003 for (tx = 1; tx < COLNO; ++tx)
1004 for (ty = 0; ty < ROWNO; ++ty)
1005 if (travel[tx][ty]) {
1006 nxtdist = distmin(ux, uy, tx, ty);
1007 if (nxtdist == dist && couldsee(tx, ty)) {
1008 nd2 = dist2(ux, uy, tx, ty);
1009 if (nd2 < d2) {
1010 /* prefer non-zigzag path */
1011 px = tx;
1012 py = ty;
1013 d2 = nd2;
1015 } else if (nxtdist < dist && couldsee(tx, ty)) {
1016 px = tx;
1017 py = ty;
1018 dist = nxtdist;
1019 d2 = dist2(ux, uy, tx, ty);
1023 if (px == u.ux && py == u.uy) {
1024 /* no guesses, just go in the general direction */
1025 u.dx = sgn(u.tx - u.ux);
1026 u.dy = sgn(u.ty - u.uy);
1027 if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE))
1028 return TRUE;
1029 goto found;
1031 #ifdef DEBUG
1032 if (trav_debug) {
1033 /* Use of warning glyph is arbitrary. It stands out. */
1034 tmp_at(DISP_ALL, warning_to_glyph(2));
1035 tmp_at(px, py);
1036 delay_output();
1037 if (flags.runmode == RUN_CRAWL) {
1038 delay_output();
1039 delay_output();
1040 delay_output();
1041 delay_output();
1043 tmp_at(DISP_END, 0);
1045 #endif /* DEBUG */
1046 tx = px;
1047 ty = py;
1048 ux = u.ux;
1049 uy = u.uy;
1050 set = 0;
1051 n = radius = 1;
1052 guess = FALSE;
1053 goto noguess;
1055 return FALSE;
1058 found:
1059 u.dx = 0;
1060 u.dy = 0;
1061 nomul(0);
1062 return FALSE;
1065 /* try to escape being stuck in a trapped state by walking out of it;
1066 return true iff moving should continue to intended destination
1067 (all failures and most successful escapes leave hero at original spot) */
1068 STATIC_OVL boolean
1069 trapmove(x, y, desttrap)
1070 int x, y; /* targetted destination, <u.ux+u.dx,u.uy+u.dy> */
1071 struct trap *desttrap; /* nonnull if another trap at <x,y> */
1073 boolean anchored;
1074 const char *predicament, *culprit;
1075 char *steedname = !u.usteed ? (char *) 0 : y_monnam(u.usteed);
1077 if (!u.utrap)
1078 return TRUE; /* sanity check */
1080 switch (u.utraptype) {
1081 case TT_BEARTRAP:
1082 if (flags.verbose) {
1083 predicament = "caught in a bear trap";
1084 if (u.usteed)
1085 Norep("%s is %s.", upstart(steedname), predicament);
1086 else
1087 Norep("You are %s.", predicament);
1089 /* [why does diagonal movement give quickest escape?] */
1090 if ((u.dx && u.dy) || !rn2(5))
1091 u.utrap--;
1092 break;
1093 case TT_PIT:
1094 if (desttrap && desttrap->tseen
1095 && (desttrap->ttyp == PIT || desttrap->ttyp == SPIKED_PIT))
1096 return TRUE; /* move into adjacent pit */
1097 /* try to escape; position stays same regardless of success */
1098 climb_pit();
1099 break;
1100 case TT_WEB:
1101 if (uwep && uwep->oartifact == ART_STING) {
1102 u.utrap = 0;
1103 pline("Sting cuts through the web!");
1104 break; /* escape trap but don't move */
1106 if (--u.utrap) {
1107 if (flags.verbose) {
1108 predicament = "stuck to the web";
1109 if (u.usteed)
1110 Norep("%s is %s.", upstart(steedname), predicament);
1111 else
1112 Norep("You are %s.", predicament);
1114 } else {
1115 if (u.usteed)
1116 pline("%s breaks out of the web.", upstart(steedname));
1117 else
1118 You("disentangle yourself.");
1120 break;
1121 case TT_LAVA:
1122 if (flags.verbose) {
1123 predicament = "stuck in the lava";
1124 if (u.usteed)
1125 Norep("%s is %s.", upstart(steedname), predicament);
1126 else
1127 Norep("You are %s.", predicament);
1129 if (!is_lava(x, y)) {
1130 u.utrap--;
1131 if ((u.utrap & 0xff) == 0) {
1132 u.utrap = 0;
1133 if (u.usteed)
1134 You("lead %s to the edge of the lava.", steedname);
1135 else
1136 You("pull yourself to the edge of the lava.");
1139 u.umoved = TRUE;
1140 break;
1141 case TT_INFLOOR:
1142 case TT_BURIEDBALL:
1143 anchored = (u.utraptype == TT_BURIEDBALL);
1144 if (anchored) {
1145 coord cc;
1147 cc.x = u.ux, cc.y = u.uy;
1148 /* can move normally within radius 1 of buried ball */
1149 if (buried_ball(&cc) && dist2(x, y, cc.x, cc.y) <= 2) {
1150 /* ugly hack: we need to issue some message here
1151 in case "you are chained to the buried ball"
1152 was the most recent message given, otherwise
1153 our next attempt to move out of tether range
1154 after this successful move would have its
1155 can't-do-that message suppressed by Norep */
1156 if (flags.verbose)
1157 Norep("You move within the chain's reach.");
1158 return TRUE;
1161 if (--u.utrap) {
1162 if (flags.verbose) {
1163 if (anchored) {
1164 predicament = "chained to the";
1165 culprit = "buried ball";
1166 } else {
1167 predicament = "stuck in the";
1168 culprit = surface(u.ux, u.uy);
1170 if (u.usteed) {
1171 if (anchored)
1172 Norep("You and %s are %s %s.", steedname, predicament,
1173 culprit);
1174 else
1175 Norep("%s is %s %s.", upstart(steedname), predicament,
1176 culprit);
1177 } else
1178 Norep("You are %s %s.", predicament, culprit);
1180 } else {
1181 if (u.usteed)
1182 pline("%s finally %s free.", upstart(steedname),
1183 !anchored ? "lurches" : "wrenches the ball");
1184 else
1185 You("finally %s free.",
1186 !anchored ? "wriggle" : "wrench the ball");
1187 if (anchored)
1188 buried_ball_to_punishment();
1190 break;
1191 default:
1192 impossible("trapmove: stuck in unknown trap? (%d)",
1193 (int) u.utraptype);
1194 break;
1196 return FALSE;
1199 boolean
1200 u_rooted()
1202 if (!youmonst.data->mmove) {
1203 You("are rooted %s.",
1204 Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
1205 ? "in place"
1206 : "to the ground");
1207 nomul(0);
1208 return TRUE;
1210 return FALSE;
1213 void
1214 domove()
1216 register struct monst *mtmp;
1217 register struct rm *tmpr;
1218 register xchar x, y;
1219 struct trap *trap = NULL;
1220 int wtcap;
1221 boolean on_ice;
1222 xchar chainx = 0, chainy = 0,
1223 ballx = 0, bally = 0; /* ball&chain new positions */
1224 int bc_control = 0; /* control for ball&chain */
1225 boolean cause_delay = FALSE; /* dragging ball will skip a move */
1227 u_wipe_engr(rnd(5));
1229 if (context.travel) {
1230 if (!findtravelpath(FALSE))
1231 (void) findtravelpath(TRUE);
1232 context.travel1 = 0;
1235 if (((wtcap = near_capacity()) >= OVERLOADED
1236 || (wtcap > SLT_ENCUMBER
1237 && (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
1238 : (u.uhp < 10 && u.uhp != u.uhpmax))))
1239 && !Is_airlevel(&u.uz)) {
1240 if (wtcap < OVERLOADED) {
1241 You("don't have enough stamina to move.");
1242 exercise(A_CON, FALSE);
1243 } else
1244 You("collapse under your load.");
1245 nomul(0);
1246 return;
1248 if (u.uswallow) {
1249 u.dx = u.dy = 0;
1250 u.ux = x = u.ustuck->mx;
1251 u.uy = y = u.ustuck->my;
1252 mtmp = u.ustuck;
1253 } else {
1254 if (Is_airlevel(&u.uz) && rn2(4) && !Levitation && !Flying) {
1255 switch (rn2(3)) {
1256 case 0:
1257 You("tumble in place.");
1258 exercise(A_DEX, FALSE);
1259 break;
1260 case 1:
1261 You_cant("control your movements very well.");
1262 break;
1263 case 2:
1264 pline("It's hard to walk in thin air.");
1265 exercise(A_DEX, TRUE);
1266 break;
1268 return;
1271 /* check slippery ice */
1272 on_ice = !Levitation && is_ice(u.ux, u.uy);
1273 if (on_ice) {
1274 static int skates = 0;
1275 if (!skates)
1276 skates = find_skates();
1277 if ((uarmf && uarmf->otyp == skates) || resists_cold(&youmonst)
1278 || Flying || is_floater(youmonst.data)
1279 || is_clinger(youmonst.data) || is_whirly(youmonst.data))
1280 on_ice = FALSE;
1281 else if (!rn2(Cold_resistance ? 3 : 2)) {
1282 HFumbling |= FROMOUTSIDE;
1283 HFumbling &= ~TIMEOUT;
1284 HFumbling += 1; /* slip on next move */
1287 if (!on_ice && (HFumbling & FROMOUTSIDE))
1288 HFumbling &= ~FROMOUTSIDE;
1290 x = u.ux + u.dx;
1291 y = u.uy + u.dy;
1292 if (Stunned || (Confusion && !rn2(5))) {
1293 register int tries = 0;
1295 do {
1296 if (tries++ > 50) {
1297 nomul(0);
1298 return;
1300 confdir();
1301 x = u.ux + u.dx;
1302 y = u.uy + u.dy;
1303 } while (!isok(x, y) || bad_rock(youmonst.data, x, y));
1305 /* turbulence might alter your actual destination */
1306 if (u.uinwater) {
1307 water_friction();
1308 if (!u.dx && !u.dy) {
1309 nomul(0);
1310 return;
1312 x = u.ux + u.dx;
1313 y = u.uy + u.dy;
1315 if (!isok(x, y)) {
1316 nomul(0);
1317 return;
1319 if (((trap = t_at(x, y)) && trap->tseen)
1320 || (Blind && !Levitation && !Flying && !is_clinger(youmonst.data)
1321 && is_pool_or_lava(x, y) && levl[x][y].seenv)) {
1322 if (context.run >= 2) {
1323 nomul(0);
1324 context.move = 0;
1325 return;
1326 } else
1327 nomul(0);
1330 if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) {
1331 if (distu(u.ustuck->mx, u.ustuck->my) > 2) {
1332 /* perhaps it fled (or was teleported or ... ) */
1333 u.ustuck = 0;
1334 } else if (sticks(youmonst.data)) {
1335 /* When polymorphed into a sticking monster,
1336 * u.ustuck means it's stuck to you, not you to it.
1338 You("release %s.", mon_nam(u.ustuck));
1339 u.ustuck = 0;
1340 } else {
1341 /* If holder is asleep or paralyzed:
1342 * 37.5% chance of getting away,
1343 * 12.5% chance of waking/releasing it;
1344 * otherwise:
1345 * 7.5% chance of getting away.
1346 * [strength ought to be a factor]
1347 * If holder is tame and there is no conflict,
1348 * guaranteed escape.
1350 switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
1351 case 0:
1352 case 1:
1353 case 2:
1354 pull_free:
1355 You("pull free from %s.", mon_nam(u.ustuck));
1356 u.ustuck = 0;
1357 break;
1358 case 3:
1359 if (!u.ustuck->mcanmove) {
1360 /* it's free to move on next turn */
1361 u.ustuck->mfrozen = 1;
1362 u.ustuck->msleeping = 0;
1364 /*FALLTHRU*/
1365 default:
1366 if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf)
1367 goto pull_free;
1368 You("cannot escape from %s!", mon_nam(u.ustuck));
1369 nomul(0);
1370 return;
1375 mtmp = m_at(x, y);
1376 if (mtmp && !is_safepet(mtmp)) {
1377 /* Don't attack if you're running, and can see it */
1378 /* It's fine to displace pets, though */
1379 /* We should never get here if forcefight */
1380 if (context.run && ((!Blind && mon_visible(mtmp)
1381 && ((mtmp->m_ap_type != M_AP_FURNITURE
1382 && mtmp->m_ap_type != M_AP_OBJECT)
1383 || Protection_from_shape_changers))
1384 || sensemon(mtmp))) {
1385 nomul(0);
1386 context.move = 0;
1387 return;
1392 u.ux0 = u.ux;
1393 u.uy0 = u.uy;
1394 bhitpos.x = x;
1395 bhitpos.y = y;
1396 tmpr = &levl[x][y];
1398 /* attack monster */
1399 if (mtmp) {
1400 /* don't stop travel when displacing pets; if the
1401 displace fails for some reason, attack() in uhitm.c
1402 will stop travel rather than domove */
1403 if (!is_safepet(mtmp) || context.forcefight) nomul(0);
1404 /* only attack if we know it's there */
1405 /* or if we used the 'F' command to fight blindly */
1406 /* or if it hides_under, in which case we call attack() to print
1407 * the Wait! message.
1408 * This is different from ceiling hiders, who aren't handled in
1409 * attack().
1412 /* If they used a 'm' command, trying to move onto a monster
1413 * prints the below message and wastes a turn. The exception is
1414 * if the monster is unseen and the player doesn't remember an
1415 * invisible monster--then, we fall through to attack() and
1416 * attack_check(), which still wastes a turn, but prints a
1417 * different message and makes the player remember the monster.
1419 if (context.nopick && !context.travel
1420 && (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))) {
1421 if (mtmp->m_ap_type && !Protection_from_shape_changers
1422 && !sensemon(mtmp))
1423 stumble_onto_mimic(mtmp);
1424 else if (mtmp->mpeaceful && !Hallucination)
1425 pline("Pardon me, %s.", m_monnam(mtmp));
1426 else
1427 You("move right into %s.", mon_nam(mtmp));
1428 return;
1430 if (context.forcefight || !mtmp->mundetected || sensemon(mtmp)
1431 || ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)
1432 && !is_safepet(mtmp))) {
1433 /* try to attack; note that it might evade */
1434 /* also, we don't attack tame when _safepet_ */
1435 if (attack(mtmp))
1436 return;
1440 if (context.forcefight && levl[x][y].typ == IRONBARS && uwep) {
1441 struct obj *obj = uwep;
1442 if (breaktest(obj)) {
1443 if (obj->quan > 1L)
1444 obj = splitobj(obj, 1L);
1445 else
1446 setuwep((struct obj *)0);
1447 freeinv(obj);
1449 hit_bars(&obj, u.ux, u.uy, x, y, TRUE, TRUE);
1450 return;
1453 /* specifying 'F' with no monster wastes a turn */
1454 if (context.forcefight
1455 /* remembered an 'I' && didn't use a move command */
1456 || (glyph_is_invisible(levl[x][y].glyph) && !context.nopick)) {
1457 struct obj *boulder = 0;
1458 boolean explo = (Upolyd && attacktype(youmonst.data, AT_EXPL)),
1459 solid = !accessible(x, y);
1460 int glyph = glyph_at(x, y); /* might be monster */
1461 char buf[BUFSZ];
1463 if (!Underwater) {
1464 boulder = sobj_at(BOULDER, x, y);
1465 /* if a statue is displayed at the target location,
1466 player is attempting to attack it [and boulder
1467 handling below is suitable for handling that] */
1468 if (glyph_is_statue(glyph)
1469 || (Hallucination && glyph_is_monster(glyph)))
1470 boulder = sobj_at(STATUE, x, y);
1472 /* force fight at boulder/statue or wall/door while wielding
1473 pick: start digging to break the boulder or wall */
1474 if (context.forcefight
1475 /* can we dig? */
1476 && uwep && dig_typ(uwep, x, y)
1477 /* should we dig? */
1478 && !glyph_is_invisible(glyph) && !glyph_is_monster(glyph)) {
1479 (void) use_pick_axe2(uwep);
1480 return;
1484 /* about to become known empty -- remove 'I' if present */
1485 unmap_object(x, y);
1486 if (boulder)
1487 map_object(boulder, TRUE);
1488 newsym(x, y);
1489 glyph = glyph_at(x, y); /* might have just changed */
1491 if (boulder)
1492 Strcpy(buf, ansimpleoname(boulder));
1493 else if (Underwater && !is_pool(x, y))
1494 /* Underwater, targetting non-water; the map just shows blank
1495 because you don't see remembered terrain while underwater;
1496 although the hero can attack an adjacent monster this way,
1497 assume he can't reach out far enough to distinguish terrain */
1498 Sprintf(buf, (Is_waterlevel(&u.uz) && levl[x][y].typ == AIR)
1499 ? "an air bubble"
1500 : "nothing");
1501 else if (solid)
1502 /* glyph might indicate unseen terrain if hero is blind;
1503 unlike searching, this won't reveal what that terrain is
1504 (except for solid rock, where the glyph would otherwise
1505 yield ludicrous "dark part of a room") */
1506 Strcpy(buf,
1507 (levl[x][y].typ == STONE)
1508 ? "solid rock"
1509 : glyph_is_cmap(glyph)
1510 ? the(defsyms[glyph_to_cmap(glyph)].explanation)
1511 : (const char *) "an unknown obstacle");
1512 /* note: 'solid' is misleadingly named and catches pools
1513 of water and lava as well as rock and walls */
1514 else
1515 Strcpy(buf, "thin air");
1516 You("%s%s %s.",
1517 !(boulder || solid) ? "" : !explo ? "harmlessly " : "futilely ",
1518 explo ? "explode at" : "attack", buf);
1520 nomul(0);
1521 if (explo) {
1522 wake_nearby();
1523 u.mh = -1; /* dead in the current form */
1524 rehumanize();
1526 return;
1528 if (glyph_is_invisible(levl[x][y].glyph)) {
1529 unmap_object(x, y);
1530 newsym(x, y);
1532 /* not attacking an animal, so we try to move */
1533 if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) {
1534 nomul(0);
1535 return;
1538 if (u_rooted())
1539 return;
1541 if (u.utrap) {
1542 if (!trapmove(x, y, trap))
1543 return;
1546 if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) {
1547 if (!context.door_opened) {
1548 context.move = 0;
1549 nomul(0);
1551 return;
1554 /* Move ball and chain. */
1555 if (Punished)
1556 if (!drag_ball(x, y, &bc_control, &ballx, &bally, &chainx, &chainy,
1557 &cause_delay, TRUE))
1558 return;
1560 /* Check regions entering/leaving */
1561 if (!in_out_region(x, y))
1562 return;
1564 /* now move the hero */
1565 mtmp = m_at(x, y);
1566 u.ux += u.dx;
1567 u.uy += u.dy;
1568 /* Move your steed, too */
1569 if (u.usteed) {
1570 u.usteed->mx = u.ux;
1571 u.usteed->my = u.uy;
1572 exercise_steed();
1576 * If safepet at destination then move the pet to the hero's
1577 * previous location using the same conditions as in attack().
1578 * there are special extenuating circumstances:
1579 * (1) if the pet dies then your god angers,
1580 * (2) if the pet gets trapped then your god may disapprove,
1581 * (3) if the pet was already trapped and you attempt to free it
1582 * not only do you encounter the trap but you may frighten your
1583 * pet causing it to go wild! moral: don't abuse this privilege.
1585 * Ceiling-hiding pets are skipped by this section of code, to
1586 * be caught by the normal falling-monster code.
1588 if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) {
1589 /* if trapped, there's a chance the pet goes wild */
1590 if (mtmp->mtrapped) {
1591 if (!rn2(mtmp->mtame)) {
1592 mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
1593 if (mtmp->mleashed)
1594 m_unleash(mtmp, TRUE);
1595 growl(mtmp);
1596 } else {
1597 yelp(mtmp);
1600 mtmp->mundetected = 0;
1601 if (mtmp->m_ap_type)
1602 seemimic(mtmp);
1603 else if (!mtmp->mtame)
1604 newsym(mtmp->mx, mtmp->my);
1606 if (mtmp->mtrapped && (trap = t_at(mtmp->mx, mtmp->my)) != 0
1607 && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)
1608 && sobj_at(BOULDER, trap->tx, trap->ty)) {
1609 /* can't swap places with pet pinned in a pit by a boulder */
1610 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1611 } else if (u.ux0 != x && u.uy0 != y && NODIAG(mtmp->data - mons)) {
1612 /* can't swap places when pet can't move to your spot */
1613 u.ux = u.ux0, u.uy = u.uy0;
1614 You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp)));
1615 } else if (u.ux0 != x && u.uy0 != y && bad_rock(mtmp->data, x, u.uy0)
1616 && bad_rock(mtmp->data, u.ux0, y)
1617 && (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) {
1618 /* can't swap places when pet won't fit thru the opening */
1619 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1620 You("stop. %s won't fit through.", upstart(y_monnam(mtmp)));
1621 } else {
1622 char pnambuf[BUFSZ];
1624 /* save its current description in case of polymorph */
1625 Strcpy(pnambuf, y_monnam(mtmp));
1626 mtmp->mtrapped = 0;
1627 remove_monster(x, y);
1628 place_monster(mtmp, u.ux0, u.uy0);
1629 newsym(x, y);
1630 newsym(u.ux0, u.uy0);
1632 You("%s %s.", mtmp->mtame ? "swap places with" : "frighten",
1633 pnambuf);
1635 /* check for displacing it into pools and traps */
1636 switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) {
1637 case 0:
1638 break;
1639 case 1: /* trapped */
1640 case 3: /* changed levels */
1641 /* there's already been a trap message, reinforce it */
1642 abuse_dog(mtmp);
1643 adjalign(-3);
1644 break;
1645 case 2:
1646 /* drowned or died...
1647 * you killed your pet by direct action, so get experience
1648 * and possibly penalties;
1649 * we want the level gain message, if it happens, to occur
1650 * before the guilt message below
1653 /* minliquid() and mintrap() call mondead() rather than
1654 killed() so we duplicate some of the latter here */
1655 int tmp, mndx;
1657 u.uconduct.killer++;
1658 mndx = monsndx(mtmp->data);
1659 tmp = experience(mtmp, (int) mvitals[mndx].died);
1660 more_experienced(tmp, 0);
1661 newexplevel(); /* will decide if you go up */
1663 /* That's no way to treat a pet! Your god gets angry.
1665 * [This has always been pretty iffy. Why does your
1666 * patron deity care at all, let alone enough to get mad?]
1668 if (rn2(4)) {
1669 You_feel("guilty about losing your pet like this.");
1670 u.ugangr++;
1671 adjalign(-15);
1673 break;
1674 default:
1675 pline("that's strange, unknown mintrap result!");
1676 break;
1681 reset_occupations();
1682 if (context.run) {
1683 if (context.run < 8)
1684 if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ)
1685 || IS_FURNITURE(tmpr->typ))
1686 nomul(0);
1689 if (hides_under(youmonst.data) || (youmonst.data->mlet == S_EEL) || u.dx
1690 || u.dy)
1691 (void) hideunder(&youmonst);
1694 * Mimics (or whatever) become noticeable if they move and are
1695 * imitating something that doesn't move. We could extend this
1696 * to non-moving monsters...
1698 if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT
1699 || youmonst.m_ap_type == M_AP_FURNITURE))
1700 youmonst.m_ap_type = M_AP_NOTHING;
1702 check_leash(u.ux0, u.uy0);
1704 if (u.ux0 != u.ux || u.uy0 != u.uy) {
1705 u.umoved = TRUE;
1706 /* Clean old position -- vision_recalc() will print our new one. */
1707 newsym(u.ux0, u.uy0);
1708 /* Since the hero has moved, adjust what can be seen/unseen. */
1709 vision_recalc(1); /* Do the work now in the recover time. */
1710 invocation_message();
1713 if (Punished) /* put back ball and chain */
1714 move_bc(0, bc_control, ballx, bally, chainx, chainy);
1716 spoteffects(TRUE);
1718 /* delay next move because of ball dragging */
1719 /* must come after we finished picking up, in spoteffects() */
1720 if (cause_delay) {
1721 nomul(-2);
1722 multi_reason = "dragging an iron ball";
1723 nomovemsg = "";
1726 if (context.run && flags.runmode != RUN_TPORT) {
1727 /* display every step or every 7th step depending upon mode */
1728 if (flags.runmode != RUN_LEAP || !(moves % 7L)) {
1729 if (flags.time)
1730 context.botl = 1;
1731 curs_on_u();
1732 delay_output();
1733 if (flags.runmode == RUN_CRAWL) {
1734 delay_output();
1735 delay_output();
1736 delay_output();
1737 delay_output();
1743 /* combat increases metabolism */
1744 boolean
1745 overexertion()
1747 /* this used to be part of domove() when moving to a monster's
1748 position, but is now called by attack() so that it doesn't
1749 execute if you decline to attack a peaceful monster */
1750 gethungry();
1751 if ((moves % 3L) != 0L && near_capacity() >= HVY_ENCUMBER) {
1752 int *hp = (!Upolyd ? &u.uhp : &u.mh);
1754 if (*hp > 1) {
1755 *hp -= 1;
1756 } else {
1757 You("pass out from exertion!");
1758 exercise(A_CON, FALSE);
1759 fall_asleep(-10, FALSE);
1762 return (boolean) (multi < 0); /* might have fainted (forced to sleep) */
1765 void
1766 invocation_message()
1768 /* a special clue-msg when on the Invocation position */
1769 if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
1770 char buf[BUFSZ];
1771 struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION);
1773 nomul(0); /* stop running or travelling */
1774 if (u.usteed)
1775 Sprintf(buf, "beneath %s", y_monnam(u.usteed));
1776 else if (Levitation || Flying)
1777 Strcpy(buf, "beneath you");
1778 else
1779 Sprintf(buf, "under your %s", makeplural(body_part(FOOT)));
1781 You_feel("a strange vibration %s.", buf);
1782 u.uevent.uvibrated = 1;
1783 if (otmp && otmp->spe == 7 && otmp->lamplit)
1784 pline("%s %s!", The(xname(otmp)),
1785 Blind ? "throbs palpably" : "glows with a strange light");
1789 /* moving onto different terrain;
1790 might be going into solid rock, inhibiting levitation or flight,
1791 or coming back out of such, reinstating levitation/flying */
1792 STATIC_OVL void
1793 switch_terrain()
1795 struct rm *lev = &levl[u.ux][u.uy];
1796 boolean blocklev = (IS_ROCK(lev->typ) || closed_door(u.ux, u.uy)
1797 || (Is_waterlevel(&u.uz) && lev->typ == WATER));
1799 if (blocklev) {
1800 /* called from spoteffects(), skip float_down() */
1801 if (Levitation)
1802 You_cant("levitate in here.");
1803 BLevitation |= FROMOUTSIDE;
1804 } else if (BLevitation) {
1805 BLevitation &= ~FROMOUTSIDE;
1806 if (Levitation)
1807 float_up();
1809 /* the same terrain that blocks levitation also blocks flight */
1810 if (blocklev) {
1811 if (Flying)
1812 You_cant("fly in here.");
1813 BFlying |= FROMOUTSIDE;
1814 } else if (BFlying) {
1815 BFlying &= ~FROMOUTSIDE;
1816 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
1817 /* [minor bug: we don't know whether this is beginning flight or
1818 resuming it; that could be tracked so that this message could
1819 be adjusted to "resume flying", but isn't worth the effort...] */
1820 if (Flying)
1821 You("start flying.");
1825 /* extracted from spoteffects; called by spoteffects to check for entering or
1826 leaving a pool of water/lava, and by moveloop to check for staying on one
1828 returns true to skip rest of spoteffects */
1829 boolean
1830 pooleffects(newspot)
1831 boolean newspot; /* true if called by spoteffects */
1833 /* check for leaving water */
1834 if (u.uinwater) {
1835 boolean still_inwater = FALSE; /* assume we're getting out */
1837 if (!is_pool(u.ux, u.uy)) {
1838 if (Is_waterlevel(&u.uz))
1839 You("pop into an air bubble.");
1840 else if (is_lava(u.ux, u.uy))
1841 You("leave the water..."); /* oops! */
1842 else
1843 You("are on solid %s again.",
1844 is_ice(u.ux, u.uy) ? "ice" : "land");
1845 } else if (Is_waterlevel(&u.uz)) {
1846 still_inwater = TRUE;
1847 } else if (Levitation) {
1848 You("pop out of the water like a cork!");
1849 } else if (Flying) {
1850 You("fly out of the water.");
1851 } else if (Wwalking) {
1852 You("slowly rise above the surface.");
1853 } else {
1854 still_inwater = TRUE;
1856 if (!still_inwater) {
1857 boolean was_underwater = (Underwater && !Is_waterlevel(&u.uz));
1859 u.uinwater = 0; /* leave the water */
1860 if (was_underwater) { /* restore vision */
1861 docrt();
1862 vision_full_recalc = 1;
1867 /* check for entering water or lava */
1868 if (!u.ustuck && !Levitation && !Flying && is_pool_or_lava(u.ux, u.uy)) {
1869 if (u.usteed
1870 && (is_flyer(u.usteed->data) || is_floater(u.usteed->data)
1871 || is_clinger(u.usteed->data))) {
1872 /* floating or clinging steed keeps hero safe (is_flyer() test
1873 is redundant; it can't be true since Flying yielded false) */
1874 return FALSE;
1875 } else if (u.usteed) {
1876 /* steed enters pool */
1877 dismount_steed(Underwater ? DISMOUNT_FELL : DISMOUNT_GENERIC);
1878 /* dismount_steed() -> float_down() -> pickup()
1879 (float_down doesn't do autopickup on Air or Water) */
1880 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
1881 return FALSE;
1882 /* even if we actually end up at same location, float_down()
1883 has already done spoteffect()'s trap and pickup actions */
1884 if (newspot)
1885 check_special_room(FALSE); /* spoteffects */
1886 return TRUE;
1888 /* not mounted */
1890 /* drown(),lava_effects() return true if hero changes
1891 location while surviving the problem */
1892 if (is_lava(u.ux, u.uy)) {
1893 if (lava_effects())
1894 return TRUE;
1895 } else if (!Wwalking
1896 && (newspot || !u.uinwater || !(Swimming || Amphibious))) {
1897 if (drown())
1898 return TRUE;
1901 return FALSE;
1904 void
1905 spoteffects(pick)
1906 boolean pick;
1908 static int inspoteffects = 0;
1909 static coord spotloc;
1910 static int spotterrain;
1911 static struct trap *spottrap = (struct trap *) 0;
1912 static unsigned spottraptyp = NO_TRAP;
1913 struct trap *trap = t_at(u.ux, u.uy);
1914 register struct monst *mtmp;
1916 /* prevent recursion from affecting the hero all over again
1917 [hero poly'd to iron golem enters water here, drown() inflicts
1918 damage that triggers rehumanize() which calls spoteffects()...] */
1919 if (inspoteffects && u.ux == spotloc.x && u.uy == spotloc.y
1920 /* except when reason is transformed terrain (ice -> water) */
1921 && spotterrain == levl[u.ux][u.uy].typ
1922 /* or transformed trap (land mine -> pit) */
1923 && (!spottrap || !trap || trap->ttyp == spottraptyp))
1924 return;
1926 ++inspoteffects;
1927 spotterrain = levl[u.ux][u.uy].typ;
1928 spotloc.x = u.ux, spotloc.y = u.uy;
1930 /* moving onto different terrain might cause Levitation to toggle */
1931 if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0))
1932 switch_terrain();
1934 if (pooleffects(TRUE))
1935 goto spotdone;
1937 check_special_room(FALSE);
1938 if (IS_SINK(levl[u.ux][u.uy].typ) && Levitation)
1939 dosinkfall();
1940 if (!in_steed_dismounting) { /* if dismounting, we'll check again later */
1941 boolean pit;
1943 /* if levitation is due to time out at the end of this
1944 turn, allowing it to do so could give the perception
1945 that a trap here is being triggered twice, so adjust
1946 the timeout to prevent that */
1947 if (trap && (HLevitation & TIMEOUT) == 1L && !ELevitation
1948 && !(HLevitation & ~TIMEOUT)) {
1949 if (rn2(2)) { /* defer timeout */
1950 incr_itimeout(&HLevitation, 1L);
1951 } else { /* timeout early */
1952 if (float_down(I_SPECIAL | TIMEOUT, 0L)) {
1953 /* levitation has ended; we've already triggered
1954 any trap and [usually] performed autopickup */
1955 trap = 0;
1956 pick = FALSE;
1961 * If not a pit, pickup before triggering trap.
1962 * If pit, trigger trap before pickup.
1964 pit = (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT));
1965 if (pick && !pit)
1966 (void) pickup(1);
1968 if (trap) {
1970 * dotrap on a fire trap calls melt_ice() which triggers
1971 * spoteffects() (again) which can trigger the same fire
1972 * trap (again). Use static spottrap to prevent that.
1973 * We track spottraptyp because some traps morph
1974 * (landmine to pit) and any new trap type
1975 * should get triggered.
1977 if (!spottrap || spottraptyp != trap->ttyp) {
1978 spottrap = trap;
1979 spottraptyp = trap->ttyp;
1980 dotrap(trap, 0); /* fall into arrow trap, etc. */
1981 spottrap = (struct trap *) 0;
1982 spottraptyp = NO_TRAP;
1985 if (pick && pit)
1986 (void) pickup(1);
1988 /* Warning alerts you to ice danger */
1989 if (Warning && is_ice(u.ux, u.uy)) {
1990 static const char *const icewarnings[] = {
1991 "The ice seems very soft and slushy.",
1992 "You feel the ice shift beneath you!",
1993 "The ice, is gonna BREAK!", /* The Dead Zone */
1995 long time_left = spot_time_left(u.ux, u.uy, MELT_ICE_AWAY);
1996 if (time_left && time_left < 15L)
1997 pline1((time_left < 5L) ? icewarnings[2] : (time_left < 10L)
1998 ? icewarnings[1]
1999 : icewarnings[0]);
2001 if ((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
2002 mtmp->mundetected = mtmp->msleeping = 0;
2003 switch (mtmp->data->mlet) {
2004 case S_PIERCER:
2005 pline("%s suddenly drops from the %s!", Amonnam(mtmp),
2006 ceiling(u.ux, u.uy));
2007 if (mtmp->mtame) /* jumps to greet you, not attack */
2009 else if (uarmh && is_metallic(uarmh))
2010 pline("Its blow glances off your %s.",
2011 helm_simple_name(uarmh));
2012 else if (u.uac + 3 <= rnd(20))
2013 You("are almost hit by %s!",
2014 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2015 else {
2016 int dmg;
2017 You("are hit by %s!",
2018 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2019 dmg = d(4, 6);
2020 if (Half_physical_damage)
2021 dmg = (dmg + 1) / 2;
2022 mdamageu(mtmp, dmg);
2024 break;
2025 default: /* monster surprises you. */
2026 if (mtmp->mtame)
2027 pline("%s jumps near you from the %s.", Amonnam(mtmp),
2028 ceiling(u.ux, u.uy));
2029 else if (mtmp->mpeaceful) {
2030 You("surprise %s!",
2031 Blind && !sensemon(mtmp) ? something : a_monnam(mtmp));
2032 mtmp->mpeaceful = 0;
2033 } else
2034 pline("%s attacks you by surprise!", Amonnam(mtmp));
2035 break;
2037 mnexto(mtmp); /* have to move the monster */
2039 spotdone:
2040 if (!--inspoteffects) {
2041 spotterrain = STONE; /* 0 */
2042 spotloc.x = spotloc.y = 0;
2044 return;
2047 /* returns first matching monster */
2048 STATIC_OVL struct monst *
2049 monstinroom(mdat, roomno)
2050 struct permonst *mdat;
2051 int roomno;
2053 register struct monst *mtmp;
2055 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2056 if (DEADMONSTER(mtmp))
2057 continue;
2058 if (mtmp->data == mdat
2059 && index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET))
2060 return mtmp;
2062 return (struct monst *) 0;
2065 char *
2066 in_rooms(x, y, typewanted)
2067 register xchar x, y;
2068 register int typewanted;
2070 static char buf[5];
2071 char rno, *ptr = &buf[4];
2072 int typefound, min_x, min_y, max_x, max_y_offset, step;
2073 register struct rm *lev;
2075 #define goodtype(rno) \
2076 (!typewanted \
2077 || ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) \
2078 || ((typewanted == SHOPBASE) && (typefound > SHOPBASE)))
2080 switch (rno = levl[x][y].roomno) {
2081 case NO_ROOM:
2082 return ptr;
2083 case SHARED:
2084 step = 2;
2085 break;
2086 case SHARED_PLUS:
2087 step = 1;
2088 break;
2089 default: /* i.e. a regular room # */
2090 if (goodtype(rno))
2091 *(--ptr) = rno;
2092 return ptr;
2095 min_x = x - 1;
2096 max_x = x + 1;
2097 if (x < 1)
2098 min_x += step;
2099 else if (x >= COLNO)
2100 max_x -= step;
2102 min_y = y - 1;
2103 max_y_offset = 2;
2104 if (min_y < 0) {
2105 min_y += step;
2106 max_y_offset -= step;
2107 } else if ((min_y + max_y_offset) >= ROWNO)
2108 max_y_offset -= step;
2110 for (x = min_x; x <= max_x; x += step) {
2111 lev = &levl[x][min_y];
2112 y = 0;
2113 if (((rno = lev[y].roomno) >= ROOMOFFSET) && !index(ptr, rno)
2114 && goodtype(rno))
2115 *(--ptr) = rno;
2116 y += step;
2117 if (y > max_y_offset)
2118 continue;
2119 if (((rno = lev[y].roomno) >= ROOMOFFSET) && !index(ptr, rno)
2120 && goodtype(rno))
2121 *(--ptr) = rno;
2122 y += step;
2123 if (y > max_y_offset)
2124 continue;
2125 if (((rno = lev[y].roomno) >= ROOMOFFSET) && !index(ptr, rno)
2126 && goodtype(rno))
2127 *(--ptr) = rno;
2129 return ptr;
2132 /* is (x,y) in a town? */
2133 boolean
2134 in_town(x, y)
2135 register int x, y;
2137 s_level *slev = Is_special(&u.uz);
2138 register struct mkroom *sroom;
2139 boolean has_subrooms = FALSE;
2141 if (!slev || !slev->flags.town)
2142 return FALSE;
2145 * See if (x,y) is in a room with subrooms, if so, assume it's the
2146 * town. If there are no subrooms, the whole level is in town.
2148 for (sroom = &rooms[0]; sroom->hx > 0; sroom++) {
2149 if (sroom->nsubrooms > 0) {
2150 has_subrooms = TRUE;
2151 if (inside_room(sroom, x, y))
2152 return TRUE;
2156 return !has_subrooms;
2159 STATIC_OVL void
2160 move_update(newlev)
2161 register boolean newlev;
2163 char *ptr1, *ptr2, *ptr3, *ptr4;
2165 Strcpy(u.urooms0, u.urooms);
2166 Strcpy(u.ushops0, u.ushops);
2167 if (newlev) {
2168 u.urooms[0] = '\0';
2169 u.uentered[0] = '\0';
2170 u.ushops[0] = '\0';
2171 u.ushops_entered[0] = '\0';
2172 Strcpy(u.ushops_left, u.ushops0);
2173 return;
2175 Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0));
2177 for (ptr1 = &u.urooms[0], ptr2 = &u.uentered[0], ptr3 = &u.ushops[0],
2178 ptr4 = &u.ushops_entered[0];
2179 *ptr1; ptr1++) {
2180 if (!index(u.urooms0, *ptr1))
2181 *(ptr2++) = *ptr1;
2182 if (IS_SHOP(*ptr1 - ROOMOFFSET)) {
2183 *(ptr3++) = *ptr1;
2184 if (!index(u.ushops0, *ptr1))
2185 *(ptr4++) = *ptr1;
2188 *ptr2 = '\0';
2189 *ptr3 = '\0';
2190 *ptr4 = '\0';
2192 /* filter u.ushops0 -> u.ushops_left */
2193 for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++)
2194 if (!index(u.ushops, *ptr1))
2195 *(ptr2++) = *ptr1;
2196 *ptr2 = '\0';
2199 void
2200 check_special_room(newlev)
2201 register boolean newlev;
2203 register struct monst *mtmp;
2204 char *ptr;
2206 move_update(newlev);
2208 if (*u.ushops0)
2209 u_left_shop(u.ushops_left, newlev);
2211 if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */
2212 return; /* no entrance messages necessary */
2214 /* Did we just enter a shop? */
2215 if (*u.ushops_entered)
2216 u_entered_shop(u.ushops_entered);
2218 for (ptr = &u.uentered[0]; *ptr; ptr++) {
2219 int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype;
2220 boolean msg_given = TRUE;
2222 /* Did we just enter some other special room? */
2223 /* vault.c insists that a vault remain a VAULT,
2224 * and temples should remain TEMPLEs,
2225 * but everything else gives a message only the first time */
2226 switch (rt) {
2227 case ZOO:
2228 pline("Welcome to David's treasure zoo!");
2229 break;
2230 case SWAMP:
2231 pline("It %s rather %s down here.", Blind ? "feels" : "looks",
2232 Blind ? "humid" : "muddy");
2233 break;
2234 case COURT:
2235 You("enter an opulent throne room!");
2236 break;
2237 case LEPREHALL:
2238 You("enter a leprechaun hall!");
2239 break;
2240 case MORGUE:
2241 if (midnight()) {
2242 const char *run = locomotion(youmonst.data, "Run");
2243 pline("%s away! %s away!", run, run);
2244 } else
2245 You("have an uncanny feeling...");
2246 break;
2247 case BEEHIVE:
2248 You("enter a giant beehive!");
2249 break;
2250 case COCKNEST:
2251 You("enter a disgusting nest!");
2252 break;
2253 case ANTHOLE:
2254 You("enter an anthole!");
2255 break;
2256 case BARRACKS:
2257 if (monstinroom(&mons[PM_SOLDIER], roomno)
2258 || monstinroom(&mons[PM_SERGEANT], roomno)
2259 || monstinroom(&mons[PM_LIEUTENANT], roomno)
2260 || monstinroom(&mons[PM_CAPTAIN], roomno))
2261 You("enter a military barracks!");
2262 else
2263 You("enter an abandoned barracks.");
2264 break;
2265 case DELPHI: {
2266 struct monst *oracle = monstinroom(&mons[PM_ORACLE], roomno);
2267 if (oracle) {
2268 if (!oracle->mpeaceful)
2269 verbalize("You're in Delphi, %s.", plname);
2270 else
2271 verbalize("%s, %s, welcome to Delphi!",
2272 Hello((struct monst *) 0), plname);
2273 } else
2274 msg_given = FALSE;
2275 break;
2277 case TEMPLE:
2278 intemple(roomno + ROOMOFFSET);
2279 /*FALLTHRU*/
2280 default:
2281 msg_given = (rt == TEMPLE);
2282 rt = 0;
2283 break;
2285 if (msg_given)
2286 room_discovered(roomno);
2288 if (rt != 0) {
2289 rooms[roomno].rtype = OROOM;
2290 if (!search_special(rt)) {
2291 /* No more room of that type */
2292 switch (rt) {
2293 case COURT:
2294 level.flags.has_court = 0;
2295 break;
2296 case SWAMP:
2297 level.flags.has_swamp = 0;
2298 break;
2299 case MORGUE:
2300 level.flags.has_morgue = 0;
2301 break;
2302 case ZOO:
2303 level.flags.has_zoo = 0;
2304 break;
2305 case BARRACKS:
2306 level.flags.has_barracks = 0;
2307 break;
2308 case TEMPLE:
2309 level.flags.has_temple = 0;
2310 break;
2311 case BEEHIVE:
2312 level.flags.has_beehive = 0;
2313 break;
2316 if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO)
2317 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2318 if (DEADMONSTER(mtmp))
2319 continue;
2320 if (!Stealth && !rn2(3))
2321 mtmp->msleeping = 0;
2326 return;
2330 dopickup()
2332 int count;
2333 struct trap *traphere = t_at(u.ux, u.uy);
2334 /* awful kludge to work around parse()'s pre-decrement */
2335 count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0;
2336 multi = 0; /* always reset */
2337 /* uswallow case added by GAN 01/29/87 */
2338 if (u.uswallow) {
2339 if (!u.ustuck->minvent) {
2340 if (is_animal(u.ustuck->data)) {
2341 You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
2342 pline("But it's kind of slimy, so you drop it.");
2343 } else
2344 You("don't %s anything in here to pick up.",
2345 Blind ? "feel" : "see");
2346 return 1;
2347 } else {
2348 int tmpcount = -count;
2349 return loot_mon(u.ustuck, &tmpcount, (boolean *) 0);
2352 if (is_pool(u.ux, u.uy)) {
2353 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2354 || (Flying && !Breathless)) {
2355 You("cannot dive into the water to pick things up.");
2356 return 0;
2357 } else if (!Underwater) {
2358 You_cant("even see the bottom, let alone pick up %s.", something);
2359 return 0;
2362 if (is_lava(u.ux, u.uy)) {
2363 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2364 || (Flying && !Breathless)) {
2365 You_cant("reach the bottom to pick things up.");
2366 return 0;
2367 } else if (!likes_lava(youmonst.data)) {
2368 You("would burn to a crisp trying to pick things up.");
2369 return 0;
2372 if (!OBJ_AT(u.ux, u.uy)) {
2373 register struct rm *lev = &levl[u.ux][u.uy];
2374 if (IS_THRONE(lev->typ))
2375 pline("It must weigh%s a ton!", lev->looted ? " almost" : "");
2376 else if (IS_SINK(lev->typ))
2377 pline_The("plumbing connects it to the floor.");
2378 else if (IS_GRAVE(lev->typ))
2379 You("don't need a gravestone. Yet.");
2380 else if (IS_FOUNTAIN(lev->typ))
2381 You("could drink the water...");
2382 else if (IS_DOOR(lev->typ) && (lev->doormask & D_ISOPEN))
2383 pline("It won't come off the hinges.");
2384 else
2385 There("is nothing here to pick up.");
2386 return 0;
2388 if (!can_reach_floor(TRUE)) {
2389 if (traphere && uteetering_at_seen_pit(traphere))
2390 You("cannot reach the bottom of the pit.");
2391 else if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
2392 rider_cant_reach();
2393 else if (Blind && !can_reach_floor(TRUE))
2394 You("cannot reach anything here.");
2395 else
2396 You("cannot reach the %s.", surface(u.ux, u.uy));
2397 return 0;
2400 return pickup(-count);
2403 /* stop running if we see something interesting */
2404 /* turn around a corner if that is the only way we can proceed */
2405 /* do not turn left or right twice */
2406 void
2407 lookaround()
2409 register int x, y, i, x0 = 0, y0 = 0, m0 = 1, i0 = 9;
2410 register int corrct = 0, noturn = 0;
2411 register struct monst *mtmp;
2412 register struct trap *trap;
2414 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
2415 /* they polymorphed while in the middle of a long move. */
2416 if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
2417 nomul(0);
2418 return;
2421 if (Blind || context.run == 0)
2422 return;
2423 for (x = u.ux - 1; x <= u.ux + 1; x++)
2424 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2425 if (!isok(x, y))
2426 continue;
2428 if (u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy)
2429 continue;
2431 if (x == u.ux && y == u.uy)
2432 continue;
2434 if ((mtmp = m_at(x, y)) && mtmp->m_ap_type != M_AP_FURNITURE
2435 && mtmp->m_ap_type != M_AP_OBJECT
2436 && (!mtmp->minvis || See_invisible) && !mtmp->mundetected) {
2437 if ((context.run != 1 && !mtmp->mtame)
2438 || (x == u.ux + u.dx && y == u.uy + u.dy && !context.travel))
2439 goto stop;
2442 if (levl[x][y].typ == STONE)
2443 continue;
2444 if (x == u.ux - u.dx && y == u.uy - u.dy)
2445 continue;
2447 if (IS_ROCK(levl[x][y].typ) || (levl[x][y].typ == ROOM)
2448 || IS_AIR(levl[x][y].typ))
2449 continue;
2450 else if (closed_door(x, y)
2451 || (mtmp && is_door_mappear(mtmp))) {
2452 if (x != u.ux && y != u.uy)
2453 continue;
2454 if (context.run != 1)
2455 goto stop;
2456 goto bcorr;
2457 } else if (levl[x][y].typ == CORR) {
2458 bcorr:
2459 if (levl[u.ux][u.uy].typ != ROOM) {
2460 if (context.run == 1 || context.run == 3
2461 || context.run == 8) {
2462 i = dist2(x, y, u.ux + u.dx, u.uy + u.dy);
2463 if (i > 2)
2464 continue;
2465 if (corrct == 1 && dist2(x, y, x0, y0) != 1)
2466 noturn = 1;
2467 if (i < i0) {
2468 i0 = i;
2469 x0 = x;
2470 y0 = y;
2471 m0 = mtmp ? 1 : 0;
2474 corrct++;
2476 continue;
2477 } else if ((trap = t_at(x, y)) && trap->tseen) {
2478 if (context.run == 1)
2479 goto bcorr; /* if you must */
2480 if (x == u.ux + u.dx && y == u.uy + u.dy)
2481 goto stop;
2482 continue;
2483 } else if (is_pool_or_lava(x, y)) {
2484 /* water and lava only stop you if directly in front, and stop
2485 * you even if you are running
2487 if (!Levitation && !Flying && !is_clinger(youmonst.data)
2488 && x == u.ux + u.dx && y == u.uy + u.dy)
2489 /* No Wwalking check; otherwise they'd be able
2490 * to test boots by trying to SHIFT-direction
2491 * into a pool and seeing if the game allowed it
2493 goto stop;
2494 continue;
2495 } else { /* e.g. objects or trap or stairs */
2496 if (context.run == 1)
2497 goto bcorr;
2498 if (context.run == 8)
2499 continue;
2500 if (mtmp)
2501 continue; /* d */
2502 if (((x == u.ux - u.dx) && (y != u.uy + u.dy))
2503 || ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
2504 continue;
2506 stop:
2507 nomul(0);
2508 return;
2509 } /* end for loops */
2511 if (corrct > 1 && context.run == 2)
2512 goto stop;
2513 if ((context.run == 1 || context.run == 3 || context.run == 8) && !noturn
2514 && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) {
2515 /* make sure that we do not turn too far */
2516 if (i0 == 2) {
2517 if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
2518 i = 2; /* straight turn right */
2519 else
2520 i = -2; /* straight turn left */
2521 } else if (u.dx && u.dy) {
2522 if ((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy))
2523 i = -1; /* half turn left */
2524 else
2525 i = 1; /* half turn right */
2526 } else {
2527 if ((x0 - u.ux == y0 - u.uy && !u.dy)
2528 || (x0 - u.ux != y0 - u.uy && u.dy))
2529 i = 1; /* half turn right */
2530 else
2531 i = -1; /* half turn left */
2534 i += u.last_str_turn;
2535 if (i <= 2 && i >= -2) {
2536 u.last_str_turn = i;
2537 u.dx = x0 - u.ux;
2538 u.dy = y0 - u.uy;
2543 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2544 STATIC_OVL boolean
2545 doorless_door(x, y)
2546 int x, y;
2548 struct rm *lev_p = &levl[x][y];
2550 if (!IS_DOOR(lev_p->typ))
2551 return FALSE;
2552 /* all rogue level doors are doorless but disallow diagonal access, so
2553 we treat them as if their non-existant doors were actually present */
2554 if (Is_rogue_level(&u.uz))
2555 return FALSE;
2556 return !(lev_p->doormask & ~(D_NODOOR | D_BROKEN));
2559 /* used by drown() to check whether hero can crawl from water to <x,y> */
2560 boolean
2561 crawl_destination(x, y)
2562 int x, y;
2564 /* is location ok in general? */
2565 if (!goodpos(x, y, &youmonst, 0))
2566 return FALSE;
2568 /* orthogonal movement is unrestricted when destination is ok */
2569 if (x == u.ux || y == u.uy)
2570 return TRUE;
2572 /* diagonal movement has some restrictions */
2573 if (NODIAG(u.umonnum))
2574 return FALSE; /* poly'd into a grid bug... */
2575 if (Passes_walls)
2576 return TRUE; /* or a xorn... */
2577 /* pool could be next to a door, conceivably even inside a shop */
2578 if (IS_DOOR(levl[x][y].typ) && (!doorless_door(x, y) || block_door(x, y)))
2579 return FALSE;
2580 /* finally, are we trying to squeeze through a too-narrow gap? */
2581 return !(bad_rock(youmonst.data, u.ux, y)
2582 && bad_rock(youmonst.data, x, u.uy));
2585 /* something like lookaround, but we are not running */
2586 /* react only to monsters that might hit us */
2588 monster_nearby()
2590 register int x, y;
2591 register struct monst *mtmp;
2593 /* Also see the similar check in dochugw() in monmove.c */
2594 for (x = u.ux - 1; x <= u.ux + 1; x++)
2595 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2596 if (!isok(x, y))
2597 continue;
2598 if (x == u.ux && y == u.uy)
2599 continue;
2600 if ((mtmp = m_at(x, y)) && mtmp->m_ap_type != M_AP_FURNITURE
2601 && mtmp->m_ap_type != M_AP_OBJECT
2602 && (!mtmp->mpeaceful || Hallucination)
2603 && (!is_hider(mtmp->data) || !mtmp->mundetected)
2604 && !noattacks(mtmp->data) && mtmp->mcanmove
2605 && !mtmp->msleeping /* aplvax!jcn */
2606 && !onscary(u.ux, u.uy, mtmp) && canspotmon(mtmp))
2607 return 1;
2609 return 0;
2612 void
2613 nomul(nval)
2614 register int nval;
2616 if (multi < nval)
2617 return; /* This is a bug fix by ab@unido */
2618 u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */
2619 u.usleep = 0;
2620 multi = nval;
2621 if (nval == 0)
2622 multi_reason = NULL;
2623 context.travel = context.travel1 = context.mv = context.run = 0;
2626 /* called when a non-movement, multi-turn action has completed */
2627 void
2628 unmul(msg_override)
2629 const char *msg_override;
2631 multi = 0; /* caller will usually have done this already */
2632 if (msg_override)
2633 nomovemsg = msg_override;
2634 else if (!nomovemsg)
2635 nomovemsg = You_can_move_again;
2636 if (*nomovemsg)
2637 pline1(nomovemsg);
2638 nomovemsg = 0;
2639 u.usleep = 0;
2640 multi_reason = NULL;
2641 if (afternmv)
2642 (*afternmv)();
2643 afternmv = 0;
2646 STATIC_OVL void
2647 maybe_wail()
2649 static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
2650 SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES,
2651 TELEPORT_CONTROL, STEALTH, FAST, INVIS };
2653 if (moves <= wailmsg + 50)
2654 return;
2656 wailmsg = moves;
2657 if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) {
2658 const char *who;
2659 int i, powercnt;
2661 who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? urole.name.m
2662 : "Elf";
2663 if (u.uhp == 1) {
2664 pline("%s is about to die.", who);
2665 } else {
2666 for (i = 0, powercnt = 0; i < SIZE(powers); ++i)
2667 if (u.uprops[powers[i]].intrinsic & INTRINSIC)
2668 ++powercnt;
2670 pline(powercnt >= 4 ? "%s, all your powers will be lost..."
2671 : "%s, your life force is running out.",
2672 who);
2674 } else {
2675 You_hear(u.uhp == 1 ? "the wailing of the Banshee..."
2676 : "the howling of the CwnAnnwn...");
2680 void
2681 losehp(n, knam, k_format)
2682 register int n;
2683 register const char *knam;
2684 boolean k_format;
2686 if (Upolyd) {
2687 u.mh -= n;
2688 if (u.mhmax < u.mh)
2689 u.mhmax = u.mh;
2690 context.botl = 1;
2691 if (u.mh < 1)
2692 rehumanize();
2693 else if (n > 0 && u.mh * 10 < u.mhmax && Unchanging)
2694 maybe_wail();
2695 return;
2698 u.uhp -= n;
2699 if (u.uhp > u.uhpmax)
2700 u.uhpmax = u.uhp; /* perhaps n was negative */
2701 else
2702 context.travel = context.travel1 = context.mv = context.run = 0;
2703 context.botl = 1;
2704 if (u.uhp < 1) {
2705 killer.format = k_format;
2706 if (killer.name != knam) /* the thing that killed you */
2707 Strcpy(killer.name, knam ? knam : "");
2708 You("die...");
2709 done(DIED);
2710 } else if (n > 0 && u.uhp * 10 < u.uhpmax) {
2711 maybe_wail();
2716 weight_cap()
2718 register long carrcap;
2720 carrcap = 25 * (ACURRSTR + ACURR(A_CON)) + 50;
2721 if (Upolyd) {
2722 /* consistent with can_carry() in mon.c */
2723 if (youmonst.data->mlet == S_NYMPH)
2724 carrcap = MAX_CARR_CAP;
2725 else if (!youmonst.data->cwt)
2726 carrcap = (carrcap * (long) youmonst.data->msize) / MZ_HUMAN;
2727 else if (!strongmonst(youmonst.data)
2728 || (strongmonst(youmonst.data)
2729 && (youmonst.data->cwt > WT_HUMAN)))
2730 carrcap = (carrcap * (long) youmonst.data->cwt / WT_HUMAN);
2733 if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */
2734 || (u.usteed && strongmonst(u.usteed->data)))
2735 carrcap = MAX_CARR_CAP;
2736 else {
2737 if (carrcap > MAX_CARR_CAP)
2738 carrcap = MAX_CARR_CAP;
2739 if (!Flying) {
2740 if (EWounded_legs & LEFT_SIDE)
2741 carrcap -= 100;
2742 if (EWounded_legs & RIGHT_SIDE)
2743 carrcap -= 100;
2745 if (carrcap < 0)
2746 carrcap = 0;
2748 return (int) carrcap;
2751 static int wc; /* current weight_cap(); valid after call to inv_weight() */
2753 /* returns how far beyond the normal capacity the player is currently. */
2754 /* inv_weight() is negative if the player is below normal capacity. */
2756 inv_weight()
2758 register struct obj *otmp = invent;
2759 register int wt = 0;
2761 while (otmp) {
2762 if (otmp->oclass == COIN_CLASS)
2763 wt += (int) (((long) otmp->quan + 50L) / 100L);
2764 else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
2765 wt += otmp->owt;
2766 otmp = otmp->nobj;
2768 wc = weight_cap();
2769 return (wt - wc);
2773 * Returns 0 if below normal capacity, or the number of "capacity units"
2774 * over the normal capacity the player is loaded. Max is 5.
2777 calc_capacity(xtra_wt)
2778 int xtra_wt;
2780 int cap, wt = inv_weight() + xtra_wt;
2782 if (wt <= 0)
2783 return UNENCUMBERED;
2784 if (wc <= 1)
2785 return OVERLOADED;
2786 cap = (wt * 2 / wc) + 1;
2787 return min(cap, OVERLOADED);
2791 near_capacity()
2793 return calc_capacity(0);
2797 max_capacity()
2799 int wt = inv_weight();
2801 return (wt - (2 * wc));
2804 boolean
2805 check_capacity(str)
2806 const char *str;
2808 if (near_capacity() >= EXT_ENCUMBER) {
2809 if (str)
2810 pline1(str);
2811 else
2812 You_cant("do that while carrying so much stuff.");
2813 return 1;
2815 return 0;
2819 inv_cnt(incl_gold)
2820 boolean incl_gold;
2822 register struct obj *otmp = invent;
2823 register int ct = 0;
2825 while (otmp) {
2826 if (incl_gold || otmp->invlet != GOLD_SYM)
2827 ct++;
2828 otmp = otmp->nobj;
2830 return ct;
2833 /* Counts the money in an object chain. */
2834 /* Intended use is for your or some monsters inventory, */
2835 /* now that u.gold/m.gold is gone.*/
2836 /* Counting money in a container might be possible too. */
2837 long
2838 money_cnt(otmp)
2839 struct obj *otmp;
2841 while (otmp) {
2842 /* Must change when silver & copper is implemented: */
2843 if (otmp->oclass == COIN_CLASS)
2844 return otmp->quan;
2845 otmp = otmp->nobj;
2847 return 0L;
2850 /*hack.c*/