/*	$NetBSD: hack.c,v 1.12 2021/05/02 12:50:44 rillig Exp $	*/

/*
 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
 * Amsterdam
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Stichting Centrum voor Wiskunde en
 * Informatica, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: hack.c,v 1.12 2021/05/02 12:50:44 rillig Exp $");
#endif				/* not lint */

#include "hack.h"
#include "extern.h"

static void movobj(struct obj *, int, int);
static int inv_cnt(void);

/*
 * called on movement: 1. when throwing ball+chain far away 2. when
 * teleporting 3. when walking out of a lit room
 */
void
unsee(void)
{
	int x, y;
	struct rm *lev;

	/*
		if(u.udispl){
			u.udispl = 0;
			newsym(u.udisx, u.udisy);
		}
	*/
#ifndef QUEST
	if (seehx) {
		seehx = 0;
	} else
#endif	/* QUEST */
		for (x = u.ux - 1; x < u.ux + 2; x++)
			for (y = u.uy - 1; y < u.uy + 2; y++) {
				if (!isok(x, y))
					continue;
				lev = &levl[x][y];
				if (!lev->lit && lev->scrsym == '.') {
					lev->scrsym = ' ';
					lev->new = 1;
					on_scr(x, y);
				}
			}
}

/*
 * called: in hack.eat.c: seeoff(0) - blind after eating rotten food in
 * hack.mon.c: seeoff(0) - blinded by a yellow light in hack.mon.c: seeoff(1)
 * - swallowed in hack.do.c:  seeoff(0) - blind after drinking potion in
 * hack.do.c:  seeoff(1) - go up or down the stairs in hack.trap.c:seeoff(1)
 * - fall through trapdoor
 */
/* mode: */
	/* 1 to redo @, 0 to leave them *//* 1 means
	 * misc movement, 0 means blindness */
void
seeoff(int mode)
{
	int x, y;
	struct rm *lev;

	if (u.udispl && mode) {
		u.udispl = 0;
		levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy);
	}
#ifndef QUEST
	if (seehx) {
		seehx = 0;
	} else
#endif	/* QUEST */
	if (!mode) {
		for (x = u.ux - 1; x < u.ux + 2; x++)
			for (y = u.uy - 1; y < u.uy + 2; y++) {
				if (!isok(x, y))
					continue;
				lev = &levl[x][y];
				if (!lev->lit && lev->scrsym == '.')
					lev->seen = 0;
			}
	}
}

