/*	$NetBSD: hack.do.c,v 1.11 2011/08/06 20:29:37 dholland 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.do.c,v 1.11 2011/08/06 20:29:37 dholland Exp $");
#endif				/* not lint */

/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */

#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "hack.h"
#include "extern.h"


static int drop(struct obj *);
static void dropy(struct obj *);

int
dodrop(void)
{
	return (drop(getobj("0$#", "drop")));
}

static int
drop(struct obj *obj)
{
	if (!obj)
		return (0);
	if (obj->olet == '$') {	/* pseudo object */
		long            amount = OGOLD(obj);

		if (amount == 0)
			pline("You didn't drop any gold pieces.");
		else {
			mkgold(amount, u.ux, u.uy);
			pline("You dropped %ld gold piece%s.",
			      amount, plur(amount));
			if (Invisible)
				newsym(u.ux, u.uy);
		}
		free(obj);
		return (1);
	}
	if (obj->owornmask & (W_ARMOR | W_RING)) {
		pline("You cannot drop something you are wearing.");
		return (0);
	}
	if (obj == uwep) {
		if (uwep->cursed) {
			pline("Your weapon is welded to your hand!");
			return (0);
		}
		setuwep((struct obj *) 0);
	}
	pline("You dropped %s.", doname(obj));
	dropx(obj);
	return (1);
}

/* Called in several places - should not produce texts */
void
dropx(struct obj *obj)
{
	freeinv(obj);
	dropy(obj);
}

static void
dropy(struct obj *obj)
{
	if (obj->otyp == CRYSKNIFE)
		obj->otyp = WORM_TOOTH;
	obj->ox = u.ux;
	obj->oy = u.uy;
	obj->nobj = fobj;
	fobj = obj;
	if (Invisible)
		newsym(u.ux, u.uy);
	subfrombill(obj);
	stackobj(obj);
}

/* drop several things */
int
doddrop(void)
{
	return (ggetobj("drop", drop, 0));
}

int
dodown(void)
{
	if (u.ux != xdnstair || u.uy != ydnstair) {
		pline("You can't go down here.");
		return (0);
	}
	if (u.ustuck) {
		pline("You are being held, and cannot go down.");
		return (1);
	}
	if (Levitation) {
		pline("You're floating high above the stairs.");
		return (0);
	}
	goto_level(dlevel + 1, TRUE);
	return (1);
}

int
doup(void)
{
	if (u.ux != xupstair || u.uy != yupstair) {
		pline("You can't go up here.");
		return (0);
	}
	if (u.ustuck) {
		pline("You are being held, and cannot go up.");
		return (1);
	}
	if (!Levitation && inv_weight() + 5 > 0) {
		pline("Your load is too heavy to climb the stairs.");
		return (1);
	}
	goto_level(dlevel - 1, TRUE);
	return (1);
}

void
goto_level(int newlevel, boolean at_stairs)
{
	int fd;
	boolean         up = (newlevel < dlevel);

	if (newlevel <= 0)
		done("escaped");/* in fact < 0 is impossible */
	if (newlevel > MAXLEVEL)
		newlevel = MAXLEVEL;	/* strange ... */
	if (newlevel == dlevel)
		return;		/* this can happen */

	glo(dlevel);
	fd = creat(lock, FMASK);
	if (fd < 0) {
		/*
		 * This is not quite impossible: e.g., we may have
		 * exceeded our quota. If that is the case then we
		 * cannot leave this level, and cannot save either.
		 * Another possibility is that the directory was not
		 * writable.
		 */
		pline("A mysterious force prevents you from going %s.",
		      up ? "up" : "down");
		return;
	}
	if (Punished)
		unplacebc();
	u.utrap = 0;		/* needed in level_tele */
	u.ustuck = 0;		/* idem */
	keepdogs();
	seeoff(1);
	if (u.uswallow)		/* idem */
		u.uswldtim = u.uswallow = 0;
	flags.nscrinh = 1;
	u.ux = FAR;		/* hack */
	(void) inshop();	/* probably was a trapdoor */

	savelev(fd, dlevel);
	(void) close(fd);

	dlevel = newlevel;
	if (maxdlevel < dlevel)
		maxdlevel = dlevel;
	glo(dlevel);

	if (!level_exists[dlevel])
		mklev();
	else {
		if ((fd = open(lock, O_RDONLY)) < 0) {
			pline("Cannot open %s .", lock);
			pline("Probably someone removed it.");
			done("tricked");
		}
		getlev(fd, hackpid, dlevel);
		(void) close(fd);
	}

	if (at_stairs) {
		if (up) {
			u.ux = xdnstair;
			u.uy = ydnstair;
			if (!u.ux) {	/* entering a maze from below? */
				u.ux = xupstair;	/* this will confuse the
							 * player! */
				u.uy = yupstair;
			}
			if (Punished && !Levitation) {
				pline("With great effort you climb the stairs.");
				placebc(1);
			}
		} else {
			u.ux = xupstair;
			u.uy = yupstair;
			if (inv_weight() + 5 > 0 || Punished) {
				pline("You fall down the stairs.");	/* %% */
				losehp(rnd(3), "fall");
				if (Punished) {
					if (uwep != uball && rn2(3)) {
						pline("... and are hit by the iron ball.");
						losehp(rnd(20), "iron ball");
					}
					placebc(1);
				}
				selftouch("Falling, you");
			}
		}
		{
			struct monst   *mtmp = m_at(u.ux, u.uy);
			if (mtmp)
				mnexto(mtmp);
		}
	} else {		/* trapdoor or level_tele */
		do {
			u.ux = rnd(COLNO - 1);
			u.uy = rn2(ROWNO);
		} while (levl[u.ux][u.uy].typ != ROOM ||
			 m_at(u.ux, u.uy));
		if (Punished) {
			if (uwep != uball && !up /* %% */ && rn2(5)) {
				pline("The iron ball falls on your head.");
				losehp(rnd(25), "iron ball");
			}
			placebc(1);
		}
		selftouch("Falling, you");
	}
	(void) inshop();
	initrack();

	losedogs();
	{
		struct monst   *mtmp;
		if ((mtmp = m_at(u.ux, u.uy)) != NULL)
			mnexto(mtmp);	/* riv05!a3 */
	}
	flags.nscrinh = 0;
	setsee();
	seeobjs();		/* make old cadavers disappear - riv05!a3 */
	docrt();
	pickup(1);
	read_engr_at(u.ux, u.uy);
}

