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