/*      SCCS Id: @(#)bones.c    3.1     93/06/05        */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "lev.h"

#ifdef MFLOPPY
extern char bones[];    /* from files.c */
extern long bytes_counted;
#endif

static boolean FDECL(no_bones_level, (d_level *));
#ifdef TUTTI_FRUTTI
static void FDECL(goodfruit, (int));
#endif
static void FDECL(resetobjs,(struct obj *,BOOLEAN_P));
static void FDECL(drop_upon_death, (struct monst *, struct obj *));

static boolean
no_bones_level(lev)
d_level *lev;
{
	extern d_level save_dlevel;             /* in do.c */
	s_level *sptr;

	if (ledger_no(&save_dlevel)) assign_level(lev, &save_dlevel);

	return (boolean)(((sptr = Is_special(lev)) && !sptr->boneid)
		|| !dungeons[lev->dnum].boneid
		   /* no bones on the last or multiway branch levels */
		   /* in any dungeon (level 1 isn't multiway).       */
		|| Is_botlevel(lev) || (Is_branchlev(lev) && lev->dlevel > 1)
		|| (lev->dlevel < 2)  /* no bones on 1st level */
		   /* no bones in the invocation level               */
		|| (In_hell(lev) && lev->dlevel == dunlevs_in_dungeon(lev) - 1)
		);
}

#ifdef TUTTI_FRUTTI
static void
goodfruit(id)
int id;
{
	register struct fruit *f;

	for(f=ffruit; f; f=f->nextf) {
		if(f->fid == -id) {
			f->fid = id;
			return;
		}
	}
}
#endif

static void
resetobjs(ochain,restore)
struct obj *ochain;
boolean restore;
{
	struct obj *otmp;

	for (otmp = ochain; otmp; otmp = otmp->nobj) {
		if (otmp->cobj)
		    resetobjs(otmp->cobj,restore);

		if (((otmp->otyp != CORPSE || otmp->corpsenm < PM_ARCHEOLOGIST)
			&& otmp->otyp != STATUE)
			&& (!otmp->oartifact ||
			    (exist_artifact(otmp->otyp,ONAME(otmp)) && restore))) {
			otmp->oartifact = 0;
			otmp->onamelth = 0;
			*ONAME(otmp) = '\0';
		} else if (otmp->oartifact && restore)
			artifact_exists(otmp,ONAME(otmp),TRUE);
		if (!restore) {
			/* resetting the o_id's after getlev has carefully
			 * created proper new ones via restobjchn is a Bad
			 * Idea */
			otmp->o_id = 0;
			if(objects[otmp->otyp].oc_uses_known) otmp->known = 0;
			otmp->dknown = otmp->bknown = 0;
			otmp->rknown = 0;
			otmp->invlet = 0;
#ifdef TUTTI_FRUTTI
			if(otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe);
			else
#endif
#ifdef MAIL
			if (otmp->otyp == SCR_MAIL) otmp->spe = 1;
			else
#endif
#ifdef POLYSELF
			if (otmp->otyp == EGG) otmp->spe = 0;
			else
#endif
			if (otmp->otyp == AMULET_OF_YENDOR) {
			    /* no longer the real Amulet */
			    otmp->otyp = FAKE_AMULET_OF_YENDOR;
			    curse(otmp);
			} else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) {
			    otmp->otyp = WAX_CANDLE;
			    otmp->age = 50L;  /* assume used */
			    if (otmp->spe > 0)
				otmp->quan = (long)otmp->spe;
			    otmp->lamplit = 0;
			    otmp->spe = 0;
			    otmp->owt = weight(otmp);
			} else if (otmp->otyp == BELL_OF_OPENING) {
			    otmp->otyp = BELL;
			    curse(otmp);
			} else if (otmp->otyp == SPE_BOOK_OF_THE_DEAD) {
			    otmp->otyp = SPE_BLANK_PAPER;
			    curse(otmp);
#ifdef MULDGN
			} else if (is_quest_artifact(otmp)) {
			    /*
			     * never leave our own quest artifact among the
			     * bones; others (via wishing) might remain though
			     */
			 /* artifact_unexist(otmp); */
			    otmp->oartifact = 0;
			    ONAME(otmp)[0] = '\0';
#endif
			}
		}
	}
}

