bones tracking (trunk only)

[See cvs log for include/rm.h or doc/window.doc for more complete description.]

Attach hero info, death reason, and date+time to a level that's being saved
as bones.  Read such data back when loading a bones file, then treat it as
part of that level for the rest of the game.  Dying on a loaded bones file
will chain the new hero+death+date to previous one(s) if new bones get saved.

outrip() now takes an extra argument of type time_t, and interface-specific
implementations of this routine need to be updated to handle that.
This commit is contained in:
nethack.rankin
2012-01-23 10:45:31 +00:00
parent b88c51deae
commit 9721470635
15 changed files with 152 additions and 64 deletions

View File

@@ -132,7 +132,7 @@ E void NDECL(drag_down);
E void FDECL(sanitize_name, (char *));
E void FDECL(drop_upon_death, (struct monst *,struct obj *,int,int));
E boolean NDECL(can_make_bones);
E void FDECL(savebones, (struct obj *));
E void FDECL(savebones, (int,time_t,struct obj *));
E int NDECL(getbones);
/* ### botl.c ### */
@@ -1947,7 +1947,7 @@ E void FDECL(put_gold_back, (struct monst *));
/* ### rip.c ### */
E void FDECL(genl_outrip, (winid,int));
E void FDECL(genl_outrip, (winid,int,time_t));
/* ### rnd.c ### */
@@ -2270,7 +2270,8 @@ E void NDECL(timer_sanity_check);
/* ### topten.c ### */
E void FDECL(topten, (int));
E void FDECL(formatkiller, (char *,unsigned,int));
E void FDECL(topten, (int,time_t));
E void FDECL(prscore, (int,char **));
E struct obj *FDECL(tt_oname, (struct obj *));

View File

@@ -13,7 +13,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 50
#define EDITLEVEL 51
#define COPYRIGHT_BANNER_A \
"NetHack, Copyright 1985-2012"

View File

