xref: /dragonfly/games/hack/hack.c (revision ad9f8794)
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */
4 /* $DragonFly: src/games/hack/hack.c,v 1.6 2007/05/13 18:33:55 swildner Exp $ */
5 
6 #include "hack.h"
7 
8 static void movobj(struct obj *, int, int);
9 #ifdef QUEST
10 static bool rroom(int, int);
11 #endif
12 static int inv_cnt(void);
13 
14 /* called on movement:
15  * 1. when throwing ball+chain far away
16  * 2. when teleporting
17  * 3. when walking out of a lit room
18  */
19 void
20 unsee(void)
21 {
22 	int x, y;
23 	struct rm *lev;
24 
25 #ifndef QUEST
26 	if (seehx)
27 		seehx = 0;
28 	else
29 #endif /* QUEST */
30 		for (x = u.ux - 1; x < u.ux + 2; x++)
31 			for (y = u.uy - 1; y < u.uy + 2; y++) {
32 				if (!isok(x, y))
33 					continue;
34 				lev = &levl[x][y];
35 				if (!lev->lit && lev->scrsym == '.') {
36 					lev->scrsym = ' ';
37 					lev->new = 1;
38 					on_scr(x, y);
39 				}
40 			}
41 }
42 
43 /* called:
44  *      in hack.eat.c: seeoff(0) - blind after eating rotten food
45  *      in hack.mon.c: seeoff(0) - blinded by a yellow light
46  *      in hack.mon.c: seeoff(1) - swallowed
47  *      in hack.do.c:  seeoff(0) - blind after drinking potion
48  *      in hack.do.c:  seeoff(1) - go up or down the stairs
49  *      in hack.trap.c:seeoff(1) - fall through trapdoor
50  * mode:
51  * 1 to redo @, 0 to leave them *//* 1 means
52  * misc movement, 0 means blindness
53  */
54 void
55 seeoff(bool mode)
56 {
57 	int x, y;
58 	struct rm *lev;
59 
60 	if (u.udispl && mode) {
61 		u.udispl = 0;
62 		levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy);
63 	}
64 #ifndef QUEST
65 	if (seehx)
66 		seehx = 0;
67 	else
68 #endif /* QUEST */
69 	if (!mode) {
70 		for (x = u.ux - 1; x < u.ux + 2; x++)
71 			for (y = u.uy - 1; y < u.uy + 2; y++) {
72 				if (!isok(x, y))
73 					continue;
74 				lev = &levl[x][y];
75 				if (!lev->lit && lev->scrsym == '.')
76 					lev->seen = 0;
77 			}
78 	}
79 }
80 
81 void
82 domove(void)
83 {
84 	xchar oldx, oldy;
85 	struct monst *mtmp = NULL;
86 	struct rm *tmpr, *ust;
87 	struct trap *trap = NULL;
88 	struct obj *otmp;
89 
90 	u_wipe_engr(rnd(5));
91 
92 	if (inv_weight() > 0) {
93 		pline("You collapse under your load.");
94 		nomul(0);
95 		return;
96 	}
97 	if (u.uswallow) {
98 		u.dx = u.dy = 0;
99 		u.ux = u.ustuck->mx;
100 		u.uy = u.ustuck->my;
101 	} else {
102 		if (Confusion) {
103 			do {
104 				confdir();
105 			} while (!isok(u.ux + u.dx, u.uy + u.dy) ||
106 			       IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ));
107 		}
108 		if (!isok(u.ux + u.dx, u.uy + u.dy)) {
109 			nomul(0);
110 			return;
111 		}
112 	}
113 
114 	ust = &levl[u.ux][u.uy];
115 	oldx = u.ux;
116 	oldy = u.uy;
117 	if (!u.uswallow &&
118 	    (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen)
119 		nomul(0);
120 	if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx ||
121 					u.uy + u.dy != u.ustuck->my)) {
122 		if (dist(u.ustuck->mx, u.ustuck->my) > 2) {
123 			/* perhaps it fled (or was teleported or ... ) */
124 			u.ustuck = 0;
125 		} else {
126 			if (Blind)
127 				pline("You cannot escape from it!");
128 			else
129 				pline("You cannot escape from %s!",
130 				    monnam(u.ustuck));
131 			nomul(0);
132 			return;
133 		}
134 	}
135 	if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) {
136 		/* attack monster */
137 
138 		nomul(0);
139 		gethungry();
140 		if (multi < 0)		/* we just fainted */
141 			return;
142 
143 		/* try to attack; note that it might evade */
144 		if (attack(u.uswallow ? u.ustuck : mtmp))
145 			return;
146 	}
147 	/* not attacking an animal, so we try to move */
148 	if (u.utrap) {
149 		if (u.utraptype == TT_PIT) {
150 			pline("You are still in a pit.");
151 			u.utrap--;
152 		} else {
153 			pline("You are caught in a beartrap.");
154 			if ((u.dx && u.dy) || !rn2(5))
155 				u.utrap--;
156 		}
157 		return;
158 	}
159 	tmpr = &levl[u.ux + u.dx][u.uy + u.dy];
160 	if (IS_ROCK(tmpr->typ) ||
161 	    (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) {
162 		flags.move = 0;
163 		nomul(0);
164 		return;
165 	}
166 	while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL) {
167 		struct trap *ttmp;
168 		xchar rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy;
169 		nomul(0);
170 		if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) &&
171 		    (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) &&
172 		    !sobj_at(ENORMOUS_ROCK, rx, ry)) {
173 			if (m_at(rx, ry)) {
174 				pline("You hear a monster behind the rock.");
175 				pline("Perhaps that's why you cannot move it.");
176 				goto cannot_push;
177 			}
178 			if ((ttmp = t_at(rx, ry)) != NULL)
179 				switch (ttmp->ttyp) {
180 				case PIT:
181 					pline("You push the rock into a pit!");
182 					deltrap(ttmp);
183 					delobj(otmp);
184 					pline("It completely fills the pit!");
185 					continue;
186 				case TELEP_TRAP:
187 					pline("You push the rock and suddenly it disappears!");
188 					delobj(otmp);
189 					continue;
190 				}
191 			if (levl[rx][ry].typ == POOL) {
192 				levl[rx][ry].typ = ROOM;
193 				mnewsym(rx, ry);
194 				prl(rx, ry);
195 				pline("You push the rock into the water.");
196 				pline("Now you can cross the water!");
197 				delobj(otmp);
198 				continue;
199 			}
200 			otmp->ox = rx;
201 			otmp->oy = ry;
202 			if (cansee(rx, ry))
203 				atl(rx, ry, otmp->olet);
204 			if (Invisible)
205 				newsym(u.ux + u.dx, u.uy + u.dy);
206 
207 			{
208 				static long lastmovetime;
209 			  /* note: this var contains garbage initially and
210 			   * after a restore */
211 				if (moves > lastmovetime + 2 || moves < lastmovetime)
212 					pline("With great effort you move the enormous rock.");
213 				lastmovetime = moves;
214 			}
215 		} else {
216 			pline("You try to move the enormous rock, but in vain.");
217 cannot_push:
218 			if ((!invent || inv_weight() + 90 <= 0) &&
219 			    (!u.dx || !u.dy ||
220 			     (IS_ROCK(levl[u.ux][u.uy + u.dy].typ)
221 			      && IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) {
222 				pline("However, you can squeeze yourself into a small opening.");
223 				break;
224 			} else
225 				return;
226 		}
227 	}
228 	if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) &&
229 	    IS_ROCK(levl[u.ux + u.dx][u.uy].typ) &&
230 	    invent && inv_weight() + 40 > 0) {
231 		pline("You are carrying too much to get through.");
232 		nomul(0);
233 		return;
234 	}
235 	if (Punished &&
236 	    DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) {
237 		if (carried(uball)) {
238 			movobj(uchain, u.ux, u.uy);
239 			goto nodrag;
240 		}
241 
242 		if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) {
243 			/* leave ball, move chain under/over ball */
244 			movobj(uchain, uball->ox, uball->oy);
245 			goto nodrag;
246 		}
247 
248 		if (inv_weight() + (int)uball->owt / 2 > 0) {
249 			pline("You cannot %sdrag the heavy iron ball.",
250 			      invent ? "carry all that and also " : "");
251 			nomul(0);
252 			return;
253 		}
254 
255 		movobj(uball, uchain->ox, uchain->oy);
256 		unpobj(uball);		/* BAH %% */
257 		uchain->ox = u.ux;
258 		uchain->oy = u.uy;
259 		nomul(-2);
260 		nomovemsg = "";
261 nodrag:	;
262 	}
263 	u.ux += u.dx;
264 	u.uy += u.dy;
265 	if (flags.run) {
266 		if (tmpr->typ == DOOR ||
267 		    (xupstair == u.ux && yupstair == u.uy) ||
268 		    (xdnstair == u.ux && ydnstair == u.uy))
269 			nomul(0);
270 	}
271 
272 	if (tmpr->typ == POOL && !Levitation)
273 		drown();	/* not necessarily fatal */
274 
275 	if (!Blind) {
276 #ifdef QUEST
277 		setsee();
278 #else
279 		if (ust->lit) {
280 			if (tmpr->lit) {
281 				if (tmpr->typ == DOOR)
282 					prl1(u.ux + u.dx, u.uy + u.dy);
283 				else if (ust->typ == DOOR)
284 					nose1(oldx - u.dx, oldy - u.dy);
285 			} else {
286 				unsee();
287 				prl1(u.ux + u.dx, u.uy + u.dy);
288 			}
289 		} else {
290 			if (tmpr->lit)
291 				setsee();
292 			else {
293 				prl1(u.ux + u.dx, u.uy + u.dy);
294 				if (tmpr->typ == DOOR) {
295 					if (u.dy) {
296 						prl(u.ux - 1, u.uy);
297 						prl(u.ux + 1, u.uy);
298 					} else {
299 						prl(u.ux, u.uy - 1);
300 						prl(u.ux, u.uy + 1);
301 					}
302 				}
303 			}
304 			nose1(oldx - u.dx, oldy - u.dy);
305 		}
306 #endif /* QUEST */
307 	} else
308 		pru();
309 	if (!flags.nopick)
310 		pickup(1);
311 	if (trap)
312 		dotrap(trap);	/* fall into pit, arrow trap, etc. */
313 	inshop();
314 	if (!Blind)
315 		read_engr_at(u.ux, u.uy);
316 }
317 
318 static void
319 movobj(struct obj *obj, int ox, int oy)
320 {
321 	/* Some dirty programming to get display right */
322 	freeobj(obj);
323 	unpobj(obj);
324 	obj->nobj = fobj;
325 	fobj = obj;
326 	obj->ox = ox;
327 	obj->oy = oy;
328 }
329 
330 int
331 dopickup(void)
332 {
333 	if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) {
334 		pline("There is nothing here to pick up.");
335 		return (0);
336 	}
337 	if (Levitation) {
338 		pline("You cannot reach the floor.");
339 		return (1);
340 	}
341 	pickup(0);
342 	return (1);
343 }
344 
345 void
346 pickup(int all)
347 {
348 	struct gold *gold;
349 	struct obj *obj, *obj2;
350 	int wt;
351 
352 	if (Levitation)
353 		return;
354 	while ((gold = g_at(u.ux, u.uy))) {
355 		pline("%ld gold piece%s.", gold->amount, plur(gold->amount));
356 		u.ugold += gold->amount;
357 		flags.botl = 1;
358 		freegold(gold);
359 		if (flags.run)
360 			nomul(0);
361 		if (Invisible)
362 			newsym(u.ux, u.uy);
363 	}
364 
365 	/* check for more than one object */
366 	if (!all) {
367 		int ct = 0;
368 
369 		for (obj = fobj; obj; obj = obj->nobj)
370 			if (obj->ox == u.ux && obj->oy == u.uy)
371 				if (!Punished || obj != uchain)
372 					ct++;
373 		if (ct < 2)
374 			all++;
375 		else
376 			pline("There are several objects here.");
377 	}
378 
379 	for (obj = fobj; obj; obj = obj2) {
380 		obj2 = obj->nobj;	/* perhaps obj will be picked up */
381 		if (obj->ox == u.ux && obj->oy == u.uy) {
382 			if (flags.run)
383 				nomul(0);
384 
385 			/* do not pick up uchain */
386 			if (Punished && obj == uchain)
387 				continue;
388 
389 			if (!all) {
390 				char c;
391 
392 				pline("Pick up %s ? [ynaq]", doname(obj));
393 				while (!strchr("ynaq ", (c = readchar())))
394 					bell();
395 				if (c == 'q')
396 					return;
397 				if (c == 'n')
398 					continue;
399 				if (c == 'a')
400 					all = 1;
401 			}
402 
403 			if (obj->otyp == DEAD_COCKATRICE && !uarmg) {
404 				pline("Touching the dead cockatrice is a fatal mistake.");
405 				pline("You turn to stone.");
406 				killer = "cockatrice cadaver";
407 				done("died");
408 			}
409 
410 			if (obj->otyp == SCR_SCARE_MONSTER) {
411 				if (!obj->spe)
412 					obj->spe = 1;
413 				else {
414 					/* Note: perhaps the 1st pickup failed: you cannot
415 					 *  carry anymore, and so we never dropped it -
416 					 *  let's assume that treading on it twice also
417 					 *  destroys the scroll */
418 					pline("The scroll turns to dust as you pick it up.");
419 					delobj(obj);
420 					continue;
421 				}
422 			}
423 
424 			wt = inv_weight() + obj->owt;
425 			if (wt > 0) {
426 				if (obj->quan > 1) {
427 					/* see how many we can lift */
428 					int savequan = obj->quan;
429 					int iw = inv_weight();
430 					int qq;
431 					for (qq = 1; qq < savequan; qq++) {
432 						obj->quan = qq;
433 						if (iw + weight(obj) > 0)
434 							break;
435 					}
436 					obj->quan = savequan;
437 					qq--;
438 					/* we can carry qq of them */
439 					if (!qq)
440 						goto too_heavy;
441 					pline("You can only carry %s of the %s lying here.",
442 					    (qq == 1) ? "one" : "some",
443 					    doname(obj));
444 					splitobj(obj, qq);
445 					/* note: obj2 is set already, so we'll never
446 					 * encounter the other half; if it should be
447 					 * otherwise then write
448 					 *	obj2 = splitobj(obj, qq);
449 					 */
450 					goto lift_some;
451 				}
452 too_heavy:
453 				pline("There %s %s here, but %s.",
454 				      (obj->quan == 1) ? "is" : "are",
455 				      doname(obj),
456 				      !invent ? "it is too heavy for you to lift"
457 				      : "you cannot carry anymore");
458 				break;
459 			}
460 lift_some:
461 			if (inv_cnt() >= 52) {
462 				pline("Your knapsack cannot accommodate anymore items.");
463 				break;
464 			}
465 			if (wt > -5)
466 				pline("You have a little trouble lifting");
467 			freeobj(obj);
468 			if (Invisible)
469 				newsym(u.ux, u.uy);
470 			addtobill(obj);	/* sets obj->unpaid if necessary */
471 			{
472 				int pickquan = obj->quan;
473 				int mergquan;
474 				if (!Blind)		/* this is done by prinv(), */
475 					obj->dknown = 1;/* but addinv() needs it */
476 							/* already for merging */
477 				obj = addinv(obj);	/* might merge it with other objects */
478 				mergquan = obj->quan;
479 				obj->quan = pickquan;	/* to fool prinv() */
480 				prinv(obj);
481 				obj->quan = mergquan;
482 			}
483 		}
484 	}
485 }
486 
487 /* stop running if we see something interesting */
488 /* turn around a corner if that is the only way we can proceed */
489 /* do not turn left or right twice */
490 void
491 lookaround(void)
492 {
493 	int x, y, i, x0, y0, m0, i0 = 9;
494 	int corrct = 0, noturn = 0;
495 	struct monst *mtmp;
496 
497 	/* suppress "used before set" message */
498 	x0 = y0 = m0 = 0;
499 	if (Blind || flags.run == 0)
500 		return;
501 	if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM)
502 		return;
503 #ifdef QUEST
504 	if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy)
505 		goto stop;
506 #endif /* QUEST */
507 	for (x = u.ux - 1; x <= u.ux + 1; x++)
508 		for (y = u.uy - 1; y <= u.uy + 1; y++) {
509 			if (x == u.ux && y == u.uy)
510 				continue;
511 			if (!levl[x][y].typ)
512 				continue;
513 			if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
514 			    (!mtmp->minvis || See_invisible)) {
515 				if (!mtmp->mtame ||
516 				    (x == u.ux + u.dx && y == u.uy + u.dy))
517 					goto stop;
518 			} else		/* invisible M cannot influence us */
519 				mtmp = 0;
520 			if (x == u.ux - u.dx && y == u.uy - u.dy)
521 				continue;
522 			switch (levl[x][y].scrsym) {
523 			case '|':
524 			case '-':
525 			case '.':
526 			case ' ':
527 				break;
528 			case '+':
529 				if (x != u.ux && y != u.uy)
530 					break;
531 				if (flags.run != 1)
532 					goto stop;
533 			/* fall into next case */
534 			case CORR_SYM:
535 corr:
536 				if (flags.run == 1 || flags.run == 3) {
537 					i = DIST(x, y, u.ux + u.dx, u.uy + u.dy);
538 					if (i > 2)
539 						break;
540 					if (corrct == 1 &&
541 					    DIST(x, y, x0, y0) != 1)
542 						noturn = 1;
543 					if (i < i0) {
544 						i0 = i;
545 						x0 = x;
546 						y0 = y;
547 						m0 = mtmp ? 1 : 0;
548 					}
549 				}
550 				corrct++;
551 				break;
552 			case '^':
553 				if (flags.run == 1)	/* if you must */
554 					goto corr;
555 				if (x == u.ux + u.dx && y == u.uy + u.dy)
556 					goto stop;
557 				break;
558 			default:	/* e.g. objects or trap or stairs */
559 				if (flags.run == 1)
560 					goto corr;
561 				if (mtmp)	/* d */
562 					break;
563 stop:
564 				nomul(0);
565 				return;
566 			}
567 		}
568 #ifdef QUEST
569 	if (corrct > 0 && (flags.run == 4 || flags.run == 5))
570 		goto stop;
571 #endif /* QUEST */
572 	if (corrct > 1 && flags.run == 2)
573 		goto stop;
574 	if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
575 	    (corrct == 1 || (corrct == 2 && i0 == 1))) {
576 		/* make sure that we do not turn too far */
577 		if (i0 == 2) {
578 			if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
579 				i = 2;	/* straight turn right */
580 			else
581 				i = -2;	/* straight turn left */
582 		} else if (u.dx && u.dy) {
583 			if ((u.dx == u.dy && y0 == u.uy) ||
584 			    (u.dx != u.dy && y0 != u.uy))
585 				i = -1;	/* half turn left */
586 			else
587 				i = 1;	/* half turn right */
588 		} else {
589 			if ((x0 - u.ux == y0 - u.uy && !u.dy) ||
590 			    (x0 - u.ux != y0 - u.uy && u.dy))
591 				i = 1;	/* half turn right */
592 			else
593 				i = -1;	/* half turn left */
594 		}
595 		i += u.last_str_turn;
596 		if (i <= 2 && i >= -2) {
597 			u.last_str_turn = i;
598 			u.dx = x0 - u.ux, u.dy = y0 - u.uy;
599 		}
600 	}
601 }
602 
603 /* something like lookaround, but we are not running */
604 /* react only to monsters that might hit us */
605 bool
606 monster_nearby(void)
607 {
608 	int x, y;
609 	struct monst *mtmp;
610 
611 	if (!Blind)
612 		for (x = u.ux - 1; x <= u.ux + 1; x++)
613 			for (y = u.uy - 1; y <= u.uy + 1; y++) {
614 				if (x == u.ux && y == u.uy)
615 					continue;
616 				if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
617 				    !mtmp->mtame &&
618 				    !mtmp->mpeaceful &&
619 				    !strchr("Ea", mtmp->data->mlet) &&
620 				    !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */
621 				    (!mtmp->minvis || See_invisible))
622 					return (1);
623 			}
624 	return (0);
625 }
626 
627 #ifdef QUEST
628 bool
629 cansee(xchar x, xchar y)
630 {
631 	int dx, dy, adx, ady, sdx, sdy, dmax, d;
632 
633 	if (Blind)
634 		return (0);
635 	if (!isok(x, y))
636 		return (0);
637 	d = dist(x, y);
638 	if (d < 3)
639 		return (1);
640 	if (d > u.uhorizon * u.uhorizon)
641 		return (0);
642 	if (!levl[x][y].lit)
643 		return (0);
644 	dx = x - u.ux;
645 	adx = abs(dx);
646 	sdx = sgn(dx);
647 	dy = y - u.uy;
648 	ady = abs(dy);
649 	sdy = sgn(dy);
650 	if (dx == 0 || dy == 0 || adx == ady) {
651 		dmax = (dx == 0) ? ady : adx;
652 		for (d = 1; d <= dmax; d++)
653 			if (!rroom(sdx * d, sdy * d))
654 				return (0);
655 		return (1);
656 	} else if (ady > adx) {
657 		for (d = 1; d <= ady; d++) {
658 			if (!rroom(sdx * ((d * adx) / ady), sdy * d) ||
659 			    !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d))
660 				return (0);
661 		}
662 		return (1);
663 	} else {
664 		for (d = 1; d <= adx; d++) {
665 			if (!rroom(sdx * d, sdy * ((d * ady) / adx)) ||
666 			    !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1)))
667 				return (0);
668 		}
669 		return (1);
670 	}
671 }
672 
673 static bool
674 rroom(int x, int y)
675 {
676 	return (IS_ROOM(levl[u.ux + x][u.uy + y].typ));
677 }
678 
679 #else
680 
681 bool
682 cansee(xchar x, xchar y)
683 {
684 	if (Blind || u.uswallow)
685 		return (0);
686 	if (dist(x, y) < 3)
687 		return (1);
688 	if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y &&
689 	    y <= seehy)
690 		return (1);
691 	return (0);
692 }
693 #endif /* QUEST */
694 
695 int
696 sgn(int a)
697 {
698 	return ((a > 0) ? 1 : (a == 0) ? 0 : -1);
699 }
700 
701 #ifdef QUEST
702 void
703 setsee(void)
704 {
705 	int x, y;
706 
707 	if (Blind) {
708 		pru();
709 		return;
710 	}
711 	for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++)
712 		for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) {
713 			if (cansee(x, y))
714 				prl(x, y);
715 		}
716 }
717 
718 #else
719 
720 void
721 setsee(void)
722 {
723 	int x, y;
724 
725 	if (Blind) {
726 		pru();
727 		return;
728 	}
729 	if (!levl[u.ux][u.uy].lit) {
730 		seelx = u.ux - 1;
731 		seehx = u.ux + 1;
732 		seely = u.uy - 1;
733 		seehy = u.uy + 1;
734 	} else {
735 		for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--) ;
736 		for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++) ;
737 		for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--) ;
738 		for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++) ;
739 	}
740 	for (y = seely; y <= seehy; y++)
741 		for (x = seelx; x <= seehx; x++)
742 			prl(x, y);
743 
744 	if (!levl[u.ux][u.uy].lit)	/* seems necessary elsewhere */
745 		seehx = 0;
746 	else {
747 		if (seely == u.uy)
748 			for (x = u.ux - 1; x <= u.ux + 1; x++)
749 				prl(x, seely - 1);
750 		if (seehy == u.uy)
751 			for (x = u.ux - 1; x <= u.ux + 1; x++)
752 				prl(x, seehy + 1);
753 		if (seelx == u.ux)
754 			for (y = u.uy - 1; y <= u.uy + 1; y++)
755 				prl(seelx - 1, y);
756 		if (seehx == u.ux)
757 			for (y = u.uy - 1; y <= u.uy + 1; y++)
758 				prl(seehx + 1, y);
759 	}
760 }
761 #endif /* QUEST */
762 
763 void
764 nomul(int nval)
765 {
766 	if (multi < 0)
767 		return;
768 	multi = nval;
769 	flags.mv = flags.run = 0;
770 }
771 
772 int
773 abon(void)
774 {
775 	if (u.ustr == 3)
776 		return (-3);
777 	else if (u.ustr < 6)
778 		return (-2);
779 	else if (u.ustr < 8)
780 		return (-1);
781 	else if (u.ustr < 17)
782 		return (0);
783 	else if (u.ustr < 69)	/* up to 18/50 */
784 		return (1);
785 	else if (u.ustr < 118)
786 		return (2);
787 	else
788 		return (3);
789 }
790 
791 int
792 dbon(void)
793 {
794 	if (u.ustr < 6)
795 		return (-1);
796 	else if (u.ustr < 16)
797 		return (0);
798 	else if (u.ustr < 18)
799 		return (1);
800 	else if (u.ustr == 18)	/* up to 18 */
801 		return (2);
802 	else if (u.ustr < 94)	/* up to 18/75 */
803 		return (3);
804 	else if (u.ustr < 109)	/* up to 18/90 */
805 		return (4);
806 	else if (u.ustr < 118)	/* up to 18/99 */
807 		return (5);
808 	else
809 		return (6);
810 }
811 
812 /* may kill you; cause may be poison or monster like 'A' */
813 void
814 losestr(int num)
815 {
816 	u.ustr -= num;
817 	while (u.ustr < 3) {
818 		u.ustr++;
819 		u.uhp -= 6;
820 		u.uhpmax -= 6;
821 	}
822 	flags.botl = 1;
823 }
824 
825 void
826 losehp(int n, const char *knam)
827 {
828 	u.uhp -= n;
829 	if (u.uhp > u.uhpmax)
830 		u.uhpmax = u.uhp;	/* perhaps n was negative */
831 	flags.botl = 1;
832 	if (u.uhp < 1) {
833 		killer = knam;		/* the thing that killed you */
834 		done("died");
835 	}
836 }
837 
838 void
839 losehp_m(int n, struct monst *mtmp)
840 {
841 	u.uhp -= n;
842 	flags.botl = 1;
843 	if (u.uhp < 1)
844 		done_in_by(mtmp);
845 }
846 
847 void
848 losexp(void)    /* hit by V or W */
849 {
850 	int num;
851 
852 	if (u.ulevel > 1)
853 		pline("Goodbye level %u.", u.ulevel--);
854 	else
855 		u.uhp = -1;
856 	num = rnd(10);
857 	u.uhp -= num;
858 	u.uhpmax -= num;
859 	u.uexp = newuexp();
860 	flags.botl = 1;
861 }
862 
863 int
864 inv_weight(void)
865 {
866 	struct obj *otmp = invent;
867 	int wt = (u.ugold + 500) / 1000;
868 	int carrcap;
869 
870 	if (Levitation)		/* pugh@cornell */
871 		carrcap = MAX_CARR_CAP;
872 	else {
873 		carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel);
874 		if (carrcap > MAX_CARR_CAP)
875 			carrcap = MAX_CARR_CAP;
876 		if (Wounded_legs & LEFT_SIDE)
877 			carrcap -= 10;
878 		if (Wounded_legs & RIGHT_SIDE)
879 			carrcap -= 10;
880 	}
881 	while (otmp) {
882 		wt += otmp->owt;
883 		otmp = otmp->nobj;
884 	}
885 	return (wt - carrcap);
886 }
887 
888 static int
889 inv_cnt(void)
890 {
891 	struct obj *otmp = invent;
892 	int ct = 0;
893 
894 	while (otmp) {
895 		ct++;
896 		otmp = otmp->nobj;
897 	}
898 	return (ct);
899 }
900 
901 long
902 newuexp(void)
903 {
904 	return (10 * (1L << (u.ulevel - 1)));
905 }
906