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/Guidebook.mn b/doc/Guidebook.mn index 8b7397336..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 @@ -3776,6 +3781,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..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 _}'. @@ -4589,6 +4595,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 56916b68e..761d1e02e 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 @@ -366,6 +365,9 @@ 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 +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 @@ -389,6 +391,13 @@ 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 +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 @@ -557,6 +566,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 1e715da07..11fb16e26 100644 --- a/include/config.h +++ b/include/config.h @@ -521,6 +521,32 @@ typedef unsigned char uchar; # define CONWAY /* Conway's Life level */ #endif +/* #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 b05688d98..2b59442c6 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: 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. */ @@ -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 ### */ @@ -1801,6 +1804,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); @@ -2714,6 +2721,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_P)); +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/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/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/cmd.c b/src/cmd.c index 4d39a8ba6..79e096249 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 * @@ -3676,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) diff --git a/src/detect.c b/src/detect.c index 7668d77bb..a46cf47f9 100644 --- a/src/detect.c +++ b/src/detect.c @@ -25,6 +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)); /* bring hero out from underwater or underground or being engulfed; return True iff any change occurred */ @@ -1715,6 +1717,109 @@ 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 +1830,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 +1841,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..7685a8226 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: 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. */ @@ -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) @@ -667,6 +671,88 @@ char *defquery; return TRUE; } +#ifdef DUMPLOG +STATIC_OVL void +dump_plines() +{ + 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, 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); +#ifdef FREE_ALL_MEMORY + free(*strp), *strp = 0; +#endif + } + } +} +#endif + +STATIC_OVL void +dump_everything(how) +int how; +{ +#ifdef DUMPLOG + struct obj *obj; + 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('d', FALSE); /* 'd' => 'y' */ + putstr(0, 0, ""); + list_genocided('d', FALSE); /* 'd' => 'y' */ + 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); +#else + nhUse(how); +#endif +} + STATIC_OVL void disclose(how, taken) int how; @@ -1016,6 +1102,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. @@ -1082,6 +1169,8 @@ int how; if (strcmp(flags.end_disclose, "none") && how != PANICKED) disclose(how, taken); + dump_everything(how); + /* finish_paybill should be called after disclosure but before bones */ if (bones_ok && taken) finish_paybill(); @@ -1179,6 +1268,15 @@ int how; } else done_stopprint = 1; /* just avoid any more output */ +#ifdef DUMPLOG + /* '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)"); } else if (how == ESCAPED) { @@ -1189,16 +1287,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 +1319,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 +1387,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 +1411,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) @@ -1594,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) @@ -1608,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; @@ -1644,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++) { @@ -1703,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); } @@ -1712,7 +1815,11 @@ 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 creatures were vanquished."); /* not pline() */ +#endif } } @@ -1733,7 +1840,7 @@ num_genocides() return n; } -int +STATIC_OVL int num_extinct() { int i, n = 0; @@ -1757,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(); @@ -1776,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; @@ -1784,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, @@ -1796,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); @@ -1809,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/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/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/makemon.c b/src/makemon.c index 97e6edbd8..9c5e4cd47 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1794,11 +1794,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), @@ -1830,8 +1830,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) { 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 diff --git a/src/pline.c b/src/pline.c index c38ff33af..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. */ @@ -14,6 +14,53 @@ static char *FDECL(You_buf, (int)); static void FDECL(execplinehandler, (const char *)); #endif +#ifdef DUMPLOG +/* 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*/ /* Note that these declarations rely on knowledge of the internals * of the variable argument handling stuff in "tradstdc.h" @@ -87,6 +134,14 @@ VA_DECL(const char *, line) return; } +#ifdef DUMPLOG + /* 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). + */ + dumplogmsg(line); +#endif + msgtyp = msgtype_type(line, no_repeat); if (msgtyp == MSGTYP_NOSHOW || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg))) @@ -104,7 +159,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-- */ 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]); diff --git a/src/save.c b/src/save.c index ef9cb06d7..71b611e1f 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. */ @@ -1413,6 +1413,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 */ diff --git a/src/sp_lev.c b/src/sp_lev.c index 9fa5a095a..d8cde1f13 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; } 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..70c9feee7 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. */ @@ -1032,4 +1032,270 @@ 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 +STATIC_VAR time_t dumplog_now; + +STATIC_DCL char *FDECL(dump_fmtstr, (const char *, char *)); + +STATIC_OVL char * +dump_fmtstr(fmt, buf) +const char *fmt; +char *buf; +{ + const char *fp = fmt; + char *bp = buf; + int slen, len = 0; + char tmpbuf[BUFSZ]; + char verbuf[BUFSZ]; + long uid; + time_t now; + + 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++; + switch (*fp) { + default: + goto finish; + case '\0': /* fallthrough */ + case '%': /* literal % */ + Sprintf(tmpbuf, "%%"); + break; + case 't': /* game start, timestamp */ + Sprintf(tmpbuf, "%lu", (unsigned long) ubirthday); + break; + case 'T': /* current time, timestamp */ + Sprintf(tmpbuf, "%lu", (unsigned long) 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, "%ld", 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; + + dumplog_now = now; +#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; + +#else /*!DUMPLOG*/ + nhUse(now); +#endif /*?DUMPLOG*/ +} + +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); +} + +/*ARGSUSED*/ +STATIC_OVL void +dump_putstr(win, attr, str) +winid win UNUSED; +int attr UNUSED; +const char *str; +{ + if (dumplog_file) + fprintf(dumplog_file, "%s\n", str); +} + +STATIC_OVL winid +dump_create_nhwindow(dummy) +int dummy; +{ + return dummy; +} + +/*ARGUSED*/ +STATIC_OVL void +dump_clear_nhwindow(win) +winid win UNUSED; +{ + return; +} + +/*ARGSUSED*/ +STATIC_OVL void +dump_display_nhwindow(win, p) +winid win UNUSED; +boolean p UNUSED; +{ + return; +} + +/*ARGUSED*/ +STATIC_OVL void +dump_destroy_nhwindow(win) +winid win UNUSED; +{ + return; +} + +/*ARGUSED*/ +STATIC_OVL void +dump_start_menu(win) +winid win UNUSED; +{ + return; +} + +/*ARGSUSED*/ +STATIC_OVL void +dump_add_menu(win, glyph, identifier, ch, gch, attr, str, 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) + fprintf(dumplog_file, " %s\n", str); + else + fprintf(dumplog_file, " %c - %s\n", ch, str); + } +} + +/*ARGSUSED*/ +STATIC_OVL void +dump_end_menu(win, str) +winid win UNUSED; +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, how, item) +winid win UNUSED; +int how UNUSED; +menu_item **item; +{ + *item = (menu_item *) 0; + return 0; +} + +void +dump_redirect(onoff_flag) +boolean onoff_flag; +{ + if (dumplog_file) { + if (onoff_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 = onoff_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 e571e3783..55ea0367d 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1448,6 +1448,9 @@ static const char *build_opts[] = { #ifdef DLB "data librarian", #endif +#ifdef DUMPLOG + "end-of-game dumplogs", +#endif #ifdef MFLOPPY "floppy drive support", #endif