int
donull(void)
{
	return (1);		/* Do nothing, but let other things happen */
}

int
dopray(void)
{
	nomovemsg = "You finished your prayer.";
	nomul(-3);
	return (1);
}

int
dothrow(void)
{
	struct obj     *obj;
	struct monst   *mon;
	int tmp;

	obj = getobj("#)", "throw");	/* it is also possible to throw food */
	/* (or jewels, or iron balls ... ) */
	if (!obj || !getdir(1))	/* ask "in what direction?" */
		return (0);
	if (obj->owornmask & (W_ARMOR | W_RING)) {
		pline("You can't throw something you are wearing.");
		return (0);
	}
	u_wipe_engr(2);

	if (obj == uwep) {
		if (obj->cursed) {
			pline("Your weapon is welded to your hand.");
			return (1);
		}
		if (obj->quan > 1)
			setuwep(splitobj(obj, 1));
		else
			setuwep((struct obj *) 0);
	} else if (obj->quan > 1)
		(void) splitobj(obj, 1);
	freeinv(obj);
	if (u.uswallow) {
		mon = u.ustuck;
		bhitpos.x = mon->mx;
		bhitpos.y = mon->my;
	} else if (u.dz) {
		if (u.dz < 0) {
			pline("%s hits the ceiling, then falls back on top of your head.",
			      Doname(obj));	/* note: obj->quan == 1 */
			if (obj->olet == POTION_SYM)
				potionhit(&youmonst, obj);
			else {
				if (uarmh)
					pline("Fortunately, you are wearing a helmet!");
				losehp(uarmh ? 1 : rnd((int) (obj->owt)), "falling object");
				dropy(obj);
			}
		} else {
			pline("%s hits the floor.", Doname(obj));
			if (obj->otyp == EXPENSIVE_CAMERA) {
				pline("It is shattered in a thousand pieces!");
				obfree(obj, Null(obj));
			} else if (obj->otyp == EGG) {
				pline("\"Splash!\"");
				obfree(obj, Null(obj));
			} else if (obj->olet == POTION_SYM) {
				pline("The flask breaks, and you smell a peculiar odor ...");
				potionbreathe(obj);
				obfree(obj, Null(obj));
			} else {
				dropy(obj);
			}
		}
		return (1);
	} else if (obj->otyp == BOOMERANG) {
		mon = boomhit(u.dx, u.dy);
		if (mon == &youmonst) {	/* the thing was caught */
			(void) addinv(obj);
			return (1);
		}
	} else {
		if (obj->otyp == PICK_AXE && shkcatch(obj))
			return (1);

		mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 :
			(!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1,
			   obj->olet,
			   (void (*)(struct monst *, struct obj *)) 0,
			   (int (*)(struct obj *, struct obj *)) 0, obj);
	}
	if (mon) {
		/* awake monster if sleeping */
		wakeup(mon);

		if (obj->olet == WEAPON_SYM) {
			tmp = -1 + u.ulevel + mon->data->ac + abon();
			if (obj->otyp < ROCK) {
				if (!uwep ||
				    uwep->otyp != obj->otyp + (BOW - ARROW))
					tmp -= 4;
				else {
					tmp += uwep->spe;
				}
			} else if (obj->otyp == BOOMERANG)
				tmp += 4;
			tmp += obj->spe;
			if (u.uswallow || tmp >= rnd(20)) {
				if (hmon(mon, obj, 1) == TRUE) {
					/* mon still alive */
#ifndef NOWORM
					cutworm(mon, bhitpos.x, bhitpos.y, obj->otyp);
#endif	/* NOWORM */
				} else
					mon = 0;
				/* weapons thrown disappear sometimes */
				if (obj->otyp < BOOMERANG && rn2(3)) {
					/* check bill; free */
					obfree(obj, (struct obj *) 0);
					return (1);
				}
			} else
				miss(objects[obj->otyp].oc_name, mon);
		} else if (obj->otyp == HEAVY_IRON_BALL) {
			tmp = -1 + u.ulevel + mon->data->ac + abon();
			if (!Punished || obj != uball)
				tmp += 2;
			if (u.utrap)
				tmp -= 2;
			if (u.uswallow || tmp >= rnd(20)) {
				if (hmon(mon, obj, 1) == FALSE)
					mon = 0;	/* he died */
			} else
				miss("iron ball", mon);
		} else if (obj->olet == POTION_SYM && u.ulevel > rn2(15)) {
			potionhit(mon, obj);
			return (1);
		} else {
			if (cansee(bhitpos.x, bhitpos.y))
				pline("You miss %s.", monnam(mon));
			else
				pline("You miss it.");
			if (obj->olet == FOOD_SYM && mon->data->mlet == 'd')
				if (tamedog(mon, obj))
					return (1);
			if (obj->olet == GEM_SYM && mon->data->mlet == 'u' &&
			    !mon->mtame) {
				if (obj->dknown && objects[obj->otyp].oc_name_known) {
					if (objects[obj->otyp].g_val > 0) {
						u.uluck += 5;
						goto valuable;
					} else {
						pline("%s is not interested in your junk.",
						      Monnam(mon));
					}
				} else {	/* value unknown to @ */
					u.uluck++;
			valuable:
					if (u.uluck > LUCKMAX)	/* dan@ut-ngp */
						u.uluck = LUCKMAX;
					pline("%s graciously accepts your gift.",
					      Monnam(mon));
					mpickobj(mon, obj);
					rloc(mon);
					return (1);
				}
			}
		}
	}
	/* the code following might become part of dropy() */
	if (obj->otyp == CRYSKNIFE)
		obj->otyp = WORM_TOOTH;
	obj->ox = bhitpos.x;
	obj->oy = bhitpos.y;
	obj->nobj = fobj;
	fobj = obj;
	/* prevent him from throwing articles to the exit and escaping */
	/* subfrombill(obj); */
	stackobj(obj);
	if (Punished && obj == uball &&
	    (bhitpos.x != u.ux || bhitpos.y != u.uy)) {
		freeobj(uchain);
		unpobj(uchain);
		if (u.utrap) {
			if (u.utraptype == TT_PIT)
				pline("The ball pulls you out of the pit!");
			else {
				long            side =
				rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
				pline("The ball pulls you out of the bear trap.");
				pline("Your %s leg is severely damaged.",
				    (side == LEFT_SIDE) ? "left" : "right");
				set_wounded_legs(side, 500 + rn2(1000));
				losehp(2, "thrown ball");
			}
			u.utrap = 0;
		}
		unsee();
		uchain->nobj = fobj;
		fobj = uchain;
		u.ux = uchain->ox = bhitpos.x - u.dx;
		u.uy = uchain->oy = bhitpos.y - u.dy;
		setsee();
		(void) inshop();
	}
	if (cansee(bhitpos.x, bhitpos.y))
		prl(bhitpos.x, bhitpos.y);
	return (1);
}