void
domove(void)
{
	xchar           oldx, oldy;
	struct monst *mtmp = NULL;
	struct rm *tmpr, *ust;
	struct trap    *trap = NULL;
	struct obj *otmp = NULL;

	u_wipe_engr(rnd(5));

	if (inv_weight() > 0) {
		pline("You collapse under your load.");
		nomul(0);
		return;
	}
	if (u.uswallow) {
		u.dx = u.dy = 0;
		u.ux = u.ustuck->mx;
		u.uy = u.ustuck->my;
	} else {
		if (Confusion) {
			do {
				confdir();
			} while (!isok(u.ux + u.dx, u.uy + u.dy) ||
			       IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ));
		}
		if (!isok(u.ux + u.dx, u.uy + u.dy)) {
			nomul(0);
			return;
		}
	}

	ust = &levl[u.ux][u.uy];
	oldx = u.ux;
	oldy = u.uy;
	if (!u.uswallow && (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen)
		nomul(0);
	if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx ||
					u.uy + u.dy != u.ustuck->my)) {
		if (dist(u.ustuck->mx, u.ustuck->my) > 2) {
			/* perhaps it fled (or was teleported or ... ) */
			u.ustuck = 0;
		} else {
			if (Blind)
				pline("You cannot escape from it!");
			else
				pline("You cannot escape from %s!",
				      monnam(u.ustuck));
			nomul(0);
			return;
		}
	}
	if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) {
		/* attack monster */

		nomul(0);
		gethungry();
		if (multi < 0)
			return;	/* we just fainted */

		/* try to attack; note that it might evade */
		if (attack(u.uswallow ? u.ustuck : mtmp))
			return;
	}
	/* not attacking an animal, so we try to move */
	if (u.utrap) {
		if (u.utraptype == TT_PIT) {
			pline("You are still in a pit.");
			u.utrap--;
		} else {
			pline("You are caught in a beartrap.");
			if ((u.dx && u.dy) || !rn2(5))
				u.utrap--;
		}
		return;
	}
	tmpr = &levl[u.ux + u.dx][u.uy + u.dy];
	if (IS_ROCK(tmpr->typ) ||
	    (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) {
		flags.move = 0;
		nomul(0);
		return;
	}
	while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL){
		xchar  rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy;
		struct trap *ttmp;
		nomul(0);
		if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) &&
		    (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) &&
		    !sobj_at(ENORMOUS_ROCK, rx, ry)) {
			if (m_at(rx, ry)) {
				pline("You hear a monster behind the rock.");
				pline("Perhaps that's why you cannot move it.");
				goto cannot_push;
			}
			if ((ttmp = t_at(rx, ry)) != NULL)
				switch (ttmp->ttyp) {
				case PIT:
					pline("You push the rock into a pit!");
					deltrap(ttmp);
					delobj(otmp);
					pline("It completely fills the pit!");
					continue;
				case TELEP_TRAP:
					pline("You push the rock and suddenly it disappears!");
					delobj(otmp);
					continue;
				}
			if (levl[rx][ry].typ == POOL) {
				levl[rx][ry].typ = ROOM;
				mnewsym(rx, ry);
				prl(rx, ry);
				pline("You push the rock into the water.");
				pline("Now you can cross the water!");
				delobj(otmp);
				continue;
			}
			otmp->ox = rx;
			otmp->oy = ry;
			/* pobj(otmp); */
			if (cansee(rx, ry))
				atl(rx, ry, otmp->olet);
			if (Invisible)
				newsym(u.ux + u.dx, u.uy + u.dy);

			{
				static long     lastmovetime;
				/*
				 * note: this var contains garbage initially
				 * and after a restore
				 */
				if (moves > lastmovetime + 2 || moves < lastmovetime)
					pline("With great effort you move the enormous rock.");
				lastmovetime = moves;
			}
		} else {
			pline("You try to move the enormous rock, but in vain.");
	cannot_push:
			if ((!invent || inv_weight() + 90 <= 0) &&
			    (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][u.uy + u.dy].typ)
				&& IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) {
				pline("However, you can squeeze yourself into a small opening.");
				break;
			} else
				return;
		}
	}
	if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) &&
	    IS_ROCK(levl[u.ux + u.dx][u.uy].typ) &&
	    invent && inv_weight() + 40 > 0) {
		pline("You are carrying too much to get through.");
		nomul(0);
		return;
	}
	if (Punished &&
	    DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) {
		if (carried(uball)) {
			movobj(uchain, u.ux, u.uy);
			goto nodrag;
		}
		if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) {
			/* leave ball, move chain under/over ball */
			movobj(uchain, uball->ox, uball->oy);
			goto nodrag;
		}
		if (inv_weight() + (int) uball->owt / 2 > 0) {
			pline("You cannot %sdrag the heavy iron ball.",
			      invent ? "carry all that and also " : "");
			nomul(0);
			return;
		}
		movobj(uball, uchain->ox, uchain->oy);
		unpobj(uball);	/* BAH %% */
		uchain->ox = u.ux;
		uchain->oy = u.uy;
		nomul(-2);
		nomovemsg = "";