@@ -69,7 +69,7 @@ struct window_procs {
void NDECL((*win_start_screen));
void NDECL((*win_end_screen));
void FDECL((*win_outrip), (winid,int));
void FDECL((*win_outrip), (winid,int,time_t));
void FDECL((*win_preference_update), (const char *));
char * FDECL((*win_getmsghistory), (BOOLEAN_P));
void FDECL((*win_putmsghistory), (const char *,BOOLEAN_P));
@@ -351,7 +351,7 @@ struct chain_procs {
void FDECL((*win_start_screen), (CARGS));
void FDECL((*win_end_screen), (CARGS));
void FDECL((*win_outrip), (CARGS, winid,int));
void FDECL((*win_outrip), (CARGS, winid,int,time_t));
void FDECL((*win_preference_update), (CARGS, const char *));
char * FDECL((*win_getmsghistory), (CARGS, BOOLEAN_P));
void FDECL((*win_putmsghistory), (CARGS, const char *,BOOLEAN_P));

View File

@@ -226,7 +226,7 @@ E char * NDECL(tty_get_color_string);
E void NDECL(tty_start_screen);
E void NDECL(tty_end_screen);
E void FDECL(genl_outrip, (winid,int));
E void FDECL(genl_outrip, (winid,int,time_t));
E char *FDECL(tty_getmsghistory, (BOOLEAN_P));
E void FDECL(tty_putmsghistory, (const char *,BOOLEAN_P));

View File

@@ -273,7 +273,9 @@ can_make_bones()
/* save bones and possessions of a deceased adventurer */
void
savebones(corpse)
savebones(how, when, corpse)
int how;
time_t when;
struct obj *corpse;
{
int fd, x, y;
@@ -281,6 +283,7 @@ struct obj *corpse;
struct monst *mtmp;
struct permonst *mptr;
struct fruit *f;
struct cemetery *newbones;
char c, *bonesid;
char whynot[BUFSZ];
@@ -407,6 +410,29 @@ struct obj *corpse;
#endif
}
/* Attach bones info to the current level before saving. */
newbones = (struct cemetery *)alloc(sizeof *newbones);
/* entries are '\0' terminated but have fixed length allocations,
so pre-fill with spaces to initialize any excess room */
(void)memset((genericptr_t)newbones, ' ', sizeof *newbones);
/* format name+role,&c, death reason, and date+time;
gender and alignment reflect final values rather than what the
character started out as, same as topten and logfile entries */
Sprintf(newbones->who, "%s-%.3s-%.3s-%.3s-%.3s",
plname, urole.filecode, urace.filecode,
genders[flags.female].filecode,
aligns[1 - u.ualign.type].filecode);
formatkiller(newbones->how, sizeof newbones->how, how);
Strcpy(newbones->when, yyyymmddhhmmss(when));
/* if current character died on a bones level, the cememtery list
will have multiple entries, most recent (this dead hero) first */
newbones->next = level.bonesinfo;
level.bonesinfo = newbones;
/* flag these bones if they are being created in wizard mode;
they might already be flagged as such, even when we're playing
in normal mode, if this level came from a previous bones file */
if (wizard) level.flags.wizard_bones = 1;
fd = create_bonesfile(&u.uz, &bonesid, whynot);
if(fd < 0) {
#ifdef WIZARD

View File

@@ -830,6 +830,7 @@ int how;
winid endwin = WIN_ERR;
boolean bones_ok, have_windows = iflags.window_inited;
struct obj *corpse = (struct obj *)0;
time_t endtime;
long umoney;
long tmp;
@@ -909,6 +910,11 @@ die:
if (thrownobj && thrownobj->where == OBJ_FREE) dealloc_obj(thrownobj);
if (kickedobj && kickedobj->where == OBJ_FREE) dealloc_obj(kickedobj);
/* remember time of death here instead of having bones, rip, and
topten figure it out separately and possibly getting different
time or even day if player is slow responding to --More-- */
endtime = getnow();
/* Sometimes you die on the first move. Life's not fair.
* On those rare occasions you get hosed immediately, go out
* smiling... :-) -3.
@@ -1035,7 +1041,7 @@ die:
#ifdef WIZARD
if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
#endif
savebones(corpse);
savebones(how, endtime, corpse);
/* corpse may be invalid pointer now so
ensure that it isn't used again */
corpse = (struct obj *)0;
@@ -1067,7 +1073,7 @@ die:
endwin = create_nhwindow(NHW_TEXT);
if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
outrip(endwin, how);
outrip(endwin, how, endtime);
} else
done_stopprint = 1; /* just avoid any more output */
@@ -1220,15 +1226,11 @@ die:
/* "So when I die, the first thing I will see in Heaven is a
* score list?" */
if (iflags.toptenwin) {
topten(how);
if (have_windows)
exit_nhwindows((char *)0);
} else {
if (have_windows)
exit_nhwindows((char *)0);
topten(how);
}
if (have_windows && !iflags.toptenwin)
exit_nhwindows((char *)0), have_windows = FALSE;
topten(how, endtime);
if (have_windows)
exit_nhwindows((char *)0);
if(done_stopprint) { raw_print(""); raw_print(""); }
terminate(EXIT_SUCCESS);

View File

@@ -572,6 +572,7 @@ clear_level_structures()
level.buriedobjlist = (struct obj *)0;
level.monlist = (struct monst *)0;
level.damagelist = (struct damage *)0;
level.bonesinfo = (struct cemetery *)0;
level.flags.nfountains = 0;
level.flags.nsinks = 0;
@@ -593,6 +594,7 @@ clear_level_structures()
level.flags.is_maze_lev = 0;
level.flags.is_cavernous_lev = 0;
level.flags.arboreal = 0;
level.flags.wizard_bones = 0;
nroom = 0;
rooms[0].hx = -1;

View File

@@ -39,6 +39,7 @@ STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int));
STATIC_DCL int FDECL(restlevelfile, (int,XCHAR_P));
STATIC_OVL void FDECL(restore_msghistory, (int));
STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P));
STATIC_DCL void FDECL(restcemetery, (int));
STATIC_DCL void FDECL(rest_levl, (int,BOOLEAN_P));
static struct restore_procs {
@@ -901,6 +902,27 @@ register int fd;
return(1);
}
STATIC_OVL void
restcemetery(fd)
int fd;
{
struct cemetery *bonesinfo, **bonesaddr;
int flag;
mread(fd, (genericptr_t)&flag, sizeof flag);
if (flag == 0) {
bonesaddr = &level.bonesinfo;
do {
bonesinfo = (struct cemetery *)alloc(sizeof *bonesinfo);
mread(fd, (genericptr_t)bonesinfo, sizeof *bonesinfo);
*bonesaddr = bonesinfo;
bonesaddr = &(*bonesaddr)->next;
} while (*bonesaddr);
} else {
level.bonesinfo = 0;
}
}
/*ARGSUSED*/
STATIC_OVL void
rest_levl(fd, rlecomp)
@@ -998,6 +1020,7 @@ boolean ghostly;
#endif
trickery(trickbuf);
}
restcemetery(fd);
rest_levl(fd, (boolean)((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
#ifdef DUNGEON_OVERVIEW
mread(fd, (genericptr_t)lastseentyp, sizeof(lastseentyp));

View File

@@ -1,5 +1,4 @@
/* NetHack 3.5 rip.c $Date$ $Revision$ */
/* SCCS Id: @(#)rip.c 3.5 2003/01/08 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -86,13 +85,15 @@ char *text;
void
genl_outrip(tmpwin, how)
genl_outrip(tmpwin, how, when)
winid tmpwin;
int how;
time_t when;
{
register char **dp;
register char *dpx;
char buf[BUFSZ];
long year;
register int x;
int line;
@@ -118,20 +119,7 @@ int how;
center(GOLD_LINE, buf);
/* Put together death description */
switch (killer.format) {
default: impossible("bad killer format?");
case KILLED_BY_AN:
Strcpy(buf, killed_by_prefix[how]);
Strcat(buf, an(killer.name));
break;
case KILLED_BY:
Strcpy(buf, killed_by_prefix[how]);
Strcat(buf, killer.name);
break;
case NO_KILLER_PREFIX:
Strcpy(buf, killer.name);
break;
}
formatkiller(buf, sizeof buf, how);
/* Put death type on stone */
for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
@@ -154,7 +142,8 @@ int how;
}
/* Put year on stone */
Sprintf(buf, "%4d", getyear());
year = yyyymmdd(when) / 10000L;
Sprintf(buf, "%4ld", year);
center(YEAR_LINE, buf);
putstr(tmpwin, 0, "");

View File

@@ -22,6 +22,7 @@ int dotcnt, dotrow; /* also used in restore */
#endif
STATIC_DCL void FDECL(savelevchn, (int,int));
STATIC_DCL void FDECL(savecemetery, (int,int));
STATIC_DCL void FDECL(savedamage, (int,int));
STATIC_DCL void FDECL(saveobj, (int,struct obj *));
STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int));
@@ -520,6 +521,7 @@ int mode;
#else
bwrite(fd,(genericptr_t) &lev,sizeof(lev));
#endif
savecemetery(fd, mode);
savelevl(fd, (boolean)((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
#ifdef DUNGEON_OVERVIEW
bwrite(fd,(genericptr_t) lastseentyp,sizeof(lastseentyp));
@@ -554,6 +556,7 @@ int mode;
fobj = 0;
level.buriedobjlist = 0;
billobjs = 0;
level.bonesinfo = 0;
}
save_engravings(fd, mode);
savedamage(fd, mode);
@@ -918,6 +921,27 @@ register int fd, mode;
sp_levchn = 0;
}
STATIC_OVL void
savecemetery(fd, mode)
int fd;
int mode;
{
struct cemetery *thisbones, *nextbones;
int flag;
flag = level.bonesinfo ? 0 : -1;
if (perform_bwrite(mode))
bwrite(fd, (genericptr_t)&flag, sizeof flag);
nextbones = level.bonesinfo;
while ((thisbones = nextbones) != 0) {
nextbones = thisbones->next;
if (perform_bwrite(mode))
bwrite(fd, (genericptr_t)thisbones, sizeof *thisbones);
if (release_data(mode))
free((genericptr_t)thisbones);
}
}
STATIC_OVL void
savedamage(fd, mode)
register int fd, mode;

View File

@@ -82,6 +82,37 @@ NEARDATA const char * const killed_by_prefix[] = {
static winid toptenwin = WIN_ERR;
/* "killed by",&c ["an"] 'killer.name' */
void
formatkiller(buf, siz, how)
char *buf;
unsigned siz;
int how;
{
unsigned l;
char *kname = killer.name;
buf[0] = '\0'; /* so strncat() can find the end */
switch (killer.format) {
default:
impossible("bad killer format? (%d)", killer.format);
/*FALLTHRU*/
case NO_KILLER_PREFIX:
break;
case KILLED_BY_AN:
kname = an(kname);
/*FALLTHRU*/
case KILLED_BY:
(void) strncat(buf, killed_by_prefix[how], siz - 1);
l = strlen(buf);
buf += l, siz -= l;
break;
}
/* we're writing into buf[0] (after possibly advancing buf) rather than
appending, but strncat() appends a terminator and strncpy() doesn't */
(void) strncat(buf, kname, siz - 1);
}
STATIC_OVL void
topten_print(x)
const char *x;
@@ -271,8 +302,9 @@ struct toptenentry *tt;
}
void
topten(how)
topten(how, when)
int how;
time_t when;
{
int uid = getuid();
int rank, rank0 = -1, rank1 = 0;
@@ -339,25 +371,9 @@ int how;
copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
copynchars(t0->name, plname, NAMSZ);
t0->death[0] = '\0';
switch (killer.format) {
default: impossible("bad killer format?");
case KILLED_BY_AN:
Strcat(t0->death, killed_by_prefix[how]);
(void) strncat(t0->death, an(killer.name),
DTHSZ-strlen(t0->death));
break;
case KILLED_BY:
Strcat(t0->death, killed_by_prefix[how]);
(void) strncat(t0->death, killer.name,
DTHSZ-strlen(t0->death));
break;
case NO_KILLER_PREFIX:
(void) strncat(t0->death, killer.name, DTHSZ);
break;
}
formatkiller(t0->death, sizeof t0->death, how);
t0->birthdate = yyyymmdd(ubirthday);
t0->deathdate = yyyymmdd((time_t)0L);
t0->deathdate = yyyymmdd(when);
t0->tt_next = 0;
#ifdef UPDATE_RECORD_IN_PLACE
t0->fpos = -1L;

View File

@@ -425,7 +425,7 @@ static void FDECL(hup_add_menu, (winid,int,const anything *,CHAR_P,CHAR_P,
static void FDECL(hup_end_menu, (winid,const char *));
static void FDECL(hup_putstr, (winid,int,const char *));
static void FDECL(hup_print_glyph, (winid,XCHAR_P,XCHAR_P,int));
static void FDECL(hup_outrip, (winid,int));
static void FDECL(hup_outrip, (winid,int,time_t));
static void FDECL(hup_curs, (winid,int,int));
static void FDECL(hup_display_nhwindow, (winid,BOOLEAN_P));
static void FDECL(hup_display_file, (const char *,BOOLEAN_P));
@@ -670,9 +670,10 @@ int glyph UNUSED;
/*ARGSUSED*/
static void
hup_outrip(tmpwin, how)
hup_outrip(tmpwin, how, when)
winid tmpwin UNUSED;
int how UNUSED;
time_t when UNUSED;
{
return;
}

View File

@@ -429,11 +429,12 @@ chainin_end_screen()
}
void
chainin_outrip(tmpwin, how)
chainin_outrip(tmpwin, how, when)
winid tmpwin;
int how;
time_t when;
{
(*cibase->nprocs->win_outrip)(cibase->ndata, tmpwin, how);
(*cibase->nprocs->win_outrip)(cibase->ndata, tmpwin, how, when);
}
void

View File

@@ -541,14 +541,15 @@ chainout_end_screen(vp)
}
void
chainout_outrip(vp, tmpwin, how)
chainout_outrip(vp, tmpwin, how, when)
void *vp;
winid tmpwin;
int how;
time_t when;
{
struct chainout_data *tdp = vp;
(*tdp->nprocs->win_outrip)(tmpwin, how);
(*tdp->nprocs->win_outrip)(tmpwin, how, when);
}
void

View File

@@ -928,17 +928,19 @@ trace_end_screen(vp)
}
void
trace_outrip(vp, tmpwin, how)
trace_outrip(vp, tmpwin, how, when)
void *vp;
winid tmpwin;
int how;
time_t when;
{
struct trace_data *tdp = vp;
fprintf(wc_tracelogf, "%soutrip(%d, %d)\n", INDENT, tmpwin, how);
fprintf(wc_tracelogf, "%soutrip(%d, %d, %ld)\n",
INDENT, (int)tmpwin, how, (long)when);
PRE;
(*tdp->nprocs->win_outrip)(tdp->ndata, tmpwin, how);
(*tdp->nprocs->win_outrip)(tdp->ndata, tmpwin, how, when);
POST;
}