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:
@@ -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 *));
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
28
src/bones.c
28
src/bones.c
@@ -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
|
||||
|
||||
24
src/end.c
24
src/end.c
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
23
src/rip.c
23
src/rip.c
@@ -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, "");
|
||||
|
||||
24
src/save.c
24
src/save.c
@@ -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;
|
||||
|
||||
54
src/topten.c
54
src/topten.c
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user