nodrag:	;
	}
	u.ux += u.dx;
	u.uy += u.dy;
	if (flags.run) {
		if (tmpr->typ == DOOR ||
		    (xupstair == u.ux && yupstair == u.uy) ||
		    (xdnstair == u.ux && ydnstair == u.uy))
			nomul(0);
	}
	if (tmpr->typ == POOL && !Levitation)
		drown();	/* not necessarily fatal */

	/*
		if(u.udispl) {
			u.udispl = 0;
			newsym(oldx,oldy);
		}
	*/
	if (!Blind) {
#ifdef QUEST
		setsee();
#else
		if (ust->lit) {
			if (tmpr->lit) {
				if (tmpr->typ == DOOR)
					prl1(u.ux + u.dx, u.uy + u.dy);
				else if (ust->typ == DOOR)
					nose1(oldx - u.dx, oldy - u.dy);
			} else {
				unsee();
				prl1(u.ux + u.dx, u.uy + u.dy);
			}
		} else {
			if (tmpr->lit)
				setsee();
			else {
				prl1(u.ux + u.dx, u.uy + u.dy);
				if (tmpr->typ == DOOR) {
					if (u.dy) {
						prl(u.ux - 1, u.uy);
						prl(u.ux + 1, u.uy);
					} else {
						prl(u.ux, u.uy - 1);
						prl(u.ux, u.uy + 1);
					}
				}
			}
			nose1(oldx - u.dx, oldy - u.dy);
		}
#endif	/* QUEST */
	} else {
		pru();
	}
	if (!flags.nopick)
		pickup(1);
	if (trap)
		dotrap(trap);	/* fall into pit, arrow trap, etc. */
	(void) inshop();
	if (!Blind)
		read_engr_at(u.ux, u.uy);
}

static void
movobj(struct obj *obj, int ox, int oy)
{
	/* Some dirty programming to get display right */
	freeobj(obj);
	unpobj(obj);
	obj->nobj = fobj;
	fobj = obj;
	obj->ox = ox;
	obj->oy = oy;
}

int
dopickup(void)
{
	if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) {
		pline("There is nothing here to pick up.");
		return (0);
	}
	if (Levitation) {
		pline("You cannot reach the floor.");
		return (1);
	}
	pickup(0);
	return (1);
}

void
pickup(int all)
{
	struct gold *gold;
	struct obj *obj, *obj2;
	int    wt;

	if (Levitation)
		return;
	while ((gold = g_at(u.ux, u.uy)) != NULL) {
		pline("%ld gold piece%s.", gold->amount, plur(gold->amount));
		u.ugold += gold->amount;
		flags.botl = 1;
		freegold(gold);
		if (flags.run)
			nomul(0);
		if (Invisible)
			newsym(u.ux, u.uy);
	}

	/* check for more than one object */
	if (!all) {
		int    ct = 0;

		for (obj = fobj; obj; obj = obj->nobj)
			if (obj->ox == u.ux && obj->oy == u.uy)
				if (!Punished || obj != uchain)
					ct++;
		if (ct < 2)
			all++;
		else
			pline("There are several objects here.");
	}
	for (obj = fobj; obj; obj = obj2) {
		obj2 = obj->nobj;	/* perhaps obj will be picked up */
		if (obj->ox == u.ux && obj->oy == u.uy) {
			if (flags.run)
				nomul(0);

			/* do not pick up uchain */
			if (Punished && obj == uchain)
				continue;

			if (!all) {
				char            c;

				pline("Pick up %s ? [ynaq]", doname(obj));
				while (!strchr("ynaq ", (c = readchar())))
					sound_bell();
				if (c == 'q')
					return;
				if (c == 'n')
					continue;
				if (c == 'a')
					all = 1;
			}
			if (obj->otyp == DEAD_COCKATRICE && !uarmg) {
				pline("Touching the dead cockatrice is a fatal mistake.");
				pline("You turn to stone.");
				killer = "cockatrice cadaver";
				done("died");
			}
			if (obj->otyp == SCR_SCARE_MONSTER) {
				if (!obj->spe)
					obj->spe = 1;
				else {
					/*
					 * Note: perhaps the 1st pickup
					 * failed: you cannot carry anymore,
					 * and so we never dropped it - let's
					 * assume that treading on it twice
					 * also destroys the scroll
					 */
					pline("The scroll turns to dust as you pick it up.");
					delobj(obj);
					continue;
				}
			}
			wt = inv_weight() + obj->owt;
			if (wt > 0) {
				if (obj->quan > 1) {
					/* see how many we can lift */
					int             savequan = obj->quan;
					int             iw = inv_weight();
					int             qq;
					for (qq = 1; qq < savequan; qq++) {
						obj->quan = qq;
						if (iw + weight(obj) > 0)
							break;
					}
					obj->quan = savequan;
					qq--;
					/* we can carry qq of them */
					if (!qq)
						goto too_heavy;
					pline("You can only carry %s of the %s lying here.",
					      (qq == 1) ? "one" : "some",
					      doname(obj));
					(void) splitobj(obj, qq);
					/*
					 * note: obj2 is set already, so
					 * we'll never encounter the other
					 * half; if it should be otherwise
					 * then write obj2 =
					 * splitobj(obj,qq);
					 */
					goto lift_some;
				}
		too_heavy:
				pline("There %s %s here, but %s.",
				      (obj->quan == 1) ? "is" : "are",
				      doname(obj),
				 !invent ? "it is too heavy for you to lift"
				      : "you cannot carry anymore");
				break;
			}
	lift_some:
			if (inv_cnt() >= 52) {
				pline("Your knapsack cannot accommodate anymore items.");
				break;
			}
			if (wt > -5)
				pline("You have a little trouble lifting");
			freeobj(obj);
			if (Invisible)
				newsym(u.ux, u.uy);
			addtobill(obj);	/* sets obj->unpaid if necessary */
			{
				int             pickquan = obj->quan;
				int             mergquan;
				if (!Blind)
					obj->dknown = 1;	/* this is done by
								 * prinv(), but addinv()
								 * needs it already for
								 * merging */
				obj = addinv(obj);	/* might merge it with
							 * other objects */
				mergquan = obj->quan;
				obj->quan = pickquan;	/* to fool prinv() */
				prinv(obj);
				obj->quan = mergquan;
			}
		}
	}
}

