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