From 76d7044872f1a401a164aaaeff9f9e5c7bfbb91e Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 6 Feb 2017 16:42:33 +0200 Subject: [PATCH 01/15] Fix #H5056/bz1086: Bug in Achievement Recording Bug report was: > "Completed sokoban" achievement was logged when picking up > a randomly generated bag of holding in the gnomish mines. The picking-up code was missing checks for the branches, so you could get the achievements outside the correct branches. --- doc/fixes36.1 | 1 + include/obj.h | 8 ++++++++ src/invent.c | 6 ++---- src/sp_lev.c | 6 +----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 56916b68e..b2972a89a 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -366,6 +366,7 @@ when sitting at a trap spot: You sit down. You step on a level teleporter. (likewise for polymorph trap, and similar issue for web) show all statusline information in #attributes add option status_updates to prevent bottom of screen status line updates +fix achievement recording bug with mines and sokoban prizes Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/obj.h b/include/obj.h index e2dc585d4..824ccca39 100644 --- a/include/obj.h +++ b/include/obj.h @@ -342,6 +342,14 @@ struct obj { && !undiscovered_artifact(ART_EYES_OF_THE_OVERWORLD))) #define pair_of(o) ((o)->otyp == LENSES || is_gloves(o) || is_boots(o)) +#define is_mines_prize(o) \ + ((o)->otyp == LUCKSTONE && Is_mineend_level(&u.uz)) +#define is_soko_prize(o) \ + (((o)->otyp == AMULET_OF_REFLECTION \ + || (o)->otyp == BAG_OF_HOLDING) \ + && Is_sokoend_level(&u.uz)) + + /* Flags for get_obj_location(). */ #define CONTAINED_TOO 0x1 #define BURIED_TOO 0x2 diff --git a/src/invent.c b/src/invent.c index fa478b516..fb0099bb8 100644 --- a/src/invent.c +++ b/src/invent.c @@ -513,12 +513,10 @@ struct obj *obj; } set_artifact_intrinsic(obj, 1, W_ART); } - if (obj->otyp == LUCKSTONE && obj->record_achieve_special) { + if (is_mines_prize(obj) && obj->record_achieve_special) { u.uachieve.mines_luckstone = 1; obj->record_achieve_special = 0; - } else if ((obj->otyp == AMULET_OF_REFLECTION - || obj->otyp == BAG_OF_HOLDING) - && obj->record_achieve_special) { + } else if (is_soko_prize(obj) && obj->record_achieve_special) { u.uachieve.finish_sokoban = 1; obj->record_achieve_special = 0; } diff --git a/src/sp_lev.c b/src/sp_lev.c index 6f0445bd5..179d45fa3 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1951,11 +1951,7 @@ struct mkroom *croom; * "prize" and then set record_achieve_special (maps to corpsenm) * for the object. That field will later be checked to find out if * the player obtained the prize. */ - if (otmp->otyp == LUCKSTONE && Is_mineend_level(&u.uz)) { - otmp->record_achieve_special = 1; - } else if ((otmp->otyp == AMULET_OF_REFLECTION - || otmp->otyp == BAG_OF_HOLDING) - && Is_sokoend_level(&u.uz)) { + if (is_mines_prize(otmp) || is_soko_prize(otmp)) { otmp->record_achieve_special = 1; } From 440d9d74bdece61945f812eee38d6e4396a96ad9 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 8 Feb 2017 13:10:15 -0800 Subject: [PATCH 02/15] fix g.cubes eating green slime Back when dead green slime left a corpse, gelatinous cubes wouldn't eat that, but they would eat globs of green slime without being affected. Add the missing glob check so g.cubes will engulf globs of green slime instead of eating those. --- doc/fixes36.1 | 1 + src/mon.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index b2972a89a..1d3b03886 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -367,6 +367,7 @@ when sitting at a trap spot: You sit down. You step on a level teleporter. show all statusline information in #attributes add option status_updates to prevent bottom of screen status line updates fix achievement recording bug with mines and sokoban prizes +g.cubes would eat globs of green slime without harm; engulf those instead Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mon.c b/src/mon.c index dbfbd5a40..ebb680780 100644 --- a/src/mon.c +++ b/src/mon.c @@ -929,7 +929,8 @@ struct monst *mtmp; || otmp->otyp == RIN_SLOW_DIGESTION) /* cockatrice corpses handled above; this touch_petrifies() check catches eggs */ - || ((otmp->otyp == CORPSE || otmp->otyp == EGG) + || ((otmp->otyp == CORPSE || otmp->otyp == EGG + || otmp->globby) && ((touch_petrifies(&mons[otmp->corpsenm]) && !resists_ston(mtmp)) || (otmp->corpsenm == PM_GREEN_SLIME From 1151ec19a14d1b184e7371215ef6d9a52fecaee0 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 8 Feb 2017 13:23:00 -0800 Subject: [PATCH 03/15] fix rumors.tru typo "They say that an ooze will bite your boots and a rockmole will eat them." 'rockmole' should be two words. Also, rock moles won't eat leather boots, so change 'will eat' to 'might eat'. --- dat/rumors.tru | 2 +- doc/fixes36.1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dat/rumors.tru b/dat/rumors.tru index bcd37d044..8181c5abc 100644 --- a/dat/rumors.tru +++ b/dat/rumors.tru @@ -207,7 +207,7 @@ They say that a xorn knows of no obstacles when pursuing you. They say that abusing a credit card could shock you sooner or later. They say that amulets, like most things, can be deadly or life saving. They say that an altar can identify blessings. -They say that an ooze will bite your boots and a rockmole will eat them. +They say that an ooze will bite your boots and a rock mole might eat them. They say that an unlucky hacker was once killed by an exploding tin. They say that antique dealers are always interested in precious stones. They say that bandaging one's wounds helps to keep up one's appearance. diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 1d3b03886..e659f505c 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -368,6 +368,7 @@ show all statusline information in #attributes add option status_updates to prevent bottom of screen status line updates fix achievement recording bug with mines and sokoban prizes g.cubes would eat globs of green slime without harm; engulf those instead +fix up true rumor about rock moles vs boots Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository From bfab1aa5b6a247ff79addfece75d2ab33a12b44d Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 12 Feb 2017 00:24:29 -0800 Subject: [PATCH 04/15] some fixes36.1 fixes Fix a couple of typos, change one entry which referenced internal coding issues to a more general description for end-users, and remove a duplicate about color handling for status hilites and menucolors. (I've only skimmed through the first section. There are likely more inconsistencies in the file.) --- doc/fixes36.1 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index e659f505c..4c2a18fb9 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -41,7 +41,7 @@ make a previously-discovered scroll written with marker while blind have its don't show the old level when you die going down the stairs because of an iron ball new high score with ", while helpless" attribute appended would erroneously - result in ", while helpless" being appended to all scores + result in ", while helpless" being appended to all following entries allow bright aliases for colors in menucolors avoid hearing yelps when you are deaf make corpse visible if stethoscope told you about it being there @@ -83,7 +83,7 @@ when a pet moves reluctantly, name the top item of the pile it is reluctant ensure sufficient messages are given to clarify the transition from detected vampire bats to fog clouds in Vlad's tower fix "killing by kicking something weird" when kicking an object causes death -guard macros available for mextra fields similar to those for oextra fields +avoid accessing mextra fields which haven't been allocated compile-time option for an alternate paniclog format for public server use make monsters forget where they stepped when fleeing or teleporting requiver pickup_thrown objects if quiver is empty @@ -125,10 +125,9 @@ make status hilites use same color names as menucolors limit monster spell aggravate monsters to either outside or inside of the Wizard's tower prevent revived unique from inheriting a user-defined name -make hurtling out of water bubble on the Plane of Water handle entering water +make hurtling out of air bubble on the Plane of Water handle entering water fix floor descriptions on the Planes when levitating fix warning glyph description when monster symbol coincided the warning symbol -allow the same color names for status hilites and menucolors override MSGTYPE=norep while executing the ':' command if a chameleon took vampire form, it would stop periodically changing shape corpses obtained from tipping an ice box wouldn't rot away @@ -302,7 +301,7 @@ conduct: wishing for an artifact and not getting it because it already exists because of quest restrictions or too many artifacts in play does avoid ring of poison resistance as starting equipment for orcish wizard gelatinous cube shouldn't be able to eat a scroll of scare monster -make fireballs or cones of cold cast a skilled or higher not go through walls +make fireballs or cones of cold cast at skilled or higher not go through walls prevent flying monsters from hovering over unreachable underwater objects lembas wafer gives increased nutrition to elves, reduced nutrition to orcs; cram ration gives increase nutrition to dwarves From 8c92d2921f8aa8a54e7dc57e611f962b648232da Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 17 Feb 2017 12:24:24 -0800 Subject: [PATCH 05/15] fix #H5082 - growing into opposite sex monster When a female dwarf grows (via level gain) into a dwarf lord, it changes sex as well as base monster form because all dwarf lords are male. The earlier fix for #H4276 (16-Mar-2016, to give an alternate grow-up message acknowledging the change) used the wrong monster form (monst's old one instead of new one). --- doc/fixes36.1 | 3 +++ src/makemon.c | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 4c2a18fb9..3a0149e98 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -391,6 +391,9 @@ when picking a destination for #jump, '$' to show valid dest. is more accurate enchant armor gave "your pair of boots/gloves glow", should have been "glows" when autopickup is overridden in a shop, always-pick-up exceptions and pickup_thrown still picked unpaid items up +the fix for giving an alternate grow up message when a monster becomes a new + form with the opposite sex (female gnome into male gnome lord, for + example) had the logic wrong Platform- and/or Interface-Specific Fixes diff --git a/src/makemon.c b/src/makemon.c index 3422cb2bf..c60adfa1f 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1793,11 +1793,11 @@ struct monst *mtmp, *victim; else if (lev_limit > 49) lev_limit = (ptr->mlevel > 49 ? 50 : 49); - /* new form might force gender change */ - fem = is_male(ptr) ? 0 : is_female(ptr) ? 1 : mtmp->female; - if ((int) ++mtmp->m_lev >= mons[newtype].mlevel && newtype != oldtype) { ptr = &mons[newtype]; + /* new form might force gender change */ + fem = is_male(ptr) ? 0 : is_female(ptr) ? 1 : mtmp->female; + if (mvitals[newtype].mvflags & G_GENOD) { /* allow G_EXTINCT */ if (canspotmon(mtmp)) pline("As %s grows up into %s, %s %s!", mon_nam(mtmp), @@ -1829,8 +1829,9 @@ struct monst *mtmp, *victim; set_mon_data(mtmp, ptr, 1); /* preserve intrinsics */ newsym(mtmp->mx, mtmp->my); /* color may change */ lev_limit = (int) mtmp->m_lev; /* never undo increment */ + + mtmp->female = fem; /* gender might be changing */ } - mtmp->female = fem; /* gender might be changing */ /* sanity checks */ if ((int) mtmp->m_lev > lev_limit) { From 7d8b4d4f972918a25a416bba27424d7ae7e32983 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 19 Feb 2017 15:30:46 +0200 Subject: [PATCH 06/15] 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. --- doc/Guidebook.mn | 18 ++++ doc/Guidebook.tex | 18 ++++ doc/fixes36.1 | 1 + include/config.h | 26 +++++ include/extern.h | 8 ++ include/flag.h | 1 + include/sys.h | 3 + src/botl.c | 37 ++++--- src/detect.c | 180 +++++++++++++++++++------------- src/end.c | 171 +++++++++++++++++++++++-------- src/files.c | 9 +- src/pline.c | 13 +++ src/sys.c | 7 ++ src/windows.c | 239 +++++++++++++++++++++++++++++++++++++++++++ sys/unix/hints/linux | 1 + sys/unix/sysconf | 14 +++ sys/winnt/sysconf | 14 +++ util/makedefs.c | 3 + 18 files changed, 631 insertions(+), 132 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 8b7397336..cf0c5f73c 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -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 diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index b4243b8db..e2ea3bbf6 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -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 diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 3a0149e98..39188f466 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.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 diff --git a/include/config.h b/include/config.h index 9fc7dc6fe..ba5db5443 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/extern.h b/include/extern.h index 52b810a60..ca51e491f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/include/flag.h b/include/flag.h index 612a14720..f5072081a 100644 --- a/include/flag.h +++ b/include/flag.h @@ -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 */ diff --git a/include/sys.h b/include/sys.h index bf652ede2..91c5d3522 100644 --- a/include/sys.h +++ b/include/sys.h @@ -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; diff --git a/src/botl.c b/src/botl.c index f39086174..e6a771bdc 100644 --- a/src/botl.c +++ b/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) diff --git a/src/detect.c b/src/detect.c index 7668d77bb..8a5af31dd 100644 --- a/src/detect.c +++ b/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); } diff --git a/src/end.c b/src/end.c index 09213205b..e2e8a13af 100644 --- a/src/end.c +++ b/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) diff --git a/src/files.c b/src/files.c index 1138284a9..ddba24d09 100644 --- a/src/files.c +++ b/src/files.c @@ -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) diff --git a/src/pline.c b/src/pline.c index c38ff33af..0f21b444b 100644 --- a/src/pline.c +++ b/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))) diff --git a/src/sys.c b/src/sys.c index deab17eb5..c0803d480 100644 --- a/src/sys.c +++ b/src/sys.c @@ -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; diff --git a/src/windows.c b/src/windows.c index bdbd76e2d..b2939d31f 100644 --- a/src/windows.c +++ b/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*/ diff --git a/sys/unix/hints/linux b/sys/unix/hints/linux index 888fd9050..4310e6afe 100644 --- a/sys/unix/hints/linux +++ b/sys/unix/hints/linux @@ -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: diff --git a/sys/unix/sysconf b/sys/unix/sysconf index f7426f785..d83aeff7d 100644 --- a/sys/unix/sysconf +++ b/sys/unix/sysconf @@ -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. diff --git a/sys/winnt/sysconf b/sys/winnt/sysconf index d6cc37a6a..2ce1da300 100644 --- a/sys/winnt/sysconf +++ b/sys/winnt/sysconf @@ -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 diff --git a/util/makedefs.c b/util/makedefs.c index 25614d8b2..7ebd5136b 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -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 From fd2cfba54488f4d9db53482db0fa089951c860c6 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 25 Feb 2017 18:27:21 -0800 Subject: [PATCH 07/15] dumplog lint and formatting --- include/extern.h | 4 +- src/end.c | 38 +++++++++-------- src/windows.c | 109 +++++++++++++++++++++++++++-------------------- 3 files changed, 86 insertions(+), 65 deletions(-) diff --git a/include/extern.h b/include/extern.h index ca51e491f..800969d6f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1471112244 2016/08/13 18:17:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.570 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1488075978 2017/02/26 02:26:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.582 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2716,7 +2716,7 @@ E void FDECL(genl_status_threshold, (int, int, anything, int, int, int)); E void FDECL(dump_open_log, (time_t)); E void NDECL(dump_close_log); -E void FDECL(dump_redirect, (boolean)); +E void FDECL(dump_redirect, (BOOLEAN_P)); E void FDECL(dump_forward_putstr, (winid, int, const char*, int)); /* ### wizard.c ### */ diff --git a/src/end.c b/src/end.c index e2e8a13af..817ca4111 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1461919723 2016/04/29 08:48:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.127 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -56,6 +56,10 @@ STATIC_DCL int NDECL(set_vanq_order); STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P)); STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P)); STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *)); +#ifdef DUMPLOG +STATIC_DCL void NDECL(dump_plines); +#endif +STATIC_DCL void FDECL(dump_everything, (int)); STATIC_DCL int NDECL(num_extinct); #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2) @@ -672,34 +676,31 @@ STATIC_OVL void dump_plines() { int i; - char* str; - extern char* saved_plines[]; + char buf[BUFSZ], **strp; + extern char *saved_plines[]; + Strcpy(buf, " "); 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); + for (i = 0; i < DUMPLOG_MSG_COUNT; ++i) { + strp = &saved_plines[DUMPLOG_MSG_COUNT - 1 - i]; + if (*strp) { + copynchars(&buf[1], *strp, BUFSZ - 1 - 1); putstr(0, 0, buf); - } #ifdef FREE_ALL_MEMORY - free(str); + free(*strp), *strp = 0; #endif + } } } #endif STATIC_OVL void -dump_everything(how, taken) +dump_everything(how) int how; -boolean taken; { #ifdef DUMPLOG - struct obj* obj; - struct topl* topl; + struct obj *obj; char pbuf[BUFSZ]; dump_redirect(TRUE); @@ -746,6 +747,8 @@ boolean taken; show_overview((how >= PANICKED) ? 1 : 2, how); putstr(0, 0, ""); dump_redirect(FALSE); +#else + nhUse(how); #endif } @@ -1164,7 +1167,8 @@ int how; if (strcmp(flags.end_disclose, "none") && how != PANICKED) disclose(how, taken); - dump_everything(how, taken); + + dump_everything(how); /* finish_paybill should be called after disclosure but before bones */ if (bones_ok && taken) @@ -1820,7 +1824,7 @@ num_genocides() return n; } -int +STATIC_OVL int num_extinct() { int i, n = 0; diff --git a/src/windows.c b/src/windows.c index b2939d31f..764662075 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 windows.c $NHDT-Date: 1448013599 2015/11/20 09:59:59 $ $NHDT-Branch: master $:$NHDT-Revision: 1.35 $ */ +/* NetHack 3.6 windows.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.39 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1033,27 +1033,35 @@ int behavior UNUSED, under UNUSED, over UNUSED; #endif /* STATUS_VIA_WINDOWPORT */ STATIC_VAR struct window_procs dumplog_windowprocs_backup; -STATIC_PTR FILE* dumplog_file; +STATIC_PTR FILE *dumplog_file; #ifdef DUMPLOG -char * +STATIC_VAR time_t dumplog_now; + +STATIC_DCL char *FDECL(dump_fmtstr, (const char *, char *)); + +STATIC_OVL char * dump_fmtstr(fmt, buf) -char *fmt; +const char *fmt; char *buf; { - char *fp = fmt, *bp = buf; + const char *fp = fmt; + char *bp = buf; int slen, len = 0; char tmpbuf[BUFSZ]; char verbuf[BUFSZ]; + long uid; + time_t now; - time_t now = getnow(); - int uid = getuid(); + now = dumplog_now; + uid = (long) getuid(); while (fp && *fp && len < BUFSZ-1) { if (*fp == '%') { fp++; switch (*fp) { - default: goto finish; + default: + goto finish; case '\0': /* fallthrough */ case '%': /* literal % */ Sprintf(tmpbuf,"%%"); @@ -1075,13 +1083,13 @@ char *buf; Sprintf(tmpbuf, "%s", version_string(verbuf)); break; case 'u': /* UID */ - Sprintf(tmpbuf, "%d", uid); + Sprintf(tmpbuf, "%ld", uid); break; case 'n': /* player name */ - Sprintf(tmpbuf, "%s", (plname ? plname : "unknown")); + Sprintf(tmpbuf, "%s", *plname ? plname : "unknown"); break; case 'N': /* first character of player name */ - Sprintf(tmpbuf, "%c", (plname ? *plname : 'u')); + Sprintf(tmpbuf, "%c", *plname ? *plname : 'u'); break; } @@ -1106,7 +1114,6 @@ char *buf; } #endif /* DUMPLOG */ - void dump_open_log(now) time_t now; @@ -1115,6 +1122,7 @@ time_t now; char buf[BUFSZ]; char *fname; + dumplog_now = now; #ifdef SYSCF if (!sysopt.dumplogfile) return; @@ -1122,10 +1130,12 @@ time_t now; #else fname = dump_fmtstr(DUMPLOG_FILE, buf); #endif - dumplog_file = fopen(fname, "w"); dumplog_windowprocs_backup = windowprocs; -#endif + +#else /*!DUMPLOG*/ + nhUse(now); +#endif /*?DUMPLOG*/ } void @@ -1150,7 +1160,7 @@ void dump_forward_putstr(win, attr, str, no_forward) winid win; int attr; -const char* str; +const char *str; int no_forward; { if (dumplog_file) @@ -1159,11 +1169,12 @@ int no_forward; putstr(win, attr, str); } +/*ARGSUSED*/ STATIC_OVL void dump_putstr(win, attr, str) -winid win; -int attr; -const char* str; +winid win UNUSED; +int attr UNUSED; +const char *str; { if (dumplog_file) fprintf(dumplog_file, "%s\n", str); @@ -1176,45 +1187,50 @@ int dummy; return dummy; } +/*ARGUSED*/ STATIC_OVL void dump_clear_nhwindow(win) -winid win; +winid win UNUSED; { - + return; } +/*ARGSUSED*/ STATIC_OVL void dump_display_nhwindow(win, p) -winid win; -BOOLEAN_P p; +winid win UNUSED; +boolean p UNUSED; { - + return; } +/*ARGUSED*/ STATIC_OVL void dump_destroy_nhwindow(win) -winid win; +winid win UNUSED; { - + return; } +/*ARGUSED*/ STATIC_OVL void dump_start_menu(win) -winid win; +winid win UNUSED; { - + return; } +/*ARGSUSED*/ 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; +winid win UNUSED; +int glyph UNUSED; +const anything *identifier UNUSED; +char ch; +char gch UNUSED; +int attr UNUSED; +const char *str; +boolean preselected UNUSED; { if (dumplog_file) { if (glyph == NO_GLYPH) @@ -1224,10 +1240,11 @@ BOOLEAN_P preselected; } } +/*ARGSUSED*/ STATIC_OVL void dump_end_menu(win, str) -winid win; -const char* str; +winid win UNUSED; +const char *str; { if (dumplog_file) { if (str) @@ -1238,21 +1255,21 @@ const char* str; } STATIC_OVL int -dump_select_menu(win, index, item) -winid win; -int index; -MENU_ITEM_P** item; +dump_select_menu(win, how, item) +winid win UNUSED; +int how UNUSED; +menu_item **item; { - *item = NULL; + *item = (menu_item *) 0; return 0; } void -dump_redirect(flag) -boolean flag; +dump_redirect(onoff_flag) +boolean onoff_flag; { if (dumplog_file) { - if (flag) { + if (onoff_flag) { windowprocs.win_create_nhwindow = dump_create_nhwindow; windowprocs.win_clear_nhwindow = dump_clear_nhwindow; windowprocs.win_display_nhwindow = dump_display_nhwindow; @@ -1265,7 +1282,7 @@ boolean flag; } else { windowprocs = dumplog_windowprocs_backup; } - iflags.in_dumplog = flag; + iflags.in_dumplog = onoff_flag; } else { iflags.in_dumplog = FALSE; } From 92c5a56364646ad206c72980557e9931f1f701ea Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 25 Feb 2017 18:29:31 -0800 Subject: [PATCH 08/15] miscellaneous formatting --- src/cmd.c | 213 +++++++++++++++++++++++++++++++++------------------ src/detect.c | 33 ++++---- 2 files changed, 155 insertions(+), 91 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 4d39a8ba6..bf2c80e30 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2756,133 +2756,198 @@ int final; #define ISCTRL(c) ((uchar)(c) < 0x20) #define UNCTRL(c) (ISCTRL(c) ? (0x60 | (c)) : (c)) +/* ordered by command name */ struct ext_func_tab extcmdlist[] = { - { '#', "#", "perform an extended command", doextcmd, IFBURIED|GENERALCMD }, - { M('?'), "?", "get this list of extended commands", doextlist, IFBURIED|AUTOCOMPLETE|GENERALCMD }, - { M('a'), "adjust", "adjust inventory letters", doorganize, IFBURIED|AUTOCOMPLETE }, - { M('A'), "annotate", "name current level", donamelevel, IFBURIED|AUTOCOMPLETE }, - { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply }, - { C('x'), "attributes", "show your attributes", doattributes, IFBURIED }, - { '@', "autopickup", "toggle the pickup option on/off", dotogglepickup, IFBURIED }, + { '#', "#", "perform an extended command", + doextcmd, IFBURIED | GENERALCMD }, + { M('?'), "?", "get this list of extended commands", + doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD }, + { M('a'), "adjust", "adjust inventory letters", + doorganize, IFBURIED | AUTOCOMPLETE }, + { M('A'), "annotate", "name current level", + donamelevel, IFBURIED | AUTOCOMPLETE }, + { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", + doapply }, + { C('x'), "attributes", "show your attributes", + doattributes, IFBURIED }, + { '@', "autopickup", "toggle the pickup option on/off", + dotogglepickup, IFBURIED }, { 'C', "call", "call (name) something", docallcmd, IFBURIED }, { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED }, - { M('c'), "chat", "talk to someone", dotalk, IFBURIED|AUTOCOMPLETE }, + { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE }, { 'c', "close", "close a door", doclose }, - { M('C'), "conduct", "list voluntary challenges you have maintained", doconduct, IFBURIED|AUTOCOMPLETE }, + { M('C'), "conduct", "list voluntary challenges you have maintained", + doconduct, IFBURIED | AUTOCOMPLETE }, { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE }, { '>', "down", "go down a staircase", dodown }, { 'd', "drop", "drop an item", dodrop }, { 'D', "droptype", "drop specific item types", doddrop }, { 'e', "eat", "eat something", doeat }, { 'E', "engrave", "engrave writing on the floor", doengrave }, - { M('e'), "enhance", "advance or check weapon and spell skills", enhance_weapon_skill, IFBURIED|AUTOCOMPLETE }, - { '\0', "exploremode", "enter explore (discovery) mode", enter_explore_mode, IFBURIED }, + { M('e'), "enhance", "advance or check weapon and spell skills", + enhance_weapon_skill, IFBURIED | AUTOCOMPLETE }, + { '\0', "exploremode", "enter explore (discovery) mode", + enter_explore_mode, IFBURIED }, { 'f', "fire", "fire ammunition from quiver", dofire }, { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE }, - { ';', "glance", "show what type of thing a map symbol corresponds to", doquickwhatis, IFBURIED|GENERALCMD }, - { '?', "help", "give a help message", dohelp, IFBURIED|GENERALCMD }, - { 'V', "history", "show long version and game history", dohistory, IFBURIED|GENERALCMD }, + { ';', "glance", "show what type of thing a map symbol corresponds to", + doquickwhatis, IFBURIED | GENERALCMD }, + { '?', "help", "give a help message", dohelp, IFBURIED | GENERALCMD }, + { 'V', "history", "show long version and game history", + dohistory, IFBURIED | GENERALCMD }, { 'i', "inventory", "show your inventory", ddoinv, IFBURIED }, - { 'I', "inventtype", "inventory specific item types", dotypeinv, IFBURIED }, - { M('i'), "invoke", "invoke an object's special powers", doinvoke, IFBURIED|AUTOCOMPLETE }, + { 'I', "inventtype", "inventory specific item types", + dotypeinv, IFBURIED }, + { M('i'), "invoke", "invoke an object's special powers", + doinvoke, IFBURIED | AUTOCOMPLETE }, { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE }, { C('d'), "kick", "kick something", dokick }, - { '\\', "known", "show what object types have been discovered", dodiscovered, IFBURIED|GENERALCMD }, - { '`', "knownclass", "show discovered types for one class of objects", doclassdisco, IFBURIED|GENERALCMD }, - { '\0', "levelchange", "change experience level", wiz_level_change, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "lightsources", "show mobile light sources", wiz_light_sources, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\\', "known", "show what object types have been discovered", + dodiscovered, IFBURIED | GENERALCMD }, + { '`', "knownclass", "show discovered types for one class of objects", + doclassdisco, IFBURIED|GENERALCMD }, + { '\0', "levelchange", "change experience level", + wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "lightsources", "show mobile light sources", + wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, { ':', "look", "look at what is here", dolook, IFBURIED }, { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE }, #ifdef DEBUG_MIGRATING_MONS - { '\0', "migratemons", "migrate n random monsters", wiz_migrate_mons, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "migratemons", "migrate N random monsters", + wiz_migrate_mons, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, #endif - { '\0', "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { M('m'), "monster", "use a monster's special ability", domonability, IFBURIED|AUTOCOMPLETE }, - { 'N', "name", "name a monster or an object", docallcmd, IFBURIED|AUTOCOMPLETE }, - { M('o'), "offer", "offer a sacrifice to the gods", dosacrifice, AUTOCOMPLETE }, + { '\0', "monpolycontrol", "control monster polymorphs", + wiz_mon_polycontrol, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { M('m'), "monster", "use monster's special ability", + domonability, IFBURIED | AUTOCOMPLETE }, + { 'N', "name", "name a monster or an object", + docallcmd, IFBURIED | AUTOCOMPLETE }, + { M('o'), "offer", "offer a sacrifice to the gods", + dosacrifice, AUTOCOMPLETE }, { 'o', "open", "open a door", doopen }, - { 'O', "options", "show option settings, possibly change them", doset, IFBURIED|GENERALCMD }, - { C('o'), "overview", "show a summary of the explored dungeon", dooverview, IFBURIED|AUTOCOMPLETE }, - { '\0', "panic", "test panic routine (fatal to game)", wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { 'O', "options", "show option settings, possibly change them", + doset, IFBURIED|GENERALCMD }, + { C('o'), "overview", "show a summary of the explored dungeon", + dooverview, IFBURIED|AUTOCOMPLETE }, + { '\0', "panic", "test panic routine (fatal to game)", + wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, { 'p', "pay", "pay your shopping bill", dopay }, { ',', "pickup", "pick up things at the current location", dopickup }, - { '\0', "polyself", "polymorph self", wiz_polyself, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "polyself", "polymorph self", + wiz_polyself, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, #ifdef PORT_DEBUG - { '\0', "portdebug", "wizard port debug command", wiz_port_debug, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "portdebug", "wizard port debug command", + wiz_port_debug, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, #endif - { M('p'), "pray", "pray to the gods for help", dopray, IFBURIED|AUTOCOMPLETE }, - { C('p'), "prevmsg", "toggle through previously displayed game messages", doprev_message, IFBURIED|GENERALCMD }, + { M('p'), "pray", "pray to the gods for help", + dopray, IFBURIED | AUTOCOMPLETE }, + { C('p'), "prevmsg", "view recent game messages", + doprev_message, IFBURIED|GENERALCMD }, { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon }, { 'q', "quaff", "quaff (drink) something", dodrink }, - { M('q'), "quit", "exit without saving current game", done2, IFBURIED|AUTOCOMPLETE|GENERALCMD }, + { M('q'), "quit", "exit without saving current game", + done2, IFBURIED | AUTOCOMPLETE | GENERALCMD }, { 'Q', "quiver", "select ammunition for quiver", dowieldquiver }, { 'r', "read", "read a scroll or spellbook", doread }, - { C('r'), "redraw", "redraw screen", doredraw, IFBURIED|GENERALCMD }, + { C('r'), "redraw", "redraw screen", doredraw, IFBURIED | GENERALCMD }, { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring }, - { M('R'), "ride", "mount or dismount a saddled steed", doride, AUTOCOMPLETE }, + { M('R'), "ride", "mount or dismount a saddled steed", + doride, AUTOCOMPLETE }, { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE }, - { 'S', "save", "save the game", dosave, IFBURIED|GENERALCMD }, - { 's', "search", "search for traps and secret doors", dosearch, IFBURIED, "searching" }, + { 'S', "save", "save the game and exit", dosave, IFBURIED | GENERALCMD }, + { 's', "search", "search for traps and secret doors", + dosearch, IFBURIED, "searching" }, { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED }, - { AMULET_SYM, "seeamulet", "show the amulet currently worn", dopramulet, IFBURIED }, - { ARMOR_SYM, "seearmor", "show the armor currently worn", doprarm, IFBURIED }, + { AMULET_SYM, "seeamulet", "show the amulet currently worn", + dopramulet, IFBURIED }, + { ARMOR_SYM, "seearmor", "show the armor currently worn", + doprarm, IFBURIED }, { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED }, - { '\0', "seenv", "show seen vectors", wiz_show_seenv, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { RING_SYM, "seerings", "show the ring(s) currently worn", doprring, IFBURIED }, - { SPBOOK_SYM, "seespells", "list and reorder known spells", dovspell, IFBURIED }, - { TOOL_SYM, "seetools", "show the tools currently in use", doprtool, IFBURIED }, - { '^', "seetrap", "show the type of a trap", doidtrap, IFBURIED }, - { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", doprwep, IFBURIED }, + { '\0', "seenv", "show seen vectors", + wiz_show_seenv, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { RING_SYM, "seerings", "show the ring(s) currently worn", + doprring, IFBURIED }, + { SPBOOK_SYM, "seespells", "list and reorder known spells", + dovspell, IFBURIED }, + { TOOL_SYM, "seetools", "show the tools currently in use", + doprtool, IFBURIED }, + { '^', "seetrap", "show the type of adjacent trap", doidtrap, IFBURIED }, + { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", + doprwep, IFBURIED }, #ifdef SHELL - { '!', "shell", "do a shell escape", dosh, IFBURIED|GENERALCMD }, + { '!', "shell", "do a shell escape", dosh, IFBURIED | GENERALCMD }, #endif /* SHELL */ { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE }, - { '\0', "stats", "show memory statistics", wiz_show_stats, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "stats", "show memory statistics", + wiz_show_stats, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, #ifdef SUSPEND - { C('z'), "suspend", "suspend the game", dosuspend_core, IFBURIED|GENERALCMD }, + { C('z'), "suspend", "suspend the game", + dosuspend_core, IFBURIED | GENERALCMD }, #endif /* SUSPEND */ { 'x', "swap", "swap wielded and secondary weapons", doswapweapon }, { 'T', "takeoff", "take off one piece of armor", dotakeoff }, { 'A', "takeoffall", "remove all armor", doddoremarm }, { C('t'), "teleport", "teleport around the level", dotele, IFBURIED }, - { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED|AUTOCOMPLETE }, + { '\0', "terrain", "show map without obstructions", + doterrain, IFBURIED | AUTOCOMPLETE }, { 't', "throw", "throw something", dothrow }, - { '\0', "timeout", "look at timeout queue", wiz_timeout_queue, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "timeout", "look at timeout queue", + wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE }, { '_', "travel", "travel to a specific location on the map", dotravel }, - { M('t'), "turn", "turn undead away", doturn, IFBURIED|AUTOCOMPLETE }, - { 'X', "twoweapon", "toggle two-weapon combat", dotwoweapon, AUTOCOMPLETE }, + { M('t'), "turn", "turn undead away", doturn, IFBURIED | AUTOCOMPLETE }, + { 'X', "twoweapon", "toggle two-weapon combat", + dotwoweapon, AUTOCOMPLETE }, { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE }, { '<', "up", "go up a staircase", doup }, - { '\0', "vanquished", "list vanquished monsters", dovanquished, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { M('v'), "version", "list compile time options for this version of NetHack", doextversion, IFBURIED|AUTOCOMPLETE|GENERALCMD }, - { 'v', "versionshort", "show version", doversion, IFBURIED|GENERALCMD }, - { '\0', "vision", "show vision array", wiz_show_vision, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '.', "wait", "rest one move while doing nothing", donull, IFBURIED, "waiting" }, + { '\0', "vanquished", "list vanquished monsters", + dovanquished, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { M('v'), "version", + "list compile time options for this version of NetHack", + doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD }, + { 'v', "versionshort", "show version", doversion, IFBURIED | GENERALCMD }, + { '\0', "vision", "show vision array", + wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '.', "wait", "rest one move while doing nothing", + donull, IFBURIED, "waiting" }, { 'W', "wear", "wear a piece of armor", dowear }, { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED }, - { '/', "whatis", "show what type of thing a symbol corresponds to", dowhatis, IFBURIED|GENERALCMD }, + { '/', "whatis", "show what type of thing a symbol corresponds to", + dowhatis, IFBURIED | GENERALCMD }, { 'w', "wield", "wield (put in use) a weapon", dowield }, { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE }, #ifdef DEBUG - { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you", wiz_debug_cmd_bury, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display", wiz_debug_cmd_traveldisplay, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you", + wiz_debug_cmd_bury, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display", + wiz_debug_cmd_traveldisplay, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, #endif - { C('e'), "wizdetect", "search a room", wiz_detect, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { C('g'), "wizgenesis", "create a monster", wiz_genesis, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { C('i'), "wizidentify", "identify all items in inventory", wiz_identify, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizintrinsic", "set intrinsic", wiz_intrinsic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { C('v'), "wizlevelport", "teleport to another level", wiz_level_tele, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizmakemap", "recreate the current level", wiz_makemap, IFBURIED|WIZMODECMD }, - { C('f'), "wizmap", "map the level", wiz_map, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizsmell", "smell monster", wiz_smell, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wizwhere", "show locations of special levels", wiz_where, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { C('w'), "wizwish", "wish for something", wiz_wish, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, - { '\0', "wmode", "show wall modes", wiz_show_wmodes, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('e'), "wizdetect", "reveal hidden things within a small radius", + wiz_detect, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { C('g'), "wizgenesis", "create a monster", + wiz_genesis, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { C('i'), "wizidentify", "identify all items in inventory", + wiz_identify, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizintrinsic", "set an intrinsic", + wiz_intrinsic, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { C('v'), "wizlevelport", "teleport to another level", + wiz_level_tele, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizmakemap", "recreate the current level", + wiz_makemap, IFBURIED | WIZMODECMD }, + { C('f'), "wizmap", "map the level", + wiz_map, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizrumorcheck", "verify rumor boundaries", + wiz_rumor_check, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizsmell", "smell monster", + wiz_smell, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wizwhere", "show locations of special levels", + wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { C('w'), "wizwish", "wish for something", + wiz_wish, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, + { '\0', "wmode", "show wall modes", + wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, { 'z', "zap", "zap a wand", dozap }, - - { '\0', (char *)0, (char *)0, donull } /* sentinel */ + { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */ }; const char * diff --git a/src/detect.c b/src/detect.c index 8a5af31dd..a46cf47f9 100644 --- a/src/detect.c +++ b/src/detect.c @@ -25,7 +25,8 @@ 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)); +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 */ @@ -1717,8 +1718,8 @@ sokoban_detect() } STATIC_DCL int -reveal_terrain_getglyph(x,y, full, swallowed, default_glyph, which_subset) -int x,y, full; +reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset) +int x, y, full; unsigned swallowed; int default_glyph, which_subset; { @@ -1734,17 +1735,15 @@ int default_glyph, which_subset; 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; + ? 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; + ? 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 @@ -1777,14 +1776,13 @@ int default_glyph, which_subset; && 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) */ + /* 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]; @@ -1803,7 +1801,7 @@ void dump_map() { int x, y, glyph; - int subset = TER_MAP|TER_TRP|TER_OBJ|TER_MON; + 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]; @@ -1811,6 +1809,7 @@ dump_map() 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); From f55da584f40912df120ce3cfe6aae2acf11e6b81 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 27 Feb 2017 02:54:14 -0800 Subject: [PATCH 09/15] dumplog's saved_plines[] Use a simple ring buffer instead of a flat array that needed to have 49 pointers shifted down a slot every time a pline message was issued. 'saved_plines[saved_pline_index]' is the oldest message in the buffer and the next slot to use when adding a new one. --- src/end.c | 8 +++++--- src/pline.c | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/end.c b/src/end.c index 817ca4111..f0d3e2f07 100644 --- a/src/end.c +++ b/src/end.c @@ -675,15 +675,17 @@ char *defquery; STATIC_OVL void dump_plines() { - int i; + int i, j; char buf[BUFSZ], **strp; extern char *saved_plines[]; + extern unsigned saved_pline_index; Strcpy(buf, " "); putstr(0, 0, ""); putstr(0, 0, "Latest messages:"); - for (i = 0; i < DUMPLOG_MSG_COUNT; ++i) { - strp = &saved_plines[DUMPLOG_MSG_COUNT - 1 - i]; + for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT; + ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) { + strp = &saved_plines[j]; if (*strp) { copynchars(&buf[1], *strp, BUFSZ - 1 - 1); putstr(0, 0, buf); diff --git a/src/pline.c b/src/pline.c index 0f21b444b..6103d29cf 100644 --- a/src/pline.c +++ b/src/pline.c @@ -15,7 +15,9 @@ static void FDECL(execplinehandler, (const char *)); #endif #ifdef DUMPLOG -char* saved_plines[DUMPLOG_MSG_COUNT] = {0}; +/* also used in end.c */ +unsigned saved_pline_index = 0; /* slot in saved_plines[] to use next */ +char *saved_plines[DUMPLOG_MSG_COUNT] = { (char *) 0 }; #endif /*VARARGS1*/ @@ -84,6 +86,7 @@ VA_DECL(const char *, line) pbuf[BUFSZ - 1 - 1] = line[ln - 1]; pbuf[BUFSZ - 1] = '\0'; line = pbuf; + ln = BUFSZ - 1; } if (!iflags.window_inited) { raw_print(line); @@ -92,12 +95,34 @@ VA_DECL(const char *, line) } #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); + /* We hook here early to have options-agnostic output. + * Unfortunately, that means Norep() isn't honored (general issue) and + * that short lines aren't combined into one longer one (tty behavior). + */ + { + /* + * TODO: + * This essentially duplicates message history, which is + * currently implemented in an interface-specific manner. + * The core should take responsibility for that and have + * this share it. + * Ideally history for prompt lines should be augmented + * with the player's response once that has been specified. + */ + unsigned indx = saved_pline_index; /* next slot to use */ + char *oldest = saved_plines[indx]; /* current content of that slot */ + + if (oldest && (int) strlen(oldest) >= ln) { /* ln==strlen(line) */ + /* this buffer will gradually shrink until the 'else' is needed; + there's no pressing need to track allocation size instead */ + Strcpy(oldest, line); + } else { + if (oldest) + free((genericptr_t) oldest); + saved_plines[indx] = dupstr(line); + } + saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT; + } #endif msgtyp = msgtype_type(line, no_repeat); @@ -117,7 +142,7 @@ VA_DECL(const char *, line) /* this gets cleared after every pline message */ iflags.last_msg = PLNMSG_UNKNOWN; - strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0'; + (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0'; if (msgtyp == MSGTYP_STOP) display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */ From 10706ef0952a2a853045091f9848523099e40105 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 4 Mar 2017 19:45:15 -0800 Subject: [PATCH 10/15] more DUMPLOG lint The recently added dumplog code assumed that 'time_t' is 'long int' or at least will give sensible results when formatted with '%ld'. This is more of a Band-Aid(tm) than a proper fix. --- src/windows.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/windows.c b/src/windows.c index 764662075..70c9feee7 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1056,6 +1056,16 @@ char *buf; now = dumplog_now; uid = (long) getuid(); + /* + * Note: %t and %T assume that time_t is a 'long int' number of + * seconds since some epoch value. That's quite iffy.... The + * unit of time might be different and the datum size might be + * some variant of 'long long int'. [Their main purpose is to + * construct a unique file name rather than record the date and + * time; violating the 'long seconds since base-date' assumption + * may or may not interfere with that usage.] + */ + while (fp && *fp && len < BUFSZ-1) { if (*fp == '%') { fp++; @@ -1064,13 +1074,13 @@ char *buf; goto finish; case '\0': /* fallthrough */ case '%': /* literal % */ - Sprintf(tmpbuf,"%%"); + Sprintf(tmpbuf, "%%"); break; case 't': /* game start, timestamp */ - Sprintf(tmpbuf, "%ld", ubirthday); + Sprintf(tmpbuf, "%lu", (unsigned long) ubirthday); break; case 'T': /* current time, timestamp */ - Sprintf(tmpbuf, "%ld", now); + Sprintf(tmpbuf, "%lu", (unsigned long) now); break; case 'd': /* game start, YYYYMMDDhhmmss */ Sprintf(tmpbuf, "%08ld%06ld", From ed300e5fa897ce702901cab43829bc3a8e99045d Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 6 Mar 2017 00:22:00 -0800 Subject: [PATCH 11/15] dumplog fixes: genocide list, survivor's tombstone The dumplog data was including a final tombstone unconditionally, which looked awfully strange for characters who didn't die. Make it conditional, like actual end-of-game tombstone. (One difference though: dumplog has a tombstone for hero who died from genocide, end-of-game does not. I think the latter should display one even though no grave gets generated.) [Potential for future enhancement: add some alternate ascii art in place of tombstone for survivors.] The list of genocided and/or extincted species was never shown since caller passed 'a' to list_genocided() and it expected 'y'. Also, once shown, the list entries were lacking indentation that other sections of the dump generally have. Both vanquished monsters and genocided/extinct monsters included a blank line separator even when there was no feedback, making a noticeable gap in the dumplog text. Have them report "no creatures vanquished" and "no species genocided", when applicable, so that their separator lines always have something to separate. When dumping, omit a couple of blank lines each from vanquished creatures list, genocided species list, and tombstone so the relevant sections of the dump are more compact. --- doc/fixes36.1 | 4 ++++ src/end.c | 53 +++++++++++++++++++++++++++++++++++++-------------- src/rip.c | 15 ++++++++++++--- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 39188f466..761d1e02e 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -394,6 +394,10 @@ when autopickup is overridden in a shop, always-pick-up exceptions and the fix for giving an alternate grow up message when a monster becomes a new form with the opposite sex (female gnome into male gnome lord, for example) had the logic wrong +DUMPLOG: genocided and extinct species was always a blank line; + vanquished creatures was just a blank line if nothing had been killed +DUMPLOG: RIP tombstone was printed for characters who survived (ascended, + escaped dungeon, quit, trickery or panic) Platform- and/or Interface-Specific Fixes diff --git a/src/end.c b/src/end.c index f0d3e2f07..c906c10b9 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.127 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1488788512 2017/03/06 08:21:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -737,12 +737,11 @@ int how; display_inventory((char *) 0, TRUE); container_contents(invent, TRUE, TRUE, FALSE); enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT), - (how >= PANICKED) ? ENL_GAMEOVERALIVE - : ENL_GAMEOVERDEAD); + (how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD); putstr(0, 0, ""); - list_vanquished('y', FALSE); + list_vanquished('d', FALSE); /* 'd' => 'y' */ putstr(0, 0, ""); - list_genocided('a', FALSE); + list_genocided('d', FALSE); /* 'd' => 'y' */ putstr(0, 0, ""); show_conduct((how >= PANICKED) ? 1 : 2); putstr(0, 0, ""); @@ -1270,9 +1269,13 @@ int how; done_stopprint = 1; /* just avoid any more output */ #ifdef DUMPLOG - dump_redirect(TRUE); - genl_outrip(0, how, endtime); - dump_redirect(FALSE); + /* 'how' reasons beyond genocide shouldn't show tombstone; + for normal end of game, genocide doesn't either */ + if (how <= GENOCIDED) { + dump_redirect(TRUE); + genl_outrip(0, how, endtime); + dump_redirect(FALSE); + } #endif if (u.uhave.amulet) { Strcat(killer.name, " (with the Amulet)"); @@ -1687,7 +1690,7 @@ dovanquished() /* high priests aren't unique but are flagged as such to simplify something */ #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \ - && mndx != PM_HIGH_PRIEST) + && mndx != PM_HIGH_PRIEST) STATIC_OVL void list_vanquished(defquery, ask) @@ -1701,6 +1704,11 @@ boolean ask; winid klwin; short mindx[NUMMONS]; char c, buf[BUFSZ], buftoo[BUFSZ]; + boolean dumping; /* for DUMPLOG; doesn't need to be conditional */ + + dumping = (defquery == 'd'); + if (dumping) + defquery = 'y'; /* get totals first */ ntypes = 0; @@ -1737,7 +1745,8 @@ boolean ask; klwin = create_nhwindow(NHW_MENU); putstr(klwin, 0, "Vanquished creatures:"); - putstr(klwin, 0, ""); + if (!dumping) + putstr(klwin, 0, ""); qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp); for (ni = 0; ni < ntypes; ni++) { @@ -1796,7 +1805,8 @@ boolean ask; * putstr(klwin, 0, "and a partridge in a pear tree"); */ if (ntypes > 1) { - putstr(klwin, 0, ""); + if (!dumping) + putstr(klwin, 0, ""); Sprintf(buf, "%ld creatures vanquished.", total_killed); putstr(klwin, 0, buf); } @@ -1806,6 +1816,10 @@ boolean ask; } else if (defquery == 'a') { /* #dovanquished rather than final disclosure, so pline() is ok */ pline("No monsters have been vanquished."); +#ifdef DUMPLOG + } else if (dumping) { + putstr(0, 0, "No monsters were vanquished."); /* not pline() */ +#endif } } @@ -1850,6 +1864,11 @@ boolean ask; char c; winid klwin; char buf[BUFSZ]; + boolean dumping; /* for DUMPLOG; doesn't need to be conditional */ + + dumping = (defquery == 'd'); + if (dumping) + defquery = 'y'; ngenocided = num_genocides(); nextinct = num_extinct(); @@ -1869,7 +1888,8 @@ boolean ask; (ngenocided) ? "Genocided" : "Extinct", (nextinct && ngenocided) ? " or extinct" : ""); putstr(klwin, 0, buf); - putstr(klwin, 0, ""); + if (!dumping) + putstr(klwin, 0, ""); for (i = LOW_PM; i < NUMMONS; i++) { /* uniques can't be genocided but can become extinct; @@ -1877,7 +1897,7 @@ boolean ask; if (UniqCritterIndx(i)) continue; if (mvitals[i].mvflags & G_GONE) { - Strcpy(buf, makeplural(mons[i].mname)); + Sprintf(buf, " %s", makeplural(mons[i].mname)); /* * "Extinct" is unfortunate terminology. A species * is marked extinct when its birth limit is reached, @@ -1889,7 +1909,8 @@ boolean ask; putstr(klwin, 0, buf); } } - putstr(klwin, 0, ""); + if (!dumping) + putstr(klwin, 0, ""); if (ngenocided > 0) { Sprintf(buf, "%d species genocided.", ngenocided); putstr(klwin, 0, buf); @@ -1902,6 +1923,10 @@ boolean ask; display_nhwindow(klwin, TRUE); destroy_nhwindow(klwin); } +#ifdef DUMPLOG + } else if (dumping) { + putstr(0, 0, "No species were genocided or became extinct."); +#endif } } diff --git a/src/rip.c b/src/rip.c index 64bd8dd01..11abaea63 100644 --- a/src/rip.c +++ b/src/rip.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 rip.c $NHDT-Date: 1450432760 2015/12/18 09:59:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.22 $ */ +/* NetHack 3.6 rip.c $NHDT-Date: 1488788514 2017/03/06 08:21:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.23 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -141,12 +141,21 @@ time_t when; Sprintf(buf, "%4ld", year); center(YEAR_LINE, buf); - putstr(tmpwin, 0, ""); +#ifdef DUMPLOG + if (tmpwin == 0) + dump_forward_putstr(0, 0, "Gave over:", TRUE); + else +#endif + putstr(tmpwin, 0, ""); + for (; *dp; dp++) putstr(tmpwin, 0, *dp); putstr(tmpwin, 0, ""); - putstr(tmpwin, 0, ""); +#ifdef DUMPLOG + if (tmpwin != 0) +#endif + putstr(tmpwin, 0, ""); for (x = 0; rip_txt[x]; x++) { free((genericptr_t) rip[x]); From 9623154bbcc6d0f3e5565034a610adbd9e6a04d9 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 10 Mar 2017 16:35:49 -0800 Subject: [PATCH 12/15] vanquished monsters tidbit Use the same terminology ("creatures" rather than "monsters") for the two "no {have been, were} vanquished" messages as for the "disclose vanquished " prompt and its "M vanquished" summary. --- src/end.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/end.c b/src/end.c index c906c10b9..7685a8226 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1488788512 2017/03/06 08:21:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1489192539 2017/03/11 00:35:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.130 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1815,10 +1815,10 @@ boolean ask; } } else if (defquery == 'a') { /* #dovanquished rather than final disclosure, so pline() is ok */ - pline("No monsters have been vanquished."); + pline("No creatures have been vanquished."); #ifdef DUMPLOG } else if (dumping) { - putstr(0, 0, "No monsters were vanquished."); /* not pline() */ + putstr(0, 0, "No creatures were vanquished."); /* not pline() */ #endif } } From 278b6d52ebbf475cae41a892fd5c66f95fc5d610 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 10 Mar 2017 16:41:49 -0800 Subject: [PATCH 13/15] dumplog message history groundwork Separate the message logging out of pline so that other things (for instance, one-line summary for quest block messages) can be logged. The code that utilizes this isn't ready for prime time yet. For FREE_ALL_MEMORY, release DUMPLOG message history when saving. (Actually, this frees it unconditionally rather just doing so for FREE_ALL_MEMORY.) It was being freed when logged at end of game, but not during save. If dumplog message history and interface message history get integrated, the existing message history save/restore handling should become applicable instead. --- include/extern.h | 6 ++++- src/pline.c | 69 ++++++++++++++++++++++++++++++------------------ src/save.c | 5 +++- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/include/extern.h b/include/extern.h index 800969d6f..3f5b34971 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1488075978 2017/02/26 02:26:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.582 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1489192904 2017/03/11 00:41:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.583 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1801,6 +1801,10 @@ E boolean FDECL(is_autopickup_exception, (struct obj *, BOOLEAN_P)); /* ### pline.c ### */ +#ifdef DUMPLOG +E void FDECL(dumplogmsg, (const char *)); +E void NDECL(dumplogfreemessages); +#endif E void VDECL(pline, (const char *, ...)) PRINTF_F(1, 2); E void VDECL(Norep, (const char *, ...)) PRINTF_F(1, 2); E void NDECL(free_youbuf); diff --git a/src/pline.c b/src/pline.c index 6103d29cf..2a473625b 100644 --- a/src/pline.c +++ b/src/pline.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pline.c $NHDT-Date: 1461437814 2016/04/23 18:56:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.51 $ */ +/* NetHack 3.6 pline.c $NHDT-Date: 1489192905 2017/03/11 00:41:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.57 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -18,6 +18,47 @@ static void FDECL(execplinehandler, (const char *)); /* also used in end.c */ unsigned saved_pline_index = 0; /* slot in saved_plines[] to use next */ char *saved_plines[DUMPLOG_MSG_COUNT] = { (char *) 0 }; + +/* keep the most recent DUMPLOG_MSG_COUNT messages */ +void +dumplogmsg(line) +const char *line; +{ + /* + * TODO: + * This essentially duplicates message history, which is + * currently implemented in an interface-specific manner. + * The core should take responsibility for that and have + * this share it. + */ + unsigned indx = saved_pline_index; /* next slot to use */ + char *oldest = saved_plines[indx]; /* current content of that slot */ + + if (oldest && strlen(oldest) >= strlen(line)) { + /* this buffer will gradually shrink until the 'else' is needed; + there's no pressing need to track allocation size instead */ + Strcpy(oldest, line); + } else { + if (oldest) + free((genericptr_t) oldest); + saved_plines[indx] = dupstr(line); + } + saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT; +} + +/* called during save (unlike the interface-specific message history, + this data isn't saved and restored); end-of-game releases saved_pline[] + while writing its contents to the final dump log */ +void +dumplogfreemessages() +{ + unsigned indx; + + for (indx = 0; indx < DUMPLOG_MSG_COUNT; ++indx) + if (saved_plines[indx]) + free((genericptr_t) saved_plines[indx]), saved_plines[indx] = 0; + saved_pline_index = 0; +} #endif /*VARARGS1*/ @@ -86,7 +127,6 @@ VA_DECL(const char *, line) pbuf[BUFSZ - 1 - 1] = line[ln - 1]; pbuf[BUFSZ - 1] = '\0'; line = pbuf; - ln = BUFSZ - 1; } if (!iflags.window_inited) { raw_print(line); @@ -99,30 +139,7 @@ VA_DECL(const char *, line) * Unfortunately, that means Norep() isn't honored (general issue) and * that short lines aren't combined into one longer one (tty behavior). */ - { - /* - * TODO: - * This essentially duplicates message history, which is - * currently implemented in an interface-specific manner. - * The core should take responsibility for that and have - * this share it. - * Ideally history for prompt lines should be augmented - * with the player's response once that has been specified. - */ - unsigned indx = saved_pline_index; /* next slot to use */ - char *oldest = saved_plines[indx]; /* current content of that slot */ - - if (oldest && (int) strlen(oldest) >= ln) { /* ln==strlen(line) */ - /* this buffer will gradually shrink until the 'else' is needed; - there's no pressing need to track allocation size instead */ - Strcpy(oldest, line); - } else { - if (oldest) - free((genericptr_t) oldest); - saved_plines[indx] = dupstr(line); - } - saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT; - } + dumplogmsg(line); #endif msgtyp = msgtype_type(line, no_repeat); diff --git a/src/save.c b/src/save.c index 078f276af..5270cc657 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 save.c $NHDT-Date: 1450231175 2015/12/16 01:59:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 save.c $NHDT-Date: 1489192905 2017/03/11 00:41:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.101 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1410,6 +1410,9 @@ freedynamicdata() #ifdef STATUS_VIA_WINDOWPORT status_finish(); #endif +#ifdef DUMPLOG + dumplogfreemessages(); +#endif /* last, because it frees data that might be used by panic() to provide feedback to the user; conceivably other freeing might trigger panic */ From f9438f6e530d9cd442ec16bda5af5be62bb185d5 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 13 Mar 2017 17:17:58 +0200 Subject: [PATCH 14/15] Document explicitly what m-prefix does to each command --- doc/Guidebook.mn | 5 +++++ doc/Guidebook.tex | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index cf0c5f73c..d9263acf9 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -929,6 +929,7 @@ dungeon level. All levels with annotations are displayed by the ``#overview'' command. Autocompletes. Default key is 'M-A', and '^N' if number_pad is on. .lp #apply Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is 'a'. +If the tool used acts on items on the floor, using the `m' prefix skips those items. .lp #attributes Show your attributes. Default key is '^X'. .lp #autopickup @@ -957,6 +958,7 @@ Drop an item. Default key is 'd'. Drop specific item types. Default key is 'D'. .lp "#eat " Eat something. Default key is 'e'. +The `m' prefix skips eating items on the floor. .lp #engrave Engrave writing on the floor. Default key is 'E'. .lp #enhance @@ -1008,6 +1010,7 @@ Name a monster, an individual object, or a type of object. Same as #call. Autocompletes. Default keys are 'N', 'M-n', and 'M-N'. .lp #offer Offer a sacrifice to the gods. Autocompletes. Default key is 'M-o'. +The `m' prefix skips offering items on the altar. .lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. @@ -1029,6 +1032,7 @@ Test the panic routine. Autocompletes. Wizard-mode only. Pay your shopping bill. Default key is 'p'. .lp #pickup Pick up things at the current location. Default key is ','. +The `m' prefix forces use of a menu. .lp #polyself Polymorph self. Autocompletes. Wizard-mode only. .lp #pray @@ -1118,6 +1122,7 @@ Look at the timeout queue. Autocompletes. Wizard-mode only. .lp "#tip " Tip over a container (bag or box) to pour out its contents. Autocompletes. Default key is 'M-T'. +The `m' prefix makes the command use a menu. .lp #travel Travel to a specific location on the map. Default key is '_'. Using the ``request menu'' prefix shows a menu of interesting targets in sight diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index e2ea3bbf6..11c40a89a 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1113,6 +1113,8 @@ and '{\tt \^{}N}' if {\it number\verb+_+pad\/} is on. \item[\tb{\#apply}] Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is '{\tt a}'. +If the tool used acts on items on the floor, using the `{\tt m}' prefix +skips those items. %.lp \item[\tb{\#attributes}] Show your attributes. Default key is '{\tt \^{}X}'.\\ @@ -1154,6 +1156,7 @@ Drop specific item types. Default key is '{\tt D}'. %.lp \item[\tb{\#eat}] Eat something. Default key is '{\tt e}'. +The `{\tt m}' prefix skips eating items on the floor. %.lp \item[\tb{\#engrave}] Engrave writing on the floor. Default key is '{\tt E}'. @@ -1230,6 +1233,7 @@ Autocompletes. Default keys are '{\tt N}', '{\tt M-n}', and '{\tt M-N}'. %.lp \item[\tb{\#offer}] Offer a sacrifice to the gods. Autocompletes. Default key is '{\tt M-o}'.\\ +The `{\tt m}' prefix skips offering items on the altar. %.lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. @@ -1257,6 +1261,7 @@ Pay your shopping bill. Default key is '{\tt p}'. %.lp \item[\tb{\#pickup}] Pick up things at the current location. Default key is '{\tt ,}'. +The `{\tt m}' prefix forces use of a menu. %.lp \item[\tb{\#polyself}] Polymorph self. Autocompletes. Wizard-mode only. @@ -1384,6 +1389,7 @@ Look at the timeout queue. Autocompletes. Wizard-mode only. \item[\tb{\#tip}] Tip over a container (bag or box) to pour out its contents. Autocompletes. Default key is '{\tt M-T}'. +The `{\tt m}' prefix makes the command use a menu. %.lp \item[\tb{\#travel}] Travel to a specific location on the map. Default key is '{\tt _}'. From 08fe086124838cd6d9b4914ad7899e0c179da134 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 13 Mar 2017 20:32:43 +0200 Subject: [PATCH 15/15] Show control key combos with uppercase alpha Instead of ^r, show ^R, as that one is used everywhere. --- src/cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd.c b/src/cmd.c index bf2c80e30..79e096249 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3741,7 +3741,7 @@ char *txt; /* sufficiently long buffer */ else if (c == '\n') Sprintf(txt, ""); else if (ISCTRL(c)) - Sprintf(txt, "^%c", UNCTRL(c)); + Sprintf(txt, "^%c", highc(UNCTRL(c))); else if (ISMETA(c)) Sprintf(txt, "M-%c", UNMETA(c)); else if (c >= 33 && c <= 126)