/* stop running if we see something interesting */
/* turn around a corner if that is the only way we can proceed */
/* do not turn left or right twice */
void
lookaround(void)
{
	int    x, y, i, x0 = 0, y0 = 0, m0 = 0, i0 = 9;
	int    corrct = 0, noturn = 0;
	struct monst *mtmp;
	if (Blind || flags.run == 0)
		return;
	if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM)
		return;
#ifdef QUEST
	if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy)
		goto stop;
#endif	/* QUEST */
	for (x = u.ux - 1; x <= u.ux + 1; x++)
		for (y = u.uy - 1; y <= u.uy + 1; y++) {
			if (x == u.ux && y == u.uy)
				continue;
			if (!levl[x][y].typ)
				continue;
			if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
			    (!mtmp->minvis || See_invisible)) {
				if (!mtmp->mtame || (x == u.ux + u.dx && y == u.uy + u.dy))
					goto stop;
			} else
				mtmp = 0;	/* invisible M cannot
						 * influence us */
			if (x == u.ux - u.dx && y == u.uy - u.dy)
				continue;
			switch (levl[x][y].scrsym) {
			case '|':
			case '-':
			case '.':
			case ' ':
				break;
			case '+':
				if (x != u.ux && y != u.uy)
					break;
				if (flags.run != 1)
					goto stop;
				/* FALLTHROUGH */
			case CORR_SYM:
		corr:
				if (flags.run == 1 || flags.run == 3) {
					i = DIST(x, y, u.ux + u.dx, u.uy + u.dy);
					if (i > 2)
						break;
					if (corrct == 1 && DIST(x, y, x0, y0) != 1)
						noturn = 1;
					if (i < i0) {
						i0 = i;
						x0 = x;
						y0 = y;
						m0 = mtmp ? 1 : 0;
					}
				}
				corrct++;
				break;
			case '^':
				if (flags.run == 1)
					goto corr;	/* if you must */
				if (x == u.ux + u.dx && y == u.uy + u.dy)
					goto stop;
				break;
			default:	/* e.g. objects or trap or stairs */
				if (flags.run == 1)
					goto corr;
				if (mtmp)
					break;	/* d */
		stop:
				nomul(0);
				return;
			}
		}
#ifdef QUEST
	if (corrct > 0 && (flags.run == 4 || flags.run == 5))
		goto stop;
