Add end-of-game dumplogs
This is based on the "new" dumplog patch for 3.6.0, by Maxime Bacoux. Define DUMPLOG to enable. By default only enabled for the TTY linux.
This commit is contained in:
@@ -3776,6 +3776,24 @@ to identify unique people for the score file.
|
||||
.lp
|
||||
MAX_STATUENAME_RANK\ =\ Maximum number of score file entries to use for
|
||||
random statue names (default is 10).
|
||||
.lp
|
||||
DUMPLOGFILE\ =\ A filename where the end-of-game dumplog is saved.
|
||||
Not defining this will prevent dumplog from being created. Only available
|
||||
if your game is compiled with DUMPLOG. Allows the following placeholders:
|
||||
.sd
|
||||
.si
|
||||
%% - literal '%'
|
||||
%v - version (eg. "3.6.1-0")
|
||||
%u - game UID
|
||||
%t - game start time, UNIX timestamp format
|
||||
%T - current time, UNIX timestamp format
|
||||
%d - game start time, YYYYMMDDhhmmss format
|
||||
%D - current time, YYYYMMDDhhmmss format
|
||||
%n - player name
|
||||
%N - first character of player name
|
||||
.ei
|
||||
.ed
|
||||
|
||||
.hn 1
|
||||
Scoring
|
||||
.pg
|
||||
|
||||
@@ -4589,6 +4589,24 @@ Minimum number of points to get an entry in the score file.
|
||||
\item[\ib{PERS\verb+_+IS\verb+_+UID}]
|
||||
0 or 1 to use user names or numeric userids, respectively, to identify
|
||||
unique people for the score file
|
||||
%.lp
|
||||
\item[\ib{DUMPLOGFILE}]
|
||||
A filename where the end-of-game dumplog is saved.
|
||||
Not defining this will prevent dumplog from being created. Only available
|
||||
if your game is compiled with DUMPLOG. Allows the following placeholders:
|
||||
%.sd
|
||||
%.si
|
||||
{\tt \%\%} --- literal `{\tt \%}'\\
|
||||
{\tt \%v} --- version (eg. "3.6.1-0")\\
|
||||
{\tt \%u} --- game UID\\
|
||||
{\tt \%t} --- game start time, UNIX timestamp format\\
|
||||
{\tt \%T} --- current time, UNIX timestamp format\\
|
||||
{\tt \%d} --- game start time, YYYYMMDDhhmmss format\\
|
||||
{\tt \%D} --- current time, YYYYMMDDhhmmss format\\
|
||||
{\tt \%n} --- player name\\
|
||||
{\tt \%N} --- first character of player name
|
||||
%.ei
|
||||
%.ed
|
||||
\elist
|
||||
|
||||
%.hn 1
|
||||
|
||||
@@ -562,6 +562,7 @@ Ray Chason's MS-DOS port restored to functionality with credit to Reddit user
|
||||
Ray Chason's MSDOS port support for some VESA modes
|
||||
Darshan Shaligram's pet ranged attack
|
||||
Jason Dorje Short's key rebinding
|
||||
Maxime Bacoux's new dumplog
|
||||
|
||||
|
||||
Code Cleanup and Reorganization
|
||||
|
||||
@@ -508,6 +508,32 @@ typedef unsigned char uchar;
|
||||
but it isn't necessary for successful operation of the program */
|
||||
#define FREE_ALL_MEMORY /* free all memory at exit */
|
||||
|
||||
/* #define DUMPLOG */ /* End-of-game dump logs */
|
||||
#ifdef DUMPLOG
|
||||
|
||||
#ifndef DUMPLOG_MSG_COUNT
|
||||
#define DUMPLOG_MSG_COUNT 50
|
||||
#endif
|
||||
|
||||
#ifndef DUMPLOG_FILE
|
||||
#define DUMPLOG_FILE "/tmp/nethack.%n.%d.log"
|
||||
/* DUMPLOG_FILE allows following placeholders:
|
||||
%% literal '%'
|
||||
%v version (eg. "3.6.1-0")
|
||||
%u game UID
|
||||
%t game start time, UNIX timestamp format
|
||||
%T current time, UNIX timestamp format
|
||||
%d game start time, YYYYMMDDhhmmss format
|
||||
%D current time, YYYYMMDDhhmmss format
|
||||
%n player name
|
||||
%N first character of player name
|
||||
DUMPLOG_FILE is not used if SYSCF is defined
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* End of Section 4 */
|
||||
|
||||
#ifdef TTY_TILES_ESCCODES
|
||||
|
||||
@@ -141,6 +141,8 @@ E int NDECL(getbones);
|
||||
|
||||
/* ### botl.c ### */
|
||||
|
||||
E char *NDECL(do_statusline1);
|
||||
E char *NDECL(do_statusline2);
|
||||
E int FDECL(xlev_to_rank, (int));
|
||||
E int FDECL(title_to_mon, (const char *, int *, int *));
|
||||
E void NDECL(max_rank_sz);
|
||||
@@ -268,6 +270,7 @@ E void NDECL(warnreveal);
|
||||
E int FDECL(dosearch0, (int));
|
||||
E int NDECL(dosearch);
|
||||
E void NDECL(sokoban_detect);
|
||||
E void NDECL(dump_map);
|
||||
E void FDECL(reveal_terrain, (int, int));
|
||||
|
||||
/* ### dig.c ### */
|
||||
@@ -2711,6 +2714,11 @@ E void FDECL(genl_status_threshold, (int, int, anything, int, int, int));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
E void FDECL(dump_open_log, (time_t));
|
||||
E void NDECL(dump_close_log);
|
||||
E void FDECL(dump_redirect, (boolean));
|
||||
E void FDECL(dump_forward_putstr, (winid, int, const char*, int));
|
||||
|
||||
/* ### wizard.c ### */
|
||||
|
||||
E void NDECL(amulet);
|
||||
|
||||
@@ -201,6 +201,7 @@ struct instance_flags {
|
||||
boolean vision_inited; /* true if vision is ready */
|
||||
boolean sanity_check; /* run sanity checks */
|
||||
boolean mon_polycontrol; /* debug: control monster polymorphs */
|
||||
boolean in_dumplog; /* doing the dumplog right now? */
|
||||
|
||||
/* stuff that is related to options and/or user or platform preferences
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,9 @@ struct sysopt {
|
||||
char *shellers; /* like wizards, for ! command (-DSHELL); also ^Z */
|
||||
char *genericusers; /* usernames that prompt for user name */
|
||||
char *debugfiles; /* files to show debugplines in. '*' is all. */
|
||||
#ifdef DUMPLOG
|
||||
char *dumplogfile; /* where the dump file is saved */
|
||||
#endif
|
||||
int env_dbgfl; /* 1: debugfiles comes from getenv("DEBUGFILES")
|
||||
* so sysconf's DEBUGFILES shouldn't override it;
|
||||
* 0: getenv() hasn't been attempted yet;
|
||||
|
||||
37
src/botl.c
37
src/botl.c
@@ -13,15 +13,12 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed",
|
||||
STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
|
||||
STATIC_DCL const char *NDECL(rank);
|
||||
|
||||
#ifndef STATUS_VIA_WINDOWPORT
|
||||
#if !defined(STATUS_VIA_WINDOWPORT) || defined(DUMPLOG)
|
||||
|
||||
STATIC_DCL void NDECL(bot1);
|
||||
STATIC_DCL void NDECL(bot2);
|
||||
|
||||
STATIC_OVL void
|
||||
bot1()
|
||||
char *
|
||||
do_statusline1()
|
||||
{
|
||||
char newbot1[MAXCO];
|
||||
static char newbot1[BUFSZ];
|
||||
register char *nb;
|
||||
register int i, j;
|
||||
|
||||
@@ -71,14 +68,13 @@ bot1()
|
||||
if (flags.showscore)
|
||||
Sprintf(nb = eos(nb), " S:%ld", botl_score());
|
||||
#endif
|
||||
curs(WIN_STATUS, 1, 0);
|
||||
putstr(WIN_STATUS, 0, newbot1);
|
||||
return newbot1;
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
bot2()
|
||||
char *
|
||||
do_statusline2()
|
||||
{
|
||||
char newbot2[MAXCO], /* MAXCO: botl.h */
|
||||
static char newbot2[BUFSZ], /* MAXCO: botl.h */
|
||||
/* dungeon location (and gold), hero health (HP, PW, AC),
|
||||
experience (HD if poly'd, else Exp level and maybe Exp points),
|
||||
time (in moves), varying number of status conditions */
|
||||
@@ -102,7 +98,8 @@ bot2()
|
||||
if ((money = money_cnt(invent)) < 0L)
|
||||
money = 0L; /* ought to issue impossible() and then discard gold */
|
||||
Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
|
||||
encglyph(objnum_to_glyph(GOLD_PIECE)), min(money, 999999L));
|
||||
iflags.in_dumplog ? "$" : encglyph(objnum_to_glyph(GOLD_PIECE)),
|
||||
min(money, 999999L));
|
||||
dln = strlen(dloc);
|
||||
/* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
|
||||
dx = strstri(dloc, "\\G") ? 9 : 0;
|
||||
@@ -205,23 +202,25 @@ bot2()
|
||||
/* only two or three consecutive spaces available to squeeze out */
|
||||
mungspaces(newbot2);
|
||||
}
|
||||
|
||||
curs(WIN_STATUS, 1, 1);
|
||||
putmixed(WIN_STATUS, 0, newbot2);
|
||||
return newbot2;
|
||||
}
|
||||
|
||||
#ifndef STATUS_VIA_WINDOWPORT
|
||||
void
|
||||
bot()
|
||||
{
|
||||
if (youmonst.data && iflags.status_updates) {
|
||||
bot1();
|
||||
bot2();
|
||||
curs(WIN_STATUS, 1, 0);
|
||||
putstr(WIN_STATUS, 0, do_statusline1());
|
||||
curs(WIN_STATUS, 1, 1);
|
||||
putmixed(WIN_STATUS, 0, do_statusline2());
|
||||
}
|
||||
context.botl = context.botlx = 0;
|
||||
}
|
||||
|
||||
#endif /* !STATUS_VIA_WINDOWPORT */
|
||||
|
||||
#endif /* !STATUS_VIA_WINDOWPORT || DUMPLOG */
|
||||
|
||||
/* convert experience level (1..30) to rank index (0..8) */
|
||||
int
|
||||
xlev_to_rank(xlev)
|
||||
|
||||
180
src/detect.c
180
src/detect.c
@@ -25,6 +25,7 @@ STATIC_DCL void FDECL(show_map_spot, (int, int));
|
||||
STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
|
||||
STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
|
||||
STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
|
||||
STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int, unsigned, int, int));
|
||||
|
||||
/* bring hero out from underwater or underground or being engulfed;
|
||||
return True iff any change occurred */
|
||||
@@ -1715,6 +1716,111 @@ sokoban_detect()
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_DCL int
|
||||
reveal_terrain_getglyph(x,y, full, swallowed, default_glyph, which_subset)
|
||||
int x,y, full;
|
||||
unsigned swallowed;
|
||||
int default_glyph, which_subset;
|
||||
{
|
||||
int glyph, levl_glyph;
|
||||
uchar seenv;
|
||||
boolean keep_traps = (which_subset & TER_TRP) !=0,
|
||||
keep_objs = (which_subset & TER_OBJ) != 0,
|
||||
keep_mons = (which_subset & TER_MON) != 0;
|
||||
struct monst *mtmp;
|
||||
struct trap *t;
|
||||
|
||||
/* for 'full', show the actual terrain for the entire level,
|
||||
otherwise what the hero remembers for seen locations with
|
||||
monsters, objects, and/or traps removed as caller dictates */
|
||||
seenv = (full || level.flags.hero_memory)
|
||||
? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
|
||||
if (full) {
|
||||
levl[x][y].seenv = SVALL;
|
||||
glyph = back_to_glyph(x, y);
|
||||
levl[x][y].seenv = seenv;
|
||||
} else {
|
||||
levl_glyph = level.flags.hero_memory
|
||||
? levl[x][y].glyph
|
||||
: seenv
|
||||
? back_to_glyph(x, y)
|
||||
: default_glyph;
|
||||
/* glyph_at() returns the displayed glyph, which might
|
||||
be a monster. levl[][].glyph contains the remembered
|
||||
glyph, which will never be a monster (unless it is
|
||||
the invisible monster glyph, which is handled like
|
||||
an object, replacing any object or trap at its spot) */
|
||||
glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
|
||||
if (keep_mons && x == u.ux && y == u.uy && swallowed)
|
||||
glyph = mon_to_glyph(u.ustuck);
|
||||
else if (((glyph_is_monster(glyph)
|
||||
|| glyph_is_warning(glyph)) && !keep_mons)
|
||||
|| glyph_is_swallow(glyph))
|
||||
glyph = levl_glyph;
|
||||
if (((glyph_is_object(glyph) && !keep_objs)
|
||||
|| glyph_is_invisible(glyph))
|
||||
&& keep_traps && !covers_traps(x, y)) {
|
||||
if ((t = t_at(x, y)) != 0 && t->tseen)
|
||||
glyph = trap_to_glyph(t);
|
||||
}
|
||||
if ((glyph_is_object(glyph) && !keep_objs)
|
||||
|| (glyph_is_trap(glyph) && !keep_traps)
|
||||
|| glyph_is_invisible(glyph)) {
|
||||
if (!seenv) {
|
||||
glyph = default_glyph;
|
||||
} else if (lastseentyp[x][y] == levl[x][y].typ) {
|
||||
glyph = back_to_glyph(x, y);
|
||||
} else {
|
||||
/* look for a mimic here posing as furniture;
|
||||
if we don't find one, we'll have to fake it */
|
||||
if ((mtmp = m_at(x, y)) != 0
|
||||
&& mtmp->m_ap_type == M_AP_FURNITURE) {
|
||||
glyph = cmap_to_glyph(mtmp->mappearance);
|
||||
} else {
|
||||
/* we have a topology type but we want a
|
||||
screen symbol in order to derive a glyph;
|
||||
some screen symbols need the flags field
|
||||
of levl[][] in addition to the type
|
||||
(to disambiguate STAIRS to S_upstair or
|
||||
S_dnstair, for example; current flags
|
||||
might not be intended for remembered
|
||||
type, but we've got no other choice) */
|
||||
schar save_typ = levl[x][y].typ;
|
||||
|
||||
levl[x][y].typ = lastseentyp[x][y];
|
||||
glyph = back_to_glyph(x, y);
|
||||
levl[x][y].typ = save_typ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (glyph == cmap_to_glyph(S_darkroom))
|
||||
glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
|
||||
return glyph;
|
||||
}
|
||||
|
||||
void
|
||||
dump_map()
|
||||
{
|
||||
int x, y, glyph;
|
||||
int subset = TER_MAP|TER_TRP|TER_OBJ|TER_MON;
|
||||
int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
|
||||
char buf[BUFSZ];
|
||||
|
||||
for (y = 0; y < ROWNO; y++) {
|
||||
for (x = 1; x < COLNO; x++) {
|
||||
int ch, color;
|
||||
unsigned special;
|
||||
glyph = reveal_terrain_getglyph(x,y, FALSE, u.uswallow,
|
||||
default_glyph, subset);
|
||||
(void) mapglyph(glyph, &ch, &color, &special, x, y);
|
||||
buf[x-1] = ch;
|
||||
}
|
||||
buf[x-2] = '\0';
|
||||
putstr(0,0, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* idea from crawl; show known portion of map without any monsters,
|
||||
objects, or traps occluding the view of the underlying terrain */
|
||||
void
|
||||
@@ -1725,10 +1831,7 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
|
||||
if ((Hallucination || Stunned || Confusion) && !full) {
|
||||
You("are too disoriented for this.");
|
||||
} else {
|
||||
int x, y, glyph, levl_glyph, default_glyph;
|
||||
uchar seenv;
|
||||
struct monst *mtmp;
|
||||
struct trap *t;
|
||||
int x, y, glyph, default_glyph;
|
||||
char buf[BUFSZ];
|
||||
/* there is a TER_MAP bit too; we always show map regardless of it */
|
||||
boolean keep_traps = (which_subset & TER_TRP) !=0,
|
||||
@@ -1739,74 +1842,11 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
|
||||
if (unconstrain_map())
|
||||
docrt();
|
||||
default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
|
||||
/* for 'full', show the actual terrain for the entire level,
|
||||
otherwise what the hero remembers for seen locations with
|
||||
monsters, objects, and/or traps removed as caller dictates */
|
||||
|
||||
for (x = 1; x < COLNO; x++)
|
||||
for (y = 0; y < ROWNO; y++) {
|
||||
seenv = (full || level.flags.hero_memory)
|
||||
? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
|
||||
if (full) {
|
||||
levl[x][y].seenv = SVALL;
|
||||
glyph = back_to_glyph(x, y);
|
||||
levl[x][y].seenv = seenv;
|
||||
} else {
|
||||
levl_glyph = level.flags.hero_memory
|
||||
? levl[x][y].glyph
|
||||
: seenv
|
||||
? back_to_glyph(x, y)
|
||||
: default_glyph;
|
||||
/* glyph_at() returns the displayed glyph, which might
|
||||
be a monster. levl[][].glyph contains the remembered
|
||||
glyph, which will never be a monster (unless it is
|
||||
the invisible monster glyph, which is handled like
|
||||
an object, replacing any object or trap at its spot) */
|
||||
glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
|
||||
if (keep_mons && x == u.ux && y == u.uy && swallowed)
|
||||
glyph = mon_to_glyph(u.ustuck);
|
||||
else if (((glyph_is_monster(glyph)
|
||||
|| glyph_is_warning(glyph)) && !keep_mons)
|
||||
|| glyph_is_swallow(glyph))
|
||||
glyph = levl_glyph;
|
||||
if (((glyph_is_object(glyph) && !keep_objs)
|
||||
|| glyph_is_invisible(glyph))
|
||||
&& keep_traps && !covers_traps(x, y)) {
|
||||
if ((t = t_at(x, y)) != 0 && t->tseen)
|
||||
glyph = trap_to_glyph(t);
|
||||
}
|
||||
if ((glyph_is_object(glyph) && !keep_objs)
|
||||
|| (glyph_is_trap(glyph) && !keep_traps)
|
||||
|| glyph_is_invisible(glyph)) {
|
||||
if (!seenv) {
|
||||
glyph = default_glyph;
|
||||
} else if (lastseentyp[x][y] == levl[x][y].typ) {
|
||||
glyph = back_to_glyph(x, y);
|
||||
} else {
|
||||
/* look for a mimic here posing as furniture;
|
||||
if we don't find one, we'll have to fake it */
|
||||
if ((mtmp = m_at(x, y)) != 0
|
||||
&& mtmp->m_ap_type == M_AP_FURNITURE) {
|
||||
glyph = cmap_to_glyph(mtmp->mappearance);
|
||||
} else {
|
||||
/* we have a topology type but we want a
|
||||
screen symbol in order to derive a glyph;
|
||||
some screen symbols need the flags field
|
||||
of levl[][] in addition to the type
|
||||
(to disambiguate STAIRS to S_upstair or
|
||||
S_dnstair, for example; current flags
|
||||
might not be intended for remembered
|
||||
type, but we've got no other choice) */
|
||||
schar save_typ = levl[x][y].typ;
|
||||
|
||||
levl[x][y].typ = lastseentyp[x][y];
|
||||
glyph = back_to_glyph(x, y);
|
||||
levl[x][y].typ = save_typ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (glyph == cmap_to_glyph(S_darkroom))
|
||||
glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
|
||||
glyph = reveal_terrain_getglyph(x,y, full, swallowed,
|
||||
default_glyph, which_subset);
|
||||
show_glyph(x, y, glyph);
|
||||
}
|
||||
|
||||
|
||||
171
src/end.c
171
src/end.c
@@ -667,6 +667,88 @@ char *defquery;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef DUMPLOG
|
||||
STATIC_OVL void
|
||||
dump_plines()
|
||||
{
|
||||
int i;
|
||||
char* str;
|
||||
extern char* saved_plines[];
|
||||
|
||||
putstr(0, 0, "");
|
||||
putstr(0, 0, "Latest messages:");
|
||||
for (i = 0; i < DUMPLOG_MSG_COUNT; ++i)
|
||||
{
|
||||
str = saved_plines[DUMPLOG_MSG_COUNT - 1 - i];
|
||||
if (str) {
|
||||
char buf[BUFSZ];
|
||||
Sprintf(buf, " %s", str);
|
||||
putstr(0, 0, buf);
|
||||
}
|
||||
#ifdef FREE_ALL_MEMORY
|
||||
free(str);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC_OVL void
|
||||
dump_everything(how, taken)
|
||||
int how;
|
||||
boolean taken;
|
||||
{
|
||||
#ifdef DUMPLOG
|
||||
struct obj* obj;
|
||||
struct topl* topl;
|
||||
char pbuf[BUFSZ];
|
||||
|
||||
dump_redirect(TRUE);
|
||||
if (!iflags.in_dumplog)
|
||||
return;
|
||||
|
||||
init_symbols();
|
||||
|
||||
for (obj = invent; obj; obj = obj->nobj) {
|
||||
makeknown(obj->otyp);
|
||||
obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
|
||||
if (Is_container(obj) || obj->otyp == STATUE)
|
||||
obj->cknown = obj->lknown = 1;
|
||||
}
|
||||
|
||||
Sprintf(pbuf, "%s, %s %s %s %s", plname,
|
||||
aligns[1 - u.ualign.type].adj,
|
||||
genders[flags.female].adj,
|
||||
urace.adj,
|
||||
(flags.female && urole.name.f) ? urole.name.f : urole.name.m);
|
||||
putstr(0, 0, pbuf);
|
||||
putstr(0, 0, "");
|
||||
|
||||
dump_map();
|
||||
putstr(0, 0, do_statusline1());
|
||||
putstr(0, 0, do_statusline2());
|
||||
putstr(0, 0, "");
|
||||
|
||||
dump_plines();
|
||||
putstr(0, 0, "");
|
||||
putstr(0, 0, "Inventory:");
|
||||
display_inventory((char *) 0, TRUE);
|
||||
container_contents(invent, TRUE, TRUE, FALSE);
|
||||
enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
|
||||
(how >= PANICKED) ? ENL_GAMEOVERALIVE
|
||||
: ENL_GAMEOVERDEAD);
|
||||
putstr(0, 0, "");
|
||||
list_vanquished('y', FALSE);
|
||||
putstr(0, 0, "");
|
||||
list_genocided('a', FALSE);
|
||||
putstr(0, 0, "");
|
||||
show_conduct((how >= PANICKED) ? 1 : 2);
|
||||
putstr(0, 0, "");
|
||||
show_overview((how >= PANICKED) ? 1 : 2, how);
|
||||
putstr(0, 0, "");
|
||||
dump_redirect(FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
disclose(how, taken)
|
||||
int how;
|
||||
@@ -1016,6 +1098,7 @@ int how;
|
||||
urealtime.finish_time = endtime = getnow();
|
||||
urealtime.realtime += (long) (endtime - urealtime.start_timing);
|
||||
|
||||
dump_open_log(endtime);
|
||||
/* Sometimes you die on the first move. Life's not fair.
|
||||
* On those rare occasions you get hosed immediately, go out
|
||||
* smiling... :-) -3.
|
||||
@@ -1081,6 +1164,7 @@ int how;
|
||||
|
||||
if (strcmp(flags.end_disclose, "none") && how != PANICKED)
|
||||
disclose(how, taken);
|
||||
dump_everything(how, taken);
|
||||
|
||||
/* finish_paybill should be called after disclosure but before bones */
|
||||
if (bones_ok && taken)
|
||||
@@ -1179,6 +1263,11 @@ int how;
|
||||
} else
|
||||
done_stopprint = 1; /* just avoid any more output */
|
||||
|
||||
#ifdef DUMPLOG
|
||||
dump_redirect(TRUE);
|
||||
genl_outrip(0, how, endtime);
|
||||
dump_redirect(FALSE);
|
||||
#endif
|
||||
if (u.uhave.amulet) {
|
||||
Strcat(killer.name, " (with the Amulet)");
|
||||
} else if (how == ESCAPED) {
|
||||
@@ -1189,16 +1278,14 @@ int how;
|
||||
/* don't bother counting to see whether it should be plural */
|
||||
}
|
||||
|
||||
if (!done_stopprint) {
|
||||
Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
|
||||
(how != ASCENDED)
|
||||
? (const char *) ((flags.female && urole.name.f)
|
||||
? urole.name.f
|
||||
: urole.name.m)
|
||||
: (const char *) (flags.female ? "Demigoddess" : "Demigod"));
|
||||
putstr(endwin, 0, pbuf);
|
||||
putstr(endwin, 0, "");
|
||||
}
|
||||
Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
|
||||
(how != ASCENDED)
|
||||
? (const char *) ((flags.female && urole.name.f)
|
||||
? urole.name.f
|
||||
: urole.name.m)
|
||||
: (const char *) (flags.female ? "Demigoddess" : "Demigod"));
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
dump_forward_putstr(endwin, 0, "", done_stopprint);
|
||||
|
||||
if (how == ESCAPED || how == ASCENDED) {
|
||||
struct monst *mtmp;
|
||||
@@ -1223,45 +1310,48 @@ int how;
|
||||
|
||||
/* count the points for artifacts */
|
||||
artifact_score(invent, TRUE, endwin);
|
||||
#ifdef DUMPLOG
|
||||
dump_redirect(TRUE);
|
||||
artifact_score(invent, TRUE, endwin);
|
||||
dump_redirect(FALSE);
|
||||
#endif
|
||||
|
||||
viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
|
||||
mtmp = mydogs;
|
||||
if (!done_stopprint)
|
||||
Strcpy(pbuf, "You");
|
||||
Strcpy(pbuf, "You");
|
||||
if (!Schroedingers_cat) /* check here in case disclosure was off */
|
||||
Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
|
||||
if (Schroedingers_cat) {
|
||||
int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
|
||||
mhp = d(m_lev, 8);
|
||||
nowrap_add(u.urexp, mhp);
|
||||
if (!done_stopprint)
|
||||
Strcat(eos(pbuf), " and Schroedinger's cat");
|
||||
Strcat(eos(pbuf), " and Schroedinger's cat");
|
||||
}
|
||||
if (mtmp) {
|
||||
while (mtmp) {
|
||||
if (!done_stopprint)
|
||||
Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
|
||||
Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
|
||||
if (mtmp->mtame)
|
||||
nowrap_add(u.urexp, mtmp->mhp);
|
||||
mtmp = mtmp->nmon;
|
||||
}
|
||||
if (!done_stopprint)
|
||||
putstr(endwin, 0, pbuf);
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
pbuf[0] = '\0';
|
||||
} else {
|
||||
if (!done_stopprint)
|
||||
Strcat(pbuf, " ");
|
||||
}
|
||||
if (!done_stopprint) {
|
||||
Sprintf(eos(pbuf), "%s with %ld point%s,",
|
||||
how == ASCENDED ? "went to your reward"
|
||||
: "escaped from the dungeon",
|
||||
u.urexp, plur(u.urexp));
|
||||
putstr(endwin, 0, pbuf);
|
||||
Strcat(pbuf, " ");
|
||||
}
|
||||
Sprintf(eos(pbuf), "%s with %ld point%s,",
|
||||
how == ASCENDED ? "went to your reward"
|
||||
: "escaped from the dungeon",
|
||||
u.urexp, plur(u.urexp));
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
|
||||
if (!done_stopprint)
|
||||
artifact_score(invent, FALSE, endwin); /* list artifacts */
|
||||
#if DUMPLOG
|
||||
dump_redirect(TRUE);
|
||||
artifact_score(invent, FALSE, 0);
|
||||
dump_redirect(FALSE);
|
||||
#endif
|
||||
|
||||
/* list valuables here */
|
||||
for (val = valuables; val->list; val++) {
|
||||
@@ -1288,11 +1378,11 @@ int how;
|
||||
Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
|
||||
count, plur(count));
|
||||
}
|
||||
putstr(endwin, 0, pbuf);
|
||||
dump_forward_putstr(endwin, 0, pbuf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!done_stopprint) {
|
||||
} else {
|
||||
/* did not escape or ascend */
|
||||
if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
|
||||
/* level teleported out of the dungeon; `how' is DIED,
|
||||
@@ -1312,26 +1402,23 @@ int how;
|
||||
}
|
||||
|
||||
Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
|
||||
putstr(endwin, 0, pbuf);
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
}
|
||||
|
||||
if (!done_stopprint) {
|
||||
Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
|
||||
plur(umoney), moves, plur(moves));
|
||||
putstr(endwin, 0, pbuf);
|
||||
}
|
||||
if (!done_stopprint) {
|
||||
Sprintf(pbuf,
|
||||
"You were level %d with a maximum of %d hit point%s when you %s.",
|
||||
u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
|
||||
putstr(endwin, 0, pbuf);
|
||||
putstr(endwin, 0, "");
|
||||
}
|
||||
Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
|
||||
plur(umoney), moves, plur(moves));
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
Sprintf(pbuf,
|
||||
"You were level %d with a maximum of %d hit point%s when you %s.",
|
||||
u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
|
||||
dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
|
||||
dump_forward_putstr(endwin, 0, "", done_stopprint);
|
||||
if (!done_stopprint)
|
||||
display_nhwindow(endwin, TRUE);
|
||||
if (endwin != WIN_ERR)
|
||||
destroy_nhwindow(endwin);
|
||||
|
||||
dump_close_log();
|
||||
/* "So when I die, the first thing I will see in Heaven is a
|
||||
* score list?" */
|
||||
if (have_windows && !iflags.toptenwin)
|
||||
|
||||
@@ -2267,8 +2267,15 @@ int src;
|
||||
free((genericptr_t) sysopt.debugfiles);
|
||||
sysopt.debugfiles = dupstr(bufp);
|
||||
}
|
||||
} else if (src == SET_IN_SYS && match_varname(buf, "DUMPLOGFILE", 7)) {
|
||||
#ifdef DUMPLOG
|
||||
if (sysopt.dumplogfile)
|
||||
free((genericptr_t) sysopt.dumplogfile);
|
||||
sysopt.dumplogfile = dupstr(bufp);
|
||||
#endif
|
||||
} else if (src == SET_IN_SYS && match_varname(buf, "GENERICUSERS", 12)) {
|
||||
if (sysopt.genericusers) free(sysopt.genericusers);
|
||||
if (sysopt.genericusers)
|
||||
free((genericptr_t) sysopt.genericusers);
|
||||
sysopt.genericusers = dupstr(bufp);
|
||||
} else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
|
||||
if (sysopt.support)
|
||||
|
||||
13
src/pline.c
13
src/pline.c
@@ -14,6 +14,10 @@ static char *FDECL(You_buf, (int));
|
||||
static void FDECL(execplinehandler, (const char *));
|
||||
#endif
|
||||
|
||||
#ifdef DUMPLOG
|
||||
char* saved_plines[DUMPLOG_MSG_COUNT] = {0};
|
||||
#endif
|
||||
|
||||
/*VARARGS1*/
|
||||
/* Note that these declarations rely on knowledge of the internals
|
||||
* of the variable argument handling stuff in "tradstdc.h"
|
||||
@@ -87,6 +91,15 @@ VA_DECL(const char *, line)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DUMPLOG
|
||||
/* We hook here early to have options-agnostic output. */
|
||||
free(saved_plines[DUMPLOG_MSG_COUNT - 1]);
|
||||
for (ln = 0; ln < DUMPLOG_MSG_COUNT - 1; ++ln)
|
||||
saved_plines[DUMPLOG_MSG_COUNT - ln - 1] = saved_plines[DUMPLOG_MSG_COUNT - ln - 2];
|
||||
saved_plines[0] = malloc(strlen(line) + 1);
|
||||
(void) strcpy(saved_plines[0], line);
|
||||
#endif
|
||||
|
||||
msgtyp = msgtype_type(line, no_repeat);
|
||||
if (msgtyp == MSGTYP_NOSHOW
|
||||
|| (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
|
||||
|
||||
@@ -31,6 +31,9 @@ sys_early_init()
|
||||
sysopt.debugfiles = (char *) 0;
|
||||
#else
|
||||
sysopt.debugfiles = dupstr(DEBUGFILES);
|
||||
#endif
|
||||
#ifdef DUMPLOG
|
||||
sysopt.dumplogfile = (char *) 0;
|
||||
#endif
|
||||
sysopt.env_dbgfl = 0; /* haven't checked getenv("DEBUGFILES") yet */
|
||||
sysopt.shellers = (char *) 0;
|
||||
@@ -95,6 +98,10 @@ sysopt_release()
|
||||
if (sysopt.debugfiles)
|
||||
free((genericptr_t) sysopt.debugfiles),
|
||||
sysopt.debugfiles = (char *) 0;
|
||||
#ifdef DUMPLOG
|
||||
if (sysopt.dumplogfile)
|
||||
free((genericptr_t)sysopt.dumplogfile), sysopt.dumplogfile=(char *)0;
|
||||
#endif
|
||||
if (sysopt.genericusers)
|
||||
free((genericptr_t) sysopt.genericusers),
|
||||
sysopt.genericusers = (char *) 0;
|
||||
|
||||
239
src/windows.c
239
src/windows.c
@@ -1032,4 +1032,243 @@ int behavior UNUSED, under UNUSED, over UNUSED;
|
||||
#endif /* STATUS_HILITES */
|
||||
#endif /* STATUS_VIA_WINDOWPORT */
|
||||
|
||||
STATIC_VAR struct window_procs dumplog_windowprocs_backup;
|
||||
STATIC_PTR FILE* dumplog_file;
|
||||
|
||||
#ifdef DUMPLOG
|
||||
char *
|
||||
dump_fmtstr(fmt, buf)
|
||||
char *fmt;
|
||||
char *buf;
|
||||
{
|
||||
char *fp = fmt, *bp = buf;
|
||||
int slen, len = 0;
|
||||
char tmpbuf[BUFSZ];
|
||||
char verbuf[BUFSZ];
|
||||
|
||||
time_t now = getnow();
|
||||
int uid = getuid();
|
||||
|
||||
while (fp && *fp && len < BUFSZ-1) {
|
||||
if (*fp == '%') {
|
||||
fp++;
|
||||
switch (*fp) {
|
||||
default: goto finish;
|
||||
case '\0': /* fallthrough */
|
||||
case '%': /* literal % */
|
||||
Sprintf(tmpbuf,"%%");
|
||||
break;
|
||||
case 't': /* game start, timestamp */
|
||||
Sprintf(tmpbuf, "%ld", ubirthday);
|
||||
break;
|
||||
case 'T': /* current time, timestamp */
|
||||
Sprintf(tmpbuf, "%ld", now);
|
||||
break;
|
||||
case 'd': /* game start, YYYYMMDDhhmmss */
|
||||
Sprintf(tmpbuf, "%08ld%06ld",
|
||||
yyyymmdd(ubirthday), hhmmss(ubirthday));
|
||||
break;
|
||||
case 'D': /* current time, YYYYMMDDhhmmss */
|
||||
Sprintf(tmpbuf, "%08ld%06ld", yyyymmdd(now), hhmmss(now));
|
||||
break;
|
||||
case 'v': /* version, eg. "3.6.1-0" */
|
||||
Sprintf(tmpbuf, "%s", version_string(verbuf));
|
||||
break;
|
||||
case 'u': /* UID */
|
||||
Sprintf(tmpbuf, "%d", uid);
|
||||
break;
|
||||
case 'n': /* player name */
|
||||
Sprintf(tmpbuf, "%s", (plname ? plname : "unknown"));
|
||||
break;
|
||||
case 'N': /* first character of player name */
|
||||
Sprintf(tmpbuf, "%c", (plname ? *plname : 'u'));
|
||||
break;
|
||||
}
|
||||
|
||||
slen = strlen(tmpbuf);
|
||||
if (len + slen < BUFSZ-1) {
|
||||
len += slen;
|
||||
Sprintf(bp, "%s", tmpbuf);
|
||||
bp += slen;
|
||||
if (*fp) fp++;
|
||||
} else
|
||||
break;
|
||||
} else {
|
||||
*bp = *fp;
|
||||
bp++;
|
||||
fp++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
finish:
|
||||
*bp = '\0';
|
||||
return buf;
|
||||
}
|
||||
#endif /* DUMPLOG */
|
||||
|
||||
|
||||
void
|
||||
dump_open_log(now)
|
||||
time_t now;
|
||||
{
|
||||
#ifdef DUMPLOG
|
||||
char buf[BUFSZ];
|
||||
char *fname;
|
||||
|
||||
#ifdef SYSCF
|
||||
if (!sysopt.dumplogfile)
|
||||
return;
|
||||
fname = dump_fmtstr(sysopt.dumplogfile, buf);
|
||||
#else
|
||||
fname = dump_fmtstr(DUMPLOG_FILE, buf);
|
||||
#endif
|
||||
|
||||
dumplog_file = fopen(fname, "w");
|
||||
dumplog_windowprocs_backup = windowprocs;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
dump_close_log()
|
||||
{
|
||||
if (dumplog_file) {
|
||||
fclose(dumplog_file);
|
||||
dumplog_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_putc(ch)
|
||||
int ch;
|
||||
{
|
||||
/* Not very efficient, but we mostly don't care. */
|
||||
if (dumplog_file)
|
||||
putc(ch, dumplog_file);
|
||||
}
|
||||
|
||||
void
|
||||
dump_forward_putstr(win, attr, str, no_forward)
|
||||
winid win;
|
||||
int attr;
|
||||
const char* str;
|
||||
int no_forward;
|
||||
{
|
||||
if (dumplog_file)
|
||||
fprintf(dumplog_file, "%s\n", str);
|
||||
if (!no_forward)
|
||||
putstr(win, attr, str);
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_putstr(win, attr, str)
|
||||
winid win;
|
||||
int attr;
|
||||
const char* str;
|
||||
{
|
||||
if (dumplog_file)
|
||||
fprintf(dumplog_file, "%s\n", str);
|
||||
}
|
||||
|
||||
STATIC_OVL winid
|
||||
dump_create_nhwindow(dummy)
|
||||
int dummy;
|
||||
{
|
||||
return dummy;
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_clear_nhwindow(win)
|
||||
winid win;
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_display_nhwindow(win, p)
|
||||
winid win;
|
||||
BOOLEAN_P p;
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_destroy_nhwindow(win)
|
||||
winid win;
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_start_menu(win)
|
||||
winid win;
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_add_menu(win, glyph, identifier, ch, gch, attr, str, preselected)
|
||||
winid win;
|
||||
int glyph;
|
||||
const ANY_P* identifier;
|
||||
CHAR_P ch;
|
||||
CHAR_P gch;
|
||||
int attr;
|
||||
const char* str;
|
||||
BOOLEAN_P preselected;
|
||||
{
|
||||
if (dumplog_file) {
|
||||
if (glyph == NO_GLYPH)
|
||||
fprintf(dumplog_file, " %s\n", str);
|
||||
else
|
||||
fprintf(dumplog_file, " %c - %s\n", ch, str);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
dump_end_menu(win, str)
|
||||
winid win;
|
||||
const char* str;
|
||||
{
|
||||
if (dumplog_file) {
|
||||
if (str)
|
||||
fprintf(dumplog_file, "%s\n", str);
|
||||
else
|
||||
fputs("\n", dumplog_file);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_OVL int
|
||||
dump_select_menu(win, index, item)
|
||||
winid win;
|
||||
int index;
|
||||
MENU_ITEM_P** item;
|
||||
{
|
||||
*item = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
dump_redirect(flag)
|
||||
boolean flag;
|
||||
{
|
||||
if (dumplog_file) {
|
||||
if (flag) {
|
||||
windowprocs.win_create_nhwindow = dump_create_nhwindow;
|
||||
windowprocs.win_clear_nhwindow = dump_clear_nhwindow;
|
||||
windowprocs.win_display_nhwindow = dump_display_nhwindow;
|
||||
windowprocs.win_destroy_nhwindow = dump_destroy_nhwindow;
|
||||
windowprocs.win_start_menu = dump_start_menu;
|
||||
windowprocs.win_add_menu = dump_add_menu;
|
||||
windowprocs.win_end_menu = dump_end_menu;
|
||||
windowprocs.win_select_menu = dump_select_menu;
|
||||
windowprocs.win_putstr = dump_putstr;
|
||||
} else {
|
||||
windowprocs = dumplog_windowprocs_backup;
|
||||
}
|
||||
iflags.in_dumplog = flag;
|
||||
} else {
|
||||
iflags.in_dumplog = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*windows.c*/
|
||||
|
||||
@@ -25,6 +25,7 @@ CFLAGS1=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"
|
||||
CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE
|
||||
CFLAGS+=-DTIMED_DELAY
|
||||
CFLAGS+=-DHACKDIR=\"$(HACKDIR)\"
|
||||
CFLAGS+=-DDUMPLOG
|
||||
|
||||
LINK=$(CC)
|
||||
# Only needed for GLIBC stack trace:
|
||||
|
||||
@@ -84,6 +84,20 @@ MAXPLAYERS=10
|
||||
# overridden via DEBUGFILES environment variable.
|
||||
#DEBUGFILES=*
|
||||
|
||||
# Save end of game dump log to this file.
|
||||
# Only available if NetHack was compiled with DUMPLOG
|
||||
# Allows following placeholders:
|
||||
# %% literal '%'
|
||||
# %v version (eg. "3.6.1-0")
|
||||
# %u game UID
|
||||
# %t game start time, UNIX timestamp format
|
||||
# %T current time, UNIX timestamp format
|
||||
# %d game start time, YYYYMMDDhhmmss format
|
||||
# %D current time, YYYYMMDDhhmmss format
|
||||
# %n player name
|
||||
# %N first character of player name
|
||||
#DUMPLOGFILE=/tmp/nethack.%n.%d.log
|
||||
|
||||
# Try to get more info in case of a program bug or crash. Only used
|
||||
# if the program is built with the PANICTRACE compile-time option enabled.
|
||||
# By default PANICTRACE is enabled if BETA is defined, otherwise disabled.
|
||||
|
||||
@@ -18,6 +18,20 @@ WIZARDS=*
|
||||
# Only available if game has been compiled with DEBUG.
|
||||
#DEBUGFILES=*
|
||||
|
||||
# Save end of game dump log to this file.
|
||||
# Only available if NetHack was compiled with DUMPLOG
|
||||
# Allows following placeholders:
|
||||
# %% literal '%'
|
||||
# %v version (eg. "3.6.1-0")
|
||||
# %u game UID
|
||||
# %t game start time, UNIX timestamp format
|
||||
# %T current time, UNIX timestamp format
|
||||
# %d game start time, YYYYMMDDhhmmss format
|
||||
# %D current time, YYYYMMDDhhmmss format
|
||||
# %n player name
|
||||
# %N first character of player name
|
||||
#DUMPLOGFILE=nethack-%n-%d.log
|
||||
|
||||
# Limit the number of simultaneous games (see also nethack.sh).
|
||||
#MAXPLAYERS=10
|
||||
|
||||
|
||||
@@ -1434,6 +1434,9 @@ static const char *build_opts[] = {
|
||||
#ifdef DLB
|
||||
"data librarian",
|
||||
#endif
|
||||
#ifdef DUMPLOG
|
||||
"end-of-game dumplogs",
|
||||
#endif
|
||||
#ifdef MFLOPPY
|
||||
"floppy drive support",
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user