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