#endif	/* QUEST */
	if (corrct > 1 && flags.run == 2)
		goto stop;
	if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
	    (corrct == 1 || (corrct == 2 && i0 == 1))) {
		/* make sure that we do not turn too far */
		if (i0 == 2) {
			if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
				i = 2;	/* straight turn right */
			else
				i = -2;	/* straight turn left */
		} else if (u.dx && u.dy) {
			if ((u.dx == u.dy && y0 == u.uy) ||
			    (u.dx != u.dy && y0 != u.uy))
				i = -1;	/* half turn left */
			else
				i = 1;	/* half turn right */
		} else {
			if ((x0 - u.ux == y0 - u.uy && !u.dy) ||
			    (x0 - u.ux != y0 - u.uy && u.dy))
				i = 1;	/* half turn right */
			else
				i = -1;	/* half turn left */
		}
		i += u.last_str_turn;
		if (i <= 2 && i >= -2) {
			u.last_str_turn = i;
			u.dx = x0 - u.ux, u.dy = y0 - u.uy;
		}
	}
}

/* something like lookaround, but we are not running */
/* react only to monsters that might hit us */
int
monster_nearby(void)
{
	int    x, y;
	struct monst *mtmp;
	if (!Blind)
		for (x = u.ux - 1; x <= u.ux + 1; x++)
			for (y = u.uy - 1; y <= u.uy + 1; y++) {
				if (x == u.ux && y == u.uy)
					continue;
				if ((mtmp = m_at(x, y)) && !mtmp->mimic && !mtmp->mtame &&
				    !mtmp->mpeaceful && !strchr("Ea", mtmp->data->mlet) &&
				    !mtmp->mfroz && !mtmp->msleep &&	/* aplvax!jcn */
				    (!mtmp->minvis || See_invisible))
					return (1);
			}
	return (0);
}

#ifdef QUEST
int
cansee(xchar x, xchar y)
{
	int    dx, dy, adx, ady, sdx, sdy, dmax, d;
	if (Blind)
		return (0);
	if (!isok(x, y))
		return (0);
	d = dist(x, y);
	if (d < 3)
		return (1);
	if (d > u.uhorizon * u.uhorizon)
		return (0);
	if (!levl[x][y].lit)
		return (0);
	dx = x - u.ux;
	adx = abs(dx);
	sdx = sgn(dx);
	dy = y - u.uy;
	ady = abs(dy);
	sdy = sgn(dy);
	if (dx == 0 || dy == 0 || adx == ady) {
		dmax = (dx == 0) ? ady : adx;
		for (d = 1; d <= dmax; d++)
			if (!rroom(sdx * d, sdy * d))
				return (0);
		return (1);
	} else if (ady > adx) {
		for (d = 1; d <= ady; d++) {
			if (!rroom(sdx * ((d * adx) / ady), sdy * d) ||
			    !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d))
				return (0);
		}
		return (1);
	} else {
		for (d = 1; d <= adx; d++) {
			if (!rroom(sdx * d, sdy * ((d * ady) / adx)) ||
			    !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1)))
				return (0);
		}
		return (1);
	}
}

int
rroom(int x, int y)
{
	return (IS_ROOM(levl[u.ux + x][u.uy + y].typ));
}

#else

int
cansee(xchar x, xchar y)
{
	if (Blind || u.uswallow)
		return (0);
	if (dist(x, y) < 3)
		return (1);
	if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y &&
	    y <= seehy)
		return (1);
	return (0);
}
#endif	/* QUEST */

int
sgn(int a)
{
	return ((a > 0) ? 1 : (a == 0) ? 0 : -1);
}

#ifdef QUEST
void
setsee(void)
{
	int	x, y;

	if (Blind) {
		pru();
		return;
	}
	for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++)
		for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) {
			if (cansee(x, y))
				prl(x, y);
		}
}

#else

