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
, (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 returns 0 if we can squeeze through */
626 cant_squeeze_thru(mon
)
630 struct permonst
*ptr
= mon
->data
;
634 && !(amorphous(ptr
) || is_whirly(ptr
) || noncorporeal(ptr
)
635 || slithy(ptr
) || can_fog(mon
)))
638 /* lugging too much junk? */
639 amt
= (mon
== &youmonst
) ? inv_weight() + weight_cap()
640 : curr_mon_load(mon
);
644 /* Sokoban restriction applies to hero only */
645 if (mon
== &youmonst
&& Sokoban
)
648 /* can squeeze through */
656 return (boolean
) (Invocation_lev(&u
.uz
)
657 && x
== inv_pos
.x
&& y
== inv_pos
.y
);
660 /* return TRUE if (dx,dy) is an OK place to move
661 * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP
664 test_move(ux
, uy
, dx
, dy
, mode
)
670 register struct rm
*tmpr
= &levl
[x
][y
];
671 register struct rm
*ust
;
673 context
.door_opened
= FALSE
;
675 * Check for physical obstacles. First, the place we are going.
677 if (IS_ROCK(tmpr
->typ
) || tmpr
->typ
== IRONBARS
) {
678 if (Blind
&& mode
== DO_MOVE
)
680 if (Passes_walls
&& may_passwall(x
, y
)) {
682 } else if (tmpr
->typ
== IRONBARS
) {
683 if ((dmgtype(youmonst
.data
, AD_RUST
)
684 || dmgtype(youmonst
.data
, AD_CORR
)) && mode
== DO_MOVE
685 && still_chewing(x
, y
)) {
688 if (!(Passes_walls
|| passes_bars(youmonst
.data
))) {
689 if (iflags
.mention_walls
)
690 You("cannot pass through the bars.");
693 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
695 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
697 } else if (flags
.autodig
&& !context
.run
&& !context
.nopick
&& uwep
699 /* MRKR: Automatic digging when wielding the appropriate tool */
701 (void) use_pick_axe2(uwep
);
704 if (mode
== DO_MOVE
) {
705 if (Is_stronghold(&u
.uz
) && is_db_wall(x
, y
))
706 pline_The("drawbridge is up!");
707 /* sokoban restriction stays even after puzzle is solved */
708 else if (Passes_walls
&& !may_passwall(x
, y
)
709 && In_sokoban(&u
.uz
))
710 pline_The("Sokoban walls resist your ability.");
711 else if (iflags
.mention_walls
)
712 pline("It's a wall.");
716 } else if (IS_DOOR(tmpr
->typ
)) {
717 if (closed_door(x
, y
)) {
718 if (Blind
&& mode
== DO_MOVE
)
722 } else if (can_ooze(&youmonst
)) {
724 You("ooze under the door.");
725 } else if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)) {
727 if (mode
== DO_MOVE
&& still_chewing(x
, y
))
730 if (mode
== DO_MOVE
) {
731 if (amorphous(youmonst
.data
))
733 "try to ooze under the door, but can't squeeze your possessions through.");
734 if (flags
.autoopen
&& !context
.run
&& !Confusion
735 && !Stunned
&& !Fumbling
) {
736 context
.door_opened
= context
.move
=
738 } else if (x
== ux
|| y
== uy
) {
739 if (Blind
|| Stunned
|| ACURR(A_DEX
) < 10
742 You_cant("lead %s through that closed door.",
745 pline("Ouch! You bump into a door.");
746 exercise(A_DEX
, FALSE
);
749 pline("That door is closed.");
751 } else if (mode
== TEST_TRAV
|| mode
== TEST_TRAP
)
757 if (dx
&& dy
&& !Passes_walls
758 && (!doorless_door(x
, y
) || block_door(x
, y
))) {
759 /* Diagonal moves into a door are not allowed. */
760 if (Blind
&& mode
== DO_MOVE
)
766 if (dx
&& dy
&& bad_rock(youmonst
.data
, ux
, y
)
767 && bad_rock(youmonst
.data
, x
, uy
)) {
768 /* Move at a diagonal. */
769 switch (cant_squeeze_thru(&youmonst
)) {
772 You("cannot pass that way.");
776 You("are carrying too much to get through.");
780 Your("body is too large to fit through.");
783 break; /* can squeeze through */
785 } else if (dx
&& dy
&& worm_cross(ux
, uy
, x
, y
)) {
786 /* consecutive long worm segments are at <ux,y> and <x,uy> */
788 pline("%s is in your way.", Monnam(m_at(ux
, y
)));
791 /* Pick travel path that does not require crossing a trap.
792 * Avoid water and lava using the usual running rules.
793 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
794 if (context
.run
== 8 && (mode
!= DO_MOVE
)
795 && (x
!= u
.ux
|| y
!= u
.uy
)) {
796 struct trap
*t
= t_at(x
, y
);
799 || (!Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
800 && is_pool_or_lava(x
, y
) && levl
[x
][y
].seenv
))
801 return (mode
== TEST_TRAP
);
804 if (mode
== TEST_TRAP
)
805 return FALSE
; /* do not move through traps */
809 /* Now see if other things block our way . . */
810 if (dx
&& dy
&& !Passes_walls
&& IS_DOOR(ust
->typ
)
811 && (!doorless_door(ux
, uy
) || block_entry(x
, y
))) {
812 /* Can't move at a diagonal out of a doorway with door. */
816 if (sobj_at(BOULDER
, x
, y
) && (Sokoban
|| !Passes_walls
)) {
817 if (!(Blind
|| Hallucination
) && (context
.run
>= 2)
818 && mode
!= TEST_TRAV
)
820 if (mode
== DO_MOVE
) {
821 /* tunneling monsters will chew before pushing */
822 if (tunnels(youmonst
.data
) && !needspick(youmonst
.data
)
824 if (still_chewing(x
, y
))
826 } else if (moverock() < 0)
828 } else if (mode
== TEST_TRAV
) {
831 /* never travel through boulders in Sokoban */
835 /* don't pick two boulders in a row, unless there's a way thru */
836 if (sobj_at(BOULDER
, ux
, uy
) && !Sokoban
) {
838 && !(tunnels(youmonst
.data
) && !needspick(youmonst
.data
))
839 && !carrying(PICK_AXE
) && !carrying(DWARVISH_MATTOCK
)
840 && !((obj
= carrying(WAN_DIGGING
))
841 && !objects
[obj
->otyp
].oc_name_known
))
845 /* assume you'll be able to push it when you get there... */
848 /* OK, it is a legal place to move. */
853 static boolean trav_debug
= FALSE
;
855 /* in this case, toggle display of travel debug info */
856 int wiz_debug_cmd_traveldisplay()
858 trav_debug
= !trav_debug
;
864 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
865 * A shortest path is returned. If guess is TRUE, consider various
866 * inaccessible locations as valid intermediate path points.
867 * Returns TRUE if a path was found.
870 findtravelpath(guess
)
873 /* if travel to adjacent, reachable location, use normal movement rules */
874 if (!guess
&& context
.travel1
&& distmin(u
.ux
, u
.uy
, u
.tx
, u
.ty
) == 1
875 && !(u
.ux
!= u
.tx
&& u
.uy
!= u
.ty
&& NODIAG(u
.umonnum
))) {
877 if (test_move(u
.ux
, u
.uy
, u
.tx
- u
.ux
, u
.ty
- u
.uy
, TEST_MOVE
)) {
881 iflags
.travelcc
.x
= iflags
.travelcc
.y
= -1;
886 if (u
.tx
!= u
.ux
|| u
.ty
!= u
.uy
) {
887 xchar travel
[COLNO
][ROWNO
];
888 xchar travelstepx
[2][COLNO
* ROWNO
];
889 xchar travelstepy
[2][COLNO
* ROWNO
];
890 xchar tx
, ty
, ux
, uy
;
891 int n
= 1; /* max offset in travelsteps */
892 int set
= 0; /* two sets current and previous */
893 int radius
= 1; /* search radius */
896 /* If guessing, first find an "obvious" goal location. The obvious
897 * goal is the position the player knows of, or might figure out
898 * (couldsee) that is closest to the target on a straight path.
913 (void) memset((genericptr_t
) travel
, 0, sizeof(travel
));
914 travelstepx
[0][0] = tx
;
915 travelstepy
[0][0] = ty
;
920 for (i
= 0; i
< n
; i
++) {
922 int x
= travelstepx
[set
][i
];
923 int y
= travelstepy
[set
][i
];
924 static int ordered
[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
925 /* no diagonal movement for grid bugs */
926 int dirmax
= NODIAG(u
.umonnum
) ? 4 : 8;
927 boolean alreadyrepeated
= FALSE
;
929 for (dir
= 0; dir
< dirmax
; ++dir
) {
930 int nx
= x
+ xdir
[ordered
[dir
]];
931 int ny
= y
+ ydir
[ordered
[dir
]];
934 * When guessing and trying to travel as close as possible
935 * to an unreachable target space, don't include spaces
936 * that would never be picked as a guessed target in the
937 * travel matrix describing hero-reachable spaces.
938 * This stops travel from getting confused and moving
939 * the hero back and forth in certain degenerate
940 * configurations of sight-blocking obstacles, e.g.
942 * T 1. Dig this out and carry enough to not be
943 * #### able to squeeze through diagonal gaps.
944 * #--.--- Stand at @ and target travel at space T.
948 * T 2. couldsee() marks spaces marked a and x
949 * #### as eligible guess spaces to move the hero
950 * a--.--- towards. Space a is closest to T, so it
951 * @xxxxx gets chosen. Travel system moves @ right
952 * |xxxxx to travel to space a.
954 * T 3. couldsee() marks spaces marked b, c and x
955 * #### as eligible guess spaces to move the hero
956 * a--c--- towards. Since findtravelpath() is called
957 * b@xxxx repeatedly during travel, it doesn't
958 * |xxxxx remember that it wanted to go to space a,
959 * so in comparing spaces b and c, b is
960 * chosen, since it seems like the closest
961 * eligible space to T. Travel system moves @
962 * left to go to space b.
966 * By limiting the travel matrix here, space a in the
967 * example above is never included in it, preventing
970 if (!isok(nx
, ny
) || (guess
&& !couldsee(nx
, ny
)))
972 if ((!Passes_walls
&& !can_ooze(&youmonst
)
973 && closed_door(x
, y
)) || sobj_at(BOULDER
, x
, y
)
974 || test_move(x
, y
, nx
-x
, ny
-y
, TEST_TRAP
)) {
975 /* closed doors and boulders usually
976 * cause a delay, so prefer another path */
977 if (travel
[x
][y
] > radius
- 3) {
978 if (!alreadyrepeated
) {
979 travelstepx
[1 - set
][nn
] = x
;
980 travelstepy
[1 - set
][nn
] = y
;
981 /* don't change travel matrix! */
983 alreadyrepeated
= TRUE
;
988 if (test_move(x
, y
, nx
- x
, ny
- y
, TEST_TRAV
)
989 && (levl
[nx
][ny
].seenv
990 || (!Blind
&& couldsee(nx
, ny
)))) {
991 if (nx
== ux
&& ny
== uy
) {
995 if (x
== u
.tx
&& y
== u
.ty
) {
997 /* reset run so domove run checks work */
999 iflags
.travelcc
.x
= iflags
.travelcc
.y
= -1;
1003 } else if (!travel
[nx
][ny
]) {
1004 travelstepx
[1 - set
][nn
] = nx
;
1005 travelstepy
[1 - set
][nn
] = ny
;
1006 travel
[nx
][ny
] = radius
;
1015 /* Use of warning glyph is arbitrary. It stands out. */
1016 tmp_at(DISP_ALL
, warning_to_glyph(1));
1017 for (i
= 0; i
< nn
; ++i
) {
1018 tmp_at(travelstepx
[1 - set
][i
], travelstepy
[1 - set
][i
]);
1021 if (flags
.runmode
== RUN_CRAWL
) {
1025 tmp_at(DISP_END
, 0);
1034 /* if guessing, find best location in travel matrix and go there */
1036 int px
= tx
, py
= ty
; /* pick location */
1037 int dist
, nxtdist
, d2
, nd2
;
1039 dist
= distmin(ux
, uy
, tx
, ty
);
1040 d2
= dist2(ux
, uy
, tx
, ty
);
1041 for (tx
= 1; tx
< COLNO
; ++tx
)
1042 for (ty
= 0; ty
< ROWNO
; ++ty
)
1043 if (travel
[tx
][ty
]) {
1044 nxtdist
= distmin(ux
, uy
, tx
, ty
);
1045 if (nxtdist
== dist
&& couldsee(tx
, ty
)) {
1046 nd2
= dist2(ux
, uy
, tx
, ty
);
1048 /* prefer non-zigzag path */
1053 } else if (nxtdist
< dist
&& couldsee(tx
, ty
)) {
1057 d2
= dist2(ux
, uy
, tx
, ty
);
1061 if (px
== u
.ux
&& py
== u
.uy
) {
1062 /* no guesses, just go in the general direction */
1063 u
.dx
= sgn(u
.tx
- u
.ux
);
1064 u
.dy
= sgn(u
.ty
- u
.uy
);
1065 if (test_move(u
.ux
, u
.uy
, u
.dx
, u
.dy
, TEST_MOVE
))
1071 /* Use of warning glyph is arbitrary. It stands out. */
1072 tmp_at(DISP_ALL
, warning_to_glyph(2));
1075 if (flags
.runmode
== RUN_CRAWL
) {
1081 tmp_at(DISP_END
, 0);
1103 /* try to escape being stuck in a trapped state by walking out of it;
1104 return true iff moving should continue to intended destination
1105 (all failures and most successful escapes leave hero at original spot) */
1107 trapmove(x
, y
, desttrap
)
1108 int x
, y
; /* targetted destination, <u.ux+u.dx,u.uy+u.dy> */
1109 struct trap
*desttrap
; /* nonnull if another trap at <x,y> */
1112 const char *predicament
, *culprit
;
1113 char *steedname
= !u
.usteed
? (char *) 0 : y_monnam(u
.usteed
);
1116 return TRUE
; /* sanity check */
1118 switch (u
.utraptype
) {
1120 if (flags
.verbose
) {
1121 predicament
= "caught in a bear trap";
1123 Norep("%s is %s.", upstart(steedname
), predicament
);
1125 Norep("You are %s.", predicament
);
1127 /* [why does diagonal movement give quickest escape?] */
1128 if ((u
.dx
&& u
.dy
) || !rn2(5))
1132 if (desttrap
&& desttrap
->tseen
1133 && (desttrap
->ttyp
== PIT
|| desttrap
->ttyp
== SPIKED_PIT
))
1134 return TRUE
; /* move into adjacent pit */
1135 /* try to escape; position stays same regardless of success */
1139 if (uwep
&& uwep
->oartifact
== ART_STING
) {
1141 pline("Sting cuts through the web!");
1142 break; /* escape trap but don't move */
1145 if (flags
.verbose
) {
1146 predicament
= "stuck to the web";
1148 Norep("%s is %s.", upstart(steedname
), predicament
);
1150 Norep("You are %s.", predicament
);
1154 pline("%s breaks out of the web.", upstart(steedname
));
1156 You("disentangle yourself.");
1160 if (flags
.verbose
) {
1161 predicament
= "stuck in the lava";
1163 Norep("%s is %s.", upstart(steedname
), predicament
);
1165 Norep("You are %s.", predicament
);
1167 if (!is_lava(x
, y
)) {
1169 if ((u
.utrap
& 0xff) == 0) {
1172 You("lead %s to the edge of the %s.", steedname
,
1175 You("pull yourself to the edge of the %s.",
1183 anchored
= (u
.utraptype
== TT_BURIEDBALL
);
1187 cc
.x
= u
.ux
, cc
.y
= u
.uy
;
1188 /* can move normally within radius 1 of buried ball */
1189 if (buried_ball(&cc
) && dist2(x
, y
, cc
.x
, cc
.y
) <= 2) {
1190 /* ugly hack: we need to issue some message here
1191 in case "you are chained to the buried ball"
1192 was the most recent message given, otherwise
1193 our next attempt to move out of tether range
1194 after this successful move would have its
1195 can't-do-that message suppressed by Norep */
1197 Norep("You move within the chain's reach.");
1202 if (flags
.verbose
) {
1204 predicament
= "chained to the";
1205 culprit
= "buried ball";
1207 predicament
= "stuck in the";
1208 culprit
= surface(u
.ux
, u
.uy
);
1212 Norep("You and %s are %s %s.", steedname
, predicament
,
1215 Norep("%s is %s %s.", upstart(steedname
), predicament
,
1218 Norep("You are %s %s.", predicament
, culprit
);
1222 pline("%s finally %s free.", upstart(steedname
),
1223 !anchored
? "lurches" : "wrenches the ball");
1225 You("finally %s free.",
1226 !anchored
? "wriggle" : "wrench the ball");
1228 buried_ball_to_punishment();
1232 impossible("trapmove: stuck in unknown trap? (%d)",
1242 if (!youmonst
.data
->mmove
) {
1243 You("are rooted %s.",
1244 Levitation
|| Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
)
1256 register struct monst
*mtmp
;
1257 register struct rm
*tmpr
;
1258 register xchar x
, y
;
1259 struct trap
*trap
= NULL
;
1262 xchar chainx
= 0, chainy
= 0,
1263 ballx
= 0, bally
= 0; /* ball&chain new positions */
1264 int bc_control
= 0; /* control for ball&chain */
1265 boolean cause_delay
= FALSE
; /* dragging ball will skip a move */
1267 u_wipe_engr(rnd(5));
1269 if (context
.travel
) {
1270 if (!findtravelpath(FALSE
))
1271 (void) findtravelpath(TRUE
);
1272 context
.travel1
= 0;
1275 if (((wtcap
= near_capacity()) >= OVERLOADED
1276 || (wtcap
> SLT_ENCUMBER
1277 && (Upolyd
? (u
.mh
< 5 && u
.mh
!= u
.mhmax
)
1278 : (u
.uhp
< 10 && u
.uhp
!= u
.uhpmax
))))
1279 && !Is_airlevel(&u
.uz
)) {
1280 if (wtcap
< OVERLOADED
) {
1281 You("don't have enough stamina to move.");
1282 exercise(A_CON
, FALSE
);
1284 You("collapse under your load.");
1290 u
.ux
= x
= u
.ustuck
->mx
;
1291 u
.uy
= y
= u
.ustuck
->my
;
1294 if (Is_airlevel(&u
.uz
) && rn2(4) && !Levitation
&& !Flying
) {
1297 You("tumble in place.");
1298 exercise(A_DEX
, FALSE
);
1301 You_cant("control your movements very well.");
1304 pline("It's hard to walk in thin air.");
1305 exercise(A_DEX
, TRUE
);
1311 /* check slippery ice */
1312 on_ice
= !Levitation
&& is_ice(u
.ux
, u
.uy
);
1314 static int skates
= 0;
1317 skates
= find_skates();
1318 if ((uarmf
&& uarmf
->otyp
== skates
) || resists_cold(&youmonst
)
1319 || Flying
|| is_floater(youmonst
.data
)
1320 || is_clinger(youmonst
.data
) || is_whirly(youmonst
.data
)) {
1322 } else if (!rn2(Cold_resistance
? 3 : 2)) {
1323 HFumbling
|= FROMOUTSIDE
;
1324 HFumbling
&= ~TIMEOUT
;
1325 HFumbling
+= 1; /* slip on next move */
1328 if (!on_ice
&& (HFumbling
& FROMOUTSIDE
))
1329 HFumbling
&= ~FROMOUTSIDE
;
1333 if (Stunned
|| (Confusion
&& !rn2(5))) {
1334 register int tries
= 0;
1344 } while (!isok(x
, y
) || bad_rock(youmonst
.data
, x
, y
));
1346 /* turbulence might alter your actual destination */
1349 if (!u
.dx
&& !u
.dy
) {
1360 if (((trap
= t_at(x
, y
)) && trap
->tseen
)
1361 || (Blind
&& !Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
1362 && is_pool_or_lava(x
, y
) && levl
[x
][y
].seenv
)) {
1363 if (context
.run
>= 2) {
1371 if (u
.ustuck
&& (x
!= u
.ustuck
->mx
|| y
!= u
.ustuck
->my
)) {
1372 if (distu(u
.ustuck
->mx
, u
.ustuck
->my
) > 2) {
1373 /* perhaps it fled (or was teleported or ... ) */
1375 } else if (sticks(youmonst
.data
)) {
1376 /* When polymorphed into a sticking monster,
1377 * u.ustuck means it's stuck to you, not you to it.
1379 You("release %s.", mon_nam(u
.ustuck
));
1382 /* If holder is asleep or paralyzed:
1383 * 37.5% chance of getting away,
1384 * 12.5% chance of waking/releasing it;
1386 * 7.5% chance of getting away.
1387 * [strength ought to be a factor]
1388 * If holder is tame and there is no conflict,
1389 * guaranteed escape.
1391 switch (rn2(!u
.ustuck
->mcanmove
? 8 : 40)) {
1396 You("pull free from %s.", mon_nam(u
.ustuck
));
1400 if (!u
.ustuck
->mcanmove
) {
1401 /* it's free to move on next turn */
1402 u
.ustuck
->mfrozen
= 1;
1403 u
.ustuck
->msleeping
= 0;
1407 if (u
.ustuck
->mtame
&& !Conflict
&& !u
.ustuck
->mconf
)
1409 You("cannot escape from %s!", mon_nam(u
.ustuck
));
1417 if (mtmp
&& !is_safepet(mtmp
)) {
1418 /* Don't attack if you're running, and can see it */
1419 /* It's fine to displace pets, though */
1420 /* We should never get here if forcefight */
1421 if (context
.run
&& ((!Blind
&& mon_visible(mtmp
)
1422 && ((mtmp
->m_ap_type
!= M_AP_FURNITURE
1423 && mtmp
->m_ap_type
!= M_AP_OBJECT
)
1424 || Protection_from_shape_changers
))
1425 || sensemon(mtmp
))) {
1439 /* attack monster */
1441 /* don't stop travel when displacing pets; if the
1442 displace fails for some reason, attack() in uhitm.c
1443 will stop travel rather than domove */
1444 if (!is_safepet(mtmp
) || context
.forcefight
)
1446 /* only attack if we know it's there */
1447 /* or if we used the 'F' command to fight blindly */
1448 /* or if it hides_under, in which case we call attack() to print
1449 * the Wait! message.
1450 * This is different from ceiling hiders, who aren't handled in
1454 /* If they used a 'm' command, trying to move onto a monster
1455 * prints the below message and wastes a turn. The exception is
1456 * if the monster is unseen and the player doesn't remember an
1457 * invisible monster--then, we fall through to attack() and
1458 * attack_check(), which still wastes a turn, but prints a
1459 * different message and makes the player remember the monster.
1461 if (context
.nopick
&& !context
.travel
1462 && (canspotmon(mtmp
) || glyph_is_invisible(levl
[x
][y
].glyph
))) {
1463 if (mtmp
->m_ap_type
&& !Protection_from_shape_changers
1465 stumble_onto_mimic(mtmp
);
1466 else if (mtmp
->mpeaceful
&& !Hallucination
)
1467 pline("Pardon me, %s.", m_monnam(mtmp
));
1469 You("move right into %s.", mon_nam(mtmp
));
1472 if (context
.forcefight
|| !mtmp
->mundetected
|| sensemon(mtmp
)
1473 || ((hides_under(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
)
1474 && !is_safepet(mtmp
))) {
1475 /* try to attack; note that it might evade */
1476 /* also, we don't attack tame when _safepet_ */
1482 if (context
.forcefight
&& levl
[x
][y
].typ
== IRONBARS
&& uwep
) {
1483 struct obj
*obj
= uwep
;
1485 if (breaktest(obj
)) {
1487 obj
= splitobj(obj
, 1L);
1489 setuwep((struct obj
*)0);
1492 hit_bars(&obj
, u
.ux
, u
.uy
, x
, y
, TRUE
, TRUE
);
1496 /* specifying 'F' with no monster wastes a turn */
1497 if (context
.forcefight
1498 /* remembered an 'I' && didn't use a move command */
1499 || (glyph_is_invisible(levl
[x
][y
].glyph
) && !context
.nopick
)) {
1500 struct obj
*boulder
= 0;
1501 boolean explo
= (Upolyd
&& attacktype(youmonst
.data
, AT_EXPL
)),
1502 solid
= !accessible(x
, y
);
1503 int glyph
= glyph_at(x
, y
); /* might be monster */
1507 boulder
= sobj_at(BOULDER
, x
, y
);
1508 /* if a statue is displayed at the target location,
1509 player is attempting to attack it [and boulder
1510 handling below is suitable for handling that] */
1511 if (glyph_is_statue(glyph
)
1512 || (Hallucination
&& glyph_is_monster(glyph
)))
1513 boulder
= sobj_at(STATUE
, x
, y
);
1515 /* force fight at boulder/statue or wall/door while wielding
1516 pick: start digging to break the boulder or wall */
1517 if (context
.forcefight
1519 && uwep
&& dig_typ(uwep
, x
, y
)
1520 /* should we dig? */
1521 && !glyph_is_invisible(glyph
) && !glyph_is_monster(glyph
)) {
1522 (void) use_pick_axe2(uwep
);
1527 /* about to become known empty -- remove 'I' if present */
1530 map_object(boulder
, TRUE
);
1532 glyph
= glyph_at(x
, y
); /* might have just changed */
1535 Strcpy(buf
, ansimpleoname(boulder
));
1536 else if (Underwater
&& !is_pool(x
, y
))
1537 /* Underwater, targetting non-water; the map just shows blank
1538 because you don't see remembered terrain while underwater;
1539 although the hero can attack an adjacent monster this way,
1540 assume he can't reach out far enough to distinguish terrain */
1541 Sprintf(buf
, (Is_waterlevel(&u
.uz
) && levl
[x
][y
].typ
== AIR
)
1545 /* glyph might indicate unseen terrain if hero is blind;
1546 unlike searching, this won't reveal what that terrain is
1547 (except for solid rock, where the glyph would otherwise
1548 yield ludicrous "dark part of a room") */
1550 (levl
[x
][y
].typ
== STONE
)
1552 : glyph_is_cmap(glyph
)
1553 ? the(defsyms
[glyph_to_cmap(glyph
)].explanation
)
1554 : (const char *) "an unknown obstacle");
1555 /* note: 'solid' is misleadingly named and catches pools
1556 of water and lava as well as rock and walls */
1558 Strcpy(buf
, "thin air");
1560 !(boulder
|| solid
) ? "" : !explo
? "harmlessly " : "futilely ",
1561 explo
? "explode at" : "attack", buf
);
1566 u
.mh
= -1; /* dead in the current form */
1571 if (glyph_is_invisible(levl
[x
][y
].glyph
)) {
1575 /* not attacking an animal, so we try to move */
1576 if ((u
.dx
|| u
.dy
) && u
.usteed
&& stucksteed(FALSE
)) {
1585 if (!trapmove(x
, y
, trap
))
1589 if (!test_move(u
.ux
, u
.uy
, x
- u
.ux
, y
- u
.uy
, DO_MOVE
)) {
1590 if (!context
.door_opened
) {
1597 /* Move ball and chain. */
1599 if (!drag_ball(x
, y
, &bc_control
, &ballx
, &bally
, &chainx
, &chainy
,
1600 &cause_delay
, TRUE
))
1603 /* Check regions entering/leaving */
1604 if (!in_out_region(x
, y
))
1607 /* now move the hero */
1611 /* Move your steed, too */
1613 u
.usteed
->mx
= u
.ux
;
1614 u
.usteed
->my
= u
.uy
;
1619 * If safepet at destination then move the pet to the hero's
1620 * previous location using the same conditions as in attack().
1621 * there are special extenuating circumstances:
1622 * (1) if the pet dies then your god angers,
1623 * (2) if the pet gets trapped then your god may disapprove,
1624 * (3) if the pet was already trapped and you attempt to free it
1625 * not only do you encounter the trap but you may frighten your
1626 * pet causing it to go wild! moral: don't abuse this privilege.
1628 * Ceiling-hiding pets are skipped by this section of code, to
1629 * be caught by the normal falling-monster code.
1631 if (is_safepet(mtmp
) && !(is_hider(mtmp
->data
) && mtmp
->mundetected
)) {
1632 /* if trapped, there's a chance the pet goes wild */
1633 if (mtmp
->mtrapped
) {
1634 if (!rn2(mtmp
->mtame
)) {
1635 mtmp
->mtame
= mtmp
->mpeaceful
= mtmp
->msleeping
= 0;
1637 m_unleash(mtmp
, TRUE
);
1643 mtmp
->mundetected
= 0;
1644 if (mtmp
->m_ap_type
)
1646 else if (!mtmp
->mtame
)
1647 newsym(mtmp
->mx
, mtmp
->my
);
1649 if (mtmp
->mtrapped
&& (trap
= t_at(mtmp
->mx
, mtmp
->my
)) != 0
1650 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)
1651 && sobj_at(BOULDER
, trap
->tx
, trap
->ty
)) {
1652 /* can't swap places with pet pinned in a pit by a boulder */
1653 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
; /* didn't move after all */
1654 } else if (u
.ux0
!= x
&& u
.uy0
!= y
&& NODIAG(mtmp
->data
- mons
)) {
1655 /* can't swap places when pet can't move to your spot */
1656 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
;
1657 You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp
)));
1658 } else if (u
.ux0
!= x
&& u
.uy0
!= y
&& bad_rock(mtmp
->data
, x
, u
.uy0
)
1659 && bad_rock(mtmp
->data
, u
.ux0
, y
)
1660 && (bigmonst(mtmp
->data
) || (curr_mon_load(mtmp
) > 600))) {
1661 /* can't swap places when pet won't fit thru the opening */
1662 u
.ux
= u
.ux0
, u
.uy
= u
.uy0
; /* didn't move after all */
1663 You("stop. %s won't fit through.", upstart(y_monnam(mtmp
)));
1665 char pnambuf
[BUFSZ
];
1667 /* save its current description in case of polymorph */
1668 Strcpy(pnambuf
, y_monnam(mtmp
));
1670 remove_monster(x
, y
);
1671 place_monster(mtmp
, u
.ux0
, u
.uy0
);
1673 newsym(u
.ux0
, u
.uy0
);
1675 You("%s %s.", mtmp
->mtame
? "swap places with" : "frighten",
1678 /* check for displacing it into pools and traps */
1679 switch (minliquid(mtmp
) ? 2 : mintrap(mtmp
)) {
1682 case 1: /* trapped */
1683 case 3: /* changed levels */
1684 /* there's already been a trap message, reinforce it */
1689 /* drowned or died...
1690 * you killed your pet by direct action, so get experience
1691 * and possibly penalties;
1692 * we want the level gain message, if it happens, to occur
1693 * before the guilt message below
1696 /* minliquid() and mintrap() call mondead() rather than
1697 killed() so we duplicate some of the latter here */
1700 u
.uconduct
.killer
++;
1701 mndx
= monsndx(mtmp
->data
);
1702 tmp
= experience(mtmp
, (int) mvitals
[mndx
].died
);
1703 more_experienced(tmp
, 0);
1704 newexplevel(); /* will decide if you go up */
1706 /* That's no way to treat a pet! Your god gets angry.
1708 * [This has always been pretty iffy. Why does your
1709 * patron deity care at all, let alone enough to get mad?]
1712 You_feel("guilty about losing your pet like this.");
1718 pline("that's strange, unknown mintrap result!");
1724 reset_occupations();
1726 if (context
.run
< 8)
1727 if (IS_DOOR(tmpr
->typ
) || IS_ROCK(tmpr
->typ
)
1728 || IS_FURNITURE(tmpr
->typ
))
1732 if (hides_under(youmonst
.data
) || youmonst
.data
->mlet
== S_EEL
1734 (void) hideunder(&youmonst
);
1737 * Mimics (or whatever) become noticeable if they move and are
1738 * imitating something that doesn't move. We could extend this
1739 * to non-moving monsters...
1741 if ((u
.dx
|| u
.dy
) && (youmonst
.m_ap_type
== M_AP_OBJECT
1742 || youmonst
.m_ap_type
== M_AP_FURNITURE
))
1743 youmonst
.m_ap_type
= M_AP_NOTHING
;
1745 check_leash(u
.ux0
, u
.uy0
);
1747 if (u
.ux0
!= u
.ux
|| u
.uy0
!= u
.uy
) {
1749 /* Clean old position -- vision_recalc() will print our new one. */
1750 newsym(u
.ux0
, u
.uy0
);
1751 /* Since the hero has moved, adjust what can be seen/unseen. */
1752 vision_recalc(1); /* Do the work now in the recover time. */
1753 invocation_message();
1756 if (Punished
) /* put back ball and chain */
1757 move_bc(0, bc_control
, ballx
, bally
, chainx
, chainy
);
1761 /* delay next move because of ball dragging */
1762 /* must come after we finished picking up, in spoteffects() */
1765 multi_reason
= "dragging an iron ball";
1769 if (context
.run
&& flags
.runmode
!= RUN_TPORT
) {
1770 /* display every step or every 7th step depending upon mode */
1771 if (flags
.runmode
!= RUN_LEAP
|| !(moves
% 7L)) {
1776 if (flags
.runmode
== RUN_CRAWL
) {
1786 /* combat increases metabolism */
1790 /* this used to be part of domove() when moving to a monster's
1791 position, but is now called by attack() so that it doesn't
1792 execute if you decline to attack a peaceful monster */
1794 if ((moves
% 3L) != 0L && near_capacity() >= HVY_ENCUMBER
) {
1795 int *hp
= (!Upolyd
? &u
.uhp
: &u
.mh
);
1800 You("pass out from exertion!");
1801 exercise(A_CON
, FALSE
);
1802 fall_asleep(-10, FALSE
);
1805 return (boolean
) (multi
< 0); /* might have fainted (forced to sleep) */
1809 invocation_message()
1811 /* a special clue-msg when on the Invocation position */
1812 if (invocation_pos(u
.ux
, u
.uy
) && !On_stairs(u
.ux
, u
.uy
)) {
1814 struct obj
*otmp
= carrying(CANDELABRUM_OF_INVOCATION
);
1816 nomul(0); /* stop running or travelling */
1818 Sprintf(buf
, "beneath %s", y_monnam(u
.usteed
));
1819 else if (Levitation
|| Flying
)
1820 Strcpy(buf
, "beneath you");
1822 Sprintf(buf
, "under your %s", makeplural(body_part(FOOT
)));
1824 You_feel("a strange vibration %s.", buf
);
1825 u
.uevent
.uvibrated
= 1;
1826 if (otmp
&& otmp
->spe
== 7 && otmp
->lamplit
)
1827 pline("%s %s!", The(xname(otmp
)),
1828 Blind
? "throbs palpably" : "glows with a strange light");
1832 /* moving onto different terrain;
1833 might be going into solid rock, inhibiting levitation or flight,
1834 or coming back out of such, reinstating levitation/flying */
1838 struct rm
*lev
= &levl
[u
.ux
][u
.uy
];
1839 boolean blocklev
= (IS_ROCK(lev
->typ
) || closed_door(u
.ux
, u
.uy
)
1840 || (Is_waterlevel(&u
.uz
) && lev
->typ
== WATER
));
1843 /* called from spoteffects(), skip float_down() */
1845 You_cant("levitate in here.");
1846 BLevitation
|= FROMOUTSIDE
;
1847 } else if (BLevitation
) {
1848 BLevitation
&= ~FROMOUTSIDE
;
1852 /* the same terrain that blocks levitation also blocks flight */
1855 You_cant("fly in here.");
1856 BFlying
|= FROMOUTSIDE
;
1857 } else if (BFlying
) {
1858 BFlying
&= ~FROMOUTSIDE
;
1859 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
1860 /* [minor bug: we don't know whether this is beginning flight or
1861 resuming it; that could be tracked so that this message could
1862 be adjusted to "resume flying", but isn't worth the effort...] */
1864 You("start flying.");
1868 /* extracted from spoteffects; called by spoteffects to check for entering or
1869 leaving a pool of water/lava, and by moveloop to check for staying on one;
1870 returns true to skip rest of spoteffects */
1872 pooleffects(newspot
)
1873 boolean newspot
; /* true if called by spoteffects */
1875 /* check for leaving water */
1877 boolean still_inwater
= FALSE
; /* assume we're getting out */
1879 if (!is_pool(u
.ux
, u
.uy
)) {
1880 if (Is_waterlevel(&u
.uz
))
1881 You("pop into an air bubble.");
1882 else if (is_lava(u
.ux
, u
.uy
))
1883 You("leave the %s...", hliquid("water")); /* oops! */
1885 You("are on solid %s again.",
1886 is_ice(u
.ux
, u
.uy
) ? "ice" : "land");
1887 } else if (Is_waterlevel(&u
.uz
)) {
1888 still_inwater
= TRUE
;
1889 } else if (Levitation
) {
1890 You("pop out of the %s like a cork!", hliquid("water"));
1891 } else if (Flying
) {
1892 You("fly out of the %s.", hliquid("water"));
1893 } else if (Wwalking
) {
1894 You("slowly rise above the surface.");
1896 still_inwater
= TRUE
;
1898 if (!still_inwater
) {
1899 boolean was_underwater
= (Underwater
&& !Is_waterlevel(&u
.uz
));
1901 u
.uinwater
= 0; /* leave the water */
1902 if (was_underwater
) { /* restore vision */
1904 vision_full_recalc
= 1;
1909 /* check for entering water or lava */
1910 if (!u
.ustuck
&& !Levitation
&& !Flying
&& is_pool_or_lava(u
.ux
, u
.uy
)) {
1912 && (is_flyer(u
.usteed
->data
) || is_floater(u
.usteed
->data
)
1913 || is_clinger(u
.usteed
->data
))) {
1914 /* floating or clinging steed keeps hero safe (is_flyer() test
1915 is redundant; it can't be true since Flying yielded false) */
1917 } else if (u
.usteed
) {
1918 /* steed enters pool */
1919 dismount_steed(Underwater
? DISMOUNT_FELL
: DISMOUNT_GENERIC
);
1920 /* dismount_steed() -> float_down() -> pickup()
1921 (float_down doesn't do autopickup on Air or Water) */
1922 if (Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
))
1924 /* even if we actually end up at same location, float_down()
1925 has already done spoteffect()'s trap and pickup actions */
1927 check_special_room(FALSE
); /* spoteffects */
1932 /* drown(),lava_effects() return true if hero changes
1933 location while surviving the problem */
1934 if (is_lava(u
.ux
, u
.uy
)) {
1937 } else if (!Wwalking
1938 && (newspot
|| !u
.uinwater
|| !(Swimming
|| Amphibious
))) {
1950 static int inspoteffects
= 0;
1951 static coord spotloc
;
1952 static int spotterrain
;
1953 static struct trap
*spottrap
= (struct trap
*) 0;
1954 static unsigned spottraptyp
= NO_TRAP
;
1957 struct trap
*trap
= t_at(u
.ux
, u
.uy
);
1959 /* prevent recursion from affecting the hero all over again
1960 [hero poly'd to iron golem enters water here, drown() inflicts
1961 damage that triggers rehumanize() which calls spoteffects()...] */
1962 if (inspoteffects
&& u
.ux
== spotloc
.x
&& u
.uy
== spotloc
.y
1963 /* except when reason is transformed terrain (ice -> water) */
1964 && spotterrain
== levl
[u
.ux
][u
.uy
].typ
1965 /* or transformed trap (land mine -> pit) */
1966 && (!spottrap
|| !trap
|| trap
->ttyp
== spottraptyp
))
1970 spotterrain
= levl
[u
.ux
][u
.uy
].typ
;
1971 spotloc
.x
= u
.ux
, spotloc
.y
= u
.uy
;
1973 /* moving onto different terrain might cause Levitation to toggle */
1974 if (spotterrain
!= levl
[u
.ux0
][u
.uy0
].typ
|| !on_level(&u
.uz
, &u
.uz0
))
1977 if (pooleffects(TRUE
))
1980 check_special_room(FALSE
);
1981 if (IS_SINK(levl
[u
.ux
][u
.uy
].typ
) && Levitation
)
1983 if (!in_steed_dismounting
) { /* if dismounting, we'll check again later */
1986 /* if levitation is due to time out at the end of this
1987 turn, allowing it to do so could give the perception
1988 that a trap here is being triggered twice, so adjust
1989 the timeout to prevent that */
1990 if (trap
&& (HLevitation
& TIMEOUT
) == 1L
1991 && !(ELevitation
|| (HLevitation
& ~(I_SPECIAL
| TIMEOUT
)))) {
1992 if (rn2(2)) { /* defer timeout */
1993 incr_itimeout(&HLevitation
, 1L);
1994 } else { /* timeout early */
1995 if (float_down(I_SPECIAL
| TIMEOUT
, 0L)) {
1996 /* levitation has ended; we've already triggered
1997 any trap and [usually] performed autopickup */
2004 * If not a pit, pickup before triggering trap.
2005 * If pit, trigger trap before pickup.
2007 pit
= (trap
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
));
2013 * dotrap on a fire trap calls melt_ice() which triggers
2014 * spoteffects() (again) which can trigger the same fire
2015 * trap (again). Use static spottrap to prevent that.
2016 * We track spottraptyp because some traps morph
2017 * (landmine to pit) and any new trap type
2018 * should get triggered.
2020 if (!spottrap
|| spottraptyp
!= trap
->ttyp
) {
2022 spottraptyp
= trap
->ttyp
;
2023 dotrap(trap
, 0); /* fall into arrow trap, etc. */
2024 spottrap
= (struct trap
*) 0;
2025 spottraptyp
= NO_TRAP
;
2031 /* Warning alerts you to ice danger */
2032 if (Warning
&& is_ice(u
.ux
, u
.uy
)) {
2033 static const char *const icewarnings
[] = {
2034 "The ice seems very soft and slushy.",
2035 "You feel the ice shift beneath you!",
2036 "The ice, is gonna BREAK!", /* The Dead Zone */
2038 long time_left
= spot_time_left(u
.ux
, u
.uy
, MELT_ICE_AWAY
);
2040 if (time_left
&& time_left
< 15L)
2041 pline("%s", icewarnings
[(time_left
< 5L) ? 2
2042 : (time_left
< 10L) ? 1
2045 if ((mtmp
= m_at(u
.ux
, u
.uy
)) && !u
.uswallow
) {
2046 mtmp
->mundetected
= mtmp
->msleeping
= 0;
2047 switch (mtmp
->data
->mlet
) {
2049 pline("%s suddenly drops from the %s!", Amonnam(mtmp
),
2050 ceiling(u
.ux
, u
.uy
));
2051 if (mtmp
->mtame
) { /* jumps to greet you, not attack */
2053 } else if (uarmh
&& is_metallic(uarmh
)) {
2054 pline("Its blow glances off your %s.",
2055 helm_simple_name(uarmh
));
2056 } else if (u
.uac
+ 3 <= rnd(20)) {
2057 You("are almost hit by %s!",
2058 x_monnam(mtmp
, ARTICLE_A
, "falling", 0, TRUE
));
2062 You("are hit by %s!",
2063 x_monnam(mtmp
, ARTICLE_A
, "falling", 0, TRUE
));
2065 if (Half_physical_damage
)
2066 dmg
= (dmg
+ 1) / 2;
2067 mdamageu(mtmp
, dmg
);
2070 default: /* monster surprises you. */
2072 pline("%s jumps near you from the %s.", Amonnam(mtmp
),
2073 ceiling(u
.ux
, u
.uy
));
2074 else if (mtmp
->mpeaceful
) {
2076 Blind
&& !sensemon(mtmp
) ? something
: a_monnam(mtmp
));
2077 mtmp
->mpeaceful
= 0;
2079 pline("%s attacks you by surprise!", Amonnam(mtmp
));
2082 mnexto(mtmp
); /* have to move the monster */
2085 if (!--inspoteffects
) {
2086 spotterrain
= STONE
; /* 0 */
2087 spotloc
.x
= spotloc
.y
= 0;
2092 /* returns first matching monster */
2093 STATIC_OVL
struct monst
*
2094 monstinroom(mdat
, roomno
)
2095 struct permonst
*mdat
;
2098 register struct monst
*mtmp
;
2100 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2101 if (DEADMONSTER(mtmp
))
2103 if (mtmp
->data
== mdat
2104 && index(in_rooms(mtmp
->mx
, mtmp
->my
, 0), roomno
+ ROOMOFFSET
))
2107 return (struct monst
*) 0;
2111 in_rooms(x
, y
, typewanted
)
2112 register xchar x
, y
;
2113 register int typewanted
;
2116 char rno
, *ptr
= &buf
[4];
2117 int typefound
, min_x
, min_y
, max_x
, max_y_offset
, step
;
2118 register struct rm
*lev
;
2120 #define goodtype(rno) \
2122 || (typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted \
2123 || (typewanted == SHOPBASE && typefound > SHOPBASE))
2125 switch (rno
= levl
[x
][y
].roomno
) {
2134 default: /* i.e. a regular room # */
2144 else if (x
>= COLNO
)
2151 max_y_offset
-= step
;
2152 } else if ((min_y
+ max_y_offset
) >= ROWNO
)
2153 max_y_offset
-= step
;
2155 for (x
= min_x
; x
<= max_x
; x
+= step
) {
2156 lev
= &levl
[x
][min_y
];
2158 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2162 if (y
> max_y_offset
)
2164 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2168 if (y
> max_y_offset
)
2170 if ((rno
= lev
[y
].roomno
) >= ROOMOFFSET
&& !index(ptr
, rno
)
2177 /* is (x,y) in a town? */
2182 s_level
*slev
= Is_special(&u
.uz
);
2183 register struct mkroom
*sroom
;
2184 boolean has_subrooms
= FALSE
;
2186 if (!slev
|| !slev
->flags
.town
)
2190 * See if (x,y) is in a room with subrooms, if so, assume it's the
2191 * town. If there are no subrooms, the whole level is in town.
2193 for (sroom
= &rooms
[0]; sroom
->hx
> 0; sroom
++) {
2194 if (sroom
->nsubrooms
> 0) {
2195 has_subrooms
= TRUE
;
2196 if (inside_room(sroom
, x
, y
))
2201 return !has_subrooms
;
2206 register boolean newlev
;
2208 char *ptr1
, *ptr2
, *ptr3
, *ptr4
;
2210 Strcpy(u
.urooms0
, u
.urooms
);
2211 Strcpy(u
.ushops0
, u
.ushops
);
2214 u
.uentered
[0] = '\0';
2216 u
.ushops_entered
[0] = '\0';
2217 Strcpy(u
.ushops_left
, u
.ushops0
);
2220 Strcpy(u
.urooms
, in_rooms(u
.ux
, u
.uy
, 0));
2222 for (ptr1
= &u
.urooms
[0], ptr2
= &u
.uentered
[0], ptr3
= &u
.ushops
[0],
2223 ptr4
= &u
.ushops_entered
[0];
2225 if (!index(u
.urooms0
, *ptr1
))
2227 if (IS_SHOP(*ptr1
- ROOMOFFSET
)) {
2229 if (!index(u
.ushops0
, *ptr1
))
2237 /* filter u.ushops0 -> u.ushops_left */
2238 for (ptr1
= &u
.ushops0
[0], ptr2
= &u
.ushops_left
[0]; *ptr1
; ptr1
++)
2239 if (!index(u
.ushops
, *ptr1
))
2244 /* possibly deliver a one-time room entry message */
2246 check_special_room(newlev
)
2247 register boolean newlev
;
2249 register struct monst
*mtmp
;
2252 move_update(newlev
);
2255 u_left_shop(u
.ushops_left
, newlev
);
2257 if (!*u
.uentered
&& !*u
.ushops_entered
) /* implied by newlev */
2258 return; /* no entrance messages necessary */
2260 /* Did we just enter a shop? */
2261 if (*u
.ushops_entered
)
2262 u_entered_shop(u
.ushops_entered
);
2264 for (ptr
= &u
.uentered
[0]; *ptr
; ptr
++) {
2265 int roomno
= *ptr
- ROOMOFFSET
, rt
= rooms
[roomno
].rtype
;
2266 boolean msg_given
= TRUE
;
2268 /* Did we just enter some other special room? */
2269 /* vault.c insists that a vault remain a VAULT,
2270 * and temples should remain TEMPLEs,
2271 * but everything else gives a message only the first time */
2274 pline("Welcome to David's treasure zoo!");
2277 pline("It %s rather %s down here.", Blind
? "feels" : "looks",
2278 Blind
? "humid" : "muddy");
2281 You("enter an opulent throne room!");
2284 You("enter a leprechaun hall!");
2288 const char *run
= locomotion(youmonst
.data
, "Run");
2289 pline("%s away! %s away!", run
, run
);
2291 You("have an uncanny feeling...");
2294 You("enter a giant beehive!");
2297 You("enter a disgusting nest!");
2300 You("enter an anthole!");
2303 if (monstinroom(&mons
[PM_SOLDIER
], roomno
)
2304 || monstinroom(&mons
[PM_SERGEANT
], roomno
)
2305 || monstinroom(&mons
[PM_LIEUTENANT
], roomno
)
2306 || monstinroom(&mons
[PM_CAPTAIN
], roomno
))
2307 You("enter a military barracks!");
2309 You("enter an abandoned barracks.");
2312 struct monst
*oracle
= monstinroom(&mons
[PM_ORACLE
], roomno
);
2314 if (!oracle
->mpeaceful
)
2315 verbalize("You're in Delphi, %s.", plname
);
2317 verbalize("%s, %s, welcome to Delphi!",
2318 Hello((struct monst
*) 0), plname
);
2324 intemple(roomno
+ ROOMOFFSET
);
2327 msg_given
= (rt
== TEMPLE
);
2332 room_discovered(roomno
);
2335 rooms
[roomno
].rtype
= OROOM
;
2336 if (!search_special(rt
)) {
2337 /* No more room of that type */
2340 level
.flags
.has_court
= 0;
2343 level
.flags
.has_swamp
= 0;
2346 level
.flags
.has_morgue
= 0;
2349 level
.flags
.has_zoo
= 0;
2352 level
.flags
.has_barracks
= 0;
2355 level
.flags
.has_temple
= 0;
2358 level
.flags
.has_beehive
= 0;
2362 if (rt
== COURT
|| rt
== SWAMP
|| rt
== MORGUE
|| rt
== ZOO
)
2363 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
2364 if (DEADMONSTER(mtmp
))
2366 if (!Stealth
&& !rn2(3))
2367 mtmp
->msleeping
= 0;
2375 /* the ',' command */
2379 int count
, tmpcount
;
2380 struct trap
*traphere
= t_at(u
.ux
, u
.uy
);
2382 /* awful kludge to work around parse()'s pre-decrement */
2383 count
= (multi
|| (save_cm
&& *save_cm
== ',')) ? multi
+ 1 : 0;
2384 multi
= 0; /* always reset */
2385 /* uswallow case added by GAN 01/29/87 */
2387 if (!u
.ustuck
->minvent
) {
2388 if (is_animal(u
.ustuck
->data
)) {
2389 You("pick up %s tongue.", s_suffix(mon_nam(u
.ustuck
)));
2390 pline("But it's kind of slimy, so you drop it.");
2392 You("don't %s anything in here to pick up.",
2393 Blind
? "feel" : "see");
2397 return loot_mon(u
.ustuck
, &tmpcount
, (boolean
*) 0);
2400 if (is_pool(u
.ux
, u
.uy
)) {
2401 if (Wwalking
|| is_floater(youmonst
.data
) || is_clinger(youmonst
.data
)
2402 || (Flying
&& !Breathless
)) {
2403 You("cannot dive into the %s to pick things up.",
2406 } else if (!Underwater
) {
2407 You_cant("even see the bottom, let alone pick up %s.", something
);
2411 if (is_lava(u
.ux
, u
.uy
)) {
2412 if (Wwalking
|| is_floater(youmonst
.data
) || is_clinger(youmonst
.data
)
2413 || (Flying
&& !Breathless
)) {
2414 You_cant("reach the bottom to pick things up.");
2416 } else if (!likes_lava(youmonst
.data
)) {
2417 You("would burn to a crisp trying to pick things up.");
2421 if (!OBJ_AT(u
.ux
, u
.uy
)) {
2422 register struct rm
*lev
= &levl
[u
.ux
][u
.uy
];
2424 if (IS_THRONE(lev
->typ
))
2425 pline("It must weigh%s a ton!", lev
->looted
? " almost" : "");
2426 else if (IS_SINK(lev
->typ
))
2427 pline_The("plumbing connects it to the floor.");
2428 else if (IS_GRAVE(lev
->typ
))
2429 You("don't need a gravestone. Yet.");
2430 else if (IS_FOUNTAIN(lev
->typ
))
2431 You("could drink the %s...", hliquid("water"));
2432 else if (IS_DOOR(lev
->typ
) && (lev
->doormask
& D_ISOPEN
))
2433 pline("It won't come off the hinges.");
2435 There("is nothing here to pick up.");
2438 if (!can_reach_floor(TRUE
)) {
2439 if (traphere
&& uteetering_at_seen_pit(traphere
))
2440 You("cannot reach the bottom of the pit.");
2441 else if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
)
2443 else if (Blind
&& !can_reach_floor(TRUE
))
2444 You("cannot reach anything here.");
2446 You("cannot reach the %s.", surface(u
.ux
, u
.uy
));
2450 return pickup(-count
);
2453 /* stop running if we see something interesting */
2454 /* turn around a corner if that is the only way we can proceed */
2455 /* do not turn left or right twice */
2460 int i
, x0
= 0, y0
= 0, m0
= 1, i0
= 9;
2461 int corrct
= 0, noturn
= 0;
2465 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
2466 /* they polymorphed while in the middle of a long move. */
2467 if (u
.umonnum
== PM_GRID_BUG
&& u
.dx
&& u
.dy
) {
2472 if (Blind
|| context
.run
== 0)
2474 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
2475 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++) {
2476 if (!isok(x
, y
) || (x
== u
.ux
&& y
== u
.uy
))
2478 if (NODIAG(u
.umonnum
) && x
!= u
.ux
&& y
!= u
.uy
)
2481 if ((mtmp
= m_at(x
, y
)) != 0
2482 && mtmp
->m_ap_type
!= M_AP_FURNITURE
2483 && mtmp
->m_ap_type
!= M_AP_OBJECT
2484 && (!mtmp
->minvis
|| See_invisible
) && !mtmp
->mundetected
) {
2485 if ((context
.run
!= 1 && !mtmp
->mtame
)
2486 || (x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
2487 && !context
.travel
))
2491 if (levl
[x
][y
].typ
== STONE
)
2493 if (x
== u
.ux
- u
.dx
&& y
== u
.uy
- u
.dy
)
2496 if (IS_ROCK(levl
[x
][y
].typ
) || levl
[x
][y
].typ
== ROOM
2497 || IS_AIR(levl
[x
][y
].typ
)) {
2499 } else if (closed_door(x
, y
) || (mtmp
&& is_door_mappear(mtmp
))) {
2500 if (x
!= u
.ux
&& y
!= u
.uy
)
2502 if (context
.run
!= 1)
2505 } else if (levl
[x
][y
].typ
== CORR
) {
2507 if (levl
[u
.ux
][u
.uy
].typ
!= ROOM
) {
2508 if (context
.run
== 1 || context
.run
== 3
2509 || context
.run
== 8) {
2510 i
= dist2(x
, y
, u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
2513 if (corrct
== 1 && dist2(x
, y
, x0
, y0
) != 1)
2525 } else if ((trap
= t_at(x
, y
)) && trap
->tseen
) {
2526 if (context
.run
== 1)
2527 goto bcorr
; /* if you must */
2528 if (x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
)
2531 } else if (is_pool_or_lava(x
, y
)) {
2532 /* water and lava only stop you if directly in front, and stop
2533 * you even if you are running
2535 if (!Levitation
&& !Flying
&& !is_clinger(youmonst
.data
)
2536 && x
== u
.ux
+ u
.dx
&& y
== u
.uy
+ u
.dy
)
2537 /* No Wwalking check; otherwise they'd be able
2538 * to test boots by trying to SHIFT-direction
2539 * into a pool and seeing if the game allowed it
2543 } else { /* e.g. objects or trap or stairs */
2544 if (context
.run
== 1)
2546 if (context
.run
== 8)
2550 if (((x
== u
.ux
- u
.dx
) && (y
!= u
.uy
+ u
.dy
))
2551 || ((y
== u
.uy
- u
.dy
) && (x
!= u
.ux
+ u
.dx
)))
2557 } /* end for loops */
2559 if (corrct
> 1 && context
.run
== 2)
2561 if ((context
.run
== 1 || context
.run
== 3 || context
.run
== 8) && !noturn
2562 && !m0
&& i0
&& (corrct
== 1 || (corrct
== 2 && i0
== 1))) {
2563 /* make sure that we do not turn too far */
2565 if (u
.dx
== y0
- u
.uy
&& u
.dy
== u
.ux
- x0
)
2566 i
= 2; /* straight turn right */
2568 i
= -2; /* straight turn left */
2569 } else if (u
.dx
&& u
.dy
) {
2570 if ((u
.dx
== u
.dy
&& y0
== u
.uy
) || (u
.dx
!= u
.dy
&& y0
!= u
.uy
))
2571 i
= -1; /* half turn left */
2573 i
= 1; /* half turn right */
2575 if ((x0
- u
.ux
== y0
- u
.uy
&& !u
.dy
)
2576 || (x0
- u
.ux
!= y0
- u
.uy
&& u
.dy
))
2577 i
= 1; /* half turn right */
2579 i
= -1; /* half turn left */
2582 i
+= u
.last_str_turn
;
2583 if (i
<= 2 && i
>= -2) {
2584 u
.last_str_turn
= i
;
2591 /* check for a doorway which lacks its door (NODOOR or BROKEN) */
2596 struct rm
*lev_p
= &levl
[x
][y
];
2598 if (!IS_DOOR(lev_p
->typ
))
2600 /* all rogue level doors are doorless but disallow diagonal access, so
2601 we treat them as if their non-existant doors were actually present */
2602 if (Is_rogue_level(&u
.uz
))
2604 return !(lev_p
->doormask
& ~(D_NODOOR
| D_BROKEN
));
2607 /* used by drown() to check whether hero can crawl from water to <x,y> */
2609 crawl_destination(x
, y
)
2612 /* is location ok in general? */
2613 if (!goodpos(x
, y
, &youmonst
, 0))
2616 /* orthogonal movement is unrestricted when destination is ok */
2617 if (x
== u
.ux
|| y
== u
.uy
)
2620 /* diagonal movement has some restrictions */
2621 if (NODIAG(u
.umonnum
))
2622 return FALSE
; /* poly'd into a grid bug... */
2624 return TRUE
; /* or a xorn... */
2625 /* pool could be next to a door, conceivably even inside a shop */
2626 if (IS_DOOR(levl
[x
][y
].typ
) && (!doorless_door(x
, y
) || block_door(x
, y
)))
2628 /* finally, are we trying to squeeze through a too-narrow gap? */
2629 return !(bad_rock(youmonst
.data
, u
.ux
, y
)
2630 && bad_rock(youmonst
.data
, x
, u
.uy
));
2633 /* something like lookaround, but we are not running */
2634 /* react only to monsters that might hit us */
2639 register struct monst
*mtmp
;
2641 /* Also see the similar check in dochugw() in monmove.c */
2642 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
2643 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++) {
2644 if (!isok(x
, y
) || (x
== u
.ux
&& y
== u
.uy
))
2646 if ((mtmp
= m_at(x
, y
)) && mtmp
->m_ap_type
!= M_AP_FURNITURE
2647 && mtmp
->m_ap_type
!= M_AP_OBJECT
2648 && (!mtmp
->mpeaceful
|| Hallucination
)
2649 && (!is_hider(mtmp
->data
) || !mtmp
->mundetected
)
2650 && !noattacks(mtmp
->data
) && mtmp
->mcanmove
2651 && !mtmp
->msleeping
/* aplvax!jcn */
2652 && !onscary(u
.ux
, u
.uy
, mtmp
) && canspotmon(mtmp
))
2663 return; /* This is a bug fix by ab@unido */
2664 u
.uinvulnerable
= FALSE
; /* Kludge to avoid ctrl-C bug -dlc */
2668 multi_reason
= NULL
;
2669 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
2672 /* called when a non-movement, multi-turn action has completed */
2675 const char *msg_override
;
2677 multi
= 0; /* caller will usually have done this already */
2679 nomovemsg
= msg_override
;
2680 else if (!nomovemsg
)
2681 nomovemsg
= You_can_move_again
;
2686 multi_reason
= NULL
;
2695 static short powers
[] = { TELEPORT
, SEE_INVIS
, POISON_RES
, COLD_RES
,
2696 SHOCK_RES
, FIRE_RES
, SLEEP_RES
, DISINT_RES
,
2697 TELEPORT_CONTROL
, STEALTH
, FAST
, INVIS
};
2699 if (moves
<= wailmsg
+ 50)
2703 if (Role_if(PM_WIZARD
) || Race_if(PM_ELF
) || Role_if(PM_VALKYRIE
)) {
2707 who
= (Role_if(PM_WIZARD
) || Role_if(PM_VALKYRIE
)) ? urole
.name
.m
2710 pline("%s is about to die.", who
);
2712 for (i
= 0, powercnt
= 0; i
< SIZE(powers
); ++i
)
2713 if (u
.uprops
[powers
[i
]].intrinsic
& INTRINSIC
)
2716 pline((powercnt
>= 4) ? "%s, all your powers will be lost..."
2717 : "%s, your life force is running out.",
2721 You_hear(u
.uhp
== 1 ? "the wailing of the Banshee..."
2722 : "the howling of the CwnAnnwn...");
2727 losehp(n
, knam
, k_format
)
2729 register const char *knam
;
2739 else if (n
> 0 && u
.mh
* 10 < u
.mhmax
&& Unchanging
)
2745 if (u
.uhp
> u
.uhpmax
)
2746 u
.uhpmax
= u
.uhp
; /* perhaps n was negative */
2748 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
2751 killer
.format
= k_format
;
2752 if (killer
.name
!= knam
) /* the thing that killed you */
2753 Strcpy(killer
.name
, knam
? knam
: "");
2756 } else if (n
> 0 && u
.uhp
* 10 < u
.uhpmax
) {
2764 register long carrcap
;
2766 carrcap
= 25 * (ACURRSTR
+ ACURR(A_CON
)) + 50;
2768 /* consistent with can_carry() in mon.c */
2769 if (youmonst
.data
->mlet
== S_NYMPH
)
2770 carrcap
= MAX_CARR_CAP
;
2771 else if (!youmonst
.data
->cwt
)
2772 carrcap
= (carrcap
* (long) youmonst
.data
->msize
) / MZ_HUMAN
;
2773 else if (!strongmonst(youmonst
.data
)
2774 || (strongmonst(youmonst
.data
)
2775 && (youmonst
.data
->cwt
> WT_HUMAN
)))
2776 carrcap
= (carrcap
* (long) youmonst
.data
->cwt
/ WT_HUMAN
);
2779 if (Levitation
|| Is_airlevel(&u
.uz
) /* pugh@cornell */
2780 || (u
.usteed
&& strongmonst(u
.usteed
->data
))) {
2781 carrcap
= MAX_CARR_CAP
;
2783 if (carrcap
> MAX_CARR_CAP
)
2784 carrcap
= MAX_CARR_CAP
;
2786 if (EWounded_legs
& LEFT_SIDE
)
2788 if (EWounded_legs
& RIGHT_SIDE
)
2794 return (int) carrcap
;
2797 static int wc
; /* current weight_cap(); valid after call to inv_weight() */
2799 /* returns how far beyond the normal capacity the player is currently. */
2800 /* inv_weight() is negative if the player is below normal capacity. */
2804 register struct obj
*otmp
= invent
;
2805 register int wt
= 0;
2808 if (otmp
->oclass
== COIN_CLASS
)
2809 wt
+= (int) (((long) otmp
->quan
+ 50L) / 100L);
2810 else if (otmp
->otyp
!= BOULDER
|| !throws_rocks(youmonst
.data
))
2819 * Returns 0 if below normal capacity, or the number of "capacity units"
2820 * over the normal capacity the player is loaded. Max is 5.
2823 calc_capacity(xtra_wt
)
2826 int cap
, wt
= inv_weight() + xtra_wt
;
2829 return UNENCUMBERED
;
2832 cap
= (wt
* 2 / wc
) + 1;
2833 return min(cap
, OVERLOADED
);
2839 return calc_capacity(0);
2845 int wt
= inv_weight();
2847 return (wt
- (2 * wc
));
2854 if (near_capacity() >= EXT_ENCUMBER
) {
2858 You_cant("do that while carrying so much stuff.");
2868 register struct obj
*otmp
= invent
;
2869 register int ct
= 0;
2872 if (incl_gold
|| otmp
->invlet
!= GOLD_SYM
)
2879 /* Counts the money in an object chain. */
2880 /* Intended use is for your or some monster's inventory, */
2881 /* now that u.gold/m.gold is gone.*/
2882 /* Counting money in a container might be possible too. */
2888 /* Must change when silver & copper is implemented: */
2889 if (otmp
->oclass
== COIN_CLASS
)