1 /* SCCS Id: @(#)hack.c 3.4 2003/04/30 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include <limits.h>
6
7 #include "hack.h"
8
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 #ifdef SINKS
13 STATIC_DCL void NDECL(dosinkfall);
14 #endif
15 STATIC_DCL boolean FDECL(findtravelpath, (boolean(*)(int, int)));
16 STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int));
17 STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
18 STATIC_DCL void FDECL(struggle_sub, (const char *));
19
20 static boolean door_opened; /* set to true if door was opened during test_move */
21
22
23 #define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE)
24
25 #ifdef DUNGEON_GROWTH
26 void
rndmappos(x,y)27 rndmappos(x,y) /* guaranteed to return a valid coord */
28 xchar *x;
29 xchar *y;
30 {
31 if (*x >= COLNO) *x = COLNO;
32 else if (*x == -1) *x = rn2(COLNO-1)+1;
33 else if (*x < 1) *x = 1;
34
35 if (*y >= ROWNO) *y = ROWNO;
36 else if (*y == -1) *y = rn2(ROWNO);
37 else if (*y < 0) *y = 0;
38 }
39
40 #define HERB_GROWTH_LIMIT 3 /* to limit excessive farming */
41
42 static const struct herb_info {
43 int herb;
44 boolean in_water;
45 } herb_info[] = {
46 { SPRIG_OF_WOLFSBANE, FALSE },
47 { CLOVE_OF_GARLIC, FALSE },
48 { CARROT, FALSE },
49 { KELP_FROND, TRUE }
50 };
51
52 long
count_herbs_at(x,y,watery)53 count_herbs_at(x,y, watery)
54 xchar x,y;
55 boolean watery;
56 {
57 register int dd;
58 register long count = 0;
59
60 if (isok(x,y)) {
61 for (dd = 0; dd < SIZE(herb_info); dd++) {
62 if (watery == herb_info[dd].in_water) {
63 register struct obj *otmp = sobj_at(herb_info[dd].herb, x,y);
64 if (otmp)
65 count += otmp->quan;
66 }
67 }
68 }
69 return count;
70 }
71
72 /* returns TRUE if a herb can grow at (x,y) */
73 boolean
herb_can_grow_at(x,y,watery)74 herb_can_grow_at(x,y, watery)
75 xchar x,y;
76 boolean watery;
77 {
78 register struct rm *lev = &levl[x][y];
79 if (inside_shop(x,y)) return FALSE;
80 if (watery)
81 return (IS_POOL(lev->typ) &&
82 ((count_herbs_at(x,y, watery)) < HERB_GROWTH_LIMIT));
83 return (lev->lit && (lev->typ == ROOM || lev->typ == CORR ||
84 (IS_DOOR(lev->typ) &&
85 ((lev->doormask == D_NODOOR) ||
86 (lev->doormask == D_ISOPEN) ||
87 (lev->doormask == D_BROKEN)))) &&
88 (count_herbs_at(x,y, watery) < HERB_GROWTH_LIMIT));
89 }
90
91 /* grow herbs in water. return true if did something. */
92 boolean
grow_water_herbs(herb,x,y)93 grow_water_herbs(herb, x,y)
94 int herb;
95 xchar x,y;
96 {
97 struct obj *otmp;
98
99 rndmappos(&x, &y);
100 otmp = sobj_at(herb, x, y);
101 if (otmp && herb_can_grow_at(x,y, TRUE)) {
102 otmp->quan++;
103 otmp->owt = weight(otmp);
104 return TRUE;
105 /* There's no need to start growing these on the neighboring
106 * mapgrids, as they move around (see water_current())
107 */
108 }
109 return FALSE;
110 }
111
112 /* grow herb on ground at (x,y), or maybe spread out.
113 return true if did something. */
114 boolean
grow_herbs(herb,x,y,showmsg,update)115 grow_herbs(herb, x,y, showmsg, update)
116 int herb;
117 xchar x,y;
118 boolean showmsg, update;
119 {
120 struct obj *otmp;
121
122 rndmappos(&x, &y);
123 otmp = sobj_at(herb, x, y);
124 if (otmp && herb_can_grow_at(x,y, FALSE)) {
125 if (otmp->quan <= rn2(HERB_GROWTH_LIMIT)) {
126 otmp->quan++;
127 otmp->owt = weight(otmp);
128 return TRUE;
129 } else {
130 int dd, dofs = rn2(8);
131 /* check surroundings, maybe grow there? */
132 for (dd = 0; dd < 8; dd++) {
133 coord pos;
134
135 dtoxy(&pos, (dd+dofs) % 8);
136 pos.x += x;
137 pos.y += y;
138 if (isok(pos.x,pos.y) && herb_can_grow_at(pos.x,pos.y, FALSE)) {
139 otmp = sobj_at(herb, pos.x, pos.y);
140 if (otmp) {
141 if (otmp->quan <= rn2(HERB_GROWTH_LIMIT)) {
142 otmp->quan++;
143 otmp->owt = weight(otmp);
144 return TRUE;
145 }
146 } else {
147 otmp = mksobj(herb, TRUE, FALSE);
148 otmp->quan = 1;
149 otmp->owt = weight(otmp);
150 place_object(otmp, pos.x, pos.y);
151 if (update) newsym(pos.x,pos.y);
152 if (cansee(pos.x,pos.y)) {
153 if (showmsg && flags.verbose) {
154 const char *what;
155 if (herb == CLOVE_OF_GARLIC)
156 what = "some garlic";
157 else
158 what = an(xname(otmp));
159 Norep("Suddenly you notice %s growing on the %s.",
160 what, surface(pos.x,pos.y));
161 }
162 }
163 return TRUE;
164 }
165 }
166 }
167 }
168 }
169 return FALSE;
170 }
171
172 /* moves topmost object in water at (x,y) to dir.
173 return true if did something. */
174 boolean
water_current(x,y,dir,waterforce,showmsg,update)175 water_current(x,y,dir,waterforce, showmsg, update)
176 xchar x,y;
177 int dir;
178 unsigned waterforce; /* strength of the water current */
179 boolean showmsg, update;
180 {
181 struct obj *otmp;
182 coord pos;
183
184 rndmappos(&x,&y);
185 dtoxy(&pos, dir);
186 pos.x += x;
187 pos.y += y;
188 if (isok(pos.x,pos.y) && IS_POOL(levl[x][y].typ) &&
189 IS_POOL(levl[pos.x][pos.y].typ)) {
190 otmp = level.objects[x][y];
191 if (otmp && otmp->where == OBJ_FLOOR) {
192 if (otmp->quan > 1)
193 otmp = splitobj(otmp, otmp->quan - 1);
194 if (otmp->owt <= waterforce) {
195 if (showmsg && Underwater &&
196 (cansee(pos.x,pos.y) || cansee(x,y))) {
197 Norep("%s floats%s in%s the murky water.",
198 An(xname(otmp)),
199 (cansee(x,y) && cansee(pos.x,pos.y)) ? "" :
200 (cansee(x,y) ? " away from you" : " towards you"),
201 flags.verbose ? " the currents of" : "");
202 }
203 obj_extract_self(otmp);
204 place_object(otmp, pos.x,pos.y);
205 stackobj(otmp);
206 if (update) {
207 newsym(x,y);
208 newsym(pos.x,pos.y);
209 }
210 return TRUE;
211 } else /* the object didn't move, put it back */
212 stackobj(otmp);
213 }
214 }
215 return FALSE;
216 }
217
218 /* a tree at (x,y) spontaneously drops a ripe fruit */
219 boolean
drop_ripe_treefruit(x,y,showmsg,update)220 drop_ripe_treefruit(x,y,showmsg, update)
221 xchar x,y;
222 boolean showmsg, update;
223 {
224 register struct rm *lev;
225
226 rndmappos(&x,&y);
227 lev = &levl[x][y];
228 if (IS_TREE(lev->typ) && !(lev->looted & TREE_LOOTED) && may_dig(x,y)) {
229 coord pos;
230 int dir, dofs = rn2(8);
231 for (dir = 0; dir < 8; dir++) {
232 dtoxy(&pos, (dir + dofs) % 8);
233 pos.x += x;
234 pos.y += y;
235 if (!isok(pos.x, pos.y)) return FALSE;
236 lev = &levl[pos.x][pos.y];
237 if (SPACE_POS(lev->typ) || IS_POOL(lev->typ)) {
238 struct obj *otmp;
239 otmp = rnd_treefruit_at(pos.x,pos.y);
240 if (otmp) {
241 otmp->quan = 1;
242 otmp->owt = weight(otmp);
243 obj_extract_self(otmp);
244 if (showmsg) {
245 if ((cansee(pos.x,pos.y) || cansee(x,y))) {
246 Norep("%s falls from %s%s.",
247 cansee(pos.x,pos.y) ? An(xname(otmp)) : Something,
248 cansee(x,y) ? "the tree" : "somewhere",
249 (cansee(x,y) && IS_POOL(lev->typ)) ?
250 " into the water" : "");
251 } else if (distu(pos.x,pos.y) < 9 &&
252 otmp->otyp != EUCALYPTUS_LEAF) {
253 /* a leaf is too light to cause any sound */
254 You_hear("a %s!",
255 (IS_POOL(lev->typ) || IS_FOUNTAIN(lev->typ)) ?
256 "plop" : "splut"); /* rainforesty sounds */
257 }
258 }
259 place_object(otmp, pos.x,pos.y);
260 stackobj(otmp);
261 if (rn2(6)) levl[x][y].looted |= TREE_LOOTED;
262 if (update) newsym(pos.x,pos.y);
263 return TRUE;
264 }
265 }
266 }
267 }
268 return FALSE;
269 }
270
271 /* Tree at (x,y) seeds. returns TRUE if a new tree was created.
272 * Creates a kind of forest, with (hopefully) most places available.
273 */
274 boolean
seed_tree(x,y)275 seed_tree(x,y)
276 xchar x,y;
277 {
278 coord pos, pos2;
279 struct rm *lev;
280
281 rndmappos(&x,&y);
282 if (IS_TREE(levl[x][y].typ) && may_dig(x,y)) {
283 int dir = rn2(8);
284 dtoxy(&pos, dir);
285 pos.x += x;
286 pos.y += y;
287 if (!rn2(3)) {
288 dtoxy(&pos2, (dir+rn2(2)) % 8);
289 pos.x += pos2.x;
290 pos.y += pos2.y;
291 }
292 if (!isok(pos.x,pos.y)) return FALSE;
293 lev = &levl[pos.x][pos.y];
294 if (lev->lit && !cansee(pos.x,pos.y) && !inside_shop(pos.x,pos.y) &&
295 (lev->typ == ROOM || lev->typ == CORR) &&
296 !(u.ux == pos.x && u.uy == pos.y) && !m_at(pos.x,pos.y) &&
297 !t_at(pos.x,pos.y) && !OBJ_AT(pos.x,pos.y)) {
298 int nogrow = 0;
299 int dx,dy;
300 for (dx = pos.x-1; dx <= pos.x+1; dx++) {
301 for (dy = pos.y-1; dy <= pos.y+1; dy++) {
302 if (!isok(dx,dy) ||
303 (isok(dx,dy) && !SPACE_POS(levl[dx][dy].typ)))
304 nogrow++;
305 }
306 }
307 if (nogrow < 3) {
308 lev->typ = TREE;
309 lev->looted &= ~TREE_LOOTED;
310 block_point(pos.x,pos.y);
311 return TRUE;
312 }
313 }
314 }
315 return FALSE;
316 }
317
318 void
dgn_growths(showmsg,update)319 dgn_growths(showmsg, update)
320 boolean showmsg; /* show messages */
321 boolean update; /* do newsym() */
322 {
323 int herbnum = rn2(SIZE(herb_info));
324 (void) seed_tree(-1,-1);
325 if (herb_info[herbnum].in_water)
326 (void) grow_water_herbs(herb_info[herbnum].herb, -1,-1);
327 else
328 (void) grow_herbs(herb_info[herbnum].herb, -1,-1, showmsg, update);
329 if (!rn2(30))
330 (void) drop_ripe_treefruit(-1,-1, showmsg, update);
331 (void) water_current(-1,-1, rn2(8),
332 Is_waterlevel(&u.uz) ? 200 : 25, showmsg, update);
333 }
334
335 /* catch up with growths when returning to a previously visited level */
336 void
catchup_dgn_growths(mvs)337 catchup_dgn_growths(mvs)
338 int mvs;
339 {
340 if (mvs < 0) mvs = 0;
341 else if (mvs > LARGEST_INT) mvs = LARGEST_INT;
342 while (mvs-- > 0)
343 dgn_growths(FALSE, FALSE);
344 }
345 #endif /* DUNGEON_GROWTH */
346
347 boolean
revive_nasty(x,y,msg)348 revive_nasty(x, y, msg)
349 int x,y;
350 const char *msg;
351 {
352 register struct obj *otmp, *otmp2;
353 struct monst *mtmp;
354 coord cc;
355 boolean revived = FALSE;
356
357 for(otmp = level.objects[x][y]; otmp; otmp = otmp2) {
358 otmp2 = otmp->nexthere;
359 if (otmp->otyp == CORPSE &&
360 (is_rider(&mons[otmp->corpsenm]) ||
361 otmp->corpsenm == PM_WIZARD_OF_YENDOR)) {
362 /* move any living monster already at that location */
363 if((mtmp = m_at(x,y)) && enexto(&cc, x, y, mtmp->data))
364 rloc_to(mtmp, cc.x, cc.y);
365 if(msg) Norep("%s", msg);
366 revived = revive_corpse(otmp);
367 }
368 }
369
370 /* this location might not be safe, if not, move revived monster */
371 if (revived) {
372 mtmp = m_at(x,y);
373 if (mtmp && !goodpos(x, y, mtmp, 0) &&
374 enexto(&cc, x, y, mtmp->data)) {
375 rloc_to(mtmp, cc.x, cc.y);
376 }
377 /* else impossible? */
378 }
379
380 return (revived);
381 }
382
383 STATIC_OVL int
moverock()384 moverock()
385 {
386 register xchar rx, ry, sx, sy;
387 register struct obj *otmp;
388 register struct trap *ttmp;
389 register struct monst *mtmp;
390
391 sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */
392 while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
393 /* make sure that this boulder is visible as the top object */
394 if (otmp != level.objects[sx][sy]) movobj(otmp, sx, sy);
395
396 rx = u.ux + 2 * u.dx; /* boulder destination position */
397 ry = u.uy + 2 * u.dy;
398 nomul(0, 0);
399 if (Levitation || Is_airlevel(&u.uz)) {
400 if (Blind) feel_location(sx, sy);
401 You("don't have enough leverage to push %s.", the(xname(otmp)));
402 /* Give them a chance to climb over it? */
403 return -1;
404 }
405 if (verysmall(youmonst.data)
406 #ifdef STEED
407 && !u.usteed
408 #endif
409 ) {
410 if (Blind) feel_location(sx, sy);
411 pline("You're too small to push that %s.", xname(otmp));
412 goto cannot_push;
413 }
414 if (isok(rx,ry) && !IS_ROCK(levl[rx][ry].typ) &&
415 levl[rx][ry].typ != IRONBARS &&
416 (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy) || (
417 #ifdef REINCARNATION
418 !Is_rogue_level(&u.uz) &&
419 #endif
420 (levl[rx][ry].doormask & ~D_BROKEN) == D_NODOOR)) &&
421 !sobj_at(BOULDER, rx, ry)) {
422 ttmp = t_at(rx, ry);
423 mtmp = m_at(rx, ry);
424
425 /* KMH -- Sokoban doesn't let you push boulders diagonally */
426 if (In_sokoban(&u.uz) && u.dx && u.dy) {
427 if (Blind) feel_location(sx,sy);
428 pline("%s won't roll diagonally on this %s.",
429 The(xname(otmp)), surface(sx, sy));
430 goto cannot_push;
431 }
432
433 if (revive_nasty(rx, ry, "You sense movement on the other side."))
434 return (-1);
435
436 if (mtmp && !noncorporeal(mtmp->data) &&
437 (!mtmp->mtrapped ||
438 !(ttmp && ((ttmp->ttyp == PIT) ||
439 (ttmp->ttyp == SPIKED_PIT))))) {
440 if (Blind) feel_location(sx, sy);
441 if (canspotmon(mtmp))
442 pline("There's %s on the other side.", a_monnam(mtmp));
443 else {
444 You_hear("a monster behind %s.", the(xname(otmp)));
445 map_invisible(rx, ry);
446 }
447 if (flags.verbose)
448 pline("Perhaps that's why %s cannot move it.",
449 #ifdef STEED
450 u.usteed ? y_monnam(u.usteed) :
451 #endif
452 "you");
453 goto cannot_push;
454 }
455
456 if (ttmp)
457 switch(ttmp->ttyp) {
458 case LANDMINE:
459 if (rn2(10)) {
460 obj_extract_self(otmp);
461 place_object(otmp, rx, ry);
462 unblock_point(sx, sy);
463 newsym(sx, sy);
464 pline("KAABLAMM!!! %s %s land mine.",
465 Tobjnam(otmp, "trigger"),
466 ttmp->madeby_u ? "your" : "a");
467 blow_up_landmine(ttmp);
468 /* if the boulder remains, it should fill the pit */
469 fill_pit(u.ux, u.uy);
470 if (cansee(rx,ry)) newsym(rx,ry);
471 continue;
472 }
473 break;
474 case SPIKED_PIT:
475 case PIT:
476 obj_extract_self(otmp);
477 /* vision kludge to get messages right;
478 the pit will temporarily be seen even
479 if this is one among multiple boulders */
480 if (!Blind) viz_array[ry][rx] |= IN_SIGHT;
481 if (!flooreffects(otmp, rx, ry, "fall")) {
482 place_object(otmp, rx, ry);
483 }
484 if (mtmp && !Blind) newsym(rx, ry);
485 continue;
486 case HOLE:
487 case TRAPDOOR:
488 if (Blind)
489 pline("Kerplunk! You no longer feel %s.",
490 the(xname(otmp)));
491 else
492 pline("%s%s and %s a %s in the %s!",
493 Tobjnam(otmp,
494 (ttmp->ttyp == TRAPDOOR) ? "trigger" : "fall"),
495 (ttmp->ttyp == TRAPDOOR) ? nul : " into",
496 otense(otmp, "plug"),
497 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole",
498 surface(rx, ry));
499 deltrap(ttmp);
500 delobj(otmp);
501 bury_objs(rx, ry);
502 if (cansee(rx,ry)) newsym(rx,ry);
503 continue;
504 case LEVEL_TELEP:
505 case TELEP_TRAP:
506 #ifdef STEED
507 if (u.usteed)
508 pline("%s pushes %s and suddenly it disappears!",
509 upstart(y_monnam(u.usteed)), the(xname(otmp)));
510 else
511 #endif
512 You("push %s and suddenly it disappears!",
513 the(xname(otmp)));
514 if (ttmp->ttyp == TELEP_TRAP)
515 rloco(otmp);
516 else {
517 int newlev = random_teleport_level();
518 d_level dest;
519
520 if (newlev == depth(&u.uz) || In_endgame(&u.uz))
521 continue;
522 obj_extract_self(otmp);
523 add_to_migration(otmp);
524 get_level(&dest, newlev);
525 otmp->ox = dest.dnum;
526 otmp->oy = dest.dlevel;
527 otmp->owornmask = (long)MIGR_RANDOM;
528 }
529 seetrap(ttmp);
530 continue;
531 }
532 if (closed_door(rx, ry))
533 goto nopushmsg;
534 if (boulder_hits_pool(otmp, rx, ry, TRUE))
535 continue;
536 /*
537 * Re-link at top of fobj chain so that pile order is preserved
538 * when level is restored.
539 */
540 if (otmp != fobj) {
541 remove_object(otmp);
542 place_object(otmp, otmp->ox, otmp->oy);
543 }
544
545 {
546 #ifdef LINT /* static long lastmovetime; */
547 long lastmovetime;
548 lastmovetime = 0;
549 #else
550 /* note: reset to zero after save/restore cycle */
551 static NEARDATA long lastmovetime;
552 #endif
553 #ifdef STEED
554 if (!u.usteed) {
555 #endif
556 if (moves > lastmovetime+2 || moves < lastmovetime)
557 pline("With %s effort you move %s.",
558 throws_rocks(youmonst.data) ? "little" : "great",
559 the(xname(otmp)));
560 exercise(A_STR, TRUE);
561 #ifdef STEED
562 } else
563 pline("%s moves %s.",
564 upstart(y_monnam(u.usteed)), the(xname(otmp)));
565 #endif
566 lastmovetime = moves;
567 }
568
569 /* Move the boulder *after* the message. */
570 if (glyph_is_invisible(levl[rx][ry].glyph))
571 unmap_object(rx, ry);
572 movobj(otmp, rx, ry); /* does newsym(rx,ry) */
573 if (Blind) {
574 feel_location(rx,ry);
575 feel_location(sx, sy);
576 } else {
577 newsym(sx, sy);
578 }
579 } else {
580 nopushmsg:
581 #ifdef STEED
582 if (u.usteed)
583 pline("%s tries to move %s, but cannot.",
584 upstart(y_monnam(u.usteed)), the(xname(otmp)));
585 else
586 #endif
587 You("try to move %s, but in vain.", the(xname(otmp)));
588 if (Blind) feel_location(sx, sy);
589 cannot_push:
590 if (throws_rocks(youmonst.data)) {
591 #ifdef STEED
592 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
593 You("aren't skilled enough to %s %s from %s.",
594 (flags.pickup && !In_sokoban(&u.uz))
595 ? "pick up" : "push aside",
596 the(xname(otmp)), y_monnam(u.usteed));
597 } else
598 #endif
599 {
600 pline("However, you can easily %s.",
601 (flags.pickup && !In_sokoban(&u.uz))
602 ? "pick it up" : "push it aside");
603 if (In_sokoban(&u.uz))
604 sokoban_trickster(); /* Sokoban guilt */
605 break;
606 }
607 break;
608 }
609
610 if (
611 #ifdef STEED
612 !u.usteed &&
613 #endif
614 (((!invent || inv_weight() <= -850) &&
615 (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ)
616 && IS_ROCK(levl[sx][u.uy].typ))))
617 || verysmall(youmonst.data))) {
618 pline("However, you can squeeze yourself into a small opening.");
619 if (In_sokoban(&u.uz))
620 sokoban_trickster(); /* Sokoban guilt */
621 break;
622 } else
623 return (-1);
624 }
625 }
626 return (0);
627 }
628
629 /*
630 * still_chewing()
631 *
632 * Chew on a wall, door, boulder, or iron bars. Returns TRUE if still
633 * eating, FALSE when done.
634 */
635 STATIC_OVL int
still_chewing(x,y)636 still_chewing(x,y)
637 xchar x, y;
638 {
639 struct rm *lev = &levl[x][y];
640 struct obj *boulder = sobj_at(BOULDER,x,y);
641 const char *digtxt = (char *)0, *dmgtxt = (char *)0;
642
643 if (digging.down) /* not continuing previous dig (w/ pick-axe) */
644 (void) memset((genericptr_t)&digging, 0, sizeof digging);
645
646 if (!boulder && (IS_ROCK(lev->typ) || IS_ICEWALL(lev->typ)) &&
647 !may_dig(x,y)) {
648 You("hurt your teeth on the %s.",
649 IS_TREES(lev->typ) ? "tree" : "hard stone");
650 nomul(0, 0);
651 return 1;
652 } else if (digging.pos.x != x || digging.pos.y != y ||
653 !on_level(&digging.level, &u.uz)) {
654 digging.down = FALSE;
655 digging.chew = TRUE;
656 digging.warned = FALSE;
657 digging.pos.x = x;
658 digging.pos.y = y;
659 assign_level(&digging.level, &u.uz);
660 /* solid rock takes more work & time to dig through */
661 digging.effort =
662 (IS_ROCK(lev->typ) && !IS_TREES(lev->typ) ? 30 : 60) + u.udaminc;
663 You("start chewing %s.",
664 boulder ? "on a boulder" :
665 lev->typ == IRONBARS ? "on the iron bars" :
666 IS_TREES(lev->typ) ? "on a tree" :
667 IS_ROCK(lev->typ) ? "a hole in the rock" :
668 "a hole in the door");
669 watch_dig((struct monst *)0, x, y, FALSE);
670 return 1;
671 } else if ((digging.effort += (30 + u.udaminc)) <= 100) {
672 if (flags.verbose)
673 You("%s chewing on the %s.",
674 digging.chew ? "continue" : "begin",
675 boulder ? "boulder" :
676 lev->typ == IRONBARS ? "bars" :
677 IS_TREES(lev->typ) ? "tree" :
678 IS_ICEWALL(lev->typ) ? "ice wall" :
679 IS_ROCK(lev->typ) ? "rock" : "door");
680 digging.chew = TRUE;
681 watch_dig((struct monst *)0, x, y, FALSE);
682 return 1;
683 }
684
685 /* Okay, you've chewed through something */
686 violated(CONDUCT_FOODLESS);
687 u.uhunger += rnd(20);
688
689 if (boulder) {
690 delobj(boulder); /* boulder goes bye-bye */
691 You("eat the boulder."); /* yum */
692
693 /*
694 * The location could still block because of
695 * 1. More than one boulder
696 * 2. Boulder stuck in a wall/stone/door.
697 *
698 * [perhaps use does_block() below (from vision.c)]
699 */
700 if (IS_ROCK(lev->typ) || closed_door(x,y) || sobj_at(BOULDER,x,y)) {
701 block_point(x,y); /* delobj will unblock the point */
702 /* reset dig state */
703 (void) memset((genericptr_t)&digging, 0, sizeof digging);
704 return 1;
705 }
706
707 } else if (IS_WALL(lev->typ)) {
708 if (*in_rooms(x, y, SHOPBASE)) {
709 add_damage(x, y, 10L * ACURRSTR);
710 dmgtxt = "damage";
711 }
712 digtxt = "chew a hole in the wall.";
713 if (level.flags.is_maze_lev) {
714 lev->typ = ROOM;
715 } else if (level.flags.is_cavernous_lev && !in_town(x, y)) {
716 lev->typ = CORR;
717 } else {
718 lev->typ = DOOR;
719 lev->doormask = D_NODOOR;
720 }
721 } else if (IS_TREE(lev->typ)) {
722 digtxt = "chew through the tree.";
723 lev->typ = ROOM;
724 } else if (IS_ICEWALL(lev->typ)) {
725 digtxt = "chew through the ice.";
726 lev->typ = ICE;
727 } else if (lev->typ == IRONBARS) {
728 digtxt = "chew through the iron bars.";
729 dissolve_bars(x,y);
730 if (In_sokoban(&u.uz))
731 sokoban_trickster();
732 } else if (lev->typ == SDOOR) {
733 if (lev->doormask & D_TRAPPED) {
734 lev->doormask = D_NODOOR;
735 b_trapped("secret door", 0);
736 } else {
737 digtxt = "chew through the secret door.";
738 lev->doormask = D_BROKEN;
739 }
740 lev->typ = DOOR;
741
742 } else if (IS_DOOR(lev->typ)) {
743 if (*in_rooms(x, y, SHOPBASE)) {
744 add_damage(x, y, 400L);
745 dmgtxt = "break";
746 }
747 if (lev->doormask & D_TRAPPED) {
748 lev->doormask = D_NODOOR;
749 b_trapped("door", 0);
750 } else {
751 digtxt = "chew through the door.";
752 lev->doormask = D_BROKEN;
753 }
754
755 } else { /* STONE or SCORR */
756 digtxt = "chew a passage through the rock.";
757 lev->typ = CORR;
758 }
759
760 unblock_point(x, y); /* vision */
761 newsym(x, y);
762 if (digtxt) You("%s",digtxt); /* after newsym */
763 if (dmgtxt) pay_for_damage(dmgtxt, FALSE);
764 (void) memset((genericptr_t)&digging, 0, sizeof digging);
765 return 0;
766 }
767
768 void
movobj(obj,ox,oy)769 movobj(obj, ox, oy)
770 register struct obj *obj;
771 register xchar ox, oy;
772 {
773 /* optimize by leaving on the fobj chain? */
774 remove_object(obj);
775 newsym(obj->ox, obj->oy);
776 place_object(obj, ox, oy);
777 newsym(ox, oy);
778 }
779
780 #ifdef SINKS
781 static NEARDATA const char fell_on_sink[] = "fell onto a sink";
782
783 STATIC_OVL void
dosinkfall()784 dosinkfall()
785 {
786 register struct obj *obj;
787
788 if (is_floater(youmonst.data) || (HLevitation & FROMOUTSIDE)) {
789 You("wobble unsteadily for a moment.");
790 } else {
791 long save_ELev = ELevitation, save_HLev = HLevitation;
792
793 /* fake removal of levitation in advance so that final
794 disclosure will be right in case this turns out to
795 be fatal; fortunately the fact that rings and boots
796 are really still worn has no effect on bones data */
797 ELevitation = HLevitation = 0L;
798 You("crash to the floor!");
799 losehp(rn1(8, 25 - (int)ACURR(A_CON)),
800 fell_on_sink, NO_KILLER_PREFIX);
801 exercise(A_DEX, FALSE);
802 selftouch("Falling, you");
803 for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
804 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) {
805 You("fell on %s.", doname(obj));
806 losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX);
807 exercise(A_CON, FALSE);
808 }
809 ELevitation = save_ELev;
810 HLevitation = save_HLev;
811 }
812
813 ELevitation &= ~W_ARTI;
814 HLevitation &= ~(I_SPECIAL|TIMEOUT);
815 HLevitation++;
816 if(uleft && uleft->otyp == RIN_LEVITATION) {
817 obj = uleft;
818 Ring_off(obj);
819 off_msg(obj);
820 }
821 if(uright && uright->otyp == RIN_LEVITATION) {
822 obj = uright;
823 Ring_off(obj);
824 off_msg(obj);
825 }
826 if(uarmf && uarmf->otyp == LEVITATION_BOOTS) {
827 obj = uarmf;
828 (void)Boots_off();
829 off_msg(obj);
830 }
831 HLevitation--;
832 }
833 #endif
834
835 boolean
may_dig(x,y)836 may_dig(x,y)
837 register xchar x,y;
838 /* intended to be called only on ROCKs */
839 {
840 return (boolean)(!((IS_STWALL(levl[x][y].typ) ||
841 IS_TREES(levl[x][y].typ) ||
842 IS_ICEWALL(levl[x][y].typ)) &&
843 (levl[x][y].wall_info & W_NONDIGGABLE)));
844 }
845
846 boolean
may_passwall(x,y)847 may_passwall(x,y)
848 register xchar x,y;
849 {
850 return (boolean)(!((IS_STWALL(levl[x][y].typ) || IS_TREES(levl[x][y].typ)) &&
851 (levl[x][y].wall_info & W_NONPASSWALL)));
852 }
853
854 boolean
bad_rock(mdat,x,y)855 bad_rock(mdat,x,y)
856 struct permonst *mdat;
857 register xchar x,y;
858 {
859 return((boolean) ((In_sokoban(&u.uz) && sobj_at(BOULDER,x,y)) ||
860 (IS_ROCK(levl[x][y].typ)
861 && (!tunnels(mdat) || needspick(mdat) || !may_dig(x,y))
862 && !(passes_walls(mdat) && may_passwall(x,y)))));
863 }
864
865 boolean
invocation_pos(x,y)866 invocation_pos(x, y)
867 xchar x, y;
868 {
869 return((boolean)(Invocation_lev(&u.uz) && x == inv_pos.x && y == inv_pos.y));
870 }
871
872 static void
autoexplore_msg(text,mode)873 autoexplore_msg(text, mode)
874 const char *text;
875 int mode;
876 {
877 if (iflags.autoexplore) {
878 char tmp[BUFSZ];
879 Strcpy(tmp, text);
880 pline("%s blocks your way.", upstart(tmp));
881 }
882 }
883
884 /** Return TRUE if (dx,dy) is an OK place to move
885 * mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV or TEST_TRAP
886 */
887 boolean
test_move(ux,uy,dx,dy,mode)888 test_move(ux, uy, dx, dy, mode)
889 int ux, uy, dx, dy;
890 int mode;
891 {
892 int x = ux+dx;
893 int y = uy+dy;
894 register struct rm *tmpr = &levl[x][y];
895 register struct rm *ust;
896
897 /*
898 * Check for physical obstacles. First, the place we are going.
899 */
900 if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
901 if (Blind && mode == DO_MOVE) feel_location(x,y);
902 if (Passes_walls && may_passwall(x,y)) {
903 ; /* do nothing */
904 } else if (tmpr->typ == IRONBARS) {
905 /* Eat the bars if you can */
906 if ((mode == DO_MOVE &&
907 (!flags.nopick && metallivorous(youmonst.data) &&
908 still_chewing(x,y)))
909 || !(Passes_walls || passes_bars(youmonst.data)))
910 return FALSE;
911 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
912 /* Eat the rock. */
913 if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
914 } else if (flags.autodig && !flags.run && !flags.nopick &&
915 uwep && is_pick(uwep)) {
916 /* MRKR: Automatic digging when wielding the appropriate tool */
917 if (mode == DO_MOVE)
918 (void) use_pick_axe2(uwep);
919 return FALSE;
920 } else {
921 if (mode == DO_MOVE) {
922 if (Is_stronghold(&u.uz) && is_db_wall(x,y))
923 pline_The("drawbridge is up!");
924 if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
925 pline_The("Sokoban walls resist your ability.");
926 }
927 return FALSE;
928 }
929 } else if (IS_DOOR(tmpr->typ)) {
930 if (closed_door(x,y)) {
931 if (Blind && mode == DO_MOVE) feel_location(x,y);
932 /* ALI - artifact doors */
933 if (artifact_door(x, y)) {
934 if (mode == DO_MOVE) {
935 if (amorphous(youmonst.data))
936 You("try to ooze under the door, but the gap is too small.");
937 else if (tunnels(youmonst.data) && !needspick(youmonst.data))
938 You("hurt your teeth on the reinforced door.");
939 else if (x == u.ux || y == u.uy) {
940 if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) { pline("Ouch! You bump into a heavy door.");
941 exercise(A_DEX, FALSE);
942 } else pline("That door is closed.");
943 }
944 }
945 return FALSE;
946 } else
947 if (Passes_walls)
948 ; /* do nothing */
949 else if (can_ooze(&youmonst)) {
950 if (mode == DO_MOVE) You("ooze under the door.");
951 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
952 /* Eat the door. */
953 if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
954 } else {
955 if (mode == DO_MOVE) {
956 if (amorphous(youmonst.data))
957 You("try to ooze under the door, but can't squeeze your possessions through.");
958 #ifdef AUTO_OPEN
959 else if (iflags.autoopen
960 && !Confusion && !Stunned && !Fumbling) {
961 door_opened = flags.move = doopen_indir(x, y);
962 }
963 #endif
964 else if (x == ux || y == uy) {
965 if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
966 #ifdef STEED
967 if (u.usteed) {
968 You_cant("lead %s through that closed door.",
969 y_monnam(u.usteed));
970 } else
971 #endif
972 {
973 pline("Ouch! You bump into a door.");
974 exercise(A_DEX, FALSE);
975 }
976 } else pline("That door is closed.");
977 }
978 } else if (mode == TEST_TRAV || mode == TEST_TRAP) goto testdiag;
979 return FALSE;
980 }
981 } else {
982 testdiag:
983 if (dx && dy && !Passes_walls
984 && ((tmpr->doormask & ~D_BROKEN)
985 #ifdef REINCARNATION
986 || Is_rogue_level(&u.uz)
987 #endif
988 || block_door(x,y))) {
989 /* Diagonal moves into a door are not allowed. */
990 if (Blind && mode == DO_MOVE)
991 feel_location(x,y);
992 return FALSE;
993 }
994 }
995 }
996 if (dx && dy
997 && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) {
998 /* Move at a diagonal. */
999 if (In_sokoban(&u.uz)) {
1000 if (mode == DO_MOVE)
1001 You("cannot pass that way.");
1002 return FALSE;
1003 }
1004 if (bigmonst(youmonst.data)) {
1005 if (mode == DO_MOVE)
1006 Your("body is too large to fit through.");
1007 return FALSE;
1008 }
1009 if (invent && (inv_weight() + weight_cap() > 600)) {
1010 if (mode == DO_MOVE)
1011 #ifdef CONVICT
1012 if (!Passes_walls)
1013 #endif /* CONVICT */
1014 You("are carrying too much to get through.");
1015 return FALSE;
1016 }
1017 }
1018 /* Pick travel path that does not require crossing a trap.
1019 * Avoid water and lava using the usual running rules.
1020 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */
1021 if (flags.run == 8 && (mode != DO_MOVE) &&
1022 (x != u.ux || y != u.uy)) {
1023 struct trap* t = t_at(x, y);
1024
1025 if ((t && t->tseen) ||
1026 (!Levitation && !Flying &&
1027 !is_clinger(youmonst.data) &&
1028 (is_pool(x, y) || is_lava(x, y) || is_swamp(x, y)) && levl[x][y].seenv)) {
1029 if (mode == DO_MOVE) {
1030 if (t && t->tseen) autoexplore_msg("a trap", mode);
1031 else if (is_pool(x, y)) autoexplore_msg("a body of water", mode);
1032 else if (is_lava(x, y)) autoexplore_msg("a pool of lava", mode);
1033 }
1034 return mode == TEST_TRAP;
1035 }
1036 }
1037
1038 if (mode == TEST_TRAP) return FALSE; /* not a move through a trap */
1039
1040 ust = &levl[ux][uy];
1041
1042 /* Now see if other things block our way . . */
1043 if (dx && dy && !Passes_walls
1044 && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN)
1045 #ifdef REINCARNATION
1046 || Is_rogue_level(&u.uz)
1047 #endif
1048 || block_entry(x, y))
1049 )) {
1050 /* Can't move at a diagonal out of a doorway with door. */
1051 if (mode == DO_MOVE) autoexplore_msg("something", mode);
1052 return FALSE;
1053 }
1054
1055 if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
1056 if (!(Blind || Hallucination) && (flags.run >= 2) && mode != TEST_TRAV) {
1057 if (sobj_at(BOULDER,x,y) && mode == DO_MOVE) autoexplore_msg("a boulder", mode);
1058 return FALSE;
1059 }
1060 if (mode == DO_MOVE) {
1061 /* tunneling monsters will chew before pushing */
1062 if (tunnels(youmonst.data) && !needspick(youmonst.data) &&
1063 !In_sokoban(&u.uz)) {
1064 if (still_chewing(x,y)) return FALSE;
1065 } else {
1066 if (moverock() < 0) return FALSE;
1067 }
1068 } else if (mode == TEST_TRAV) {
1069 struct obj* obj;
1070
1071 /* don't pick two boulders in a row, unless there's a way thru */
1072 if (sobj_at(BOULDER,ux,uy) && !In_sokoban(&u.uz)) {
1073 if (!Passes_walls &&
1074 !(tunnels(youmonst.data) && !needspick(youmonst.data)) &&
1075 !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK) &&
1076 !((obj = carrying(WAN_DIGGING)) &&
1077 !objects[obj->otyp].oc_name_known))
1078 return FALSE;
1079 }
1080 }
1081 /* assume you'll be able to push it when you get there... */
1082 }
1083
1084 /* OK, it is a legal place to move. */
1085 return TRUE;
1086 }
1087
1088 /* Returns whether a square might be interesting to autoexplore onto.
1089 This is done purely in terms of the glyph on the square, and the
1090 stepped-on memory, i.e. information the player knows already, to
1091 avoid leaking information. The algorithm is taken from TAEB: "step
1092 on any item we haven't stepped on, or any square we haven't stepped
1093 on adjacent to stone that isn't adjacent to a square that has been
1094 stepped on; however, never step on a boulder this way". We also
1095 avoid treating traps and doors known to be locked as valid explore
1096 targets (although they are still valid travel intermediates). */
1097 static boolean
unexplored(x,y)1098 unexplored(x, y)
1099 int x, y;
1100 {
1101 int i, j, k, l;
1102 struct trap *ttmp = t_at(x, y);
1103 if (!isok(x, y)) return FALSE;
1104 if (levl[x][y].stepped_on) return FALSE;
1105 #if 0
1106 if (glyph_to_cmap(levl[x][y].glyph) == S_vcdoor ||
1107 glyph_to_cmap(levl[x][y].glyph) == S_hcdoor)
1108 if (levl[x][y].fknown & FKNOWN_LOCKED &&
1109 levl[x][y].flags & D_LOCKED) return FALSE;
1110 #endif
1111 if (ttmp && ttmp->tseen) return FALSE;
1112 if (glyph_is_object(levl[x][y].glyph) &&
1113 glyph_to_obj(levl[x][y].glyph) == BOULDER) return FALSE;
1114 if (glyph_is_object(levl[x][y].glyph) &&
1115 inside_shop(x, y)) {
1116 /* Black Market items are interesting */
1117 return (Is_blackmarket(&u.uz));
1118 }
1119 if (glyph_is_object(levl[x][y].glyph)) return TRUE;
1120
1121 /* step into shop doorways so autoexplore can be interrupted */
1122 if (IS_DOOR(levl[x][y].typ) && *in_rooms(x, y, SHOPBASE)) {
1123 return TRUE;
1124 }
1125
1126 for (i = -1; i <= 1; i++) {
1127 for (j = -1; j <= 1; j++) {
1128 if (isok(x+i, y+j) &&
1129 glyph_is_cmap(levl[x+i][y+j].glyph) &&
1130 glyph_to_cmap(levl[x+i][y+j].glyph) == S_stone) {
1131 int flag = TRUE;
1132 for (k = -1; k <= 1; k++)
1133 for (l = -1; l <= 1; l++)
1134 if (isok(x+i+k, y+j+l) && levl[x+i+k][y+j+l].stepped_on)
1135 flag = FALSE;
1136 if (flag) return TRUE;
1137 }
1138 }
1139 }
1140 return FALSE;
1141 }
1142
1143 /** Returns a distance modified by a constant factor.
1144 * The lower the value the better.*/
1145 int
autotravel_weighting(x,y,distance)1146 autotravel_weighting(x, y, distance)
1147 int x, y;
1148 unsigned distance;
1149 {
1150 if (glyph_is_object(levl[x][y].glyph)) {
1151 /* greedy for items */
1152 return distance;
1153 }
1154 /* by default return distance multiplied by a large constant factor */
1155 return distance*10;
1156 }
1157
1158
1159 /*
1160 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
1161 * A shortest path is returned. If guess is non-NULL, instead travel
1162 * as near to the target as you can, using guess as a function that
1163 * specifies what is considered to be a valid target.
1164 * Returns TRUE if a path was found.
1165 */
1166 static boolean
1167 findtravelpath(guess)
1168 boolean (*guess)(int, int);
1169 {
1170 /* if travel to adjacent, reachable location, use normal movement rules */
1171 if (!guess && iflags.travel1 && distmin(u.ux, u.uy, u.tx, u.ty) == 1) {
1172 flags.run = 0;
1173 if (test_move(u.ux, u.uy, u.tx-u.ux, u.ty-u.uy, TEST_MOVE)) {
1174 u.dx = u.tx-u.ux;
1175 u.dy = u.ty-u.uy;
1176 nomul(0, 0);
1177 iflags.travelcc.x = iflags.travelcc.y = -1;
1178 return TRUE;
1179 }
1180 flags.run = 8;
1181 }
1182 if (u.tx != u.ux || u.ty != u.uy || guess == unexplored) {
1183 unsigned travel[COLNO][ROWNO];
1184 xchar travelstepx[2][COLNO*ROWNO];
1185 xchar travelstepy[2][COLNO*ROWNO];
1186 xchar tx, ty, ux, uy;
1187 int n = 1; /* max offset in travelsteps */
1188 int set = 0; /* two sets current and previous */
1189 int radius = 1; /* search radius */
1190 int i;
1191
1192 /* If guessing, first find an "obvious" goal location. The obvious
1193 * goal is the position the player knows of, or might figure out
1194 * (couldsee) that is closest to the target on a straight path.
1195 */
1196 if (guess) {
1197 tx = u.ux; ty = u.uy; ux = u.tx; uy = u.ty;
1198 } else {
1199 tx = u.tx; ty = u.ty; ux = u.ux; uy = u.uy;
1200 }
1201
1202 noguess:
1203 (void) memset((genericptr_t)travel, 0, sizeof(travel));
1204 travelstepx[0][0] = tx;
1205 travelstepy[0][0] = ty;
1206
1207 while (n != 0) {
1208 int nn = 0;
1209
1210 for (i = 0; i < n; i++) {
1211 int dir;
1212 int x = travelstepx[set][i];
1213 int y = travelstepy[set][i];
1214 static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
1215 /* no diagonal movement for grid bugs */
1216 int dirmax = u.umonnum == PM_GRID_BUG ? 4 : 8;
1217 boolean alreadyrepeated = FALSE;
1218
1219 for (dir = 0; dir < dirmax; ++dir) {
1220 int nx = x+xdir[ordered[dir]];
1221 int ny = y+ydir[ordered[dir]];
1222
1223 if (!isok(nx, ny)) continue;
1224 if ((!Passes_walls && !can_ooze(&youmonst) &&
1225 closed_door(x, y)) || sobj_at(BOULDER, x, y) ||
1226 test_move(x, y, nx-x, ny-y, TEST_TRAP)) {
1227 /* closed doors and boulders usually
1228 * cause a delay, so prefer another path */
1229 if (travel[x][y] > radius-3) {
1230 if (!alreadyrepeated) {
1231 travelstepx[1-set][nn] = x;
1232 travelstepy[1-set][nn] = y;
1233 /* don't change travel matrix! */
1234 nn++;
1235 alreadyrepeated = TRUE;
1236 }
1237 continue;
1238 }
1239 }
1240 if (test_move(x, y, nx-x, ny-y, TEST_TRAV)) {
1241 if (levl[nx][ny].seenv || (!Blind && couldsee(nx, ny))) {
1242 if (nx == ux && ny == uy) {
1243 if (!guess) {
1244 u.dx = x-ux;
1245 u.dy = y-uy;
1246 if (x == u.tx && y == u.ty) {
1247 nomul(0, 0);
1248 /* reset run so domove run checks work */
1249 flags.run = 8;
1250 iflags.travelcc.x = iflags.travelcc.y = -1;
1251 }
1252 return TRUE;
1253 }
1254 } else if (!travel[nx][ny]) {
1255 travelstepx[1-set][nn] = nx;
1256 travelstepy[1-set][nn] = ny;
1257 travel[nx][ny] = radius;
1258 nn++;
1259 }
1260 }
1261 }
1262 }
1263 }
1264
1265 n = nn;
1266 set = 1-set;
1267 radius++;
1268 }
1269
1270 /* if guessing, find best location in travel matrix and go there */
1271 if (guess) {
1272 int px = tx, py = ty; /* pick location */
1273 int dist, nxtdist, d2, nd2;
1274 int autoexploring = (guess == unexplored);
1275
1276 dist = distmin(ux, uy, tx, ty);
1277 d2 = dist2(ux, uy, tx, ty);
1278 if (autoexploring) {
1279 dist = INT_MAX;
1280 d2 = INT_MAX;
1281 }
1282
1283 for (tx = 1; tx < COLNO; ++tx) {
1284 for (ty = 0; ty < ROWNO; ++ty) {
1285 if (travel[tx][ty]) {
1286 nxtdist = distmin(ux, uy, tx, ty);
1287 if (autoexploring) {
1288 nxtdist = autotravel_weighting(tx, ty, travel[tx][ty]);
1289 }
1290 if (nxtdist == dist && guess(tx, ty)) {
1291 nd2 = dist2(ux, uy, tx, ty);
1292 if (nd2 < d2) {
1293 /* prefer non-zigzag path */
1294 px = tx; py = ty;
1295 d2 = nd2;
1296 }
1297 } else if (nxtdist < dist && guess(tx, ty)) {
1298 px = tx; py = ty;
1299 dist = nxtdist;
1300 d2 = dist2(ux, uy, tx, ty);
1301 }
1302 }
1303 }
1304 }
1305
1306 if (px == u.ux && py == u.uy) {
1307 /* no guesses, just go in the general direction */
1308 u.dx = sgn(u.tx - u.ux);
1309 u.dy = sgn(u.ty - u.uy);
1310 if (u.dx == 0 && u.dy == 0) {
1311 nomul(0, 0);
1312 return FALSE;
1313 }
1314 if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE))
1315 return TRUE;
1316 goto found;
1317 }
1318 tx = px;
1319 ty = py;
1320 ux = u.ux;
1321 uy = u.uy;
1322 set = 0;
1323 n = radius = 1;
1324 guess = NULL;
1325 goto noguess;
1326 }
1327 return FALSE;
1328 }
1329
1330 found:
1331 u.dx = 0;
1332 u.dy = 0;
1333 nomul(0, 0);
1334 return FALSE;
1335 }
1336
1337 /* A function version of couldsee, so we can take a pointer to it. */
1338 static boolean
couldsee_func(x,y)1339 couldsee_func(x, y)
1340 int x, y;
1341 {
1342 return couldsee(x, y);
1343 }
1344
1345 /** Returns if (x,y) could be explored.
1346 * Differs from unexplore() in that it is uses information not
1347 * available to the player.
1348 * It should only leak information about "obvious" coordinates, e.g.
1349 * unexplored rooms or big areas not reachable by the player. */
1350 static boolean
interesting_to_explore(x,y)1351 interesting_to_explore(x,y) {
1352 if (!goodpos(x, y, &youmonst, 0)) return FALSE;
1353
1354 if (!unexplored(x, y)) return FALSE;
1355
1356 /* don't leak information about secret locations */
1357 if (levl[x][y].typ == SCORR ||
1358 levl[x][y].typ == SDOOR) {
1359 return FALSE;
1360 }
1361 /* don't leak information about gold vaults */
1362 if (*in_rooms(x,y,VAULT)) return FALSE;
1363
1364 /* corridors are uninteresting */
1365 if (levl[x][y].typ == CORR) return FALSE;
1366
1367 return TRUE;
1368 }
1369
1370 void
domove()1371 domove()
1372 {
1373 register struct monst *mtmp;
1374 register struct rm *tmpr;
1375 register xchar x,y;
1376 struct trap *trap;
1377 int wtcap;
1378 boolean on_ice;
1379 xchar chainx, chainy, ballx, bally; /* ball&chain new positions */
1380 int bc_control; /* control for ball&chain */
1381 boolean cause_delay = FALSE; /* dragging ball will skip a move */
1382 boolean known_wwalking = FALSE; /* player is aware of wearing water walking boots */
1383 boolean known_lwalking = FALSE; /* player knows water walking boots are fireproof */
1384 const char *predicament;
1385
1386 u_wipe_engr(rnd(5));
1387
1388 if (flags.travel) {
1389 if (iflags.autoexplore) {
1390 if (Blind) {
1391 /* necessary to not autoexplore while blind; the logic
1392 can't handle it, nor could it sensibly without
1393 somehow knowing when to search and when to move */
1394 pline("You stop exploring, because you can't see where"
1395 " you're going.");
1396 flags.move = 0;
1397 nomul(0, 0);
1398 return;
1399 }
1400 if (In_sokoban(&u.uz)) {
1401 pline("You somehow know the layout of this place"
1402 " without exploring.");
1403 pline("But, you feel you should be careful moving"
1404 " around.");
1405 flags.move = 0;
1406 nomul(0, 0);
1407 return;
1408 }
1409 if (Stunned || Confusion) {
1410 pline("Your head is spinning too much to explore.");
1411 flags.move = 0;
1412 nomul(0, 0);
1413 return;
1414 }
1415 if (Strangled) {
1416 pline("You stop exploring, because the pressure on your "
1417 "%s is a more pressing matter.", body_part(NECK));
1418 flags.move = 0;
1419 nomul(0, 0);
1420 return;
1421 }
1422 u.tx = u.ux;
1423 u.ty = u.uy;
1424 if (!findtravelpath(unexplored)) {
1425 char msg[BUFSZ];
1426 int unexplored_cnt=0, i, j;
1427 /* check if this level has been thoroughly explored */
1428 for (i=1; i < COLNO; i++) {
1429 for (j=0; j < ROWNO; j++) {
1430 if (interesting_to_explore(i,j)) {
1431 unexplored_cnt++;
1432 }
1433 }
1434 }
1435 iflags.autoexplore = FALSE;
1436 /* TODO: Check if really done (known closed doors,
1437 * boulders blocking your way) and offer doing
1438 * travel when done */
1439 if (unexplored_cnt > 0) {
1440 if (wizard) {
1441 Sprintf(msg, "Partly explored, can't reach %d places.", unexplored_cnt);
1442 } else {
1443 Strcpy(msg, "Partly explored, can't reach some places.");
1444 }
1445 } else {
1446 Strcpy(msg, "Done exploring.");
1447 }
1448 pline("%s", msg);
1449 }
1450 } else if (!findtravelpath(NULL)) {
1451 (void) findtravelpath(couldsee_func);
1452 }
1453 iflags.travel1 = 0;
1454 if (u.dx == 0 && u.dy == 0) {
1455 /* couldn't find a move; end travel without costing a turn */
1456 flags.move = 0;
1457 nomul(0, 0);
1458 return;
1459 }
1460 }
1461
1462 if(((wtcap = near_capacity()) >= OVERLOADED
1463 || (wtcap > SLT_ENCUMBER &&
1464 (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
1465 : (u.uhp < 10 && u.uhp != u.uhpmax))))
1466 && !Is_airlevel(&u.uz)) {
1467 if(wtcap < OVERLOADED) {
1468 You("don't have enough stamina to move.");
1469 exercise(A_CON, FALSE);
1470 } else
1471 You("collapse under your load.");
1472 nomul(0, 0);
1473 return;
1474 }
1475 if(u.uswallow) {
1476 u.dx = u.dy = 0;
1477 u.ux = x = u.ustuck->mx;
1478 u.uy = y = u.ustuck->my;
1479 mtmp = u.ustuck;
1480 } else {
1481 if (Is_airlevel(&u.uz) && rn2(4) &&
1482 !Levitation && !Flying) {
1483 switch(rn2(3)) {
1484 case 0:
1485 You("tumble in place.");
1486 exercise(A_DEX, FALSE);
1487 break;
1488 case 1:
1489 You_cant("control your movements very well."); break;
1490 case 2:
1491 pline("It's hard to walk in thin air.");
1492 exercise(A_DEX, TRUE);
1493 break;
1494 }
1495 return;
1496 }
1497
1498 /* check slippery ice */
1499 on_ice = !Levitation && is_ice(u.ux, u.uy);
1500 if (on_ice) {
1501 static int skates = 0;
1502 if (!skates) skates = find_skates();
1503 if ((uarmf && uarmf->otyp == skates)
1504 || resists_cold(&youmonst) || Flying
1505 || is_floater(youmonst.data) || is_clinger(youmonst.data)
1506 || is_whirly(youmonst.data))
1507 on_ice = FALSE;
1508 else if (!rn2(Cold_resistance ? 3 : 2)) {
1509 HFumbling |= FROMOUTSIDE;
1510 HFumbling &= ~TIMEOUT;
1511 HFumbling += 1; /* slip on next move */
1512 }
1513 }
1514 if (!on_ice && (HFumbling & FROMOUTSIDE))
1515 HFumbling &= ~FROMOUTSIDE;
1516
1517 x = u.ux + u.dx;
1518 y = u.uy + u.dy;
1519 if(Stunned || (Confusion && !rn2(5))) {
1520 register int tries = 0;
1521
1522 do {
1523 if(tries++ > 50) {
1524 nomul(0, 0);
1525 return;
1526 }
1527 confdir();
1528 x = u.ux + u.dx;
1529 y = u.uy + u.dy;
1530 } while(!isok(x, y) || bad_rock(youmonst.data, x, y));
1531 }
1532 /* turbulence might alter your actual destination */
1533 if (u.uinwater) {
1534 water_friction();
1535 if (!u.dx && !u.dy) {
1536 nomul(0, 0);
1537 return;
1538 }
1539 x = u.ux + u.dx;
1540 y = u.uy + u.dy;
1541 }
1542 if(!isok(x, y)) {
1543 nomul(0, 0);
1544 return;
1545 }
1546
1547 if (((trap = t_at(x, y)) && trap->tseen) ||
1548 (Blind && !Levitation && !Flying &&
1549 !is_clinger(youmonst.data) &&
1550 (is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) {
1551 if(flags.run >= 2) {
1552 nomul(0, 0);
1553 flags.move = 0;
1554 return;
1555 } else
1556 nomul(0, 0);
1557 }
1558
1559 if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) {
1560 if (distu(u.ustuck->mx, u.ustuck->my) > 2) {
1561 /* perhaps it fled (or was teleported or ... ) */
1562 u.ustuck = 0;
1563 } else if (sticks(youmonst.data)) {
1564 /* When polymorphed into a sticking monster,
1565 * u.ustuck means it's stuck to you, not you to it.
1566 */
1567 You("release %s.", mon_nam(u.ustuck));
1568 u.ustuck = 0;
1569 } else {
1570 /* If holder is asleep or paralyzed:
1571 * 37.5% chance of getting away,
1572 * 12.5% chance of waking/releasing it;
1573 * otherwise:
1574 * 7.5% chance of getting away.
1575 * [strength ought to be a factor]
1576 * If holder is tame and there is no conflict,
1577 * guaranteed escape.
1578 */
1579 switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
1580 case 0: case 1: case 2:
1581 pull_free:
1582 You("pull free from %s.", mon_nam(u.ustuck));
1583 u.ustuck = 0;
1584 break;
1585 case 3:
1586 if (!u.ustuck->mcanmove) {
1587 /* it's free to move on next turn */
1588 u.ustuck->mfrozen = 1;
1589 u.ustuck->msleeping = 0;
1590 }
1591 /*FALLTHRU*/
1592 default:
1593 if (u.ustuck->mtame &&
1594 !Conflict && !u.ustuck->mconf)
1595 goto pull_free;
1596 You("cannot escape from %s!", mon_nam(u.ustuck));
1597 nomul(0, 0);
1598 return;
1599 }
1600 }
1601 }
1602
1603 mtmp = m_at(x,y);
1604 if (mtmp && !is_safepet(mtmp)) {
1605 /* Don't attack if you're running, and can see it */
1606 /* It's fine to displace pets, though */
1607 /* We should never get here if forcefight */
1608 if (flags.run &&
1609 ((!Blind && mon_visible(mtmp) &&
1610 ((mtmp->m_ap_type != M_AP_FURNITURE &&
1611 mtmp->m_ap_type != M_AP_OBJECT) ||
1612 Protection_from_shape_changers)) ||
1613 sensemon(mtmp))) {
1614 nomul(0, 0);
1615 flags.move = 0;
1616 autoexplore_msg(Monnam(mtmp), -1);
1617 return;
1618 }
1619 }
1620 }
1621
1622 u.ux0 = u.ux;
1623 u.uy0 = u.uy;
1624 bhitpos.x = x;
1625 bhitpos.y = y;
1626 tmpr = &levl[x][y];
1627
1628 /* attack monster */
1629 if(mtmp) {
1630 /* don't stop travel when displacing pets; if the
1631 displace fails for some reason, attack() in uhitm.c
1632 will stop travel rather than domove */
1633 if (!is_safepet(mtmp) || flags.forcefight) nomul(0, 0);
1634 /* only attack if we know it's there */
1635 /* or if we used the 'F' command to fight blindly */
1636 /* or if it hides_under, in which case we call attack() to print
1637 * the Wait! message.
1638 * This is different from ceiling hiders, who aren't handled in
1639 * attack().
1640 */
1641
1642 /* If they used a 'm' command, trying to move onto a monster
1643 * prints the below message and wastes a turn. The exception is
1644 * if the monster is unseen and the player doesn't remember an
1645 * invisible monster--then, we fall through to attack() and
1646 * attack_check(), which still wastes a turn, but prints a
1647 * different message and makes the player remember the monster. */
1648 if(flags.nopick && !flags.travel &&
1649 (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))){
1650 if(mtmp->m_ap_type && !Protection_from_shape_changers
1651 && !sensemon(mtmp))
1652 stumble_onto_mimic(mtmp);
1653 else if (mtmp->mpeaceful && !Hallucination)
1654 pline("Pardon me, %s.", m_monnam(mtmp));
1655 else
1656 You("move right into %s.", mon_nam(mtmp));
1657 return;
1658 }
1659 if(flags.forcefight || !mtmp->mundetected || sensemon(mtmp) ||
1660 ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL) &&
1661 !is_safepet(mtmp))){
1662 gethungry();
1663 if(wtcap >= HVY_ENCUMBER && moves%3) {
1664 if (Upolyd && u.mh > 1) {
1665 u.mh--;
1666 } else if (!Upolyd && u.uhp > 1) {
1667 u.uhp--;
1668 } else {
1669 You("pass out from exertion!");
1670 exercise(A_CON, FALSE);
1671 fall_asleep(-10, FALSE);
1672 }
1673 }
1674 if(multi < 0) return; /* we just fainted */
1675
1676 /* try to attack; note that it might evade */
1677 /* also, we don't attack tame when _safepet_ */
1678 if(attack(mtmp)) return;
1679 }
1680 }
1681
1682 if (flags.forcefight && levl[x][y].typ == IRONBARS && uwep) {
1683 struct obj *obj = uwep;
1684 if (breaktest(obj)) {
1685 if (obj->quan > 1L)
1686 obj = splitobj(obj, 1L);
1687 else
1688 setuwep((struct obj *)0);
1689 freeinv(obj);
1690 }
1691 hit_bars(&obj, u.ux, u.uy, x, y, TRUE, TRUE);
1692 return;
1693 }
1694
1695 /* specifying 'F' with no monster wastes a turn */
1696 if (flags.forcefight ||
1697 /* remembered an 'I' && didn't use a move command */
1698 (glyph_is_invisible(levl[x][y].glyph) && !flags.nopick)) {
1699 boolean expl = (Upolyd && attacktype(youmonst.data, AT_EXPL));
1700 char buf[BUFSZ];
1701 Sprintf(buf,"a vacant spot on the %s", surface(x,y));
1702 You("%s %s.",
1703 expl ? "explode at" : "attack",
1704 !Underwater ? "thin air" :
1705 is_pool(x,y) ? "empty water" : buf);
1706 unmap_object(x, y); /* known empty -- remove 'I' if present */
1707 newsym(x, y);
1708 nomul(0, 0);
1709 if (expl) {
1710 u.mh = -1; /* dead in the current form */
1711 rehumanize();
1712 }
1713 return;
1714 }
1715 if (glyph_is_invisible(levl[x][y].glyph)) {
1716 unmap_object(x, y);
1717 newsym(x, y);
1718 }
1719 /* not attacking an animal, so we try to move */
1720 #ifdef STEED
1721 if (u.usteed && !u.usteed->mcanmove && (u.dx || u.dy)) {
1722 pline("%s won't move!", upstart(y_monnam(u.usteed)));
1723 nomul(0, 0);
1724 return;
1725 } else
1726 #endif
1727 if(!youmonst.data->mmove) {
1728 You("are rooted %s.",
1729 Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) ?
1730 "in place" : "to the ground");
1731 nomul(0, 0);
1732 return;
1733 }
1734
1735 /* warn player before walking into known traps */
1736 if (iflags.paranoid_trap &&
1737 ((trap = t_at(x, y)) && trap->tseen)) {
1738 char qbuf[BUFSZ];
1739 Sprintf(qbuf,"Do you really want to %s into that %s?",
1740 locomotion(youmonst.data, "step"),
1741 defsyms[trap_to_defsym(trap->ttyp)].explanation);
1742 if (yn(qbuf) != 'y') {
1743 nomul(0, 0);
1744 flags.move = 0;
1745 return;
1746 }
1747 }
1748 if(u.ufeetfrozen) {
1749 --u.ufeetfrozen;
1750 You("are stuck in ice.");
1751 nomul(0, 0);
1752 return;
1753 }
1754 if(u.utrap) {
1755 if(u.utraptype == TT_PIT) {
1756 if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) {
1757 Your("%s gets stuck in a crevice.", body_part(LEG));
1758 display_nhwindow(WIN_MESSAGE, FALSE);
1759 clear_nhwindow(WIN_MESSAGE);
1760 You("free your %s.", body_part(LEG));
1761 } else if (!(--u.utrap)) {
1762 You("%s to the edge of the pit.",
1763 (In_sokoban(&u.uz) && Levitation) ?
1764 "struggle against the air currents and float" :
1765 #ifdef STEED
1766 u.usteed ? "ride" :
1767 #endif
1768 "crawl");
1769 fill_pit(u.ux, u.uy);
1770 vision_full_recalc = 1; /* vision limits change */
1771 } else if (flags.verbose) {
1772 #ifdef STEED
1773 if (u.usteed)
1774 Norep("%s is still in a pit.",
1775 upstart(y_monnam(u.usteed)));
1776 else
1777 #endif
1778 Norep( (Hallucination && !rn2(5)) ?
1779 "You've fallen, and you can't get up." :
1780 "You are still in a pit." );
1781 }
1782 } else if (u.utraptype == TT_LAVA) {
1783 struggle_sub("stuck in the lava");
1784 if(!is_lava(x,y)) {
1785 u.utrap--;
1786 if((u.utrap & 0xff) == 0) {
1787 #ifdef STEED
1788 if (u.usteed)
1789 You("lead %s to the edge of the lava.",
1790 y_monnam(u.usteed));
1791 else
1792 #endif
1793 You("pull yourself to the edge of the lava.");
1794 u.utrap = 0;
1795 }
1796 }
1797 u.umoved = TRUE;
1798 } else if (u.utraptype == TT_WEB) {
1799 if(uwep && uwep->oartifact == ART_STING) {
1800 u.utrap = 0;
1801 pline("Sting cuts through the web!");
1802 return;
1803 }
1804 if(--u.utrap) {
1805 struggle_sub("stuck to the web");
1806 } else {
1807 #ifdef STEED
1808 if (u.usteed)
1809 pline("%s breaks out of the web.",
1810 upstart(y_monnam(u.usteed)));
1811 else
1812 #endif
1813 You("disentangle yourself.");
1814 }
1815 } else if (u.utraptype == TT_SWAMP) {
1816 if(--u.utrap) {
1817 struggle_sub("stuck in the mud");
1818 }
1819 } else if (u.utraptype == TT_INFLOOR) {
1820 if(--u.utrap) {
1821 if(flags.verbose) {
1822 predicament = "stuck in the";
1823 #ifdef STEED
1824 if (u.usteed)
1825 Norep("%s is %s %s.",
1826 upstart(y_monnam(u.usteed)),
1827 predicament, surface(u.ux, u.uy));
1828 else
1829 #endif
1830 Norep("You are %s %s.", predicament,
1831 surface(u.ux, u.uy));
1832 }
1833 } else {
1834 #ifdef STEED
1835 if (u.usteed)
1836 pline("%s finally wiggles free.",
1837 upstart(y_monnam(u.usteed)));
1838 else
1839 #endif
1840 You("finally wiggle free.");
1841 }
1842 } else if (u.utraptype == TT_ICE) {
1843 if (--u.utrap) {
1844 if (flags.verbose)
1845 Norep("You are hindered by the ice.");
1846 } else {
1847 if (u.usteed)
1848 pline("You and %s break out of the ice!",
1849 y_monnam(u.usteed));
1850 else
1851 pline("You break out of the ice!");
1852 }
1853 } else {
1854 if(flags.verbose) {
1855 predicament = "caught in a bear trap";
1856 #ifdef STEED
1857 if (u.usteed)
1858 Norep("%s is %s.", upstart(y_monnam(u.usteed)),
1859 predicament);
1860 else
1861 #endif
1862 Norep("You are %s.", predicament);
1863 }
1864 if((u.dx && u.dy) || !rn2(5)) u.utrap--;
1865 }
1866 return;
1867 }
1868
1869 if (!test_move(u.ux, u.uy, x-u.ux, y-u.uy, DO_MOVE)) {
1870 if (!door_opened) {
1871 flags.move = 0;
1872 nomul(0, 0);
1873 } else {
1874 door_opened = 0;
1875 }
1876 return;
1877 }
1878
1879 /* If no 'm' prefix, paranoid ask dangerous moves */
1880 if (!flags.nopick || flags.run) {
1881 known_wwalking = (uarmf && objects[uarmf->otyp].oc_name_known &&
1882 #ifdef STEED
1883 !u.usteed &&
1884 #endif
1885 uarmf->otyp == WATER_WALKING_BOOTS);
1886 known_lwalking = (known_wwalking && Fire_resistance &&
1887 uarmf->rknown && uarmf->oerodeproof);
1888 if (!Levitation && !Flying && !is_clinger(youmonst.data) &&
1889 !Stunned && !Confusion && levl[x][y].seenv &&
1890 ((is_pool(x, y) && !is_pool(u.ux, u.uy)) ||
1891 (is_swamp(x, y) && !is_swamp(u.ux, u.uy)) ||
1892 (is_lava(x, y) && !is_lava(u.ux, u.uy)))) {
1893 /* paranoid_water */
1894 if (is_pool(x, y) && iflags.paranoid_water && !known_wwalking) {
1895 if (paranoid_yn("Really enter the water?", iflags.paranoid_water) != 'y') {
1896 flags.move = 0;
1897 nomul(0, 0);
1898 return;
1899 }
1900 /* swamps are included in paranoid_water */
1901 } else if (is_swamp(x, y) && iflags.paranoid_water && !known_wwalking) {
1902 if (paranoid_yn("Really enter the muddy swamp?", iflags.paranoid_water) != 'y') {
1903 flags.move = 0;
1904 nomul(0, 0);
1905 return;
1906 }
1907 /* paranoid_lava */
1908 } else if (is_lava(x, y) && iflags.paranoid_lava && !known_lwalking) {
1909 if (paranoid_yn("Really walk into lava?", iflags.paranoid_lava) != 'y') {
1910 flags.move = 0;
1911 nomul(0, 0);
1912 return;
1913 }
1914 }
1915 }
1916 }
1917
1918 /* Move ball and chain. */
1919 if (Punished)
1920 if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy,
1921 &cause_delay, TRUE))
1922 return;
1923
1924 /* Check regions entering/leaving */
1925 if (!in_out_region(x,y))
1926 return;
1927
1928 /* now move the hero */
1929 mtmp = m_at(x, y);
1930 u.ux += u.dx;
1931 u.uy += u.dy;
1932 #ifdef STEED
1933 /* Move your steed, too */
1934 if (u.usteed) {
1935 u.usteed->mx = u.ux;
1936 u.usteed->my = u.uy;
1937 exercise_steed();
1938 }
1939 #endif
1940
1941 /*
1942 * If safepet at destination then move the pet to the hero's
1943 * previous location using the same conditions as in attack().
1944 * there are special extenuating circumstances:
1945 * (1) if the pet dies then your god angers,
1946 * (2) if the pet gets trapped then your god may disapprove,
1947 * (3) if the pet was already trapped and you attempt to free it
1948 * not only do you encounter the trap but you may frighten your
1949 * pet causing it to go wild! moral: don't abuse this privilege.
1950 *
1951 * Ceiling-hiding pets are skipped by this section of code, to
1952 * be caught by the normal falling-monster code.
1953 */
1954 if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) {
1955 /* if trapped, there's a chance the pet goes wild */
1956 if (mtmp->mtrapped) {
1957 if (!rn2(mtmp->mtame)) {
1958 mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
1959 if (mtmp->mleashed) m_unleash(mtmp, TRUE);
1960 growl(mtmp);
1961 } else {
1962 yelp(mtmp);
1963 }
1964 }
1965 mtmp->mundetected = 0;
1966 if (mtmp->m_ap_type) seemimic(mtmp);
1967 else if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my);
1968
1969 if (mtmp->mtrapped &&
1970 (trap = t_at(mtmp->mx, mtmp->my)) != 0 &&
1971 (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) &&
1972 sobj_at(BOULDER, trap->tx, trap->ty)) {
1973 /* can't swap places with pet pinned in a pit by a boulder */
1974 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1975 } else if (u.ux0 != x && u.uy0 != y &&
1976 bad_rock(mtmp->data, x, u.uy0) &&
1977 bad_rock(mtmp->data, u.ux0, y) &&
1978 (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) {
1979 /* can't swap places when pet won't fit thru the opening */
1980 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
1981 You("stop. %s won't fit through.", upstart(y_monnam(mtmp)));
1982 } else {
1983 char pnambuf[BUFSZ];
1984
1985 /* save its current description in case of polymorph */
1986 Strcpy(pnambuf, y_monnam(mtmp));
1987 mtmp->mtrapped = 0;
1988 remove_monster(x, y);
1989 place_monster(mtmp, u.ux0, u.uy0);
1990
1991 /* check for displacing it into pools and traps */
1992 switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) {
1993 case 0:
1994 You("%s %s.", mtmp->mtame ? "displaced" : "frightened",
1995 pnambuf);
1996 break;
1997 case 1: /* trapped */
1998 case 3: /* changed levels */
1999 /* there's already been a trap message, reinforce it */
2000 abuse_dog(mtmp);
2001 adjalign(-3);
2002 break;
2003 case 2:
2004 /* it may have drowned or died. that's no way to
2005 * treat a pet! your god gets angry.
2006 */
2007 if (rn2(4)) {
2008 You_feel("guilty about losing your pet like this.");
2009 u.ugangr++;
2010 adjalign(-15);
2011 }
2012
2013 /* you killed your pet by direct action.
2014 * minliquid and mintrap don't know to do this
2015 */
2016 violated(CONDUCT_PACIFISM);
2017 break;
2018 default:
2019 pline("that's strange, unknown mintrap result!");
2020 break;
2021 }
2022 }
2023 }
2024
2025 reset_occupations();
2026 if (flags.run) {
2027 if (flags.run < 8) {
2028 if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) ||
2029 IS_FURNITURE(tmpr->typ))
2030 nomul(0, 0);
2031 } else if (flags.travel && iflags.autoexplore) {
2032 /* autoexplore stoppers: being orthogonally
2033 adjacent to a boulder, being orthogonally adjacent
2034 to 3 or more walls; this logic could be incorrect
2035 when blind, but we check for that earlier; while
2036 not blind, we'll assume the hero knows about adjacent
2037 walls and boulders due to being able to see them */
2038 int wallcount = 0;
2039 if (isok(u.ux-1, u.uy ))
2040 wallcount += IS_ROCK(levl[u.ux-1][u.uy ].typ) +
2041 !!sobj_at(BOULDER, u.ux-1, u.uy ) * 3;
2042 if (isok(u.ux+1, u.uy ))
2043 wallcount += IS_ROCK(levl[u.ux+1][u.uy ].typ) +
2044 !!sobj_at(BOULDER, u.ux+1, u.uy ) * 3;
2045 if (isok(u.ux , u.uy-1))
2046 wallcount += IS_ROCK(levl[u.ux ][u.uy-1].typ) +
2047 !!sobj_at(BOULDER, u.ux , u.uy-1) * 3;
2048 if (isok(u.ux , u.uy+1))
2049 wallcount += IS_ROCK(levl[u.ux ][u.uy+1].typ) +
2050 !!sobj_at(BOULDER, u.ux , u.uy+1) * 3;
2051 if (wallcount >= 3) nomul(0, 0);
2052 }
2053 }
2054
2055 if (hides_under(youmonst.data))
2056 u.uundetected = OBJ_AT(u.ux, u.uy);
2057 else if (youmonst.data->mlet == S_EEL)
2058 u.uundetected = is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz);
2059 else if (u.dx || u.dy)
2060 u.uundetected = 0;
2061
2062 /*
2063 * Mimics (or whatever) become noticeable if they move and are
2064 * imitating something that doesn't move. We could extend this
2065 * to non-moving monsters...
2066 */
2067 if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT
2068 || youmonst.m_ap_type == M_AP_FURNITURE))
2069 youmonst.m_ap_type = M_AP_NOTHING;
2070
2071 check_leash(u.ux0,u.uy0);
2072
2073 if(u.ux0 != u.ux || u.uy0 != u.uy) {
2074 u.umoved = TRUE;
2075 /* Clean old position -- vision_recalc() will print our new one. */
2076 newsym(u.ux0,u.uy0);
2077 /* Since the hero has moved, adjust what can be seen/unseen. */
2078 vision_recalc(1); /* Do the work now in the recover time. */
2079 invocation_message();
2080 }
2081
2082 if (Punished) /* put back ball and chain */
2083 move_bc(0,bc_control,ballx,bally,chainx,chainy);
2084
2085 spoteffects(TRUE);
2086
2087 /* delay next move because of ball dragging */
2088 /* must come after we finished picking up, in spoteffects() */
2089 if (cause_delay) {
2090 nomul(-2, "dragging an iron ball");
2091 nomovemsg = "";
2092 }
2093
2094 if (flags.run && iflags.runmode != RUN_TPORT) {
2095 /* display every step or every 7th step depending upon mode */
2096 if (iflags.runmode != RUN_LEAP || !(moves % 7L)) {
2097 if (flags.time) flags.botl = 1;
2098 curs_on_u();
2099 delay_output();
2100 if (iflags.runmode == RUN_CRAWL) {
2101 delay_output();
2102 delay_output();
2103 delay_output();
2104 delay_output();
2105 }
2106 }
2107 }
2108 }
2109
2110 void
struggle_sub(predicament)2111 struggle_sub(predicament)
2112 const char *predicament;
2113 {
2114 if (flags.verbose) {
2115 #ifdef STEED
2116 if (u.usteed)
2117 Norep("%s is %s.", upstart(y_monnam(u.usteed)),
2118 predicament);
2119 else
2120 #endif
2121 Norep("You are %s.", predicament);
2122 }
2123 }
2124
2125 void
invocation_message()2126 invocation_message()
2127 {
2128 /* mark the square as stepped on, whether it's the VS or not;
2129 don't do so when blind, though, because it would misleadingly
2130 imply we'd properly explored that area */
2131 if (!Blind) levl[u.ux][u.uy].stepped_on = 1;
2132 /* a special clue-msg when near the Invocation position */
2133 if (Invocation_lev(&u.uz)) {
2134 char *strange_vibration;
2135 int dist_invocation_pos = distu(inv_pos.x, inv_pos.y);
2136
2137 /* different message depending on the distance to the vibrating square
2138 ..f..
2139 .www.
2140 fwswf
2141 .www.
2142 ..f..
2143 */
2144 if (dist_invocation_pos == 0) {
2145 strange_vibration = "strange vibration";
2146 } else if (dist_invocation_pos <= 2) {
2147 strange_vibration = "weak trembling";
2148 } else {
2149 strange_vibration = "faint trembling";
2150 }
2151
2152 /* within close proximity of the vibrating square */
2153 if (dist_invocation_pos <= 4 && !On_stairs(inv_pos.x, inv_pos.y)) {
2154 char buf[BUFSZ];
2155 struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION);
2156
2157 nomul(0, 0); /* stop running or travelling */
2158 #ifdef STEED
2159 if (u.usteed) Sprintf(buf, "beneath %s", y_monnam(u.usteed));
2160 else
2161 #endif
2162 if (Levitation || Flying) Strcpy(buf, "beneath you");
2163 else Sprintf(buf, "under your %s", makeplural(body_part(FOOT)));
2164
2165 You_feel("a %s %s.", strange_vibration, buf);
2166 /* only report if on the vibrating square */
2167 if (invocation_pos(u.ux, u.uy) &&
2168 otmp && otmp->spe == 7 && otmp->lamplit) {
2169 pline("%s %s!", The(xname(otmp)),
2170 Blind ? "throbs palpably" : "glows with a strange light");
2171 }
2172 }
2173 }
2174 }
2175
2176 static const char * const hallu_adverb[] = {
2177 "mildly", "mostly", "somewhat", "slightly", "probably", "massively", "extremely",
2178 "flagrantly", "flamboyantly", "supremely", "excessively", "truly", "terribly",
2179 "incredibly", "unbelievably", "obscenely", "insanely", "amazingly", "absolutely"
2180 };
2181
2182 void
wounds_message(mon)2183 wounds_message(mon)
2184 struct monst *mon;
2185 {
2186 if (mon_wounds(mon))
2187 pline("%s is %s.", Monnam(mon), mon_wounds(mon));
2188 }
2189
2190 char *
mon_wounds(mon)2191 mon_wounds(mon)
2192 struct monst *mon;
2193 {
2194 static char buf[BUFSZ];
2195 boolean wounded = ((!nonliving(mon->data) ||
2196 /* Zombies and mummies (but not skeletons) have flesh */
2197 ((mon->data->mlet == S_ZOMBIE && mon->data != &mons[PM_SKELETON])
2198 || mon->data->mlet == S_MUMMY || mon->data->mlet == S_VAMPIRE
2199 || mon->data == &mons[PM_FLESH_GOLEM]))
2200 && !vegetarian(mon->data));
2201
2202 /* Healers are able to detect wounds by sight */
2203 if (!(canseemon(mon) || (u.ustuck == mon && u.uswallow && !Blind))
2204 || !Role_if(PM_HEALER))
2205 return (char *)0;
2206 if (mon->mhp == mon->mhpmax || mon->mhp < 1)
2207 return (char *)0;
2208 if (!Hallucination && mon->mhp <= mon->mhpmax / 6) {
2209 Sprintf(buf,"almost ");
2210 strcat(buf, nonliving(mon->data) ? "destroyed" : "dead");
2211 } else {
2212 if (Hallucination) {
2213 Sprintf(buf, "%s", hallu_adverb[rn2(SIZE(hallu_adverb))]);
2214 strcat(buf," ");
2215 }
2216 else if (mon->mhp <= mon->mhpmax / 4)
2217 Sprintf(buf,"horribly ");
2218 else if (mon->mhp <= mon->mhpmax / 3)
2219 Sprintf(buf,"heavily ");
2220 else if (mon->mhp <= 3 * mon->mhpmax / 4)
2221 Sprintf(buf,"moderately ");
2222 else
2223 Sprintf(buf,"lightly ");
2224 strcat(buf, wounded || (Hallucination && rn2(2)) ? "wounded" : "damaged");
2225 }
2226 return buf;
2227 }
2228
2229 void
spoteffects(pick)2230 spoteffects(pick)
2231 boolean pick;
2232 {
2233 register struct monst *mtmp;
2234
2235 if(u.uinwater) {
2236 int was_underwater;
2237
2238 if (!is_pool(u.ux,u.uy)) {
2239 if (Is_waterlevel(&u.uz))
2240 You("pop into an air bubble.");
2241 else if (is_lava(u.ux, u.uy))
2242 You("leave the water..."); /* oops! */
2243 else if (is_swamp(u.ux, u.uy))
2244 You("are on shallows.");
2245 else
2246 You("are on solid %s again.",
2247 is_ice(u.ux, u.uy) ? "ice" : "land");
2248 }
2249 else if (Is_waterlevel(&u.uz))
2250 goto stillinwater;
2251 else if (Levitation)
2252 You("pop out of the water like a cork!");
2253 else if (Flying)
2254 You("fly out of the water.");
2255 else if (Wwalking)
2256 You("slowly rise above the surface.");
2257 else
2258 goto stillinwater;
2259 was_underwater = Underwater && !Is_waterlevel(&u.uz);
2260 u.uinwater = 0; /* leave the water */
2261 if (was_underwater) { /* restore vision */
2262 docrt();
2263 vision_full_recalc = 1;
2264 }
2265 }
2266 stillinwater:;
2267 if (u.utraptype == TT_SWAMP) {
2268 if (!is_swamp(u.ux, u.uy)) {
2269 if (is_lava(u.ux, u.uy) || is_pool(u.ux, u.uy))
2270 You("get out of the mud..."); /* oops! */
2271 else
2272 You("are on solid %s again.",
2273 is_ice(u.ux, u.uy) ? "ice" : "land");
2274 }
2275 else if (Levitation)
2276 You("rise out of the swamp.");
2277 else if (Flying)
2278 You("fly out of the swamp.");
2279 else if (Wwalking)
2280 You("slowly rise above the muddy water.");
2281 else goto stillinswamp;
2282 u.utrap = 0;
2283 u.utraptype = 0;
2284 }
2285 stillinswamp:
2286 if (!Levitation && !u.ustuck && !Flying) {
2287 /* limit recursive calls through teleds() */
2288 if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy) ||
2289 is_swamp(u.ux, u.uy)) {
2290 #ifdef STEED
2291 if (u.usteed && !is_flyer(u.usteed->data) &&
2292 !is_floater(u.usteed->data) &&
2293 !is_clinger(u.usteed->data)) {
2294 dismount_steed(Underwater ?
2295 DISMOUNT_FELL : DISMOUNT_GENERIC);
2296 /* dismount_steed() -> float_down() -> pickup() */
2297 if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz))
2298 pick = FALSE;
2299 } else
2300 #endif
2301 if (is_lava(u.ux, u.uy)) {
2302 if (lava_effects()) return;
2303 } else if (is_swamp(u.ux, u.uy)) {
2304 if (swamp_effects()) return;
2305 } else if (!Wwalking && drown())
2306 return;
2307 }
2308 }
2309 check_special_room(FALSE);
2310 #ifdef SINKS
2311 if(IS_SINK(levl[u.ux][u.uy].typ) && Levitation)
2312 dosinkfall();
2313 #endif
2314 if (!in_steed_dismounting) { /* if dismounting, we'll check again later */
2315 struct trap *trap = t_at(u.ux, u.uy);
2316 boolean pit;
2317 pit = (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT));
2318 if (trap && pit)
2319 dotrap(trap, 0); /* fall into pit */
2320 if (pick) (void) pickup(1);
2321 if (trap && !pit)
2322 dotrap(trap, 0); /* fall into arrow trap, etc. */
2323 }
2324 if((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
2325 mtmp->mundetected = mtmp->msleeping = 0;
2326 switch(mtmp->data->mlet) {
2327 case S_PIERCER:
2328 pline("%s suddenly drops from the %s!",
2329 Amonnam(mtmp), ceiling(u.ux,u.uy));
2330 if(mtmp->mtame) /* jumps to greet you, not attack */
2331 ;
2332 else if(uarmh && is_metallic(uarmh))
2333 pline("Its blow glances off your helmet.");
2334 else if (u.uac + 3 <= rnd(20))
2335 You("are almost hit by %s!",
2336 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2337 else {
2338 int dmg;
2339 You("are hit by %s!",
2340 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
2341 dmg = d(4,6);
2342 if(Half_physical_damage) dmg = (dmg+1) / 2;
2343 mdamageu(mtmp, dmg);
2344 }
2345 break;
2346 default: /* monster surprises you. */
2347 if(mtmp->mtame)
2348 pline("%s jumps near you from the %s.",
2349 Amonnam(mtmp), ceiling(u.ux,u.uy));
2350 else if(mtmp->mpeaceful) {
2351 You("surprise %s!",
2352 Blind && !sensemon(mtmp) ?
2353 something : a_monnam(mtmp));
2354 mtmp->mpeaceful = 0;
2355 } else
2356 pline("%s attacks you by surprise!",
2357 Amonnam(mtmp));
2358 break;
2359 }
2360 mnexto(mtmp); /* have to move the monster */
2361 }
2362 }
2363
2364 STATIC_OVL boolean
monstinroom(mdat,roomno)2365 monstinroom(mdat,roomno)
2366 struct permonst *mdat;
2367 int roomno;
2368 {
2369 register struct monst *mtmp;
2370
2371 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
2372 if(!DEADMONSTER(mtmp) && mtmp->data == mdat &&
2373 index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET))
2374 return(TRUE);
2375 return(FALSE);
2376 }
2377
2378 char *
in_rooms(x,y,typewanted)2379 in_rooms(x, y, typewanted)
2380 register xchar x, y;
2381 register int typewanted;
2382 {
2383 static char buf[5];
2384 char rno, *ptr = &buf[4];
2385 int typefound, min_x, min_y, max_x, max_y_offset, step;
2386 register struct rm *lev;
2387
2388 #define goodtype(rno) (!typewanted || \
2389 ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) || \
2390 ((typewanted == SHOPBASE) && (typefound > SHOPBASE))) \
2391
2392 switch (rno = levl[x][y].roomno) {
2393 case NO_ROOM:
2394 return(ptr);
2395 case SHARED:
2396 step = 2;
2397 break;
2398 case SHARED_PLUS:
2399 step = 1;
2400 break;
2401 default: /* i.e. a regular room # */
2402 if (goodtype(rno))
2403 *(--ptr) = rno;
2404 return(ptr);
2405 }
2406
2407 min_x = x - 1;
2408 max_x = x + 1;
2409 if (x < 1)
2410 min_x += step;
2411 else
2412 if (x >= COLNO)
2413 max_x -= step;
2414
2415 min_y = y - 1;
2416 max_y_offset = 2;
2417 if (min_y < 0) {
2418 min_y += step;
2419 max_y_offset -= step;
2420 } else
2421 if ((min_y + max_y_offset) >= ROWNO)
2422 max_y_offset -= step;
2423
2424 for (x = min_x; x <= max_x; x += step) {
2425 lev = &levl[x][min_y];
2426 y = 0;
2427 if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
2428 !index(ptr, rno) && goodtype(rno))
2429 *(--ptr) = rno;
2430 y += step;
2431 if (y > max_y_offset)
2432 continue;
2433 if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
2434 !index(ptr, rno) && goodtype(rno))
2435 *(--ptr) = rno;
2436 y += step;
2437 if (y > max_y_offset)
2438 continue;
2439 if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
2440 !index(ptr, rno) && goodtype(rno))
2441 *(--ptr) = rno;
2442 }
2443 return(ptr);
2444 }
2445
2446 /* is (x,y) in a town? */
2447 boolean
in_town(x,y)2448 in_town(x, y)
2449 register int x, y;
2450 {
2451 s_level *slev = Is_special(&u.uz);
2452 register struct mkroom *sroom;
2453 boolean has_subrooms = FALSE;
2454
2455 if (!slev || !slev->flags.town) return FALSE;
2456
2457 /*
2458 * See if (x,y) is in a room with subrooms, if so, assume it's the
2459 * town. If there are no subrooms, the whole level is in town.
2460 */
2461 for (sroom = &rooms[0]; sroom->hx > 0; sroom++) {
2462 if (sroom->nsubrooms > 0) {
2463 has_subrooms = TRUE;
2464 if (inside_room(sroom, x, y)) return TRUE;
2465 }
2466 }
2467
2468 return !has_subrooms;
2469 }
2470
2471 STATIC_OVL void
move_update(newlev)2472 move_update(newlev)
2473 register boolean newlev;
2474 {
2475 char *ptr1, *ptr2, *ptr3, *ptr4;
2476
2477 Strcpy(u.urooms0, u.urooms);
2478 Strcpy(u.ushops0, u.ushops);
2479 if (newlev) {
2480 u.urooms[0] = '\0';
2481 u.uentered[0] = '\0';
2482 u.ushops[0] = '\0';
2483 u.ushops_entered[0] = '\0';
2484 Strcpy(u.ushops_left, u.ushops0);
2485 return;
2486 }
2487 Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0));
2488
2489 for (ptr1 = &u.urooms[0],
2490 ptr2 = &u.uentered[0],
2491 ptr3 = &u.ushops[0],
2492 ptr4 = &u.ushops_entered[0];
2493 *ptr1; ptr1++) {
2494 if (!index(u.urooms0, *ptr1))
2495 *(ptr2++) = *ptr1;
2496 if (IS_SHOP(*ptr1 - ROOMOFFSET)) {
2497 *(ptr3++) = *ptr1;
2498 if (!index(u.ushops0, *ptr1))
2499 *(ptr4++) = *ptr1;
2500 }
2501 }
2502 *ptr2 = '\0';
2503 *ptr3 = '\0';
2504 *ptr4 = '\0';
2505
2506 /* filter u.ushops0 -> u.ushops_left */
2507 for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++)
2508 if (!index(u.ushops, *ptr1))
2509 *(ptr2++) = *ptr1;
2510 *ptr2 = '\0';
2511 }
2512
2513 void
check_special_room(newlev)2514 check_special_room(newlev)
2515 register boolean newlev;
2516 {
2517 register struct monst *mtmp;
2518 char *ptr;
2519
2520 move_update(newlev);
2521
2522 if (*u.ushops0)
2523 u_left_shop(u.ushops_left, newlev);
2524
2525 if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */
2526 return; /* no entrance messages necessary */
2527
2528 /* Did we just enter a shop? */
2529 if (*u.ushops_entered)
2530 u_entered_shop(u.ushops_entered);
2531
2532 for (ptr = &u.uentered[0]; *ptr; ptr++) {
2533 register int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype;
2534
2535 /* Did we just enter some other special room? */
2536 /* vault.c insists that a vault remain a VAULT,
2537 * and temples should remain TEMPLEs,
2538 * but everything else gives a message only the first time */
2539 switch (rt) {
2540 case ZOO:
2541 pline("Welcome to David's treasure zoo!");
2542 break;
2543 case GARDEN:
2544 if (Blind) pline_The("air here smells nice and fresh!");
2545 else You("enter a beautiful garden.");
2546 break;
2547 case SWAMP:
2548 pline("It %s rather %s down here.",
2549 Blind ? "feels" : "looks",
2550 Blind ? "humid" : "muddy");
2551 break;
2552 case COURT:
2553 You("enter an opulent throne room!");
2554 break;
2555 case LEPREHALL:
2556 You("enter a leprechaun hall!");
2557 break;
2558 case MORGUE:
2559 if(midnight()) {
2560 const char *run = locomotion(youmonst.data, "Run");
2561 pline("%s away! %s away!", run, run);
2562 } else
2563 You("have an uncanny feeling...");
2564 break;
2565 case BEEHIVE:
2566 You("enter a giant beehive!");
2567 break;
2568 case LEMUREPIT:
2569 You("enter a pit of screaming lemures!");
2570 break;
2571 case COCKNEST:
2572 You("enter a disgusting nest!");
2573 break;
2574 case ARMORY:
2575 You("enter a dilapidated armory.");
2576 break;
2577 case ANTHOLE:
2578 You("enter an anthole!");
2579 break;
2580 case BARRACKS:
2581 if(monstinroom(&mons[PM_SOLDIER], roomno) ||
2582 monstinroom(&mons[PM_SERGEANT], roomno) ||
2583 monstinroom(&mons[PM_LIEUTENANT], roomno) ||
2584 monstinroom(&mons[PM_CAPTAIN], roomno))
2585 You("enter a military barracks!");
2586 else
2587 You("enter an abandoned barracks.");
2588 break;
2589 case DELPHI:
2590 if(monstinroom(&mons[PM_ORACLE], roomno))
2591 verbalize("%s, %s, welcome to Delphi!",
2592 Hello((struct monst *) 0), plname);
2593 check_tutorial_message(QT_T_ORACLE);
2594 break;
2595 #ifdef BLACKMARKET
2596 case BLACKFOYER:
2597 if(monstinroom(&mons[PM_ONE_EYED_SAM], roomno)) {
2598 verbalize("%s, %s! Welcome to One-eyed Sam's black market!",
2599 Hello((struct monst *)0), plname);
2600 verbalize("Please have a look around, but don't even think about stealing anything.");
2601 }
2602 break;
2603 #endif /* BLACKMARKET */
2604 case TEMPLE:
2605 intemple(roomno + ROOMOFFSET);
2606 /* fall through */
2607 default:
2608 rt = 0;
2609 }
2610
2611 if (rt != 0) {
2612 rooms[roomno].rtype = OROOM;
2613 if (!search_special(rt)) {
2614 /* No more room of that type */
2615 switch(rt) {
2616 case COURT:
2617 level.flags.has_court = 0;
2618 break;
2619 case GARDEN:
2620 level.flags.has_garden = 0;
2621 break;
2622 case SWAMP:
2623 level.flags.has_swamp = 0;
2624 break;
2625 case MORGUE:
2626 level.flags.has_morgue = 0;
2627 break;
2628 case ZOO:
2629 level.flags.has_zoo = 0;
2630 break;
2631 case BARRACKS:
2632 level.flags.has_barracks = 0;
2633 break;
2634 case TEMPLE:
2635 level.flags.has_temple = 0;
2636 break;
2637 case BEEHIVE:
2638 level.flags.has_beehive = 0;
2639 break;
2640 case LEMUREPIT:
2641 level.flags.has_lemurepit = 0;
2642 break;
2643 }
2644 }
2645 if (rt == COURT || rt == SWAMP || rt == MORGUE ||
2646 rt == ZOO || rt == GARDEN)
2647 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
2648 if (!DEADMONSTER(mtmp) && !Stealth && !rn2(3)) mtmp->msleeping = 0;
2649 }
2650 }
2651
2652 return;
2653 }
2654
2655 int
dopickup()2656 dopickup()
2657 {
2658 int count;
2659 struct trap *traphere = t_at(u.ux, u.uy);
2660 /* awful kludge to work around parse()'s pre-decrement */
2661 count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0;
2662 multi = 0; /* always reset */
2663 /* uswallow case added by GAN 01/29/87 */
2664 if(u.uswallow) {
2665 if (!u.ustuck->minvent) {
2666 if (is_animal(u.ustuck->data)) {
2667 You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
2668 pline("But it's kind of slimy, so you drop it.");
2669 } else
2670 You("don't %s anything in here to pick up.",
2671 Blind ? "feel" : "see");
2672 return(1);
2673 } else {
2674 int tmpcount = -count;
2675 return loot_mon(u.ustuck, &tmpcount, (boolean *)0);
2676 }
2677 }
2678 if(is_pool(u.ux, u.uy)) {
2679 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2680 || (Flying && !Breathless)) {
2681 You("cannot dive into the water to pick things up.");
2682 return(0);
2683 } else if (!Underwater) {
2684 You_cant("even see the bottom, let alone pick up %s.",
2685 something);
2686 return(0);
2687 }
2688 }
2689 if (is_lava(u.ux, u.uy)) {
2690 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
2691 || (Flying && !Breathless)) {
2692 You_cant("reach the bottom to pick things up.");
2693 return(0);
2694 } else if (!likes_lava(youmonst.data)) {
2695 You("would burn to a crisp trying to pick things up.");
2696 return(0);
2697 }
2698 }
2699 if(!OBJ_AT(u.ux, u.uy)) {
2700 register struct rm *lev = &levl[u.ux][u.uy];
2701
2702 if (IS_THRONE(lev->typ))
2703 pline("It must weigh%s a ton!",
2704 lev->looted ? " almost" : "");
2705 #ifdef SINKS
2706 else if (IS_SINK(lev->typ))
2707 pline_The("plumbing connects it to the floor.");
2708 #endif
2709 else if (IS_GRAVE(lev->typ))
2710 You("don't need a gravestone. Yet.");
2711 else if (IS_FOUNTAIN(lev->typ))
2712 You("could drink the water...");
2713 else if (IS_DOOR(lev->typ) && (lev->doormask & D_ISOPEN))
2714 pline("It won't come off the hinges.");
2715 else There("is nothing here to pick up.");
2716 return(0);
2717 }
2718 if (!can_reach_floor()) {
2719 #ifdef STEED
2720 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
2721 You("aren't skilled enough to reach from %s.",
2722 y_monnam(u.usteed));
2723 else
2724 #endif
2725 You("cannot reach the %s.", surface(u.ux,u.uy));
2726 return(0);
2727 }
2728
2729 if (traphere && traphere->tseen) {
2730 /* Allow pickup from holes and trap doors that you escaped from
2731 * because that stuff is teetering on the edge just like you, but
2732 * not pits, because there is an elevation discrepancy with stuff
2733 * in pits.
2734 */
2735 if ((traphere->ttyp == PIT || traphere->ttyp == SPIKED_PIT) &&
2736 (!u.utrap || (u.utrap && u.utraptype != TT_PIT))) {
2737 You("cannot reach the bottom of the pit.");
2738 return(0);
2739 }
2740 }
2741
2742 return (pickup(-count));
2743 }
2744
2745 /* stop running if we see something interesting */
2746 /* turn around a corner if that is the only way we can proceed */
2747 /* do not turn left or right twice */
2748 void
lookaround()2749 lookaround()
2750 {
2751 register int x, y, i, x0 = 0, y0 = 0, m0 = 1, i0 = 9;
2752 register int corrct = 0, noturn = 0;
2753 register struct monst *mtmp;
2754 register struct trap *trap;
2755
2756 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
2757 /* they polymorphed while in the middle of a long move. */
2758 if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
2759 nomul(0, 0);
2760 return;
2761 }
2762
2763 /* Having a hostile monster in LOS stops running after one turn,
2764 unless sessile or shift-moving. We don't count detection just
2765 via telepathy, as it might be over the other end of the level,
2766 unless the monster is within 5 spaces of us. */
2767 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
2768 if ((canseemon(mtmp) ||
2769 (canspotmon(mtmp) &&
2770 distmin(u.ux, u.uy, mtmp->mx, mtmp->my) <= 5)) &&
2771 !mtmp->mpeaceful && !mtmp->mtame &&
2772 mtmp->data->mmove && flags.run != 1) {
2773 nomul(0, 0);
2774 return;
2775 }
2776 }
2777
2778 if(Blind || flags.run == 0) return;
2779 for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) {
2780 if(!isok(x,y)) continue;
2781
2782 if(u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy) continue;
2783
2784 if(x == u.ux && y == u.uy) continue;
2785
2786 if((mtmp = m_at(x,y)) &&
2787 mtmp->m_ap_type != M_AP_FURNITURE &&
2788 mtmp->m_ap_type != M_AP_OBJECT &&
2789 (!mtmp->minvis || See_invisible) && !mtmp->mundetected) {
2790 if((flags.run != 1 && !mtmp->mtame)
2791 || (x == u.ux+u.dx && y == u.uy+u.dy && !flags.travel))
2792 goto stop;
2793 }
2794
2795 if (levl[x][y].typ == STONE) continue;
2796 if (x == u.ux-u.dx && y == u.uy-u.dy) continue;
2797
2798 if (IS_ROCK(levl[x][y].typ) || (levl[x][y].typ == ROOM) ||
2799 IS_AIR(levl[x][y].typ))
2800 continue;
2801 else if (closed_door(x,y) ||
2802 (mtmp && mtmp->m_ap_type == M_AP_FURNITURE &&
2803 (mtmp->mappearance == S_hcdoor ||
2804 mtmp->mappearance == S_vcdoor))) {
2805 if(x != u.ux && y != u.uy) continue;
2806 if(flags.run != 1) goto stop;
2807 goto bcorr;
2808 } else if (levl[x][y].typ == CORR) {
2809 bcorr:
2810 if(levl[u.ux][u.uy].typ != ROOM) {
2811 if(flags.run == 1 || flags.run == 3 || flags.run == 8) {
2812 i = dist2(x,y,u.ux+u.dx,u.uy+u.dy);
2813 if(i > 2) continue;
2814 if(corrct == 1 && dist2(x,y,x0,y0) != 1)
2815 noturn = 1;
2816 if(i < i0) {
2817 i0 = i;
2818 x0 = x;
2819 y0 = y;
2820 m0 = mtmp ? 1 : 0;
2821 }
2822 }
2823 corrct++;
2824 }
2825 continue;
2826 } else if ((trap = t_at(x,y)) && trap->tseen) {
2827 if(flags.run == 1) goto bcorr; /* if you must */
2828 if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop;
2829 continue;
2830 } else if (is_pool(x,y) || is_lava(x,y)) {
2831 /* water and lava only stop you if directly in front, and stop
2832 * you even if you are running
2833 */
2834 if(!Levitation && !Flying && !is_clinger(youmonst.data) &&
2835 x == u.ux+u.dx && y == u.uy+u.dy)
2836 /* No Wwalking check; otherwise they'd be able
2837 * to test boots by trying to SHIFT-direction
2838 * into a pool and seeing if the game allowed it
2839 */
2840 goto stop;
2841 continue;
2842 } else { /* e.g. objects or trap or stairs */
2843 if(flags.run == 1) goto bcorr;
2844 if(flags.run == 8) continue;
2845 if(mtmp) continue; /* d */
2846 if(((x == u.ux - u.dx) && (y != u.uy + u.dy)) ||
2847 ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
2848 continue;
2849 }
2850 stop:
2851 nomul(0, 0);
2852 return;
2853 } /* end for loops */
2854
2855 if(corrct > 1 && flags.run == 2) goto stop;
2856 if((flags.run == 1 || flags.run == 3 || flags.run == 8) &&
2857 !noturn && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1)))
2858 {
2859 /* make sure that we do not turn too far */
2860 if(i0 == 2) {
2861 if(u.dx == y0-u.uy && u.dy == u.ux-x0)
2862 i = 2; /* straight turn right */
2863 else
2864 i = -2; /* straight turn left */
2865 } else if(u.dx && u.dy) {
2866 if((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy))
2867 i = -1; /* half turn left */
2868 else
2869 i = 1; /* half turn right */
2870 } else {
2871 if((x0-u.ux == y0-u.uy && !u.dy) || (x0-u.ux != y0-u.uy && u.dy))
2872 i = 1; /* half turn right */
2873 else
2874 i = -1; /* half turn left */
2875 }
2876
2877 i += u.last_str_turn;
2878 if(i <= 2 && i >= -2) {
2879 u.last_str_turn = i;
2880 u.dx = x0-u.ux;
2881 u.dy = y0-u.uy;
2882 }
2883 }
2884 }
2885
2886 /* something like lookaround, but we are not running */
2887 /* react only to monsters that might hit us */
2888 int
monster_nearby()2889 monster_nearby()
2890 {
2891 register int x,y;
2892 register struct monst *mtmp;
2893
2894 /* Also see the similar check in dochugw() in monmove.c */
2895 for(x = u.ux-1; x <= u.ux+1; x++)
2896 for(y = u.uy-1; y <= u.uy+1; y++) {
2897 if(!isok(x,y)) continue;
2898 if(x == u.ux && y == u.uy) continue;
2899 if((mtmp = m_at(x,y)) &&
2900 mtmp->m_ap_type != M_AP_FURNITURE &&
2901 mtmp->m_ap_type != M_AP_OBJECT &&
2902 (!mtmp->mpeaceful || Hallucination) &&
2903 (!is_hider(mtmp->data) || !mtmp->mundetected) &&
2904 !noattacks(mtmp->data) &&
2905 mtmp->mcanmove && !mtmp->msleeping && /* aplvax!jcn */
2906 !onscary(u.ux, u.uy, mtmp) &&
2907 canspotmon(mtmp))
2908 return(1);
2909 }
2910 return(0);
2911 }
2912
2913 void
nomul(nval,txt)2914 nomul(nval, txt)
2915 register int nval;
2916 const char *txt;
2917 {
2918 if(multi < nval) return; /* This is a bug fix by ab@unido */
2919 u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */
2920 u.usleep = 0;
2921 multi = nval;
2922 if (txt && txt[0])
2923 (void) strncpy(multi_txt, txt, BUFSZ);
2924 else
2925 (void) memset(multi_txt, 0, BUFSZ);
2926 flags.travel = iflags.travel1 = flags.mv = flags.run = 0;
2927 }
2928
2929 /* called when a non-movement, multi-turn action has completed */
2930 void
unmul(msg_override)2931 unmul(msg_override)
2932 const char *msg_override;
2933 {
2934 multi = 0; /* caller will usually have done this already */
2935 (void) memset(multi_txt, 0, BUFSZ);
2936 if (msg_override) nomovemsg = msg_override;
2937 else if (!nomovemsg) nomovemsg = You_can_move_again;
2938 if (*nomovemsg) pline("%s", nomovemsg);
2939 nomovemsg = 0;
2940 u.usleep = 0;
2941 if (afternmv) (*afternmv)();
2942 afternmv = 0;
2943 }
2944
2945 STATIC_OVL void
maybe_wail()2946 maybe_wail()
2947 {
2948 static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
2949 SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES,
2950 TELEPORT_CONTROL, STEALTH, FAST, INVIS };
2951
2952 if (moves <= wailmsg + 50) return;
2953
2954 wailmsg = moves;
2955 if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) {
2956 const char *who;
2957 int i, powercnt;
2958
2959 who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ?
2960 urole.name.m : "Elf";
2961 if (u.uhp == 1) {
2962 pline("%s is about to die.", who);
2963 } else {
2964 for (i = 0, powercnt = 0; i < SIZE(powers); ++i)
2965 if (u.uprops[powers[i]].intrinsic & INTRINSIC) ++powercnt;
2966
2967 pline(powercnt >= 4 ? "%s, all your powers will be lost..."
2968 : "%s, your life force is running out.", who);
2969 }
2970 } else {
2971 You_hear(u.uhp == 1 ? "the wailing of the Banshee..."
2972 : "the howling of the CwnAnnwn...");
2973 }
2974 }
2975
2976 /** Print the amount n of damage inflicted.
2977 * In contrast to Slash'Em, in UnNetHack the damage is only shown in
2978 * Wizard mode.
2979 */
2980 void
showdmg(n,you)2981 showdmg(n,you)
2982 int n; /**< amount of damage inflicted */
2983 boolean you; /**< true, if you are hit */
2984 {
2985 #ifdef WIZARD
2986 if (iflags.showdmg && wizard && n > 0) {
2987 if (you)
2988 pline("[%d pts.]", n);
2989 else
2990 pline("(%d pts.)", n);
2991 }
2992 #endif
2993 }
2994
2995 void
check_uhpmax()2996 check_uhpmax()
2997 {
2998 if (!heaven_or_hell_mode) return;
2999
3000 if (u.uhpmax > 1)
3001 u.uhpmax = 1;
3002 if (u.mhmax > 1)
3003 u.mhmax = 1;
3004 if (u.uhp > 1)
3005 u.uhp = 1;
3006 if (u.mh > 1)
3007 u.mh = 1;
3008 }
3009
3010 void
set_uhpmax(new_hpmax,poly)3011 set_uhpmax(new_hpmax, poly)
3012 int new_hpmax;
3013 boolean poly;
3014 {
3015 if (!heaven_or_hell_mode || new_hpmax < 1) {
3016 if (!poly) u.uhpmax = new_hpmax;
3017 else u.mhmax = new_hpmax;
3018 return;
3019 }
3020
3021 if (!poly) u.uhpmax = 1;
3022 else u.mhmax = 1;
3023 }
3024
3025 void
losehp(n,knam,k_format)3026 losehp(n, knam, k_format)
3027 int n;
3028 const char *knam;
3029 boolean k_format;
3030 {
3031 losehp_how(n, knam, k_format, DIED);
3032 }
3033
3034 void
losehp_how(n,knam,k_format,how)3035 losehp_how(n, knam, k_format, how)
3036 int n;
3037 const char *knam;
3038 int how;
3039 boolean k_format;
3040 {
3041 showdmg(n, TRUE);
3042
3043 if (Upolyd) {
3044 u.mh -= n;
3045 if (u.mhmax < u.mh) { set_uhpmax(u.mh, TRUE); };
3046 flags.botl = 1;
3047 if (u.mh < 1)
3048 rehumanize();
3049 else if (n > 0 && u.mh*10 < u.mhmax && Unchanging)
3050 maybe_wail();
3051 return;
3052 }
3053
3054 u.uhp -= n;
3055 if(u.uhp > u.uhpmax)
3056 set_uhpmax(u.uhp, FALSE); /* perhaps n was negative */
3057 else
3058 nomul(0, 0); /* taking damage stops command repeat */
3059 flags.botl = 1;
3060 if(u.uhp < 1) {
3061 killer_format = k_format;
3062 killer = knam; /* the thing that killed you */
3063 You("die...");
3064 done(how);
3065 } else if (n > 0 && u.uhp*10 < u.uhpmax) {
3066 maybe_wail();
3067 }
3068 }
3069
3070 int
weight_cap()3071 weight_cap()
3072 {
3073 register long carrcap;
3074
3075 carrcap = 25*(ACURRSTR + ACURR(A_CON)) + 50;
3076 if (Upolyd) {
3077 /* consistent with can_carry() in mon.c */
3078 if (youmonst.data->mlet == S_NYMPH)
3079 carrcap = MAX_CARR_CAP;
3080 else if (!youmonst.data->cwt)
3081 carrcap = (carrcap * (long)youmonst.data->msize) / MZ_HUMAN;
3082 else if (!strongmonst(youmonst.data)
3083 || (strongmonst(youmonst.data) && (youmonst.data->cwt > WT_HUMAN)))
3084 carrcap = (carrcap * (long)youmonst.data->cwt / WT_HUMAN);
3085 }
3086
3087 if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */
3088 #ifdef STEED
3089 || (u.usteed && strongmonst(u.usteed->data))
3090 #endif
3091 )
3092 carrcap = MAX_CARR_CAP;
3093 else {
3094 if(carrcap > MAX_CARR_CAP) carrcap = MAX_CARR_CAP;
3095 if (!Flying) {
3096 if(EWounded_legs & LEFT_SIDE) carrcap -= 100;
3097 if(EWounded_legs & RIGHT_SIDE) carrcap -= 100;
3098 }
3099 if (carrcap < 0) carrcap = 0;
3100 }
3101 return((int) carrcap);
3102 }
3103
3104 static int wc; /* current weight_cap(); valid after call to inv_weight() */
3105
3106 /* returns how far beyond the normal capacity the player is currently. */
3107 /* inv_weight() is negative if the player is below normal capacity. */
3108 int
inv_weight()3109 inv_weight()
3110 {
3111 register struct obj *otmp = invent;
3112 register int wt = 0;
3113
3114 #ifndef GOLDOBJ
3115 /* when putting stuff into containers, gold is inserted at the head
3116 of invent for easier manipulation by askchain & co, but it's also
3117 retained in u.ugold in order to keep the status line accurate; we
3118 mustn't add its weight in twice under that circumstance */
3119 wt = (otmp && otmp->oclass == COIN_CLASS) ? 0 :
3120 (int)((u.ugold + 50L) / 100L);
3121 #endif
3122 while (otmp) {
3123 #ifndef GOLDOBJ
3124 if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
3125 #else
3126 if (otmp->oclass == COIN_CLASS)
3127 wt += (int)(((long)otmp->quan + 50L) / 100L);
3128 else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
3129 #endif
3130 {
3131 int threshold = objects[STUDDED_LEATHER_ARMOR].oc_weight;
3132 /* Weight bonus for armor heavier
3133 * than studded leather armor. */
3134 if (Role_if(PM_KNIGHT) &&
3135 uarm == otmp &&
3136 otmp->owt > threshold) {
3137 /* e.g. crystal plate mail weighs 325
3138 * instead of 450 */
3139 wt += threshold + (otmp->owt-threshold)/2;
3140 } else {
3141 wt += otmp->owt;
3142 }
3143 }
3144 otmp = otmp->nobj;
3145 }
3146 wc = weight_cap();
3147 return (wt - wc);
3148 }
3149
3150 /*
3151 * Returns 0 if below normal capacity, or the number of "capacity units"
3152 * over the normal capacity the player is loaded. Max is 5.
3153 */
3154 int
calc_capacity(xtra_wt)3155 calc_capacity(xtra_wt)
3156 int xtra_wt;
3157 {
3158 int cap, wt = inv_weight() + xtra_wt;
3159
3160 if (wt <= 0) return UNENCUMBERED;
3161 if (wc <= 1) return OVERLOADED;
3162 cap = (wt*2 / wc) + 1;
3163 return min(cap, OVERLOADED);
3164 }
3165
3166 int
near_capacity()3167 near_capacity()
3168 {
3169 return calc_capacity(0);
3170 }
3171
3172 int
max_capacity()3173 max_capacity()
3174 {
3175 int wt = inv_weight();
3176
3177 return (wt - (2 * wc));
3178 }
3179
3180 boolean
check_capacity(str)3181 check_capacity(str)
3182 const char *str;
3183 {
3184 if(near_capacity() >= EXT_ENCUMBER) {
3185 if(str)
3186 pline("%s", str);
3187 else
3188 You_cant("do that while carrying so much stuff.");
3189 return 1;
3190 }
3191 return 0;
3192 }
3193
3194 int
inv_cnt()3195 inv_cnt()
3196 {
3197 register struct obj *otmp = invent;
3198 register int ct = 0;
3199
3200 while(otmp){
3201 ct++;
3202 otmp = otmp->nobj;
3203 }
3204 return(ct);
3205 }
3206
3207 #ifdef GOLDOBJ
3208 /* Counts the money in an object chain. */
3209 /* Intended use is for your or some monsters inventory, */
3210 /* now that u.gold/m.gold is gone.*/
3211 /* Counting money in a container might be possible too. */
3212 long
money_cnt(otmp)3213 money_cnt(otmp)
3214 struct obj *otmp;
3215 {
3216 while(otmp) {
3217 /* Must change when silver & copper is implemented: */
3218 if (otmp->oclass == COIN_CLASS) return otmp->quan;
3219 otmp = otmp->nobj;
3220 }
3221 return 0;
3222 }
3223 #endif
3224
3225 /*hack.c*/
3226