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