static void
drop_upon_death(mtmp, cont)
struct monst *mtmp;
struct obj *cont;
{
	struct obj *otmp = invent;
	while(otmp) {
		otmp->owornmask = 0;
		otmp->lamplit = 0;
#ifdef TUTTI_FRUTTI
		if(otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe);
#endif
		if(rn2(5)) curse(otmp);
		if(!mtmp && !cont) place_object(otmp, u.ux, u.uy);
		if(!otmp->nobj) {
			if (mtmp) {
				otmp->nobj = mtmp->minvent;
				mtmp->minvent = invent;
			} else if (cont) {
				otmp->nobj = cont->cobj;
				cont->cobj = invent;
			} else {
				otmp->nobj = fobj;
				fobj = invent;
			}
			invent = 0;     /* superfluous */
			break;
		}
		otmp = otmp->nobj;
	}
	if(u.ugold) {
		if (mtmp) mtmp->mgold = u.ugold;
		else mkgold(u.ugold, u.ux, u.uy);
	}
}

/* save bones and possessions of a deceased adventurer */
void
savebones()
{
	register int fd, x, y;
	register struct trap *ttmp;
	register struct monst *mtmp, *mtmp2;
#ifdef TUTTI_FRUTTI
	struct fruit *f;
#endif
	char c, *bonesid;

	if(ledger_no(&u.uz) <= 0 || ledger_no(&u.uz) > maxledgerno()) return;
	if(no_bones_level(&u.uz)) return; /* no bones for specific levels */
	if(!Is_branchlev(&u.uz)) {
	    /* no bones on non-branches with portals */
	    for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
		if (ttmp->ttyp == MAGIC_PORTAL) return;
	}

	if(depth(&u.uz) <= 1) return; /* bulletproofing for endgame */
/*           (!rn2(1 + (depth(&u.uz)>>2)) /* fewer ghosts on low levels */
	/* [Tom] changed to flat 50% bones */
	/* changed back to varying by depth */
	if (!rn2(2) && rn2(depth(&u.uz)) && !wizard) return;

#ifdef EXPLORE_MODE
	/* don't let multiple restarts generate multiple copies of objects
	 * in bones files */
	if(discover) return;
#endif

	fd = open_bonesfile(&u.uz, &bonesid);
	if (fd >= 0) {
		(void) close(fd);
		compress_bonesfile();
#ifdef WIZARD
		if(wizard)
			pline("Bones file already exists.");
#endif
		return;
	}

#ifdef WALKIES
	unleash_all();
#endif
	/* in case these characters are not in their home bases */
	mtmp2 = fmon;
	while((mtmp = mtmp2)) {
		mtmp2 = mtmp->nmon;
		if(mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA]) mongone(mtmp);
	}
#ifdef TUTTI_FRUTTI
	/* mark all fruits as nonexistent; when we come to them we'll mark
	 * them as existing (using goodfruit())
	 */
	for(f=ffruit; f; f=f->nextf) f->fid = -f->fid;
#endif

	/* check iron balls separately--maybe they're not carrying it */
	if (uball) uball->owornmask = uchain->owornmask = 0;

	/* dispose of your possessions, usually cursed */
	if (u.ugrave_arise == -2) {
		struct obj *otmp;

		/* embed your possessions in your statue */
		otmp = mk_named_object(STATUE,
#ifdef POLYSELF
					u.mtimedone ? uasmon :
#endif
					player_mon(), 
					u.ux, u.uy, plname,
					(int)strlen(plname));
		if (!otmp) return;
		drop_upon_death(mtmp = (struct monst *)0, otmp);
	} else if (u.ugrave_arise == -1) {
		/* drop everything */
		drop_upon_death((struct monst *)0, (struct obj *)0);
		/* trick makemon() into allowing monster creation
		 * on your location
		 */
		in_mklev = TRUE;
		mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy);
		in_mklev = FALSE;
		if (!mtmp) return;
		Strcpy((char *) mtmp->mextra, plname);
	} else {
		/* give your possessions to the monster you become */
		in_mklev = TRUE;
		mtmp = makemon(&mons[u.ugrave_arise], u.ux, u.uy);
		in_mklev = FALSE;
		if (!mtmp) return;
		mtmp = christen_monst(mtmp, plname);
		newsym(u.ux, u.uy);
		Your("body rises from the dead as %s...",
			an(mons[u.ugrave_arise].mname));
		display_nhwindow(WIN_MESSAGE, FALSE);
		drop_upon_death(mtmp, (struct obj *)0);
#ifdef MUSE
		m_dowear(mtmp, TRUE);
#endif
	}
	if (mtmp) {
		mtmp->m_lev = (u.ulevel ? u.ulevel : 1);
		mtmp->mhp = mtmp->mhpmax = u.uhpmax;
		mtmp->msleep = 1;
	}
	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
		resetobjs(mtmp->minvent,FALSE);
		mtmp->m_id = 0;
		mtmp->mlstmv = 0L;
		if(mtmp->mtame) mtmp->mtame = mtmp->mpeaceful = 0;
	}
	for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
		ttmp->tseen = 0;
	}
	resetobjs(fobj,FALSE);
	resetobjs(level.buriedobjlist, FALSE);

	/* Clear all memory from the level. */
	for(x=0; x<COLNO; x++) for(y=0; y<ROWNO; y++) {
	    levl[x][y].seen = levl[x][y].waslit = 0;
	    levl[x][y].glyph = cmap_to_glyph(S_stone);
	}

	fd = create_bonesfile(&u.uz, &bonesid);
	if(fd < 0) {
#ifdef WIZARD
		if(wizard)
			pline("Cannot create bones file - create failed");
#endif
		return;
	}

	bufon(fd);
