From b47aabe431fff7fe1b6f76ae635161963d9e74bc Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:48 +0000 Subject: [PATCH] *** empty log message *** --- src/bones.c | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 src/bones.c diff --git a/src/bones.c b/src/bones.c new file mode 100644 index 000000000..eb0c0a4ef --- /dev/null +++ b/src/bones.c @@ -0,0 +1,451 @@ +/* SCCS Id: @(#)bones.c 3.3 2001/04/12 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" + +extern char bones[]; /* from files.c */ +#ifdef MFLOPPY +extern long bytes_counted; +#endif + +STATIC_DCL boolean FDECL(no_bones_level, (d_level *)); +STATIC_DCL void FDECL(goodfruit, (int)); +STATIC_DCL void FDECL(resetobjs,(struct obj *,BOOLEAN_P)); +STATIC_DCL void FDECL(drop_upon_death, (struct monst *, struct obj *)); + +STATIC_OVL 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)) != 0 && !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) + /* no bones in the invocation level */ + || (In_hell(lev) && lev->dlevel == dunlevs_in_dungeon(lev) - 1) + ); +} + +/* Call this function for each fruit object saved in the bones level: it marks + * that particular type of fruit as existing (the marker is that that type's + * ID is positive instead of negative). This way, when we later save the + * chain of fruit types, we know to only save the types that exist. + */ +STATIC_OVL void +goodfruit(id) +int id; +{ + register struct fruit *f; + + for(f=ffruit; f; f=f->nextf) { + if(f->fid == -id) { + f->fid = id; + return; + } + } +} + +STATIC_OVL 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 < SPECIAL_PM) + && otmp->otyp != STATUE) + && (!otmp->oartifact || + (restore && (exist_artifact(otmp->otyp, ONAME(otmp)) + || is_quest_artifact(otmp))))) { + otmp->oartifact = 0; + otmp->onamelth = 0; + *ONAME(otmp) = '\0'; + } else if (otmp->oartifact && restore) + artifact_exists(otmp,ONAME(otmp),TRUE); + if (!restore) { + /* do not zero out o_ids for ghost levels anymore */ + + if(objects[otmp->otyp].oc_uses_known) otmp->known = 0; + otmp->dknown = otmp->bknown = 0; + otmp->rknown = 0; + otmp->invlet = 0; + otmp->no_charge = 0; + + if (otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe); +#ifdef MAIL + else if (otmp->otyp == SCR_MAIL) otmp->spe = 1; +#endif + else if (otmp->otyp == EGG) otmp->spe = 0; + else if (otmp->otyp == TIN) { + /* make tins of unique monster's meat be empty */ + if (otmp->corpsenm >= LOW_PM && + (mons[otmp->corpsenm].geno & G_UNIQ)) + otmp->corpsenm = NON_PM; + } else 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) { + if (otmp->lamplit) + end_burn(otmp, TRUE); + otmp->otyp = WAX_CANDLE; + otmp->age = 50L; /* assume used */ + if (otmp->spe > 0) + otmp->quan = (long)otmp->spe; + 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); + } + } + } +} + +STATIC_OVL void +drop_upon_death(mtmp, cont) +struct monst *mtmp; +struct obj *cont; +{ + struct obj *otmp; + + while ((otmp = invent) != 0) { + obj_extract_self(otmp); + + otmp->owornmask = 0; + /* lamps don't go out when dropped */ + if ((cont || artifact_light(otmp)) && obj_is_burning(otmp)) /* smother in statue */ + end_burn(otmp, otmp->otyp != MAGIC_LAMP && !artifact_light(otmp)); + + if(otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe); + + if(rn2(5)) curse(otmp); + if (mtmp) + (void) add_to_minv(mtmp, otmp); + else if (cont) + (void) add_to_container(cont, otmp); + else + place_object(otmp, u.ux, u.uy); + } +#ifndef GOLDOBJ + if(u.ugold) { + long ugold = u.ugold; + if (mtmp) mtmp->mgold = ugold; + else if (cont) (void) add_to_container(cont, mkgoldobj(ugold)); + else (void)mkgold(ugold, u.ux, u.uy); + u.ugold = ugold; /* undo mkgoldobj()'s removal */ + } +#endif + if (cont) cont->owt = weight(cont); +} + +/* check whether bones are feasible */ +boolean +can_make_bones() +{ + register struct trap *ttmp; + + if (ledger_no(&u.uz) <= 0 || ledger_no(&u.uz) > maxledgerno()) + return FALSE; + if (no_bones_level(&u.uz)) + return FALSE; /* 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 FALSE; + } + + if(depth(&u.uz) <= 0 || /* bulletproofing for endgame */ + (!rn2(1 + (depth(&u.uz)>>2)) /* fewer ghosts on low levels */ +#ifdef WIZARD + && !wizard +#endif + )) return FALSE; + /* don't let multiple restarts generate multiple copies of objects + * in bones files */ + if (discover) return FALSE; + return TRUE; +} + +/* save bones and possessions of a deceased adventurer */ +void +savebones(corpse) +struct obj *corpse; +{ + int fd, x, y; + struct trap *ttmp; + struct monst *mtmp; + struct permonst *mptr; + struct fruit *f; + char c, *bonesid; + + /* caller has already checked `can_make_bones()' */ + + fd = open_bonesfile(&u.uz, &bonesid); + if (fd >= 0) { + (void) close(fd); + compress_bonesfile(); +#ifdef WIZARD + if (wizard) { + if (yn("Bones file already exists. Replace it?") == 'y') { + if (delete_bonesfile(&u.uz)) goto make_bones; + else pline("Cannot unlink old bones."); + } + } +#endif + return; + } + + make_bones: + unleash_all(); + /* in case these characters are not in their home bases */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + mptr = mtmp->data; + if (mtmp->iswiz || mptr == &mons[PM_MEDUSA] || + mptr->msound == MS_NEMESIS || mptr->msound == MS_LEADER || + mptr == &mons[PM_VLAD_THE_IMPALER]) + mongone(mtmp); + } +#ifdef STEED + if (u.usteed) dismount_steed(DISMOUNT_BONES); +#endif + dmonsfree(); /* discard dead or gone monsters */ + + /* 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; + + /* 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 == (NON_PM - 1)) { + struct obj *otmp; + + /* embed your possessions in your statue */ + otmp = mk_named_object(STATUE, &mons[u.umonnum], + u.ux, u.uy, plname); + + drop_upon_death((struct monst *)0, otmp); + if (!otmp) return; /* couldn't make statue */ + mtmp = (struct monst *)0; + } else if (u.ugrave_arise < LOW_PM) { + /* 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, MM_NONAME); + in_mklev = FALSE; + if (!mtmp) return; + mtmp = christen_monst(mtmp, plname); + if (corpse) + (void) obj_attach_mid(corpse, mtmp->m_id); + } else { + /* give your possessions to the monster you become */ + in_mklev = TRUE; + mtmp = makemon(&mons[u.ugrave_arise], u.ux, u.uy, NO_MM_FLAGS); + in_mklev = FALSE; + if (!mtmp) { + drop_upon_death((struct monst *)0, (struct obj *)0); + 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); + m_dowear(mtmp, TRUE); + } + if (mtmp) { + mtmp->m_lev = (u.ulevel ? u.ulevel : 1); + mtmp->mhp = mtmp->mhpmax = u.uhpmax; + mtmp->female = flags.female; + mtmp->msleeping = 1; + } + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + resetobjs(mtmp->minvent,FALSE); + /* do not zero out m_ids for bones levels any more */ + mtmp->mlstmv = 0L; + if(mtmp->mtame) mtmp->mtame = mtmp->mpeaceful = 0; + } + for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + ttmp->madeby_u = 0; + ttmp->tseen = (ttmp->ttyp == HOLE); + } + resetobjs(fobj,FALSE); + resetobjs(level.buriedobjlist, FALSE); + + /* Hero is no longer on the map. */ + u.ux = u.uy = 0; + + /* Clear all memory from the level. */ + for(x=0; x freediskspace(bones)) { /* not enough room */ +# ifdef WIZARD + if (wizard) + pline("Insufficient space to create bones file."); +# endif + (void) close(fd); + cancel_bonesfile(); + return; + } + co_false(); /* make sure stuff before savelev() gets written */ + } +#endif /* MFLOPPY */ + + store_version(fd); + bwrite(fd, (genericptr_t) &c, sizeof c); + bwrite(fd, (genericptr_t) bonesid, (unsigned) c); /* DD.nnn */ + savefruitchn(fd, WRITE_SAVE | FREE_SAVE); + update_mlstmv(); /* update monsters for eventual restoration */ + savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE); + bclose(fd); + commit_bonesfile(&u.uz); + compress_bonesfile(); +} + +int +getbones() +{ + register int fd; + register int ok; + char c, *bonesid, oldbonesid[10]; + + if(discover) /* save bones files for real games */ + return(0); + + /* 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, bones)) == 0) { +#ifdef WIZARD + if (!wizard) +#endif + pline("Discarding unuseable bones; no need to panic..."); + } else { +#ifdef WIZARD + if(wizard) { + if(yn("Get bones?") == 'n') { + (void) close(fd); + compress_bonesfile(); + return(0); + } + } +#endif + 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; + int mndx; + + getlev(fd, 0, 0, TRUE); + + /* to correctly reset named artifacts on the level and + to keep tabs on unique monsters like demon lords */ + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + mndx = monsndx(mtmp->data); + if (mvitals[mndx].mvflags & G_EXTINCT) { + mongone(mtmp); + } else { + if (mons[mndx].geno & G_UNIQ) + mvitals[mndx].mvflags |= G_EXTINCT; + 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)) { + /* When N games try to simultaneously restore the same + * bones file, N-1 of them will fail to delete it + * (the first N-1 under AmigaDOS, the last N-1 under UNIX). + * So no point in a mysterious message for a normal event + * -- just generate a new level for those N-1 games. + */ + /* pline("Cannot unlink bones."); */ + return(0); + } + return(ok); +} + +/*bones.c*/