void
setsee(void)
{
	int x, y;

	if (Blind) {
		pru();
		return;
	}
	if (!levl[u.ux][u.uy].lit) {
		seelx = u.ux - 1;
		seehx = u.ux + 1;
		seely = u.uy - 1;
		seehy = u.uy + 1;
	} else {
		for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--);
		for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++);
		for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--);
		for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++);
	}
	for (y = seely; y <= seehy; y++)
		for (x = seelx; x <= seehx; x++) {
			prl(x, y);
		}
	if (!levl[u.ux][u.uy].lit)
		seehx = 0;	/* seems necessary elsewhere */
	else {
		if (seely == u.uy)
			for (x = u.ux - 1; x <= u.ux + 1; x++)
				prl(x, seely - 1);
		if (seehy == u.uy)
			for (x = u.ux - 1; x <= u.ux + 1; x++)
				prl(x, seehy + 1);
		if (seelx == u.ux)
			for (y = u.uy - 1; y <= u.uy + 1; y++)
				prl(seelx - 1, y);
		if (seehx == u.ux)
			for (y = u.uy - 1; y <= u.uy + 1; y++)
				prl(seehx + 1, y);
	}
}
#endif	/* QUEST */

void
nomul(int nval)
{
	if (multi < 0)
		return;
	multi = nval;
	flags.mv = flags.run = 0;
}

int
abon(void)
{
	if (u.ustr == 3)
		return (-3);
	else if (u.ustr < 6)
		return (-2);
	else if (u.ustr < 8)
		return (-1);
	else if (u.ustr < 17)
		return (0);
	else if (u.ustr < 69)
		return (1);	/* up to 18/50 */
	else if (u.ustr < 118)
		return (2);
	else
		return (3);
}

int
dbon(void)
{
	if (u.ustr < 6)
		return (-1);
	else if (u.ustr < 16)
		return (0);
	else if (u.ustr < 18)
		return (1);
	else if (u.ustr == 18)
		return (2);	/* up to 18 */
	else if (u.ustr < 94)
		return (3);	/* up to 18/75 */
	else if (u.ustr < 109)
		return (4);	/* up to 18/90 */
	else if (u.ustr < 118)
		return (5);	/* up to 18/99 */
	else
		return (6);
}

/* may kill you; cause may be poison or */
/* monster like 'A' */
void
losestr(int num)
{
	u.ustr -= num;
	while (u.ustr < 3) {
		u.ustr++;
		u.uhp -= 6;
		u.uhpmax -= 6;
	}
	flags.botl = 1;
}

void
losehp(int n, const char *knam)
{
	u.uhp -= n;
	if (u.uhp > u.uhpmax)
		u.uhpmax = u.uhp;	/* perhaps n was negative */
	flags.botl = 1;
	if (u.uhp < 1) {
		killer = knam;	/* the thing that killed you */
		done("died");
	}
}

void
losehp_m(int n, struct monst *mtmp)
{
	u.uhp -= n;
	flags.botl = 1;
	if (u.uhp < 1)
		done_in_by(mtmp);
}

void
losexp(void)
{				/* hit by V or W */
	int num;

	if (u.ulevel > 1)
		pline("Goodbye level %u.", u.ulevel--);
	else
		u.uhp = -1;
	num = rnd(10);
	u.uhp -= num;
	u.uhpmax -= num;
	u.uexp = newuexp();
	flags.botl = 1;
}

int
inv_weight(void)
{
	struct obj *otmp = invent;
	int    wt = (u.ugold + 500) / 1000;
	int    carrcap;
	if (Levitation)		/* pugh@cornell */
		carrcap = MAX_CARR_CAP;
	else {
		carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel);
		if (carrcap > MAX_CARR_CAP)
			carrcap = MAX_CARR_CAP;
		if (Wounded_legs & LEFT_SIDE)
			carrcap -= 10;
		if (Wounded_legs & RIGHT_SIDE)
			carrcap -= 10;
	}
	while (otmp) {
		wt += otmp->owt;
		otmp = otmp->nobj;
	}
	return (wt - carrcap);
}

static int
inv_cnt(void)
{
	struct obj *otmp = invent;
	int    ct = 0;
	while (otmp) {
		ct++;
		otmp = otmp->nobj;
	}
	return (ct);
}

long
newuexp(void)
{
	return (10 * (1L << (u.ulevel - 1)));
}