#ifdef MFLOPPY  /* check whether there is room */
	savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
# ifdef TUTTI_FRUTTI
	/* this is in the opposite order from the real save, but savelev()
	 * initializes bytes_counted to 0, so doing savefruitchn() first is
	 * useless; the extra bflush() at the end of savelev() may increase
	 * bytes_counted by a couple over what the real usage will be
	 */
	savefruitchn(fd, COUNT_SAVE);
	bflush(fd);
# endif
	if (bytes_counted > freediskspace(bones)) {     /* not enough room */
# ifdef WIZARD
		if (wizard)
			pline("Insufficient space to create bones file.");
# endif
		(void) close(fd);
		delete_bonesfile(&u.uz);
		return;
	}
	co_false();     /* make sure bonesid and savefruitchn get written */
#endif /* MFLOPPY */

	c = (char) (strlen(bonesid) + 1);
	bwrite(fd, (genericptr_t) &c, sizeof c);
	bwrite(fd, (genericptr_t) bonesid, (unsigned) c);       /* DD.nnn */
#ifdef TUTTI_FRUTTI
	savefruitchn(fd, WRITE_SAVE | FREE_SAVE);
#endif
	savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
	bclose(fd);
	compress_bonesfile();
}

int
getbones()
{
	register int fd;
	register int ok;
	char c, *bonesid, oldbonesid[10];

#ifdef EXPLORE_MODE
	if(discover)            /* save bones files for real games */
		return(0);
#endif
	/* wizard check added by GAN 02/05/87 */
	if(rn2(3)       /* only once in three times do we find bones */
#ifdef WIZARD
		&& !wizard
#endif
		) return(0);
	if(no_bones_level(&u.uz)) return(0);
	fd = open_bonesfile(&u.uz, &bonesid);
	if (fd < 0) return(0);

	if((ok = uptodate(fd)) != 0){
#ifdef WIZARD
		if(wizard)  {
			if(yn("Get bones?") == 'n') {
				(void) close(fd);
				compress_bonesfile();
				return(0);
			}
		}
#endif
		minit();        /* ZEROCOMP */
		mread(fd, (genericptr_t) &c, sizeof c); /* length incl. '\0' */
		mread(fd, (genericptr_t) oldbonesid, (unsigned) c); /* DD.nnn */
		if (strcmp(bonesid, oldbonesid)) {
#ifdef WIZARD
			if (wizard) {
				pline("This is bones level '%s', not '%s'!",
					oldbonesid, bonesid);
				ok = FALSE;     /* won't die of trickery */
			}
#endif
			trickery();
		} else {
			register struct monst *mtmp;

			getlev(fd, 0, 0, TRUE);

			/* to correctly reset named artifacts on the level */
			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
				resetobjs(mtmp->minvent,TRUE);
			resetobjs(fobj,TRUE);
			resetobjs(level.buriedobjlist,TRUE);
		}
	}
	(void) close(fd);

#ifdef WIZARD
	if(wizard) {
		if(yn("Unlink bones?") == 'n') {
			compress_bonesfile();
			return(ok);
		}
	}
#endif
	if (!delete_bonesfile(&u.uz)) {
		pline("Cannot unlink bones.");
		return(0);
	}
	return(ok);
}

/*bones.c*/
