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