/* split obj so that it gets size num */
/* remainder is put in the object structure delivered by this call */
struct obj     *
splitobj(struct obj *obj, int num)
{
	struct obj     *otmp;
	otmp = newobj(0);
	*otmp = *obj;		/* copies whole structure */
	otmp->o_id = flags.ident++;
	otmp->onamelth = 0;
	obj->quan = num;
	obj->owt = weight(obj);
	otmp->quan -= num;
	otmp->owt = weight(otmp);	/* -= obj->owt ? */
	obj->nobj = otmp;
	if (obj->unpaid)
		splitbill(obj, otmp);
	return (otmp);
}

void
more_experienced(int exp, int rexp)
{
	u.uexp += exp;
	u.urexp += 4 * exp + rexp;
	if (exp)
		flags.botl = 1;
	if (u.urexp >= ((pl_character[0] == 'W') ? 1000 : 2000))
		flags.beginner = 0;
}

void
set_wounded_legs(long side, int timex)
{
	if (!Wounded_legs || (Wounded_legs & TIMEOUT))
		Wounded_legs |= side + timex;
	else
		Wounded_legs |= side;
}

void
heal_legs(void)
{
	if (Wounded_legs) {
		if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
			pline("Your legs feel somewhat better.");
		else
			pline("Your leg feels somewhat better.");
		Wounded_legs = 0;
	}
}