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. */
7 /* #define DEBUG */ /* uncomment for debugging */
9 STATIC_DCL
void NDECL(maybe_wail
);
10 STATIC_DCL
int NDECL(moverock
);
11 STATIC_DCL
int FDECL(still_chewing
, (XCHAR_P
, XCHAR_P
));
12 STATIC_DCL
void NDECL(dosinkfall
);
13 STATIC_DCL boolean
FDECL(findtravelpath
, (int));
14 STATIC_DCL boolean
FDECL(trapmove
, (int, int, struct trap
*));
15 STATIC_DCL
void NDECL(switch_terrain
);
16 STATIC_DCL
struct monst
*FDECL(monstinroom
, (struct permonst
*, int));
17 STATIC_DCL boolean
FDECL(doorless_door
, (int, int));
18 STATIC_DCL
void FDECL(move_update
, (BOOLEAN_P
));
20 #define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE)
22 /* mode values for findtravelpath() */
23 #define TRAVP_TRAVEL 0
27 static anything tmp_anything
;
33 tmp_anything
= zeroany
;
34 tmp_anything
.a_uint
= ui
;
42 tmp_anything
= zeroany
;
43 tmp_anything
.a_long
= lng
;
51 tmp_anything
= zeroany
;
52 tmp_anything
.a_monst
= mtmp
;
60 tmp_anything
= zeroany
;
61 tmp_anything
.a_obj
= obj
;
66 revive_nasty(x
, y
, msg
)
70 register struct obj
*otmp
, *otmp2
;
73 boolean revived
= FALSE
;
75 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp2
) {
76 otmp2
= otmp
->nexthere
;
77 if (otmp
->otyp
== CORPSE
78 && (is_rider(&mons
[otmp
->corpsenm
])
79 || otmp
->corpsenm
== PM_WIZARD_OF_YENDOR
)) {
80 /* move any living monster already at that location */
81 if ((mtmp
= m_at(x
, y
)) && enexto(&cc
, x
, y
, mtmp
->data
))
82 rloc_to(mtmp
, cc
.x
, cc
.y
);
85 revived
= revive_corpse(otmp
);
89 /* this location might not be safe, if not, move revived monster */
92 if (mtmp
&& !goodpos(x
, y
, mtmp
, 0)
93 && enexto(&cc
, x
, y
, mtmp
->data
)) {
94 rloc_to(mtmp
, cc
.x
, cc
.y
);
96 /* else impossible? */
105 register xchar rx
, ry
, sx
, sy
;
106 register struct obj
*otmp
;
107 register struct trap
*ttmp
;
108 register struct monst
*mtmp
;
110 sx
= u
.ux
+ u
.dx
, sy
= u
.uy
+ u
.dy
; /* boulder starting position */
111 while ((otmp
= sobj_at(BOULDER
, sx
, sy
)) != 0) {
112 /* make sure that this boulder is visible as the top object */
113 if (otmp
!= level
.objects
[sx
][sy
])
114 movobj(otmp
, sx
, sy
);
116 rx
= u
.ux
+ 2 * u
.dx
; /* boulder destination position */
117 ry
= u
.uy
+ 2 * u
.dy
;
119 if (Levitation
|| Is_airlevel(&u
.uz
)) {
121 feel_location(sx
, sy
);
122 You("don't have enough leverage to push %s.", the(xname(otmp
)));
123 /* Give them a chance to climb over it? */
126 if (verysmall(youmonst
.data
) && !u
.usteed
) {
128 feel_location(sx
, sy
);
129 pline("You're too small to push that %s.", xname(otmp
));
132 if (isok(rx
, ry
) && !IS_ROCK(levl
[rx
][ry
].typ
)
133 && levl
[rx
][ry
].typ
!= IRONBARS
134 && (!IS_DOOR(levl
[rx
][ry
].typ
) || !(u
.dx
&& u
.dy
)
135 || doorless_door(rx
, ry
)) && !sobj_at(BOULDER
, rx
, ry
)) {
139 /* KMH -- Sokoban doesn't let you push boulders diagonally */
140 if (Sokoban
&& u
.dx
&& u
.dy
) {
142 feel_location(sx
, sy
);
143 pline("%s won't roll diagonally on this %s.",
144 The(xname(otmp
)), surface(sx
, sy
));
148 if (revive_nasty(rx
, ry
, "You sense movement on the other side."))
151 if (mtmp
&& !noncorporeal(mtmp
->data
)
153 || !(ttmp
&& ((ttmp
->ttyp
== PIT
)
154 || (ttmp
->ttyp
== SPIKED_PIT
))))) {
156 feel_location(sx
, sy
);
157 if (canspotmon(mtmp
)) {
158 pline("There's %s on the other side.", a_monnam(mtmp
));
160 You_hear("a monster behind %s.", the(xname(otmp
)));
161 map_invisible(rx
, ry
);
164 pline("Perhaps that's why %s cannot move it.",
165 u
.usteed
? y_monnam(u
.usteed
) : "you");
170 /* if a trap operates on the boulder, don't attempt
171 to move any others at this location; return -1
172 if another boulder is in hero's way, or 0 if he
173 should advance to the vacated boulder position */
174 switch (ttmp
->ttyp
) {
177 obj_extract_self(otmp
);
178 place_object(otmp
, rx
, ry
);
180 pline("KAABLAMM!!! %s %s land mine.",
181 Tobjnam(otmp
, "trigger"),
182 ttmp
->madeby_u
? "your" : "a");
183 blow_up_landmine(ttmp
);
184 /* if the boulder remains, it should fill the pit */
185 fill_pit(u
.ux
, u
.uy
);
188 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
193 obj_extract_self(otmp
);
194 /* vision kludge to get messages right;
195 the pit will temporarily be seen even
196 if this is one among multiple boulders */
198 viz_array
[ry
][rx
] |= IN_SIGHT
;
199 if (!flooreffects(otmp
, rx
, ry
, "fall")) {
200 place_object(otmp
, rx
, ry
);
204 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
208 pline("Kerplunk! You no longer feel %s.",
211 pline("%s%s and %s a %s in the %s!",
212 Tobjnam(otmp
, (ttmp
->ttyp
== TRAPDOOR
)
215 (ttmp
->ttyp
== TRAPDOOR
) ? "" : " into",
216 otense(otmp
, "plug"),
217 (ttmp
->ttyp
== TRAPDOOR
) ? "trap door" : "hole",
222 levl
[rx
][ry
].wall_info
&= ~W_NONDIGGABLE
;
223 levl
[rx
][ry
].candig
= 1;
226 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
229 int newlev
= 0; /* lint suppression */
232 if (ttmp
->ttyp
== LEVEL_TELEP
) {
233 newlev
= random_teleport_level();
234 if (newlev
== depth(&u
.uz
) || In_endgame(&u
.uz
))
235 /* trap didn't work; skip "disappears" message */
239 pline("%s pushes %s and suddenly it disappears!",
240 upstart(y_monnam(u
.usteed
)), the(xname(otmp
)));
242 You("push %s and suddenly it disappears!",
244 if (ttmp
->ttyp
== TELEP_TRAP
) {
247 obj_extract_self(otmp
);
248 add_to_migration(otmp
);
249 get_level(&dest
, newlev
);
250 otmp
->ox
= dest
.dnum
;
251 otmp
->oy
= dest
.dlevel
;
252 otmp
->owornmask
= (long) MIGR_RANDOM
;
255 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
258 break; /* boulder not affected by this trap */
262 if (closed_door(rx
, ry
))
264 if (boulder_hits_pool(otmp
, rx
, ry
, TRUE
))
267 * Re-link at top of fobj chain so that pile order is preserved
268 * when level is restored.
272 place_object(otmp
, otmp
->ox
, otmp
->oy
);
276 #ifdef LINT /* static long lastmovetime; */
280 /* note: reset to zero after save/restore cycle */
281 static NEARDATA
long lastmovetime
;
285 if (moves
> lastmovetime
+ 2 || moves
< lastmovetime
)
286 pline("With %s effort you move %s.",
287 throws_rocks(youmonst
.data
) ? "little"
290 exercise(A_STR
, TRUE
);
292 pline("%s moves %s.", upstart(y_monnam(u
.usteed
)),
294 lastmovetime
= moves
;
297 /* Move the boulder *after* the message. */
298 if (glyph_is_invisible(levl
[rx
][ry
].glyph
))
299 unmap_object(rx
, ry
);
300 movobj(otmp
, rx
, ry
); /* does newsym(rx,ry) */
302 feel_location(rx
, ry
);
303 feel_location(sx
, sy
);
310 pline("%s tries to move %s, but cannot.",
311 upstart(y_monnam(u
.usteed
)), the(xname(otmp
)));
313 You("try to move %s, but in vain.", the(xname(otmp
)));
315 feel_location(sx
, sy
);
317 if (throws_rocks(youmonst
.data
)) {
318 if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
) {
319 You("aren't skilled enough to %s %s from %s.",
320 (flags
.pickup
&& !Sokoban
) ? "pick up" : "push aside",
321 the(xname(otmp
)), y_monnam(u
.usteed
));
323 pline("However, you can easily %s.",
324 (flags
.pickup
&& !Sokoban
) ? "pick it up"
333 && (((!invent
|| inv_weight() <= -850)
334 && (!u
.dx
|| !u
.dy
|| (IS_ROCK(levl
[u
.ux
][sy
].typ
)
335 && IS_ROCK(levl
[sx
][u
.uy
].typ
))))
336 || verysmall(youmonst
.data
))) {
338 "However, you can squeeze yourself into a small opening.");
351 * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE
358 struct rm
*lev
= &levl
[x
][y
];
359 struct obj
*boulder
= sobj_at(BOULDER
, x
, y
);
360 const char *digtxt
= (char *) 0, *dmgtxt
= (char *) 0;
362 if (context
.digging
.down
) /* not continuing previous dig (w/ pick-axe) */
363 (void) memset((genericptr_t
) &context
.digging
, 0,
364 sizeof (struct dig_info
));
366 if (!boulder
&& IS_ROCK(lev
->typ
) && !may_dig(x
, y
)) {
367 You("hurt your teeth on the %s.",
368 (lev
->typ
== IRONBARS
)
375 } else if (context
.digging
.pos
.x
!= x
|| context
.digging
.pos
.y
!= y
376 || !on_level(&context
.digging
.level
, &u
.uz
)) {
377 context
.digging
.down
= FALSE
;
378 context
.digging
.chew
= TRUE
;
379 context
.digging
.warned
= FALSE
;
380 context
.digging
.pos
.x
= x
;
381 context
.digging
.pos
.y
= y
;
382 assign_level(&context
.digging
.level
, &u
.uz
);
383 /* solid rock takes more work & time to dig through */
384 context
.digging
.effort
=
385 (IS_ROCK(lev
->typ
) && !IS_TREE(lev
->typ
) ? 30 : 60) + u
.udaminc
;
386 You("start chewing %s %s.",
387 (boulder
|| IS_TREE(lev
->typ
) || lev
->typ
== IRONBARS
)
396 : (lev
->typ
== IRONBARS
)
399 watch_dig((struct monst
*) 0, x
, y
, FALSE
);
401 } else if ((context
.digging
.effort
+= (30 + u
.udaminc
)) <= 100) {
403 You("%s chewing on the %s.",
404 context
.digging
.chew
? "continue" : "begin",
411 : (lev
->typ
== IRONBARS
)
414 context
.digging
.chew
= TRUE
;
415 watch_dig((struct monst
*) 0, x
, y
, FALSE
);
419 /* Okay, you've chewed through something */
421 u
.uhunger
+= rnd(20);
424 delobj(boulder
); /* boulder goes bye-bye */
425 You("eat the boulder."); /* yum */
428 * The location could still block because of
429 * 1. More than one boulder
430 * 2. Boulder stuck in a wall/stone/door.
432 * [perhaps use does_block() below (from vision.c)]
434 if (IS_ROCK(lev
->typ
) || closed_door(x
, y
)
435 || sobj_at(BOULDER
, x
, y
)) {
436 block_point(x
, y
); /* delobj will unblock the point */
437 /* reset dig state */
438 (void) memset((genericptr_t
) &context
.digging
, 0,
439 sizeof (struct dig_info
));
443 } else if (IS_WALL(lev
->typ
)) {
444 if (*in_rooms(x
, y
, SHOPBASE
)) {
445 add_damage(x
, y
, 10L * ACURRSTR
);
448 digtxt
= "chew a hole in the wall.";
449 if (level
.flags
.is_maze_lev
) {
451 } else if (level
.flags
.is_cavernous_lev
&& !in_town(x
, y
)) {
455 lev
->doormask
= D_NODOOR
;
457 } else if (IS_TREE(lev
->typ
)) {
458 digtxt
= "chew through the tree.";
460 } else if (lev
->typ
== IRONBARS
) {
461 digtxt
= "eat through the bars.";
463 } else if (lev
->typ
== SDOOR
) {
464 if (lev
->doormask
& D_TRAPPED
) {
465 lev
->doormask
= D_NODOOR
;
466 b_trapped("secret door", 0);
468 digtxt
= "chew through the secret door.";
469 lev
->doormask
= D_BROKEN
;
473 } else if (IS_DOOR(lev
->typ
)) {
474 if (*in_rooms(x
, y
, SHOPBASE
)) {
475 add_damage(x
, y
, 400L);
478 if (lev
->doormask
& D_TRAPPED
) {
479 lev
->doormask
= D_NODOOR
;
480 b_trapped("door", 0);
482 digtxt
= "chew through the door.";
483 lev
->doormask
= D_BROKEN
;
486 } else { /* STONE or SCORR */
487 digtxt
= "chew a passage through the rock.";
491 unblock_point(x
, y
); /* vision */
494 You1(digtxt
); /* after newsym */
496 pay_for_damage(dmgtxt
, FALSE
);
497 (void) memset((genericptr_t
) &context
.digging
, 0,
498 sizeof (struct dig_info
));
504 register struct obj
*obj
;
505 register xchar ox
, oy
;
507 /* optimize by leaving on the fobj chain? */
509 newsym(obj
->ox
, obj
->oy
);
510 place_object(obj
, ox
, oy
);
514 static NEARDATA
const char fell_on_sink
[] = "fell onto a sink";
519 register struct obj
*obj
;
521 boolean lev_boots
= (uarmf
&& uarmf
->otyp
== LEVITATION_BOOTS
),
522 innate_lev
= ((HLevitation
& (FROMOUTSIDE
| FROMFORM
)) != 0L),
523 ufall
= (!innate_lev
&& !(HFlying
|| EFlying
)); /* BFlying */
526 You(innate_lev
? "wobble unsteadily for a moment."
527 : "gain control of your flight.");
529 long save_ELev
= ELevitation
, save_HLev
= HLevitation
;
531 /* fake removal of levitation in advance so that final
532 disclosure will be right in case this turns out to
533 be fatal; fortunately the fact that rings and boots
534 are really still worn has no effect on bones data */
535 ELevitation
= HLevitation
= 0L;
536 You("crash to the floor!");
537 dmg
= rn1(8, 25 - (int) ACURR(A_CON
));
538 losehp(Maybe_Half_Phys(dmg
), fell_on_sink
, NO_KILLER_PREFIX
);
539 exercise(A_DEX
, FALSE
);
540 selftouch("Falling, you");
541 for (obj
= level
.objects
[u
.ux
][u
.uy
]; obj
; obj
= obj
->nexthere
)
542 if (obj
->oclass
== WEAPON_CLASS
|| is_weptool(obj
)) {
543 You("fell on %s.", doname(obj
));
544 losehp(Maybe_Half_Phys(rnd(3)), fell_on_sink
,
546 exercise(A_CON
, FALSE
);
548 ELevitation
= save_ELev
;
549 HLevitation
= save_HLev
;
553 * Interrupt multi-turn putting on/taking off of armor (in which
554 * case we reached the sink due to being teleported while busy;
555 * in 3.4.3, Boots_on()/Boots_off() [called via (*aftermv)() when
556 * 'multi' reaches 0] triggered a crash if we were donning/doffing
557 * levitation boots [because the Boots_off() below causes 'uarmf'
558 * to be null by the time 'aftermv' gets called]).
560 * Interrupt donning/doffing if we fall onto the sink, or if the
561 * code below is going to remove levitation boots even when we
562 * haven't fallen (innate floating or flying becoming unblocked).
564 if (ufall
|| lev_boots
) {
565 (void) stop_donning(lev_boots
? uarmf
: (struct obj
*) 0);
566 /* recalculate in case uarmf just got set to null */
567 lev_boots
= (uarmf
&& uarmf
->otyp
== LEVITATION_BOOTS
);
570 /* remove worn levitation items */
571 ELevitation
&= ~W_ARTI
;
572 HLevitation
&= ~(I_SPECIAL
| TIMEOUT
);
574 if (uleft
&& uleft
->otyp
== RIN_LEVITATION
) {
579 if (uright
&& uright
->otyp
== RIN_LEVITATION
) {
590 /* probably moot; we're either still levitating or went
591 through float_down(), but make sure BFlying is up to date */
595 /* intended to be called only on ROCKs or TREEs */
600 struct rm
*lev
= &levl
[x
][y
];
602 return (boolean
) !((IS_STWALL(lev
->typ
) || IS_TREE(lev
->typ
))
603 && (lev
->wall_info
& W_NONDIGGABLE
));
610 return (boolean
) !(IS_STWALL(levl
[x
][y
].typ
)
611 && (levl
[x
][y
].wall_info
& W_NONPASSWALL
));
616 struct permonst
*mdat
;
619 return (boolean
) ((Sokoban
&& sobj_at(BOULDER
, x
, y
))
620 || (IS_ROCK(levl
[x
][y
].typ
)
621 && (!tunnels(mdat
) || needspick(mdat
)
623 && !(passes_walls(mdat
) && may_passwall(x
, y
))));
626 /* caller has already decided that it's a tight diagonal; check whether a
627 monster--who might be the hero--can fit through, and if not then return
628 the reason why: 1: can't fit, 2: possessions won't fit, 3: sokoban
629 returns 0 if we can squeeze through */
631 cant_squeeze_thru(mon
)
635 struct permonst
*ptr
= mon
->data
;
639 && !(amorphous(ptr
) || is_whirly(ptr
) || noncorporeal(ptr
)
640 || slithy(ptr
) || can_fog(mon
)))
643 /* lugging too much junk? */
644 amt
= (mon
== &youmonst
) ? inv_weight() + weight_cap()
645 : curr_mon_load(mon
);
649 /* Sokoban restriction applies to hero only */
650 if (mon
== &youmonst
&& Sokoban
)
653 /* can squeeze through */
661 return (boolean
) (Invocation_lev(&u
.uz
)
662 && x
== inv_pos
.x
&& y
== inv_pos
.y
);
665 /* return TRUE if (dx,dy) is an OK place to move
666 * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP
669 test_move(ux
, uy
, dx
, dy
, mode
)
675 register struct rm
*tmpr
= &levl
[x
][y
];
676 register struct rm
*ust
;
678 context
.door_opened
= FALSE
;
680 * Check for physical obstacles. First, the place we are going.
682 if (IS_ROCK(tmpr
->typ
) || tmpr
->typ
== IRONBARS
) {
683 if (Blind
&& mode
== DO_MOVE
)
685 if (Passes_walls
&& may_passwall(x
, y
)) {
687 } else if (Underwater
) {
688 /* note: if water_friction() changes direction due to
689 turbulence, new target destination will always be water,
690 so we won't get here, hence don't need to worry about
691 "there" being somewhere the player isn't sure of */
693 pline("There is an obstacle there.");
695 } else if (tmpr
->typ
== IRONBARS
) {
696 if ((dmgtype(youmonst
.data
, AD_RUST
)
697 || dmgtype(youmonst
.data
, AD_CORR
)) && mode
== DO_MOVE
698 && still_chewing(x
, y
)) {
701 if (!(Passes_walls
|| passes_bars(youmonst
.data
))) {
702 if (mode
== DO_MOVE
&& iflags
.mention_walls
)
703 You("cannot pass through the bars.");
706 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
708 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
710 } else if (flags
.autodig
&& !context
.run
&& !context
.nopick
&& uwep
712 /* MRKR: Automatic digging when wielding the appropriate tool */
714 (void) use_pick_axe2(uwep
);
717 if (mode
== DO_MOVE
) {
718 if (is_db_wall(x
, y
))
719 pline("That drawbridge is up!");
720 /* sokoban restriction stays even after puzzle is solved */
721 else if (Passes_walls
&& !may_passwall(x
, y
)
722 && In_sokoban(&u
.uz
))
723 pline_The("Sokoban walls resist your ability.");
724 else if (iflags
.mention_walls
)
725 pline("It's %s.", IS_WALL(tmpr
->typ
) ? "a wall"
726 : IS_TREE(tmpr
->typ
) ? "a tree"
731 } else if (IS_DOOR(tmpr
->typ
)) {
732 if (closed_door(x
, y
)) {
733 if (Blind
&& mode
== DO_MOVE
)
737 } else if (can_ooze(&youmonst
)) {
739 You("ooze under the door.");
740 } else if (Underwater
) {
742 pline("There is an obstacle there.");
744 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
746 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
749 if (mode
== DO_MOVE
) {
750 if (amorphous(youmonst
.data
))
752 "try to ooze under the door, but can't squeeze your possessions through.");
753 if (flags
.autoopen
&& !context
.run
&& !Confusion
754 && !Stunned
&& !Fumbling
) {
755 context
.door_opened
= context
.move
=
757 } else if (x
== ux
|| y
== uy
) {
758 if (Blind
|| Stunned
|| ACURR(A_DEX
) < 10
761 You_cant("lead %s through that closed door.",
764 pline("Ouch! You bump into a door.");
765 exercise(A_DEX
, FALSE
);
768 pline("That door is closed.");
770 } else if (mode
== TEST_TRAV
|| mode
== TEST_TRAP
)
776 if (dx
&& dy
&& !Passes_walls
777 && (!doorless_door(x
, y
) || block_door(x
, y
))) {
778 /* Diagonal moves into a door are not allowed. */
779 if (mode
== DO_MOVE
) {
782 if (Underwater
|| iflags
.mention_walls
)
783 You_cant("move diagonally into an intact doorway.");
789 if (dx
&& dy
&& bad_rock(youmonst
.data
, ux
, y
)
790 && bad_rock(youmonst
.data
, x
, uy
)) {
791 /* Move at a diagonal. */
792 switch (cant_squeeze_thru(&youmonst
)) {
795 You("cannot pass that way.");
799 You("are carrying too much to get through.");
803 Your("body is too large to fit through.");
806 break; /* can squeeze through */
808 } else if (dx
&& dy
&& worm_cross(ux
, uy
, x
, y
)) {
809 /* consecutive long worm segments are at <ux,y> and <x,uy> */
811 pline("%s is in your way.", Monnam(m_at(ux
, y
)));
814 /* Pick travel path that does not require crossing a trap.
815 * Avoid water and lava using the usual running rules.
816 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
817 if (context
.run
== 8 && (mode
!= DO_MOVE
)
818 && (x
!= u
.ux
|| y
!= u
.uy
)) {
819 struct trap
*t
= t_at(x
, y
);
822 || (!Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
823 && is_pool_or_lava(x
, y
) && levl
[x
][y
].seenv
))
824 return (mode
== TEST_TRAP
);
827 if (mode
== TEST_TRAP
)
828 return FALSE
; /* do not move through traps */
832 /* Now see if other things block our way . . */
833 if (dx
&& dy
&& !Passes_walls
&& IS_DOOR(ust
->typ
)
834 && (!doorless_door(ux
, uy
) || block_entry(x
, y
))) {
835 /* Can't move at a diagonal out of a doorway with door. */
836 if (mode
== DO_MOVE
&& iflags
.mention_walls
)
837 You_cant("move diagonally out of an intact doorway.");
841 if (sobj_at(BOULDER
, x
, y
) && (Sokoban
|| !Passes_walls
)) {
842 if (!(Blind
|| Hallucination
) && (context
.run
>= 2)
843 && mode
!= TEST_TRAV
)
845 if (mode
== DO_MOVE
) {
846 /* tunneling monsters will chew before pushing */
847 if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)
849 if (still_chewing(x
, y
))
851 } else if (moverock() < 0)
853 } else if (mode
== TEST_TRAV
) {
856 /* never travel through boulders in Sokoban */
860 /* don't pick two boulders in a row, unless there's a way thru */
861 if (sobj_at(BOULDER
, ux
, uy
) && !Sokoban
) {
863 && !(tunnels(youmonst
.data
) && !needspick(youmonst
.data
))
864 && !carrying(PICK_AXE
) && !carrying(DWARVISH_MATTOCK
)
865 && !((obj
= carrying(WAN_DIGGING
))
866 && !objects
[obj
->otyp
].oc_name_known
))
870 /* assume you'll be able to push it when you get there... */
873 /* OK, it is a legal place to move. */
878 static boolean trav_debug
= FALSE
;
880 /* in this case, toggle display of travel debug info */
881 int wiz_debug_cmd_traveldisplay()
883 trav_debug
= !trav_debug
;
889 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
890 * A shortest path is returned. If guess is TRUE, consider various
891 * inaccessible locations as valid intermediate path points.
892 * Returns TRUE if a path was found.
898 /* if travel to adjacent, reachable location, use normal movement rules */
899 if ((mode
== TRAVP_TRAVEL
|| mode
== TRAVP_VALID
) && context
.travel1
900 && distmin(u
.ux
, u
.uy
, u
.tx
, u
.ty
) == 1
901 && !(u
.ux
!= u
.tx
&& u
.uy
!= u
.ty
&& NODIAG(u
.umonnum
))) {
903 if (test_move(u
.ux
, u
.uy
, u
.tx
- u
.ux
, u
.ty
- u
.uy
, TEST_MOVE
)) {
904 if (mode
== TRAVP_TRAVEL
) {
908 iflags
.travelcc
.x
= iflags
.travelcc
.y
= -1;
912 if (mode
== TRAVP_TRAVEL
)
915 if (u
.tx
!= u
.ux
|| u
.ty
!= u
.uy
) {
916 xchar travel
[COLNO
][ROWNO
];
917 xchar travelstepx
[2][COLNO
* ROWNO
];
918 xchar travelstepy
[2][COLNO
* ROWNO
];
919 xchar tx
, ty
, ux
, uy
;
920 int n
= 1; /* max offset in travelsteps */
921 int set
= 0; /* two sets current and previous */
922 int radius
= 1; /* search radius */
925 /* If guessing, first find an "obvious" goal location. The obvious
926 * goal is the position the player knows of, or might figure out
927 * (couldsee) that is closest to the target on a straight path.
929 if (mode
== TRAVP_GUESS
|| mode
== TRAVP_VALID
) {
942 (void) memset((genericptr_t
) travel
, 0, sizeof(travel
));
943 travelstepx
[0][0] = tx
;
944 travelstepy
[0][0] = ty
;
949 for (i
= 0; i
< n
; i
++) {
951 int x
= travelstepx
[set
][i
];
952 int y
= travelstepy
[set
][i
];
953 static int ordered
[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
954 /* no diagonal movement for grid bugs */
955 int dirmax
= NODIAG(u
.umonnum
) ? 4 : 8;
956 boolean alreadyrepeated
= FALSE
;
958 for (dir
= 0; dir
< dirmax
; ++dir
) {
959 int nx
= x
+ xdir
[ordered
[dir
]];
960 int ny
= y
+ ydir
[ordered
[dir
]];
963 * When guessing and trying to travel as close as possible
964 * to an unreachable target space, don't include spaces
965 * that would never be picked as a guessed target in the
966 * travel matrix describing hero-reachable spaces.
967 * This stops travel from getting confused and moving
968 * the hero back and forth in certain degenerate
969 * configurations of sight-blocking obstacles, e.g.
971 * T 1. Dig this out and carry enough to not be
972 * #### able to squeeze through diagonal gaps.
973 * #--.--- Stand at @ and target travel at space T.
977 * T 2. couldsee() marks spaces marked a and x
978 * #### as eligible guess spaces to move the hero
979 * a--.--- towards. Space a is closest to T, so it
980 * @xxxxx gets chosen. Travel system moves @ right
981 * |xxxxx to travel to space a.
983 * T 3. couldsee() marks spaces marked b, c and x
984 * #### as eligible guess spaces to move the hero
985 * a--c--- towards. Since findtravelpath() is called
986 * b@xxxx repeatedly during travel, it doesn't
987 * |xxxxx remember that it wanted to go to space a,
988 * so in comparing spaces b and c, b is
989 * chosen, since it seems like the closest
990 * eligible space to T. Travel system moves @
991 * left to go to space b.
995 * By limiting the travel matrix here, space a in the
996 * example above is never included in it, preventing
1000 || ((mode
== TRAVP_GUESS
) && !couldsee(nx
, ny
)))
1002 if ((!Passes_walls
&& !can_ooze(&youmonst
)
1003 && closed_door(x
, y
)) || sobj_at(BOULDER
, x
, y
)
1004 || test_move(x
, y
, nx
-x
, ny
-y
, TEST_TRAP
)) {
1005 /* closed doors and boulders usually
1006 * cause a delay, so prefer another path */
1007 if (travel
[x
][y
] > radius
- 3) {
1008 if (!alreadyrepeated
) {
1009 travelstepx
[1 - set
][nn
] = x
;
1010 travelstepy
[1 - set
][nn
] = y
;
1011 /* don't change travel matrix! */
1013 alreadyrepeated
= TRUE
;
1018 if (test_move(x
, y
, nx
- x
, ny
- y
, TEST_TRAV
)
1019 && (levl
[nx
][ny
].seenv
1020 || (!Blind
&& couldsee(nx
, ny
)))) {
1021 if (nx
== ux
&& ny
== uy
) {
1022 if (mode
== TRAVP_TRAVEL
|| mode
== TRAVP_VALID
) {
1025 if (mode
== TRAVP_TRAVEL
1026 && x
== u
.tx
&& y
== u
.ty
) {
1028 /* reset run so domove run checks work */
1030 iflags
.travelcc
.x
= iflags
.travelcc
.y
= -1;
1034 } else if (!travel
[nx
][ny
]) {
1035 travelstepx
[1 - set
][nn
] = nx
;
1036 travelstepy
[1 - set
][nn
] = ny
;
1037 travel
[nx
][ny
] = radius
;
1046 /* Use of warning glyph is arbitrary. It stands out. */
1047 tmp_at(DISP_ALL
, warning_to_glyph(1));
1048 for (i
= 0; i
< nn
; ++i
) {
1049 tmp_at(travelstepx
[1 - set
][i
], travelstepy
[1 - set
][i
]);
1052 if (flags
.runmode
== RUN_CRAWL
) {
1056 tmp_at(DISP_END
, 0);
1065 /* if guessing, find best location in travel matrix and go there */
1066 if (mode
== TRAVP_GUESS
) {
1067 int px
= tx
, py
= ty
; /* pick location */
1068 int dist
, nxtdist
, d2
, nd2
;
1070 dist
= distmin(ux
, uy
, tx
, ty
);
1071 d2
= dist2(ux
, uy
, tx
, ty
);
1072 for (tx
= 1; tx
< COLNO
; ++tx
)
1073 for (ty
= 0; ty
< ROWNO
; ++ty
)
1074 if (travel
[tx
][ty
]) {
1075 nxtdist
= distmin(ux
, uy
, tx
, ty
);
1076 if (nxtdist
== dist
&& couldsee(tx
, ty
)) {
1077 nd2
= dist2(ux
, uy
, tx
, ty
);
1079 /* prefer non-zigzag path */
1084 } else if (nxtdist
< dist
&& couldsee(tx
, ty
)) {
1088 d2
= dist2(ux
, uy
, tx
, ty
);
1092 if (px
== u
.ux
&& py
== u
.uy
) {
1093 /* no guesses, just go in the general direction */
1094 u
.dx
= sgn(u
.tx
- u
.ux
);
1095 u
.dy
= sgn(u
.ty
- u
.uy
);
1096 if (test_move(u
.ux
, u
.uy
, u
.dx
, u
.dy
, TEST_MOVE
))
1102 /* Use of warning glyph is arbitrary. It stands out. */
1103 tmp_at(DISP_ALL
, warning_to_glyph(2));
1106 if (flags
.runmode
== RUN_CRAWL
) {
1112 tmp_at(DISP_END
, 0);
1121 mode
= TRAVP_TRAVEL
;
1135 is_valid_travelpt(x
,y
)
1141 int g
= glyph_at(x
,y
);
1142 if (x
== u
.ux
&& y
== u
.uy
)
1144 if (isok(x
,y
) && glyph_is_cmap(g
) && S_stone
== glyph_to_cmap(g
)
1145 && !levl
[x
][y
].seenv
)
1149 ret
= findtravelpath(TRAVP_VALID
);
1155 /* try to escape being stuck in a trapped state by walking out of it;
1156 return true iff moving should continue to intended destination
1157 (all failures and most successful escapes leave hero at original spot) */
1159 trapmove(x
, y
, desttrap
)
1160 int x
, y
; /* targetted destination, <u.ux+u.dx,u.uy+u.dy> */
1161 struct trap
*desttrap
; /* nonnull if another trap at <x,y> */
1164 const char *predicament
, *culprit
;
1165 char *steedname
= !u
.usteed
? (char *) 0 : y_monnam(u
.usteed
);
1168 return TRUE
; /* sanity check */
1170 switch (u
.utraptype
) {
1172 if (flags
.verbose
) {
1173 predicament
= "caught in a bear trap";
1175 Norep("%s is %s.", upstart(steedname
), predicament
);
1177 Norep("You are %s.", predicament
);
1179 /* [why does diagonal movement give quickest escape?] */
1180 if ((u
.dx
&& u
.dy
) || !rn2(5))
1184 if (desttrap
&& desttrap
->tseen
1185 && (desttrap
->ttyp
== PIT
|| desttrap
->ttyp
== SPIKED_PIT
))
1186 return TRUE
; /* move into adjacent pit */
1187 /* try to escape; position stays same regardless of success */
1191 if (uwep
&& uwep
->oartifact
== ART_STING
) {
1193 pline("Sting cuts through the web!");
1194 break; /* escape trap but don't move */
1197 if (flags
.verbose
) {
1198 predicament
= "stuck to the web";
1200 Norep("%s is %s.", upstart(steedname
), predicament
);
1202 Norep("You are %s.", predicament
);
1206 pline("%s breaks out of the web.", upstart(steedname
));
1208 You("disentangle yourself.");
1212 if (flags
.verbose
) {
1213 predicament
= "stuck in the lava";
1215 Norep("%s is %s.", upstart(steedname
), predicament
);
1217 Norep("You are %s.", predicament
);
1219 if (!is_lava(x
, y
)) {
1221 if ((u
.utrap
& 0xff) == 0) {
1224 You("lead %s to the edge of the %s.", steedname
,
1227 You("pull yourself to the edge of the %s.",
1235 anchored
= (u
.utraptype
== TT_BURIEDBALL
);
1239 cc
.x
= u
.ux
, cc
.y
= u
.uy
;
1240 /* can move normally within radius 1 of buried ball */
1241 if (buried_ball(&cc
) && dist2(x
, y
, cc
.x
, cc
.y
) <= 2) {
1242 /* ugly hack: we need to issue some message here
1243 in case "you are chained to the buried ball"
1244 was the most recent message given, otherwise
1245 our next attempt to move out of tether range
1246 after this successful move would have its
1247 can't-do-that message suppressed by Norep */
1249 Norep("You move within the chain's reach.");
1254 if (flags
.verbose
) {
1256 predicament
= "chained to the";
1257 culprit
= "buried ball";
1259 predicament
= "stuck in the";
1260 culprit
= surface(u
.ux
, u
.uy
);
1264 Norep("You and %s are %s %s.", steedname
, predicament
,
1267 Norep("%s is %s %s.", upstart(steedname
), predicament
,
1270 Norep("You are %s %s.", predicament
, culprit
);
1274 pline("%s finally %s free.", upstart(steedname
),
1275 !anchored
? "lurches" : "wrenches the ball");
1277 You("finally %s free.",
1278 !anchored
? "wriggle" : "wrench the ball");
1280 buried_ball_to_punishment();
1284 impossible("trapmove: stuck in unknown trap? (%d)",
1294 if (!youmonst
.data
->mmove
) {
1295 You("are rooted %s.",
1296 Levitation
|| Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
)
1308 register struct monst
*mtmp
;
1309 register struct rm
*tmpr
;
1310 register xchar x
, y
;
1311 struct trap
*trap
= NULL
;
1314 xchar chainx
= 0, chainy
= 0,
1315 ballx
= 0, bally
= 0; /* ball&chain new positions */
1316 int bc_control
= 0; /* control for ball&chain */
1317 boolean cause_delay
= FALSE
; /* dragging ball will skip a move */
1319 u_wipe_engr(rnd(5));
1321 if (context
.travel
) {
1322 if (!findtravelpath(FALSE
))
1323 (void) findtravelpath(TRUE
);
1324 context
.travel1
= 0;
1327 if (((wtcap
= near_capacity()) >= OVERLOADED
1328 || (wtcap
> SLT_ENCUMBER
1329 && (Upolyd
? (u
.mh
< 5 && u
.mh
!= u
.mhmax
)
1330 : (u
.uhp
< 10 && u
.uhp
!= u
.uhpmax
))))
1331 && !Is_airlevel(&u
.uz
)) {
1332 if (wtcap
< OVERLOADED
) {
1333 You("don't have enough stamina to move.");
1334 exercise(A_CON
, FALSE
);
1336 You("collapse under your load.");
1342 u
.ux
= x
= u
.ustuck
->mx
;
1343 u
.uy
= y
= u
.ustuck
->my
;
1346 if (Is_airlevel(&u
.uz
) && rn2(4) && !Levitation
&& !Flying
) {
1349 You("tumble in place.");
1350 exercise(A_DEX
, FALSE
);
1353 You_cant("control your movements very well.");
1356 pline("It's hard to walk in thin air.");
1357 exercise(A_DEX
, TRUE
);
1363 /* check slippery ice */
1364 on_ice
= !Levitation
&& is_ice(u
.ux
, u
.uy
);
1366 static int skates
= 0;
1369 skates
= find_skates();
1370 if ((uarmf
&& uarmf
->otyp
== skates
) || resists_cold(&youmonst
)
1371 || Flying
|| is_floater(youmonst
.data
)
1372 || is_clinger(youmonst
.data
) || is_whirly(youmonst
.data
)) {
1374 } else if (!rn2(Cold_resistance
? 3 : 2)) {
1375 HFumbling
|= FROMOUTSIDE
;
1376 HFumbling
&= ~TIMEOUT
;
1377 HFumbling
+= 1; /* slip on next move */
1380 if (!on_ice
&& (HFumbling
& FROMOUTSIDE
))
1381 HFumbling
&= ~FROMOUTSIDE
;
1385 if (Stunned
|| (Confusion
&& !rn2(5))) {
1386 register int tries
= 0;
1396 } while (!isok(x
, y
) || bad_rock(youmonst
.data
, x
, y
));
1398 /* turbulence might alter your actual destination */
1401 if (!u
.dx
&& !u
.dy
) {
1412 if (((trap
= t_at(x
, y
)) && trap
->tseen
)
1413 || (Blind
&& !Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
1414 && is_pool_or_lava(x
, y
) && levl
[x
][y
].seenv
)) {
1415 if (context
.run
>= 2) {
1416 if (iflags
.mention_walls
) {
1417 if (trap
&& trap
->tseen
) {
1418 int tt
= what_trap(trap
->ttyp
);
1419 You("stop in front of %s.",
1420 an(defsyms
[trap_to_defsym(tt
)].explanation
));
1421 } else if (is_pool_or_lava(x
,y
) && levl
[x
][y
].seenv
) {
1422 You("stop at the edge of the %s.",
1423 hliquid(is_pool(x
,y
) ? "water" : "lava"));
1433 if (u
.ustuck
&& (x
!= u
.ustuck
->mx
|| y
!= u
.ustuck
->my
)) {
1434 if (distu(u
.ustuck
->mx
, u
.ustuck
->my
) > 2) {
1435 /* perhaps it fled (or was teleported or ... ) */
1437 } else if (sticks(youmonst
.data
)) {
1438 /* When polymorphed into a sticking monster,
1439 * u.ustuck means it's stuck to you, not you to it.
1441 You("release %s.", mon_nam(u
.ustuck
));
1444 /* If holder is asleep or paralyzed:
1445 * 37.5% chance of getting away,
1446 * 12.5% chance of waking/releasing it;
1448 * 7.5% chance of getting away.
1449 * [strength ought to be a factor]
1450 * If holder is tame and there is no conflict,
1451 * guaranteed escape.
1453 switch (rn2(!u
.ustuck
->mcanmove
? 8 : 40)) {
1458 You("pull free from %s.", mon_nam(u
.ustuck
));
1462 if (!u
.ustuck
->mcanmove
) {
1463 /* it's free to move on next turn */
1464 u
.ustuck
->mfrozen
= 1;
1465 u
.ustuck
->msleeping
= 0;
1469 if (u
.ustuck
->mtame
&& !Conflict
&& !u
.ustuck
->mconf
)
1471 You("cannot escape from %s!", mon_nam(u
.ustuck
));
1479 if (mtmp
&& !is_safepet(mtmp
)) {
1480 /* Don't attack if you're running, and can see it */
1481 /* It's fine to displace pets, though */
1482 /* We should never get here if forcefight */
1483 if (context
.run
&& ((!Blind
&& mon_visible(mtmp
)
1484 && ((mtmp
->m_ap_type
!= M_AP_FURNITURE
1485 && mtmp
->m_ap_type
!= M_AP_OBJECT
)
1486 || Protection_from_shape_changers
))
1487 || sensemon(mtmp
))) {
1501 /* attack monster */
1503 /* don't stop travel when displacing pets; if the
1504 displace fails for some reason, attack() in uhitm.c
1505 will stop travel rather than domove */
1506 if (!is_safepet(mtmp
) || context
.forcefight
)
1508 /* only attack if we know it's there */
1509 /* or if we used the 'F' command to fight blindly */
1510 /* or if it hides_under, in which case we call attack() to print
1511 * the Wait! message.
1512 * This is different from ceiling hiders, who aren't handled in
1516 /* If they used a 'm' command, trying to move onto a monster
1517 * prints the below message and wastes a turn. The exception is
1518 * if the monster is unseen and the player doesn't remember an
1519 * invisible monster--then, we fall through to attack() and
1520 * attack_check(), which still wastes a turn, but prints a
1521 * different message and makes the player remember the monster.
1523 if (context
.nopick
&& !context
.travel
1524 && (canspotmon(mtmp
) || glyph_is_invisible(levl
[x
][y
].glyph
))) {
1525 if (mtmp
->m_ap_type
&& !Protection_from_shape_changers
1527 stumble_onto_mimic(mtmp
);
1528 else if (mtmp
->mpeaceful
&& !Hallucination
)
1529 pline("Pardon me, %s.", m_monnam(mtmp
));
1531 You("move right into %s.", mon_nam(mtmp
));
1534 if (context
.forcefight
|| !mtmp
->mundetected
|| sensemon(mtmp
)
1535 || ((hides_under(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
)
1536 && !is_safepet(mtmp
))) {
1537 /* try to attack; note that it might evade */
1538 /* also, we don't attack tame when _safepet_ */
1544 if (context
.forcefight
&& levl
[x
][y
].typ
== IRONBARS
&& uwep
) {
1545 struct obj
*obj
= uwep
;
1547 if (breaktest(obj
)) {
1549 obj
= splitobj(obj
, 1L);
1551 setuwep((struct obj
*)0);
1554 hit_bars(&obj
, u
.ux
, u
.uy
, x
, y
, TRUE
, TRUE
);
1558 /* specifying 'F' with no monster wastes a turn */
1559 if (context
.forcefight
1560 /* remembered an 'I' && didn't use a move command */
1561 || (glyph_is_invisible(levl
[x
][y
].glyph
) && !context
.nopick
)) {
1562 struct obj
*boulder
= 0;
1563 boolean explo
= (Upolyd
&& attacktype(youmonst
.data
, AT_EXPL
)),
1564 solid
= !accessible(x
, y
);
1565 int glyph
= glyph_at(x
, y
); /* might be monster */
1569 boulder
= sobj_at(BOULDER
, x
, y
);
1570 /* if a statue is displayed at the target location,
1571 player is attempting to attack it [and boulder
1572 handling below is suitable for handling that] */
1573 if (glyph_is_statue(glyph
)
1574 || (Hallucination
&& glyph_is_monster(glyph
)))
1575 boulder
= sobj_at(STATUE
, x
, y
);
1577 /* force fight at boulder/statue or wall/door while wielding
1578 pick: start digging to break the boulder or wall */
1579 if (context
.forcefight
1581 && uwep
&& dig_typ(uwep
, x
, y
)
1582 /* should we dig? */
1583 && !glyph_is_invisible(glyph
) && !glyph_is_monster(glyph
)) {
1584 (void) use_pick_axe2(uwep
);
1589 /* about to become known empty -- remove 'I' if present */
1592 map_object(boulder
, TRUE
);
1594 glyph
= glyph_at(x
, y
); /* might have just changed */
1597 Strcpy(buf
, ansimpleoname(boulder
));
1598 else if (Underwater
&& !is_pool(x
, y
))
1599 /* Underwater, targetting non-water; the map just shows blank
1600 because you don't see remembered terrain while underwater;
1601 although the hero can attack an adjacent monster this way,
1602 assume he can't reach out far enough to distinguish terrain */
1603 Sprintf(buf
, (Is_waterlevel(&u
.uz
) && levl
[x
][y
].typ
== AIR
)
1607 /* glyph might indicate unseen terrain if hero is blind;
1608 unlike searching, this won't reveal what that terrain is
1609 (except for solid rock, where the glyph would otherwise
1610 yield ludicrous "dark part of a room") */
1612 (levl
[x
][y
].typ
== STONE
)
1614 : glyph_is_cmap(glyph
)
1615 ? the(defsyms
[glyph_to_cmap(glyph
)].explanation
)
1616 : (const char *) "an unknown obstacle");
1617 /* note: 'solid' is misleadingly named and catches pools
1618 of water and lava as well as rock and walls */
1620 Strcpy(buf
, "thin air");
1622 !(boulder
|| solid
) ? "" : !explo
? "harmlessly " : "futilely ",
1623 explo
? "explode at" : "attack", buf
);
1628 u
.mh
= -1; /* dead in the current form */
1633 if (glyph_is_invisible(levl
[x
][y
].glyph
)) {
1637 /* not attacking an animal, so we try to move */
1638 if ((u
.dx
|| u
.dy
) && u
.usteed
&& stucksteed(FALSE
)) {
1647 if (!trapmove(x
, y
, trap
))
1651 if (!test_move(u
.ux
, u
.uy
, x
- u
.ux
, y
- u
.uy
, DO_MOVE
)) {
1652 if (!context
.door_opened
) {
1659 /* Move ball and chain. */
1661 if (!drag_ball(x
, y
, &bc_control
, &ballx
, &bally
, &chainx
, &chainy
,
1662 &cause_delay
, TRUE
))
1665 /* Check regions entering/leaving */
1666 if (!in_out_region(x
, y
))
1669 /* now move the hero */
1673 /* Move your steed, too */
1675 u
.usteed
->mx
= u
.ux
;
1676 u
.usteed
->my
= u
.uy
;
1681 * If safepet at destination then move the pet to the hero's
1682 * previous location using the same conditions as in attack().
1683 * there are special extenuating circumstances:
1684 * (1) if the pet dies then your god angers,
1685 * (2) if the pet gets trapped then your god may disapprove,
1686 * (3) if the pet was already trapped and you attempt to free it
1687 * not only do you encounter the trap but you may frighten your
1688 * pet causing it to go wild! moral: don't abuse this privilege.
1690 * Ceiling-hiding pets are skipped by this section of code, to
1691 * be caught by the normal falling-monster code.
1693 if (is_safepet(mtmp
) && !(is_hider(mtmp
->data
) && mtmp
->mundetected
)) {
1694 /* if trapped, there's a chance the pet goes wild */
1695 if (mtmp
->mtrapped
) {
1696 if (!rn2(mtmp
->mtame
)) {
1697 mtmp
->mtame
= mtmp
->mpeaceful
= mtmp
->msleeping
= 0;
1699 m_unleash(mtmp
, TRUE
);
1705 mtmp
->mundetected
= 0;
1706 if (mtmp
->m_ap_type
)
1708 else if (!mtmp
->mtame
)
1709 newsym(mtmp
->mx
, mtmp
->my
);
1711 if (mtmp
->mtrapped
&& (trap
= t_at(mtmp
->mx
, mtmp
->my
)) != 0
1712 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)
1713 && sobj_at(BOULDER
, trap
->tx
, trap
->ty
)) {
1714 /* can't swap places with pet pinned in a pit by a boulder */
1715 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
; /* didn't move after all */
1716 } else if (u
.ux0
!= x
&& u
.uy0
!= y
&& NODIAG(mtmp
->data
- mons
)) {
1717 /* can't swap places when pet can't move to your spot */
1718 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
;
1719 You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp
)));
1720 } else if (u
.ux0
!= x
&& u
.uy0
!= y
&& bad_rock(mtmp
->data
, x
, u
.uy0
)
1721 && bad_rock(mtmp
->data
, u
.ux0
, y
)
1722 && (bigmonst(mtmp
->data
) || (curr_mon_load(mtmp
) > 600))) {
1723 /* can't swap places when pet won't fit thru the opening */
1724 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
; /* didn't move after all */
1725 You("stop. %s won't fit through.", upstart(y_monnam(mtmp
)));
1727 char pnambuf
[BUFSZ
];
1729 /* save its current description in case of polymorph */
1730 Strcpy(pnambuf
, y_monnam(mtmp
));
1732 remove_monster(x
, y
);
1733 place_monster(mtmp
, u
.ux0
, u
.uy0
);
1735 newsym(u
.ux0
, u
.uy0
);
1737 You("%s %s.", mtmp
->mtame
? "swap places with" : "frighten",
1740 /* check for displacing it into pools and traps */
1741 switch (minliquid(mtmp
) ? 2 : mintrap(mtmp
)) {
1744 case 1: /* trapped */
1745 case 3: /* changed levels */
1746 /* there's already been a trap message, reinforce it */
1751 /* drowned or died...
1752 * you killed your pet by direct action, so get experience
1753 * and possibly penalties;
1754 * we want the level gain message, if it happens, to occur
1755 * before the guilt message below
1758 /* minliquid() and mintrap() call mondead() rather than
1759 killed() so we duplicate some of the latter here */
1762 u
.uconduct
.killer
++;
1763 mndx
= monsndx(mtmp
->data
);
1764 tmp
= experience(mtmp
, (int) mvitals
[mndx
].died
);
1765 more_experienced(tmp
, 0);
1766 newexplevel(); /* will decide if you go up */
1768 /* That's no way to treat a pet! Your god gets angry.
1770 * [This has always been pretty iffy. Why does your
1771 * patron deity care at all, let alone enough to get mad?]
1774 You_feel("guilty about losing your pet like this.");
1780 pline("that's strange, unknown mintrap result!");
1786 reset_occupations();
1788 if (context
.run
< 8)
1789 if (IS_DOOR(tmpr
->typ
) || IS_ROCK(tmpr
->typ
)
1790 || IS_FURNITURE(tmpr
->typ
))
1794 if (hides_under(youmonst
.data
) || youmonst
.data
->mlet
== S_EEL
1796 (void) hideunder(&youmonst
);
1799 * Mimics (or whatever) become noticeable if they move and are
1800 * imitating something that doesn't move. We could extend this
1801 * to non-moving monsters...
1803 if ((u
.dx
|| u
.dy
) && (youmonst
.m_ap_type
== M_AP_OBJECT
1804 || youmonst
.m_ap_type
== M_AP_FURNITURE
))
1805 youmonst
.m_ap_type
= M_AP_NOTHING
;
1807 check_leash(u
.ux0
, u
.uy0
);
1809 if (u
.ux0
!= u
.ux
|| u
.uy0
!= u
.uy
) {
1811 /* Clean old position -- vision_recalc() will print our new one. */
1812 newsym(u
.ux0
, u
.uy0
);
1813 /* Since the hero has moved, adjust what can be seen/unseen. */
1814 vision_recalc(1); /* Do the work now in the recover time. */
1815 invocation_message();
1818 if (Punished
) /* put back ball and chain */
1819 move_bc(0, bc_control
, ballx
, bally
, chainx
, chainy
);
1823 /* delay next move because of ball dragging */
1824 /* must come after we finished picking up, in spoteffects() */
1827 multi_reason
= "dragging an iron ball";
1831 if (context
.run
&& flags
.runmode
!= RUN_TPORT
) {
1832 /* display every step or every 7th step depending upon mode */
1833 if (flags
.runmode
!= RUN_LEAP
|| !(moves
% 7L)) {
1838 if (flags
.runmode
== RUN_CRAWL
) {
1848 /* combat increases metabolism */
1852 /* this used to be part of domove() when moving to a monster's
1853 position, but is now called by attack() so that it doesn't
1854 execute if you decline to attack a peaceful monster */
1856 if ((moves
% 3L) != 0L && near_capacity() >= HVY_ENCUMBER
) {
1857 int *hp
= (!Upolyd
? &u
.uhp
: &u
.mh
);
1862 You("pass out from exertion!");
1863 exercise(A_CON
, FALSE
);
1864 fall_asleep(-10, FALSE
);
1867 return (boolean
) (multi
< 0); /* might have fainted (forced to sleep) */
1871 invocation_message()
1873 /* a special clue-msg when on the Invocation position */
1874 if (invocation_pos(u
.ux
, u
.uy
) && !On_stairs(u
.ux
, u
.uy
)) {
1876 struct obj
*otmp
= carrying(CANDELABRUM_OF_INVOCATION
);
1878 nomul(0); /* stop running or travelling */
1880 Sprintf(buf
, "beneath %s", y_monnam(u
.usteed
));
1881 else if (Levitation
|| Flying
)
1882 Strcpy(buf
, "beneath you");
1884 Sprintf(buf
, "under your %s", makeplural(body_part(FOOT
)));
1886 You_feel("a strange vibration %s.", buf
);
1887 u
.uevent
.uvibrated
= 1;
1888 if (otmp
&& otmp
->spe
== 7 && otmp
->lamplit
)
1889 pline("%s %s!", The(xname(otmp
)),
1890 Blind
? "throbs palpably" : "glows with a strange light");
1894 /* moving onto different terrain;
1895 might be going into solid rock, inhibiting levitation or flight,
1896 or coming back out of such, reinstating levitation/flying */
1900 struct rm
*lev
= &levl
[u
.ux
][u
.uy
];
1901 boolean blocklev
= (IS_ROCK(lev
->typ
) || closed_door(u
.ux
, u
.uy
)
1902 || (Is_waterlevel(&u
.uz
) && lev
->typ
== WATER
));
1905 /* called from spoteffects(), skip float_down() */
1907 You_cant("levitate in here.");
1908 BLevitation
|= FROMOUTSIDE
;
1909 } else if (BLevitation
) {
1910 BLevitation
&= ~FROMOUTSIDE
;
1914 /* the same terrain that blocks levitation also blocks flight */
1917 You_cant("fly in here.");
1918 BFlying
|= FROMOUTSIDE
;
1919 } else if (BFlying
) {
1920 BFlying
&= ~FROMOUTSIDE
;
1921 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
1922 /* [minor bug: we don't know whether this is beginning flight or
1923 resuming it; that could be tracked so that this message could
1924 be adjusted to "resume flying", but isn't worth the effort...] */
1926 You("start flying.");
1930 /* extracted from spoteffects; called by spoteffects to check for entering or
1931 leaving a pool of water/lava, and by moveloop to check for staying on one;
1932 returns true to skip rest of spoteffects */
1934 pooleffects(newspot
)
1935 boolean newspot
; /* true if called by spoteffects */
1937 /* check for leaving water */
1939 boolean still_inwater
= FALSE
; /* assume we're getting out */
1941 if (!is_pool(u
.ux
, u
.uy
)) {
1942 if (Is_waterlevel(&u
.uz
))
1943 You("pop into an air bubble.");
1944 else if (is_lava(u
.ux
, u
.uy
))
1945 You("leave the %s...", hliquid("water")); /* oops! */
1947 You("are on solid %s again.",
1948 is_ice(u
.ux
, u
.uy
) ? "ice" : "land");
1949 } else if (Is_waterlevel(&u
.uz
)) {
1950 still_inwater
= TRUE
;
1951 } else if (Levitation
) {
1952 You("pop out of the %s like a cork!", hliquid("water"));
1953 } else if (Flying
) {
1954 You("fly out of the %s.", hliquid("water"));
1955 } else if (Wwalking
) {
1956 You("slowly rise above the surface.");
1958 still_inwater
= TRUE
;
1960 if (!still_inwater
) {
1961 boolean was_underwater
= (Underwater
&& !Is_waterlevel(&u
.uz
));
1963 u
.uinwater
= 0; /* leave the water */
1964 if (was_underwater
) { /* restore vision */
1966 vision_full_recalc
= 1;
1971 /* check for entering water or lava */
1972 if (!u
.ustuck
&& !Levitation
&& !Flying
&& is_pool_or_lava(u
.ux
, u
.uy
)) {
1974 && (is_flyer(u
.usteed
->data
) || is_floater(u
.usteed
->data
)
1975 || is_clinger(u
.usteed
->data
))) {
1976 /* floating or clinging steed keeps hero safe (is_flyer() test
1977 is redundant; it can't be true since Flying yielded false) */
1979 } else if (u
.usteed
) {
1980 /* steed enters pool */
1981 dismount_steed(Underwater
? DISMOUNT_FELL
: DISMOUNT_GENERIC
);
1982 /* dismount_steed() -> float_down() -> pickup()
1983 (float_down doesn't do autopickup on Air or Water) */
1984 if (Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
))
1986 /* even if we actually end up at same location, float_down()
1987 has already done spoteffect()'s trap and pickup actions */
1989 check_special_room(FALSE
); /* spoteffects */
1994 /* drown(),lava_effects() return true if hero changes
1995 location while surviving the problem */
1996 if (is_lava(u
.ux
, u
.uy
)) {
1999 } else if (!Wwalking
2000 && (newspot
|| !u
.uinwater
|| !(Swimming
|| Amphibious
))) {
2012 static int inspoteffects
= 0;
2013 static coord spotloc
;
2014 static int spotterrain
;
2015 static struct trap
*spottrap
= (struct trap
*) 0;
2016 static unsigned spottraptyp
= NO_TRAP
;
2019 struct trap
*trap
= t_at(u
.ux
, u
.uy
);
2021 /* prevent recursion from affecting the hero all over again
2022 [hero poly'd to iron golem enters water here, drown() inflicts
2023 damage that triggers rehumanize() which calls spoteffects()...] */
2024 if (inspoteffects
&& u
.ux
== spotloc
.x
&& u
.uy
== spotloc
.y
2025 /* except when reason is transformed terrain (ice -> water) */
2026 && spotterrain
== levl
[u
.ux
][u
.uy
].typ
2027 /* or transformed trap (land mine -> pit) */
2028 && (!spottrap
|| !trap
|| trap
->ttyp
== spottraptyp
))
2032 spotterrain
= levl
[u
.ux
][u
.uy
].typ
;
2033 spotloc
.x
= u
.ux
, spotloc
.y
= u
.uy
;
2035 /* moving onto different terrain might cause Levitation to toggle */
2036 if (spotterrain
!= levl
[u
.ux0
][u
.uy0
].typ
|| !on_level(&u
.uz
, &u
.uz0
))
2039 if (pooleffects(TRUE
))
2042 check_special_room(FALSE
);
2043 if (IS_SINK(levl
[u
.ux
][u
.uy
].typ
) && Levitation
)
2045 if (!in_steed_dismounting
) { /* if dismounting, we'll check again later */
2048 /* if levitation is due to time out at the end of this
2049 turn, allowing it to do so could give the perception
2050 that a trap here is being triggered twice, so adjust
2051 the timeout to prevent that */
2052 if (trap
&& (HLevitation
& TIMEOUT
) == 1L
2053 && !(ELevitation
|| (HLevitation
& ~(I_SPECIAL
| TIMEOUT
)))) {
2054 if (rn2(2)) { /* defer timeout */
2055 incr_itimeout(&HLevitation
, 1L);
2056 } else { /* timeout early */
2057 if (float_down(I_SPECIAL
| TIMEOUT
, 0L)) {
2058 /* levitation has ended; we've already triggered
2059 any trap and [usually] performed autopickup */
2066 * If not a pit, pickup before triggering trap.
2067 * If pit, trigger trap before pickup.
2069 pit
= (trap
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
));
2075 * dotrap on a fire trap calls melt_ice() which triggers
2076 * spoteffects() (again) which can trigger the same fire
2077 * trap (again). Use static spottrap to prevent that.
2078 * We track spottraptyp because some traps morph
2079 * (landmine to pit) and any new trap type
2080 * should get triggered.
2082 if (!spottrap
|| spottraptyp
!= trap
->ttyp
) {
2084 spottraptyp
= trap
->ttyp
;
2085 dotrap(trap
, 0); /* fall into arrow trap, etc. */
2086 spottrap
= (struct trap
*) 0;
2087 spottraptyp
= NO_TRAP
;
2093 /* Warning alerts you to ice danger */
2094 if (Warning
&& is_ice(u
.ux
, u
.uy
)) {
2095 static const char *const icewarnings
[] = {
2096 "The ice seems very soft and slushy.",
2097 "You feel the ice shift beneath you!",
2098 "The ice, is gonna BREAK!", /* The Dead Zone */
2100 long time_left
= spot_time_left(u
.ux
, u
.uy
, MELT_ICE_AWAY
);
2102 if (time_left
&& time_left
< 15L)
2103 pline("%s", icewarnings
[(time_left
< 5L) ? 2
2104 : (time_left
< 10L) ? 1
2107 if ((mtmp
= m_at(u
.ux
, u
.uy
)) && !u
.uswallow
) {
2108 mtmp
->mundetected
= mtmp
->msleeping
= 0;
2109 switch (mtmp
->data
->mlet
) {
2111 pline("%s suddenly drops from the %s!", Amonnam(mtmp
),
2112 ceiling(u
.ux
, u
.uy
));
2113 if (mtmp
->mtame
) { /* jumps to greet you, not attack */
2115 } else if (uarmh
&& is_metallic(uarmh
)) {
2116 pline("Its blow glances off your %s.",
2117 helm_simple_name(uarmh
));
2118 } else if (u
.uac
+ 3 <= rnd(20)) {
2119 You("are almost hit by %s!",
2120 x_monnam(mtmp
, ARTICLE_A
, "falling", 0, TRUE
));
2124 You("are hit by %s!",
2125 x_monnam(mtmp
, ARTICLE_A
, "falling", 0, TRUE
));
2127 if (Half_physical_damage
)
2128 dmg
= (dmg
+ 1) / 2;
2129 mdamageu(mtmp
, dmg
);
2132 default: /* monster surprises you. */
2134 pline("%s jumps near you from the %s.", Amonnam(mtmp
),
2135 ceiling(u
.ux
, u
.uy
));
2136 else if (mtmp
->mpeaceful
) {
2138 Blind
&& !sensemon(mtmp
) ? something
: a_monnam(mtmp
));
2139 mtmp
->mpeaceful
= 0;
2141 pline("%s attacks you by surprise!", Amonnam(mtmp
));
2144 mnexto(mtmp
); /* have to move the monster */
2147 if (!--inspoteffects
) {
2148 spotterrain
= STONE
; /* 0 */
2149 spotloc
.x
= spotloc
.y
= 0;
2154 /* returns first matching monster */
2155 STATIC_OVL
struct monst
*
2156 monstinroom(mdat
, roomno
)
2157 struct permonst
*mdat
;
2160 register struct monst
*mtmp
;
2162 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2163 if (DEADMONSTER(mtmp
))
2165 if (mtmp
->data
== mdat
2166 && index(in_rooms(mtmp
->mx
, mtmp
->my
, 0), roomno
+ ROOMOFFSET
))
2169 return (struct monst
*) 0;
2173 in_rooms(x
, y
, typewanted
)
2174 register xchar x
, y
;
2175 register int typewanted
;
2178 char rno
, *ptr
= &buf
[4];
2179 int typefound
, min_x
, min_y
, max_x
, max_y_offset
, step
;
2180 register struct rm
*lev
;
2182 #define goodtype(rno) \
2184 || (typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted \
2185 || (typewanted == SHOPBASE && typefound > SHOPBASE))
2187 switch (rno
= levl
[x
][y
].roomno
) {
2196 default: /* i.e. a regular room # */
2206 else if (x
>= COLNO
)
2213 max_y_offset
-= step
;
2214 } else if ((min_y
+ max_y_offset
) >= ROWNO
)
2215 max_y_offset
-= step
;
2217 for (x
= min_x
; x
<= max_x
; x
+= step
) {
2218 lev
= &levl
[x
][min_y
];
2220 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2224 if (y
> max_y_offset
)
2226 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2230 if (y
> max_y_offset
)
2232 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2239 /* is (x,y) in a town? */
2244 s_level
*slev
= Is_special(&u
.uz
);
2245 register struct mkroom
*sroom
;
2246 boolean has_subrooms
= FALSE
;
2248 if (!slev
|| !slev
->flags
.town
)
2252 * See if (x,y) is in a room with subrooms, if so, assume it's the
2253 * town. If there are no subrooms, the whole level is in town.
2255 for (sroom
= &rooms
[0]; sroom
->hx
> 0; sroom
++) {
2256 if (sroom
->nsubrooms
> 0) {
2257 has_subrooms
= TRUE
;
2258 if (inside_room(sroom
, x
, y
))
2263 return !has_subrooms
;
2268 register boolean newlev
;
2270 char *ptr1
, *ptr2
, *ptr3
, *ptr4
;
2272 Strcpy(u
.urooms0
, u
.urooms
);
2273 Strcpy(u
.ushops0
, u
.ushops
);
2276 u
.uentered
[0] = '\0';
2278 u
.ushops_entered
[0] = '\0';
2279 Strcpy(u
.ushops_left
, u
.ushops0
);
2282 Strcpy(u
.urooms
, in_rooms(u
.ux
, u
.uy
, 0));
2284 for (ptr1
= &u
.urooms
[0], ptr2
= &u
.uentered
[0], ptr3
= &u
.ushops
[0],
2285 ptr4
= &u
.ushops_entered
[0];
2287 if (!index(u
.urooms0
, *ptr1
))
2289 if (IS_SHOP(*ptr1
- ROOMOFFSET
)) {
2291 if (!index(u
.ushops0
, *ptr1
))
2299 /* filter u.ushops0 -> u.ushops_left */
2300 for (ptr1
= &u
.ushops0
[0], ptr2
= &u
.ushops_left
[0]; *ptr1
; ptr1
++)
2301 if (!index(u
.ushops
, *ptr1
))
2306 /* possibly deliver a one-time room entry message */
2308 check_special_room(newlev
)
2309 register boolean newlev
;
2311 register struct monst
*mtmp
;
2314 move_update(newlev
);
2317 u_left_shop(u
.ushops_left
, newlev
);
2319 if (!*u
.uentered
&& !*u
.ushops_entered
) /* implied by newlev */
2320 return; /* no entrance messages necessary */
2322 /* Did we just enter a shop? */
2323 if (*u
.ushops_entered
)
2324 u_entered_shop(u
.ushops_entered
);
2326 for (ptr
= &u
.uentered
[0]; *ptr
; ptr
++) {
2327 int roomno
= *ptr
- ROOMOFFSET
, rt
= rooms
[roomno
].rtype
;
2328 boolean msg_given
= TRUE
;
2330 /* Did we just enter some other special room? */
2331 /* vault.c insists that a vault remain a VAULT,
2332 * and temples should remain TEMPLEs,
2333 * but everything else gives a message only the first time */
2336 pline("Welcome to David's treasure zoo!");
2339 pline("It %s rather %s down here.", Blind
? "feels" : "looks",
2340 Blind
? "humid" : "muddy");
2343 You("enter an opulent throne room!");
2346 You("enter a leprechaun hall!");
2350 const char *run
= locomotion(youmonst
.data
, "Run");
2351 pline("%s away! %s away!", run
, run
);
2353 You("have an uncanny feeling...");
2356 You("enter a giant beehive!");
2359 You("enter a disgusting nest!");
2362 You("enter an anthole!");
2365 if (monstinroom(&mons
[PM_SOLDIER
], roomno
)
2366 || monstinroom(&mons
[PM_SERGEANT
], roomno
)
2367 || monstinroom(&mons
[PM_LIEUTENANT
], roomno
)
2368 || monstinroom(&mons
[PM_CAPTAIN
], roomno
))
2369 You("enter a military barracks!");
2371 You("enter an abandoned barracks.");
2374 struct monst
*oracle
= monstinroom(&mons
[PM_ORACLE
], roomno
);
2376 if (!oracle
->mpeaceful
)
2377 verbalize("You're in Delphi, %s.", plname
);
2379 verbalize("%s, %s, welcome to Delphi!",
2380 Hello((struct monst
*) 0), plname
);
2386 intemple(roomno
+ ROOMOFFSET
);
2389 msg_given
= (rt
== TEMPLE
);
2394 room_discovered(roomno
);
2397 rooms
[roomno
].rtype
= OROOM
;
2398 if (!search_special(rt
)) {
2399 /* No more room of that type */
2402 level
.flags
.has_court
= 0;
2405 level
.flags
.has_swamp
= 0;
2408 level
.flags
.has_morgue
= 0;
2411 level
.flags
.has_zoo
= 0;
2414 level
.flags
.has_barracks
= 0;
2417 level
.flags
.has_temple
= 0;
2420 level
.flags
.has_beehive
= 0;
2424 if (rt
== COURT
|| rt
== SWAMP
|| rt
== MORGUE
|| rt
== ZOO
)
2425 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2426 if (DEADMONSTER(mtmp
))
2428 if (!Stealth
&& !rn2(3))
2429 mtmp
->msleeping
= 0;
2437 /* the ',' command */
2441 int count
, tmpcount
;
2442 struct trap
*traphere
= t_at(u
.ux
, u
.uy
);
2444 /* awful kludge to work around parse()'s pre-decrement */
2445 count
= (multi
|| (save_cm
&& *save_cm
== ',')) ? multi
+ 1 : 0;
2446 multi
= 0; /* always reset */
2447 /* uswallow case added by GAN 01/29/87 */
2449 if (!u
.ustuck
->minvent
) {
2450 if (is_animal(u
.ustuck
->data
)) {
2451 You("pick up %s tongue.", s_suffix(mon_nam(u
.ustuck
)));
2452 pline("But it's kind of slimy, so you drop it.");
2454 You("don't %s anything in here to pick up.",
2455 Blind
? "feel" : "see");
2459 return loot_mon(u
.ustuck
, &tmpcount
, (boolean
*) 0);
2462 if (is_pool(u
.ux
, u
.uy
)) {
2463 if (Wwalking
|| is_floater(youmonst
.data
) || is_clinger(youmonst
.data
)
2464 || (Flying
&& !Breathless
)) {
2465 You("cannot dive into the %s to pick things up.",
2468 } else if (!Underwater
) {
2469 You_cant("even see the bottom, let alone pick up %s.", something
);
2473 if (is_lava(u
.ux
, u
.uy
)) {
2474 if (Wwalking
|| is_floater(youmonst
.data
) || is_clinger(youmonst
.data
)
2475 || (Flying
&& !Breathless
)) {
2476 You_cant("reach the bottom to pick things up.");
2478 } else if (!likes_lava(youmonst
.data
)) {
2479 You("would burn to a crisp trying to pick things up.");
2483 if (!OBJ_AT(u
.ux
, u
.uy
)) {
2484 register struct rm
*lev
= &levl
[u
.ux
][u
.uy
];
2486 if (IS_THRONE(lev
->typ
))
2487 pline("It must weigh%s a ton!", lev
->looted
? " almost" : "");
2488 else if (IS_SINK(lev
->typ
))
2489 pline_The("plumbing connects it to the floor.");
2490 else if (IS_GRAVE(lev
->typ
))
2491 You("don't need a gravestone. Yet.");
2492 else if (IS_FOUNTAIN(lev
->typ
))
2493 You("could drink the %s...", hliquid("water"));
2494 else if (IS_DOOR(lev
->typ
) && (lev
->doormask
& D_ISOPEN
))
2495 pline("It won't come off the hinges.");
2497 There("is nothing here to pick up.");
2500 if (!can_reach_floor(TRUE
)) {
2501 if (traphere
&& uteetering_at_seen_pit(traphere
))
2502 You("cannot reach the bottom of the pit.");
2503 else if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
)
2505 else if (Blind
&& !can_reach_floor(TRUE
))
2506 You("cannot reach anything here.");
2508 You("cannot reach the %s.", surface(u
.ux
, u
.uy
));
2512 return pickup(-count
);
2515 /* stop running if we see something interesting */
2516 /* turn around a corner if that is the only way we can proceed */
2517 /* do not turn left or right twice */
2522 int i
, x0
= 0, y0
= 0, m0
= 1, i0
= 9;
2523 int corrct
= 0, noturn
= 0;
2527 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
2528 /* they polymorphed while in the middle of a long move. */
2529 if (NODIAG(u
.umonnum
) && u
.dx
&& u
.dy
) {
2530 You("cannot move diagonally.");
2535 if (Blind
|| context
.run
== 0)
2537 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
2538 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++) {
2539 if (!isok(x
, y
) || (x
== u
.ux
&& y
== u
.uy
))
2541 if (NODIAG(u
.umonnum
) && x
!= u
.ux
&& y
!= u
.uy
)
2544 if ((mtmp
= m_at(x
, y
)) != 0
2545 && mtmp
->m_ap_type
!= M_AP_FURNITURE
2546 && mtmp
->m_ap_type
!= M_AP_OBJECT
2547 && (!mtmp
->minvis
|| See_invisible
) && !mtmp
->mundetected
) {
2548 if ((context
.run
!= 1 && !mtmp
->mtame
)
2549 || (x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
2550 && !context
.travel
)) {
2551 if (iflags
.mention_walls
)
2552 pline("%s blocks your path.", upstart(a_monnam(mtmp
)));
2557 if (levl
[x
][y
].typ
== STONE
)
2559 if (x
== u
.ux
- u
.dx
&& y
== u
.uy
- u
.dy
)
2562 if (IS_ROCK(levl
[x
][y
].typ
) || levl
[x
][y
].typ
== ROOM
2563 || IS_AIR(levl
[x
][y
].typ
)) {
2565 } else if (closed_door(x
, y
) || (mtmp
&& is_door_mappear(mtmp
))) {
2566 if (x
!= u
.ux
&& y
!= u
.uy
)
2568 if (context
.run
!= 1) {
2569 if (iflags
.mention_walls
)
2570 You("stop in front of the door.");
2574 } else if (levl
[x
][y
].typ
== CORR
) {
2576 if (levl
[u
.ux
][u
.uy
].typ
!= ROOM
) {
2577 if (context
.run
== 1 || context
.run
== 3
2578 || context
.run
== 8) {
2579 i
= dist2(x
, y
, u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
2582 if (corrct
== 1 && dist2(x
, y
, x0
, y0
) != 1)
2594 } else if ((trap
= t_at(x
, y
)) && trap
->tseen
) {
2595 if (context
.run
== 1)
2596 goto bcorr
; /* if you must */
2597 if (x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
) {
2598 if (iflags
.mention_walls
) {
2599 int tt
= what_trap(trap
->ttyp
);
2600 You("stop in front of %s.",
2601 an(defsyms
[trap_to_defsym(tt
)].explanation
));
2606 } else if (is_pool_or_lava(x
, y
)) {
2607 /* water and lava only stop you if directly in front, and stop
2608 * you even if you are running
2610 if (!Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
2611 && x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
) {
2612 /* No Wwalking check; otherwise they'd be able
2613 * to test boots by trying to SHIFT-direction
2614 * into a pool and seeing if the game allowed it
2616 if (iflags
.mention_walls
)
2617 You("stop at the edge of the %s.",
2618 hliquid(is_pool(x
,y
) ? "water" : "lava"));
2622 } else { /* e.g. objects or trap or stairs */
2623 if (context
.run
== 1)
2625 if (context
.run
== 8)
2629 if (((x
== u
.ux
- u
.dx
) && (y
!= u
.uy
+ u
.dy
))
2630 || ((y
== u
.uy
- u
.dy
) && (x
!= u
.ux
+ u
.dx
)))
2636 } /* end for loops */
2638 if (corrct
> 1 && context
.run
== 2) {
2639 if (iflags
.mention_walls
)
2640 pline_The("corridor widens here.");
2643 if ((context
.run
== 1 || context
.run
== 3 || context
.run
== 8) && !noturn
2644 && !m0
&& i0
&& (corrct
== 1 || (corrct
== 2 && i0
== 1))) {
2645 /* make sure that we do not turn too far */
2647 if (u
.dx
== y0
- u
.uy
&& u
.dy
== u
.ux
- x0
)
2648 i
= 2; /* straight turn right */
2650 i
= -2; /* straight turn left */
2651 } else if (u
.dx
&& u
.dy
) {
2652 if ((u
.dx
== u
.dy
&& y0
== u
.uy
) || (u
.dx
!= u
.dy
&& y0
!= u
.uy
))
2653 i
= -1; /* half turn left */
2655 i
= 1; /* half turn right */
2657 if ((x0
- u
.ux
== y0
- u
.uy
&& !u
.dy
)
2658 || (x0
- u
.ux
!= y0
- u
.uy
&& u
.dy
))
2659 i
= 1; /* half turn right */
2661 i
= -1; /* half turn left */
2664 i
+= u
.last_str_turn
;
2665 if (i
<= 2 && i
>= -2) {
2666 u
.last_str_turn
= i
;
2673 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2678 struct rm
*lev_p
= &levl
[x
][y
];
2680 if (!IS_DOOR(lev_p
->typ
))
2682 /* all rogue level doors are doorless but disallow diagonal access, so
2683 we treat them as if their non-existant doors were actually present */
2684 if (Is_rogue_level(&u
.uz
))
2686 return !(lev_p
->doormask
& ~(D_NODOOR
| D_BROKEN
));
2689 /* used by drown() to check whether hero can crawl from water to <x,y> */
2691 crawl_destination(x
, y
)
2694 /* is location ok in general? */
2695 if (!goodpos(x
, y
, &youmonst
, 0))
2698 /* orthogonal movement is unrestricted when destination is ok */
2699 if (x
== u
.ux
|| y
== u
.uy
)
2702 /* diagonal movement has some restrictions */
2703 if (NODIAG(u
.umonnum
))
2704 return FALSE
; /* poly'd into a grid bug... */
2706 return TRUE
; /* or a xorn... */
2707 /* pool could be next to a door, conceivably even inside a shop */
2708 if (IS_DOOR(levl
[x
][y
].typ
) && (!doorless_door(x
, y
) || block_door(x
, y
)))
2710 /* finally, are we trying to squeeze through a too-narrow gap? */
2711 return !(bad_rock(youmonst
.data
, u
.ux
, y
)
2712 && bad_rock(youmonst
.data
, x
, u
.uy
));
2715 /* something like lookaround, but we are not running */
2716 /* react only to monsters that might hit us */
2721 register struct monst
*mtmp
;
2723 /* Also see the similar check in dochugw() in monmove.c */
2724 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
2725 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++) {
2726 if (!isok(x
, y
) || (x
== u
.ux
&& y
== u
.uy
))
2728 if ((mtmp
= m_at(x
, y
)) && mtmp
->m_ap_type
!= M_AP_FURNITURE
2729 && mtmp
->m_ap_type
!= M_AP_OBJECT
2730 && (!mtmp
->mpeaceful
|| Hallucination
)
2731 && (!is_hider(mtmp
->data
) || !mtmp
->mundetected
)
2732 && !noattacks(mtmp
->data
) && mtmp
->mcanmove
2733 && !mtmp
->msleeping
/* aplvax!jcn */
2734 && !onscary(u
.ux
, u
.uy
, mtmp
) && canspotmon(mtmp
))
2745 return; /* This is a bug fix by ab@unido */
2746 u
.uinvulnerable
= FALSE
; /* Kludge to avoid ctrl-C bug -dlc */
2750 multi_reason
= NULL
;
2751 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
2754 /* called when a non-movement, multi-turn action has completed */
2757 const char *msg_override
;
2759 multi
= 0; /* caller will usually have done this already */
2761 nomovemsg
= msg_override
;
2762 else if (!nomovemsg
)
2763 nomovemsg
= You_can_move_again
;
2768 multi_reason
= NULL
;
2777 static short powers
[] = { TELEPORT
, SEE_INVIS
, POISON_RES
, COLD_RES
,
2778 SHOCK_RES
, FIRE_RES
, SLEEP_RES
, DISINT_RES
,
2779 TELEPORT_CONTROL
, STEALTH
, FAST
, INVIS
};
2781 if (moves
<= wailmsg
+ 50)
2785 if (Role_if(PM_WIZARD
) || Race_if(PM_ELF
) || Role_if(PM_VALKYRIE
)) {
2789 who
= (Role_if(PM_WIZARD
) || Role_if(PM_VALKYRIE
)) ? urole
.name
.m
2792 pline("%s is about to die.", who
);
2794 for (i
= 0, powercnt
= 0; i
< SIZE(powers
); ++i
)
2795 if (u
.uprops
[powers
[i
]].intrinsic
& INTRINSIC
)
2798 pline((powercnt
>= 4) ? "%s, all your powers will be lost..."
2799 : "%s, your life force is running out.",
2803 You_hear(u
.uhp
== 1 ? "the wailing of the Banshee..."
2804 : "the howling of the CwnAnnwn...");
2809 losehp(n
, knam
, k_format
)
2811 register const char *knam
;
2821 else if (n
> 0 && u
.mh
* 10 < u
.mhmax
&& Unchanging
)
2827 if (u
.uhp
> u
.uhpmax
)
2828 u
.uhpmax
= u
.uhp
; /* perhaps n was negative */
2830 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
2833 killer
.format
= k_format
;
2834 if (killer
.name
!= knam
) /* the thing that killed you */
2835 Strcpy(killer
.name
, knam
? knam
: "");
2838 } else if (n
> 0 && u
.uhp
* 10 < u
.uhpmax
) {
2846 register long carrcap
;
2848 carrcap
= 25 * (ACURRSTR
+ ACURR(A_CON
)) + 50;
2850 /* consistent with can_carry() in mon.c */
2851 if (youmonst
.data
->mlet
== S_NYMPH
)
2852 carrcap
= MAX_CARR_CAP
;
2853 else if (!youmonst
.data
->cwt
)
2854 carrcap
= (carrcap
* (long) youmonst
.data
->msize
) / MZ_HUMAN
;
2855 else if (!strongmonst(youmonst
.data
)
2856 || (strongmonst(youmonst
.data
)
2857 && (youmonst
.data
->cwt
> WT_HUMAN
)))
2858 carrcap
= (carrcap
* (long) youmonst
.data
->cwt
/ WT_HUMAN
);
2861 if (Levitation
|| Is_airlevel(&u
.uz
) /* pugh@cornell */
2862 || (u
.usteed
&& strongmonst(u
.usteed
->data
))) {
2863 carrcap
= MAX_CARR_CAP
;
2865 if (carrcap
> MAX_CARR_CAP
)
2866 carrcap
= MAX_CARR_CAP
;
2868 if (EWounded_legs
& LEFT_SIDE
)
2870 if (EWounded_legs
& RIGHT_SIDE
)
2876 return (int) carrcap
;
2879 static int wc
; /* current weight_cap(); valid after call to inv_weight() */
2881 /* returns how far beyond the normal capacity the player is currently. */
2882 /* inv_weight() is negative if the player is below normal capacity. */
2886 register struct obj
*otmp
= invent
;
2887 register int wt
= 0;
2890 if (otmp
->oclass
== COIN_CLASS
)
2891 wt
+= (int) (((long) otmp
->quan
+ 50L) / 100L);
2892 else if (otmp
->otyp
!= BOULDER
|| !throws_rocks(youmonst
.data
))
2901 * Returns 0 if below normal capacity, or the number of "capacity units"
2902 * over the normal capacity the player is loaded. Max is 5.
2905 calc_capacity(xtra_wt
)
2908 int cap
, wt
= inv_weight() + xtra_wt
;
2911 return UNENCUMBERED
;
2914 cap
= (wt
* 2 / wc
) + 1;
2915 return min(cap
, OVERLOADED
);
2921 return calc_capacity(0);
2927 int wt
= inv_weight();
2929 return (wt
- (2 * wc
));
2936 if (near_capacity() >= EXT_ENCUMBER
) {
2940 You_cant("do that while carrying so much stuff.");
2950 register struct obj
*otmp
= invent
;
2951 register int ct
= 0;
2954 if (incl_gold
|| otmp
->invlet
!= GOLD_SYM
)
2961 /* Counts the money in an object chain. */
2962 /* Intended use is for your or some monster's inventory, */
2963 /* now that u.gold/m.gold is gone.*/
2964 /* Counting money in a container might be possible too. */
2970 /* Must change when silver & copper is implemented: */
2971 if (otmp
->oclass
== COIN_CLASS
)