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. */
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
;
28 tmp_anything
= zeroany
;
29 tmp_anything
.a_uint
= ui
;
37 tmp_anything
= zeroany
;
38 tmp_anything
.a_long
= lng
;
46 tmp_anything
= zeroany
;
47 tmp_anything
.a_monst
= mtmp
;
55 tmp_anything
= zeroany
;
56 tmp_anything
.a_obj
= obj
;
61 revive_nasty(x
, y
, msg
)
65 register struct obj
*otmp
, *otmp2
;
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
);
80 revived
= revive_corpse(otmp
);
84 /* this location might not be safe, if not, move revived monster */
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? */
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
;
114 if (Levitation
|| Is_airlevel(&u
.uz
)) {
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? */
121 if (verysmall(youmonst
.data
) && !u
.usteed
) {
123 feel_location(sx
, sy
);
124 pline("You're too small to push that %s.", xname(otmp
));
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
)) {
134 /* KMH -- Sokoban doesn't let you push boulders diagonally */
135 if (Sokoban
&& u
.dx
&& u
.dy
) {
137 feel_location(sx
, sy
);
138 pline("%s won't roll diagonally on this %s.",
139 The(xname(otmp
)), surface(sx
, sy
));
143 if (revive_nasty(rx
, ry
, "You sense movement on the other side."))
146 if (mtmp
&& !noncorporeal(mtmp
->data
)
148 || !(ttmp
&& ((ttmp
->ttyp
== PIT
)
149 || (ttmp
->ttyp
== SPIKED_PIT
))))) {
151 feel_location(sx
, sy
);
152 if (canspotmon(mtmp
))
153 pline("There's %s on the other side.", a_monnam(mtmp
));
155 You_hear("a monster behind %s.", the(xname(otmp
)));
156 map_invisible(rx
, ry
);
159 pline("Perhaps that's why %s cannot move it.",
160 u
.usteed
? y_monnam(u
.usteed
) : "you");
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
) {
172 obj_extract_self(otmp
);
173 place_object(otmp
, rx
, ry
);
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
);
183 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
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 */
193 viz_array
[ry
][rx
] |= IN_SIGHT
;
194 if (!flooreffects(otmp
, rx
, ry
, "fall")) {
195 place_object(otmp
, rx
, ry
);
199 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
203 pline("Kerplunk! You no longer feel %s.",
206 pline("%s%s and %s a %s in the %s!",
207 Tobjnam(otmp
, (ttmp
->ttyp
== TRAPDOOR
)
210 (ttmp
->ttyp
== TRAPDOOR
) ? "" : " into",
211 otense(otmp
, "plug"),
212 (ttmp
->ttyp
== TRAPDOOR
) ? "trap door" : "hole",
217 levl
[rx
][ry
].wall_info
&= ~W_NONDIGGABLE
;
218 levl
[rx
][ry
].candig
= 1;
221 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
224 int newlev
= 0; /* lint suppression */
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 */
234 pline("%s pushes %s and suddenly it disappears!",
235 upstart(y_monnam(u
.usteed
)), the(xname(otmp
)));
237 You("push %s and suddenly it disappears!",
239 if (ttmp
->ttyp
== TELEP_TRAP
) {
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
;
250 return sobj_at(BOULDER
, sx
, sy
) ? -1 : 0;
253 break; /* boulder not affected by this trap */
257 if (closed_door(rx
, ry
))
259 if (boulder_hits_pool(otmp
, rx
, ry
, TRUE
))
262 * Re-link at top of fobj chain so that pile order is preserved
263 * when level is restored.
267 place_object(otmp
, otmp
->ox
, otmp
->oy
);
271 #ifdef LINT /* static long lastmovetime; */
275 /* note: reset to zero after save/restore cycle */
276 static NEARDATA
long lastmovetime
;
280 if (moves
> lastmovetime
+ 2 || moves
< lastmovetime
)
281 pline("With %s effort you move %s.",
282 throws_rocks(youmonst
.data
) ? "little"
285 exercise(A_STR
, TRUE
);
287 pline("%s moves %s.", upstart(y_monnam(u
.usteed
)),
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) */
297 feel_location(rx
, ry
);
298 feel_location(sx
, sy
);
305 pline("%s tries to move %s, but cannot.",
306 upstart(y_monnam(u
.usteed
)), the(xname(otmp
)));
308 You("try to move %s, but in vain.", the(xname(otmp
)));
310 feel_location(sx
, sy
);
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
));
318 pline("However, you can easily %s.",
319 (flags
.pickup
&& !Sokoban
) ? "pick it up"
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
))) {
333 "However, you can squeeze yourself into a small opening.");
346 * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE
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
)
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
)
391 : lev
->typ
== IRONBARS
394 watch_dig((struct monst
*) 0, x
, y
, FALSE
);
396 } else if ((context
.digging
.effort
+= (30 + u
.udaminc
)) <= 100) {
398 You("%s chewing on the %s.",
399 context
.digging
.chew
? "continue" : "begin",
406 : (lev
->typ
== IRONBARS
)
409 context
.digging
.chew
= TRUE
;
410 watch_dig((struct monst
*) 0, x
, y
, FALSE
);
414 /* Okay, you've chewed through something */
416 u
.uhunger
+= rnd(20);
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
));
438 } else if (IS_WALL(lev
->typ
)) {
439 if (*in_rooms(x
, y
, SHOPBASE
)) {
440 add_damage(x
, y
, 10L * ACURRSTR
);
443 digtxt
= "chew a hole in the wall.";
444 if (level
.flags
.is_maze_lev
) {
446 } else if (level
.flags
.is_cavernous_lev
&& !in_town(x
, y
)) {
450 lev
->doormask
= D_NODOOR
;
452 } else if (IS_TREE(lev
->typ
)) {
453 digtxt
= "chew through the tree.";
455 } else if (lev
->typ
== IRONBARS
) {
456 digtxt
= "eat through the bars.";
458 } else if (lev
->typ
== SDOOR
) {
459 if (lev
->doormask
& D_TRAPPED
) {
460 lev
->doormask
= D_NODOOR
;
461 b_trapped("secret door", 0);
463 digtxt
= "chew through the secret door.";
464 lev
->doormask
= D_BROKEN
;
468 } else if (IS_DOOR(lev
->typ
)) {
469 if (*in_rooms(x
, y
, SHOPBASE
)) {
470 add_damage(x
, y
, 400L);
473 if (lev
->doormask
& D_TRAPPED
) {
474 lev
->doormask
= D_NODOOR
;
475 b_trapped("door", 0);
477 digtxt
= "chew through the door.";
478 lev
->doormask
= D_BROKEN
;
481 } else { /* STONE or SCORR */
482 digtxt
= "chew a passage through the rock.";
486 unblock_point(x
, y
); /* vision */
489 You1(digtxt
); /* after newsym */
491 pay_for_damage(dmgtxt
, FALSE
);
492 (void) memset((genericptr_t
) &context
.digging
, 0,
493 sizeof(struct dig_info
));
499 register struct obj
*obj
;
500 register xchar ox
, oy
;
502 /* optimize by leaving on the fobj chain? */
504 newsym(obj
->ox
, obj
->oy
);
505 place_object(obj
, ox
, oy
);
509 static NEARDATA
const char fell_on_sink
[] = "fell onto a sink";
514 register struct obj
*obj
;
516 boolean lev_boots
= (uarmf
&& uarmf
->otyp
== LEVITATION_BOOTS
),
517 innate_lev
= ((HLevitation
& (FROMOUTSIDE
| FROMFORM
)) != 0L),
518 ufall
= (!innate_lev
&& !(HFlying
|| EFlying
)); /* BFlying */
521 You(innate_lev
? "wobble unsteadily for a moment."
522 : "gain control of your flight.");
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
,
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
);
569 if (uleft
&& uleft
->otyp
== RIN_LEVITATION
) {
574 if (uright
&& uright
->otyp
== RIN_LEVITATION
) {
585 /* probably moot; we're either still levitating or went
586 through float_down(), but make sure BFlying is up to date */
590 /* intended to be called only on ROCKs or TREEs */
595 struct rm
*lev
= &levl
[x
][y
];
597 return (boolean
) !((IS_STWALL(lev
->typ
) || IS_TREE(lev
->typ
))
598 && (lev
->wall_info
& W_NONDIGGABLE
));
605 return (boolean
) !(IS_STWALL(levl
[x
][y
].typ
)
606 && (levl
[x
][y
].wall_info
& W_NONPASSWALL
));
611 struct permonst
*mdat
;
614 return (boolean
) ((Sokoban
&& sobj_at(BOULDER
, x
, y
))
615 || (IS_ROCK(levl
[x
][y
].typ
)
616 && (!tunnels(mdat
) || needspick(mdat
)
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
)
629 struct permonst
*ptr
= mon
->data
;
633 && !(amorphous(ptr
) || is_whirly(ptr
) || noncorporeal(ptr
)
634 || slithy(ptr
) || can_fog(mon
)))
637 /* lugging too much junk? */
639 (mon
== &youmonst
) ? inv_weight() + weight_cap() : curr_mon_load(mon
);
643 /* Sokoban restriction applies to hero only */
644 if (mon
== &youmonst
&& Sokoban
)
647 /* can squeeze through */
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
663 test_move(ux
, uy
, dx
, dy
, mode
)
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
)
679 if (Passes_walls
&& may_passwall(x
, y
)) {
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
)) {
687 if (!(Passes_walls
|| passes_bars(youmonst
.data
))) {
688 if (iflags
.mention_walls
)
689 You("cannot pass through the bars.");
692 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
694 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
696 } else if (flags
.autodig
&& !context
.run
&& !context
.nopick
&& uwep
698 /* MRKR: Automatic digging when wielding the appropriate tool */
700 (void) use_pick_axe2(uwep
);
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.");
715 } else if (IS_DOOR(tmpr
->typ
)) {
716 if (closed_door(x
, y
)) {
717 if (Blind
&& mode
== DO_MOVE
)
721 else if (can_ooze(&youmonst
)) {
723 You("ooze under the door.");
724 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
726 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
729 if (mode
== DO_MOVE
) {
730 if (amorphous(youmonst
.data
))
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
=
737 } else if (x
== ux
|| y
== uy
) {
738 if (Blind
|| Stunned
|| ACURR(A_DEX
) < 10
741 You_cant("lead %s through that closed door.",
744 pline("Ouch! You bump into a door.");
745 exercise(A_DEX
, FALSE
);
748 pline("That door is closed.");
750 } else if (mode
== TEST_TRAV
|| mode
== TEST_TRAP
)
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
)
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
)) {
771 You("cannot pass that way.");
775 You("are carrying too much to get through.");
779 Your("body is too large to fit through.");
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> */
787 pline("%s is in your way.", Monnam(m_at(ux
, y
)));
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) */
794 && (mode
== TEST_MOVE
|| mode
== TEST_TRAP
)
795 && (x
!= u
.ux
|| y
!= u
.uy
)) {
796 struct trap
*t
= t_at(x
, y
);
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 */
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. */
815 if (sobj_at(BOULDER
, x
, y
) && (Sokoban
|| !Passes_walls
)) {
816 if (!(Blind
|| Hallucination
) && (context
.run
>= 2)
817 && mode
!= TEST_TRAV
)
819 if (mode
== DO_MOVE
) {
820 /* tunneling monsters will chew before pushing */
821 if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)
823 if (still_chewing(x
, y
))
825 } else if (moverock() < 0)
827 } else if (mode
== TEST_TRAV
) {
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
) {
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
))
843 /* assume you'll be able to push it when you get there... */
846 /* OK, it is a legal place to move. */
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
;
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.
868 findtravelpath(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
))) {
875 if (test_move(u
.ux
, u
.uy
, u
.tx
- u
.ux
, u
.ty
- u
.uy
, TEST_MOVE
)) {
879 iflags
.travelcc
.x
= iflags
.travelcc
.y
= -1;
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 */
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.
911 (void) memset((genericptr_t
) travel
, 0, sizeof(travel
));
912 travelstepx
[0][0] = tx
;
913 travelstepy
[0][0] = ty
;
918 for (i
= 0; i
< n
; i
++) {
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
]];
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! */
944 alreadyrepeated
= TRUE
;
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
) {
956 if (x
== u
.tx
&& y
== u
.ty
) {
958 /* reset run so domove run checks work */
960 iflags
.travelcc
.x
= iflags
.travelcc
.y
=
965 } else if (!travel
[nx
][ny
]) {
966 travelstepx
[1 - set
][nn
] = nx
;
967 travelstepy
[1 - set
][nn
] = ny
;
968 travel
[nx
][ny
] = radius
;
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
]);
983 if (flags
.runmode
== RUN_CRAWL
) {
996 /* if guessing, find best location in travel matrix and go there */
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
);
1010 /* prefer non-zigzag path */
1015 } else if (nxtdist
< dist
&& couldsee(tx
, ty
)) {
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
))
1033 /* Use of warning glyph is arbitrary. It stands out. */
1034 tmp_at(DISP_ALL
, warning_to_glyph(2));
1037 if (flags
.runmode
== RUN_CRAWL
) {
1043 tmp_at(DISP_END
, 0);
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) */
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> */
1074 const char *predicament
, *culprit
;
1075 char *steedname
= !u
.usteed
? (char *) 0 : y_monnam(u
.usteed
);
1078 return TRUE
; /* sanity check */
1080 switch (u
.utraptype
) {
1082 if (flags
.verbose
) {
1083 predicament
= "caught in a bear trap";
1085 Norep("%s is %s.", upstart(steedname
), predicament
);
1087 Norep("You are %s.", predicament
);
1089 /* [why does diagonal movement give quickest escape?] */
1090 if ((u
.dx
&& u
.dy
) || !rn2(5))
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 */
1101 if (uwep
&& uwep
->oartifact
== ART_STING
) {
1103 pline("Sting cuts through the web!");
1104 break; /* escape trap but don't move */
1107 if (flags
.verbose
) {
1108 predicament
= "stuck to the web";
1110 Norep("%s is %s.", upstart(steedname
), predicament
);
1112 Norep("You are %s.", predicament
);
1116 pline("%s breaks out of the web.", upstart(steedname
));
1118 You("disentangle yourself.");
1122 if (flags
.verbose
) {
1123 predicament
= "stuck in the lava";
1125 Norep("%s is %s.", upstart(steedname
), predicament
);
1127 Norep("You are %s.", predicament
);
1129 if (!is_lava(x
, y
)) {
1131 if ((u
.utrap
& 0xff) == 0) {
1134 You("lead %s to the edge of the lava.", steedname
);
1136 You("pull yourself to the edge of the lava.");
1143 anchored
= (u
.utraptype
== TT_BURIEDBALL
);
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 */
1157 Norep("You move within the chain's reach.");
1162 if (flags
.verbose
) {
1164 predicament
= "chained to the";
1165 culprit
= "buried ball";
1167 predicament
= "stuck in the";
1168 culprit
= surface(u
.ux
, u
.uy
);
1172 Norep("You and %s are %s %s.", steedname
, predicament
,
1175 Norep("%s is %s %s.", upstart(steedname
), predicament
,
1178 Norep("You are %s %s.", predicament
, culprit
);
1182 pline("%s finally %s free.", upstart(steedname
),
1183 !anchored
? "lurches" : "wrenches the ball");
1185 You("finally %s free.",
1186 !anchored
? "wriggle" : "wrench the ball");
1188 buried_ball_to_punishment();
1192 impossible("trapmove: stuck in unknown trap? (%d)",
1202 if (!youmonst
.data
->mmove
) {
1203 You("are rooted %s.",
1204 Levitation
|| Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
)
1216 register struct monst
*mtmp
;
1217 register struct rm
*tmpr
;
1218 register xchar x
, y
;
1219 struct trap
*trap
= NULL
;
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
);
1244 You("collapse under your load.");
1250 u
.ux
= x
= u
.ustuck
->mx
;
1251 u
.uy
= y
= u
.ustuck
->my
;
1254 if (Is_airlevel(&u
.uz
) && rn2(4) && !Levitation
&& !Flying
) {
1257 You("tumble in place.");
1258 exercise(A_DEX
, FALSE
);
1261 You_cant("control your movements very well.");
1264 pline("It's hard to walk in thin air.");
1265 exercise(A_DEX
, TRUE
);
1271 /* check slippery ice */
1272 on_ice
= !Levitation
&& is_ice(u
.ux
, u
.uy
);
1274 static int skates
= 0;
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
))
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
;
1292 if (Stunned
|| (Confusion
&& !rn2(5))) {
1293 register int tries
= 0;
1303 } while (!isok(x
, y
) || bad_rock(youmonst
.data
, x
, y
));
1305 /* turbulence might alter your actual destination */
1308 if (!u
.dx
&& !u
.dy
) {
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) {
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 ... ) */
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
));
1341 /* If holder is asleep or paralyzed:
1342 * 37.5% chance of getting away,
1343 * 12.5% chance of waking/releasing it;
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)) {
1355 You("pull free from %s.", mon_nam(u
.ustuck
));
1359 if (!u
.ustuck
->mcanmove
) {
1360 /* it's free to move on next turn */
1361 u
.ustuck
->mfrozen
= 1;
1362 u
.ustuck
->msleeping
= 0;
1366 if (u
.ustuck
->mtame
&& !Conflict
&& !u
.ustuck
->mconf
)
1368 You("cannot escape from %s!", mon_nam(u
.ustuck
));
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
))) {
1398 /* attack monster */
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
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
1423 stumble_onto_mimic(mtmp
);
1424 else if (mtmp
->mpeaceful
&& !Hallucination
)
1425 pline("Pardon me, %s.", m_monnam(mtmp
));
1427 You("move right into %s.", mon_nam(mtmp
));
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_ */
1440 if (context
.forcefight
&& levl
[x
][y
].typ
== IRONBARS
&& uwep
) {
1441 struct obj
*obj
= uwep
;
1442 if (breaktest(obj
)) {
1444 obj
= splitobj(obj
, 1L);
1446 setuwep((struct obj
*)0);
1449 hit_bars(&obj
, u
.ux
, u
.uy
, x
, y
, TRUE
, TRUE
);
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 */
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
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
);
1484 /* about to become known empty -- remove 'I' if present */
1487 map_object(boulder
, TRUE
);
1489 glyph
= glyph_at(x
, y
); /* might have just changed */
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
)
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") */
1507 (levl
[x
][y
].typ
== STONE
)
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 */
1515 Strcpy(buf
, "thin air");
1517 !(boulder
|| solid
) ? "" : !explo
? "harmlessly " : "futilely ",
1518 explo
? "explode at" : "attack", buf
);
1523 u
.mh
= -1; /* dead in the current form */
1528 if (glyph_is_invisible(levl
[x
][y
].glyph
)) {
1532 /* not attacking an animal, so we try to move */
1533 if ((u
.dx
|| u
.dy
) && u
.usteed
&& stucksteed(FALSE
)) {
1542 if (!trapmove(x
, y
, trap
))
1546 if (!test_move(u
.ux
, u
.uy
, x
- u
.ux
, y
- u
.uy
, DO_MOVE
)) {
1547 if (!context
.door_opened
) {
1554 /* Move ball and chain. */
1556 if (!drag_ball(x
, y
, &bc_control
, &ballx
, &bally
, &chainx
, &chainy
,
1557 &cause_delay
, TRUE
))
1560 /* Check regions entering/leaving */
1561 if (!in_out_region(x
, y
))
1564 /* now move the hero */
1568 /* Move your steed, too */
1570 u
.usteed
->mx
= u
.ux
;
1571 u
.usteed
->my
= u
.uy
;
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;
1594 m_unleash(mtmp
, TRUE
);
1600 mtmp
->mundetected
= 0;
1601 if (mtmp
->m_ap_type
)
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
)));
1622 char pnambuf
[BUFSZ
];
1624 /* save its current description in case of polymorph */
1625 Strcpy(pnambuf
, y_monnam(mtmp
));
1627 remove_monster(x
, y
);
1628 place_monster(mtmp
, u
.ux0
, u
.uy0
);
1630 newsym(u
.ux0
, u
.uy0
);
1632 You("%s %s.", mtmp
->mtame
? "swap places with" : "frighten",
1635 /* check for displacing it into pools and traps */
1636 switch (minliquid(mtmp
) ? 2 : mintrap(mtmp
)) {
1639 case 1: /* trapped */
1640 case 3: /* changed levels */
1641 /* there's already been a trap message, reinforce it */
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 */
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?]
1669 You_feel("guilty about losing your pet like this.");
1675 pline("that's strange, unknown mintrap result!");
1681 reset_occupations();
1683 if (context
.run
< 8)
1684 if (IS_DOOR(tmpr
->typ
) || IS_ROCK(tmpr
->typ
)
1685 || IS_FURNITURE(tmpr
->typ
))
1689 if (hides_under(youmonst
.data
) || (youmonst
.data
->mlet
== S_EEL
) || u
.dx
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
) {
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
);
1718 /* delay next move because of ball dragging */
1719 /* must come after we finished picking up, in spoteffects() */
1722 multi_reason
= "dragging an iron ball";
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)) {
1733 if (flags
.runmode
== RUN_CRAWL
) {
1743 /* combat increases metabolism */
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 */
1751 if ((moves
% 3L) != 0L && near_capacity() >= HVY_ENCUMBER
) {
1752 int *hp
= (!Upolyd
? &u
.uhp
: &u
.mh
);
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) */
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
)) {
1771 struct obj
*otmp
= carrying(CANDELABRUM_OF_INVOCATION
);
1773 nomul(0); /* stop running or travelling */
1775 Sprintf(buf
, "beneath %s", y_monnam(u
.usteed
));
1776 else if (Levitation
|| Flying
)
1777 Strcpy(buf
, "beneath you");
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 */
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
));
1800 /* called from spoteffects(), skip float_down() */
1802 You_cant("levitate in here.");
1803 BLevitation
|= FROMOUTSIDE
;
1804 } else if (BLevitation
) {
1805 BLevitation
&= ~FROMOUTSIDE
;
1809 /* the same terrain that blocks levitation also blocks flight */
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...] */
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 */
1830 pooleffects(newspot
)
1831 boolean newspot
; /* true if called by spoteffects */
1833 /* check for leaving water */
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! */
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.");
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 */
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
)) {
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) */
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
))
1882 /* even if we actually end up at same location, float_down()
1883 has already done spoteffect()'s trap and pickup actions */
1885 check_special_room(FALSE
); /* spoteffects */
1890 /* drown(),lava_effects() return true if hero changes
1891 location while surviving the problem */
1892 if (is_lava(u
.ux
, u
.uy
)) {
1895 } else if (!Wwalking
1896 && (newspot
|| !u
.uinwater
|| !(Swimming
|| Amphibious
))) {
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
))
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
))
1934 if (pooleffects(TRUE
))
1937 check_special_room(FALSE
);
1938 if (IS_SINK(levl
[u
.ux
][u
.uy
].typ
) && Levitation
)
1940 if (!in_steed_dismounting
) { /* if dismounting, we'll check again later */
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 */
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
));
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
) {
1979 spottraptyp
= trap
->ttyp
;
1980 dotrap(trap
, 0); /* fall into arrow trap, etc. */
1981 spottrap
= (struct trap
*) 0;
1982 spottraptyp
= NO_TRAP
;
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)
2001 if ((mtmp
= m_at(u
.ux
, u
.uy
)) && !u
.uswallow
) {
2002 mtmp
->mundetected
= mtmp
->msleeping
= 0;
2003 switch (mtmp
->data
->mlet
) {
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
));
2017 You("are hit by %s!",
2018 x_monnam(mtmp
, ARTICLE_A
, "falling", 0, TRUE
));
2020 if (Half_physical_damage
)
2021 dmg
= (dmg
+ 1) / 2;
2022 mdamageu(mtmp
, dmg
);
2025 default: /* monster surprises you. */
2027 pline("%s jumps near you from the %s.", Amonnam(mtmp
),
2028 ceiling(u
.ux
, u
.uy
));
2029 else if (mtmp
->mpeaceful
) {
2031 Blind
&& !sensemon(mtmp
) ? something
: a_monnam(mtmp
));
2032 mtmp
->mpeaceful
= 0;
2034 pline("%s attacks you by surprise!", Amonnam(mtmp
));
2037 mnexto(mtmp
); /* have to move the monster */
2040 if (!--inspoteffects
) {
2041 spotterrain
= STONE
; /* 0 */
2042 spotloc
.x
= spotloc
.y
= 0;
2047 /* returns first matching monster */
2048 STATIC_OVL
struct monst
*
2049 monstinroom(mdat
, roomno
)
2050 struct permonst
*mdat
;
2053 register struct monst
*mtmp
;
2055 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2056 if (DEADMONSTER(mtmp
))
2058 if (mtmp
->data
== mdat
2059 && index(in_rooms(mtmp
->mx
, mtmp
->my
, 0), roomno
+ ROOMOFFSET
))
2062 return (struct monst
*) 0;
2066 in_rooms(x
, y
, typewanted
)
2067 register xchar x
, y
;
2068 register int typewanted
;
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) \
2077 || ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) \
2078 || ((typewanted == SHOPBASE) && (typefound > SHOPBASE)))
2080 switch (rno
= levl
[x
][y
].roomno
) {
2089 default: /* i.e. a regular room # */
2099 else if (x
>= COLNO
)
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
];
2113 if (((rno
= lev
[y
].roomno
) >= ROOMOFFSET
) && !index(ptr
, rno
)
2117 if (y
> max_y_offset
)
2119 if (((rno
= lev
[y
].roomno
) >= ROOMOFFSET
) && !index(ptr
, rno
)
2123 if (y
> max_y_offset
)
2125 if (((rno
= lev
[y
].roomno
) >= ROOMOFFSET
) && !index(ptr
, rno
)
2132 /* is (x,y) in a town? */
2137 s_level
*slev
= Is_special(&u
.uz
);
2138 register struct mkroom
*sroom
;
2139 boolean has_subrooms
= FALSE
;
2141 if (!slev
|| !slev
->flags
.town
)
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
))
2156 return !has_subrooms
;
2161 register boolean newlev
;
2163 char *ptr1
, *ptr2
, *ptr3
, *ptr4
;
2165 Strcpy(u
.urooms0
, u
.urooms
);
2166 Strcpy(u
.ushops0
, u
.ushops
);
2169 u
.uentered
[0] = '\0';
2171 u
.ushops_entered
[0] = '\0';
2172 Strcpy(u
.ushops_left
, u
.ushops0
);
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];
2180 if (!index(u
.urooms0
, *ptr1
))
2182 if (IS_SHOP(*ptr1
- ROOMOFFSET
)) {
2184 if (!index(u
.ushops0
, *ptr1
))
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
))
2200 check_special_room(newlev
)
2201 register boolean newlev
;
2203 register struct monst
*mtmp
;
2206 move_update(newlev
);
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 */
2228 pline("Welcome to David's treasure zoo!");
2231 pline("It %s rather %s down here.", Blind
? "feels" : "looks",
2232 Blind
? "humid" : "muddy");
2235 You("enter an opulent throne room!");
2238 You("enter a leprechaun hall!");
2242 const char *run
= locomotion(youmonst
.data
, "Run");
2243 pline("%s away! %s away!", run
, run
);
2245 You("have an uncanny feeling...");
2248 You("enter a giant beehive!");
2251 You("enter a disgusting nest!");
2254 You("enter an anthole!");
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!");
2263 You("enter an abandoned barracks.");
2266 struct monst
*oracle
= monstinroom(&mons
[PM_ORACLE
], roomno
);
2268 if (!oracle
->mpeaceful
)
2269 verbalize("You're in Delphi, %s.", plname
);
2271 verbalize("%s, %s, welcome to Delphi!",
2272 Hello((struct monst
*) 0), plname
);
2278 intemple(roomno
+ ROOMOFFSET
);
2281 msg_given
= (rt
== TEMPLE
);
2286 room_discovered(roomno
);
2289 rooms
[roomno
].rtype
= OROOM
;
2290 if (!search_special(rt
)) {
2291 /* No more room of that type */
2294 level
.flags
.has_court
= 0;
2297 level
.flags
.has_swamp
= 0;
2300 level
.flags
.has_morgue
= 0;
2303 level
.flags
.has_zoo
= 0;
2306 level
.flags
.has_barracks
= 0;
2309 level
.flags
.has_temple
= 0;
2312 level
.flags
.has_beehive
= 0;
2316 if (rt
== COURT
|| rt
== SWAMP
|| rt
== MORGUE
|| rt
== ZOO
)
2317 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2318 if (DEADMONSTER(mtmp
))
2320 if (!Stealth
&& !rn2(3))
2321 mtmp
->msleeping
= 0;
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 */
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.");
2344 You("don't %s anything in here to pick up.",
2345 Blind
? "feel" : "see");
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.");
2357 } else if (!Underwater
) {
2358 You_cant("even see the bottom, let alone pick up %s.", something
);
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.");
2367 } else if (!likes_lava(youmonst
.data
)) {
2368 You("would burn to a crisp trying to pick things up.");
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.");
2385 There("is nothing here to pick up.");
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
)
2393 else if (Blind
&& !can_reach_floor(TRUE
))
2394 You("cannot reach anything here.");
2396 You("cannot reach the %s.", surface(u
.ux
, u
.uy
));
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 */
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
) {
2421 if (Blind
|| context
.run
== 0)
2423 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
2424 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++) {
2428 if (u
.umonnum
== PM_GRID_BUG
&& x
!= u
.ux
&& y
!= u
.uy
)
2431 if (x
== u
.ux
&& y
== u
.uy
)
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
))
2442 if (levl
[x
][y
].typ
== STONE
)
2444 if (x
== u
.ux
- u
.dx
&& y
== u
.uy
- u
.dy
)
2447 if (IS_ROCK(levl
[x
][y
].typ
) || (levl
[x
][y
].typ
== ROOM
)
2448 || IS_AIR(levl
[x
][y
].typ
))
2450 else if (closed_door(x
, y
)
2451 || (mtmp
&& is_door_mappear(mtmp
))) {
2452 if (x
!= u
.ux
&& y
!= u
.uy
)
2454 if (context
.run
!= 1)
2457 } else if (levl
[x
][y
].typ
== CORR
) {
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
);
2465 if (corrct
== 1 && dist2(x
, y
, x0
, y0
) != 1)
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
)
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
2495 } else { /* e.g. objects or trap or stairs */
2496 if (context
.run
== 1)
2498 if (context
.run
== 8)
2502 if (((x
== u
.ux
- u
.dx
) && (y
!= u
.uy
+ u
.dy
))
2503 || ((y
== u
.uy
- u
.dy
) && (x
!= u
.ux
+ u
.dx
)))
2509 } /* end for loops */
2511 if (corrct
> 1 && context
.run
== 2)
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 */
2517 if (u
.dx
== y0
- u
.uy
&& u
.dy
== u
.ux
- x0
)
2518 i
= 2; /* straight turn right */
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 */
2525 i
= 1; /* half turn right */
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 */
2531 i
= -1; /* half turn left */
2534 i
+= u
.last_str_turn
;
2535 if (i
<= 2 && i
>= -2) {
2536 u
.last_str_turn
= i
;
2543 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2548 struct rm
*lev_p
= &levl
[x
][y
];
2550 if (!IS_DOOR(lev_p
->typ
))
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
))
2556 return !(lev_p
->doormask
& ~(D_NODOOR
| D_BROKEN
));
2559 /* used by drown() to check whether hero can crawl from water to <x,y> */
2561 crawl_destination(x
, y
)
2564 /* is location ok in general? */
2565 if (!goodpos(x
, y
, &youmonst
, 0))
2568 /* orthogonal movement is unrestricted when destination is ok */
2569 if (x
== u
.ux
|| y
== u
.uy
)
2572 /* diagonal movement has some restrictions */
2573 if (NODIAG(u
.umonnum
))
2574 return FALSE
; /* poly'd into a grid bug... */
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
)))
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 */
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
++) {
2598 if (x
== u
.ux
&& y
== u
.uy
)
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
))
2617 return; /* This is a bug fix by ab@unido */
2618 u
.uinvulnerable
= FALSE
; /* Kludge to avoid ctrl-C bug -dlc */
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 */
2629 const char *msg_override
;
2631 multi
= 0; /* caller will usually have done this already */
2633 nomovemsg
= msg_override
;
2634 else if (!nomovemsg
)
2635 nomovemsg
= You_can_move_again
;
2640 multi_reason
= NULL
;
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)
2657 if (Role_if(PM_WIZARD
) || Race_if(PM_ELF
) || Role_if(PM_VALKYRIE
)) {
2661 who
= (Role_if(PM_WIZARD
) || Role_if(PM_VALKYRIE
)) ? urole
.name
.m
2664 pline("%s is about to die.", who
);
2666 for (i
= 0, powercnt
= 0; i
< SIZE(powers
); ++i
)
2667 if (u
.uprops
[powers
[i
]].intrinsic
& INTRINSIC
)
2670 pline(powercnt
>= 4 ? "%s, all your powers will be lost..."
2671 : "%s, your life force is running out.",
2675 You_hear(u
.uhp
== 1 ? "the wailing of the Banshee..."
2676 : "the howling of the CwnAnnwn...");
2681 losehp(n
, knam
, k_format
)
2683 register const char *knam
;
2693 else if (n
> 0 && u
.mh
* 10 < u
.mhmax
&& Unchanging
)
2699 if (u
.uhp
> u
.uhpmax
)
2700 u
.uhpmax
= u
.uhp
; /* perhaps n was negative */
2702 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
2705 killer
.format
= k_format
;
2706 if (killer
.name
!= knam
) /* the thing that killed you */
2707 Strcpy(killer
.name
, knam
? knam
: "");
2710 } else if (n
> 0 && u
.uhp
* 10 < u
.uhpmax
) {
2718 register long carrcap
;
2720 carrcap
= 25 * (ACURRSTR
+ ACURR(A_CON
)) + 50;
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
;
2737 if (carrcap
> MAX_CARR_CAP
)
2738 carrcap
= MAX_CARR_CAP
;
2740 if (EWounded_legs
& LEFT_SIDE
)
2742 if (EWounded_legs
& RIGHT_SIDE
)
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. */
2758 register struct obj
*otmp
= invent
;
2759 register int wt
= 0;
2762 if (otmp
->oclass
== COIN_CLASS
)
2763 wt
+= (int) (((long) otmp
->quan
+ 50L) / 100L);
2764 else if (otmp
->otyp
!= BOULDER
|| !throws_rocks(youmonst
.data
))
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
)
2780 int cap
, wt
= inv_weight() + xtra_wt
;
2783 return UNENCUMBERED
;
2786 cap
= (wt
* 2 / wc
) + 1;
2787 return min(cap
, OVERLOADED
);
2793 return calc_capacity(0);
2799 int wt
= inv_weight();
2801 return (wt
- (2 * wc
));
2808 if (near_capacity() >= EXT_ENCUMBER
) {
2812 You_cant("do that while carrying so much stuff.");
2822 register struct obj
*otmp
= invent
;
2823 register int ct
= 0;
2826 if (incl_gold
|| otmp
->invlet
!= GOLD_SYM
)
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. */
2842 /* Must change when silver & copper is implemented: */
2843 if (otmp
->oclass
== COIN_CLASS
)