From 6b35a8de196531a2b6be2d2d3ee1a4a32e9dce91 Mon Sep 17 00:00:00 2001 From: Vitaly Ostrosablin Date: Tue, 13 Aug 2019 20:59:53 +0300 Subject: [PATCH] add a new option: autosave This patch adds a new compound option: autosave (as proposed in GitHub issue #208). The idea behind patch is to allow artificially limiting game session duration, by automatically saving-and-quitting after playing for N turns (where N is argument of the option). This could be useful for public servers, where *Robin accounts are used as a form of multiplayer and players play for N turns, and then give control to another person. Another use case is to use this option to just keep yourself from playing too long game sessions (e.g. during coffee-break, etc.). Option is specified like this: OPTIONS=autosave:500 Which would save-and-quit after playing for 500 turns. Defaults to -1 (autosave disabled). 0 is invalid value, because game would then quit right after starting. Any positive value is used as turn countdown, and when it reaches 0, game is saved and quit automatically. Signed-off-by: Vitaly Ostrosablin --- dat/opthelp | 3 +++ doc/Guidebook.mn | 6 ++++++ doc/Guidebook.tex | 7 +++++++ doc/Guidebook.txt | 8 ++++++++ doc/fixes36.3 | 1 + include/flag.h | 1 + src/allmain.c | 2 ++ src/cmd.c | 2 ++ src/options.c | 30 ++++++++++++++++++++++++++++++ src/save.c | 2 +- 10 files changed, 61 insertions(+), 1 deletion(-) diff --git a/dat/opthelp b/dat/opthelp index ee9092ede..0091d5bbd 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -204,6 +204,9 @@ Compound options which may be set only on startup are: align Your starting alignment (lawful, neutral, chaotic, [random] or random). Many roles restrict the choice to a subset. You may specify just the first letter. +autosave Save and quit the game automatically, after playing for [off] + N turns (useful if you want to artificially limit the + game session duration). catname the name of your first cat [none] dogname the name of your first dog [none] Several roles who start with a dog have one whose name is diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index ce6d40df0..ffb4c6806 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3362,6 +3362,12 @@ A value of 0 means \(lqno limit\(rq the objects\(rq since the pile size will always be at least that big; default value is 5. Persistent. +.lp autosave +Save and quit the game automatically, after playing for N turns +\(lquseful if you want to artificially limit the game session duration\(rq. +A value of -1 (and other negative values) mean that autosave is off, while +positive values are used for turn countdown until save. 0 is not a valid +turn count because this would cause the game to quit as soon as it starts. .lp playmode Values are \(lqnormal\(rq, \(lqexplore\(rq, or \(lqdebug\(rq. Allows selection of explore mode (also known as discovery mode) or debug diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7cb9bf0b1..b012d38d5 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3701,6 +3701,13 @@ \subsection*{Customization options} the objects'' since the pile size will always be at least that big; default value is 5. Persistent. %.lp +\item[\ib{autosave}] +Save and quit the game automatically, after playing for N turns +(useful if you want to artificially limit the game session duration). +A value of -1 (and other negative values) mean that autosave is off, while +positive values are used for turn countdown until save. 0 is not a valid +turn count because this would cause the game to quit as soon as it starts. +%.lp \item[\ib{playmode}] Values are {\it normal\/}, {\it explore\/}, or {\it debug\/}. Allows selection of explore mode (also known as discovery mode) or debug diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index c622c832d..ff5671940 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -3887,6 +3887,14 @@ of 1 effectively means "never show the objects" since the pile size will always be at least that big; default value is 5. + autosave + Save and quit the game automatically, after playing for N turns + (useful if you want to artificially limit the game session du- + ration). A value of -1 (and other negative values) mean that + autosave is off, while positive values are used for turn count- + down until save. 0 is not a valid turn count because this would + cause the game to quit as soon as it starts. + NetHack 3.6 May 7, 2019 diff --git a/doc/fixes36.3 b/doc/fixes36.3 index ea02fdf39..949b9cc30 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -258,6 +258,7 @@ curses: give menus and text windows a minimum size of 5x25 since tiny ones can curses+'perm_invent': since persistent inventory is narrow, strip off "a", "an", or "the" prefix on inventory entries shown there so that a tiny bit more of the interesting portion is visible +autosave option, allowing to automatically save-and-quit game after N turns NetHack Community Patches (or Variation) Included diff --git a/include/flag.h b/include/flag.h index 3ea3ada26..f86fbbe4b 100644 --- a/include/flag.h +++ b/include/flag.h @@ -276,6 +276,7 @@ struct instance_flags { int menu_headings; /* ATR for menu headings */ int *opt_booldup; /* for duplication of boolean opts in config file */ int *opt_compdup; /* for duplication of compound opts in conf file */ + int autosave; /* controls, how many turns to wait before autosave */ #ifdef ALTMETA boolean altmeta; /* Alt-c sends ESC c rather than M-c */ #endif diff --git a/src/allmain.c b/src/allmain.c index 57170b66c..077fe1107 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -185,6 +185,8 @@ boolean resuming; u.ublesscnt--; if (flags.time && !context.run) iflags.time_botl = TRUE; + if (iflags.autosave > 0) + iflags.autosave--; /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. diff --git a/src/cmd.c b/src/cmd.c index 82a7819c7..69866d86d 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -4712,6 +4712,8 @@ register char *cmd; if (program_state.done_hup) end_of_input(); #endif + if (!iflags.autosave) + dosave(); if (firsttime) { context.nopick = 0; cmd = parse(); diff --git a/src/options.c b/src/options.c index 2f601df33..0acfde37c 100644 --- a/src/options.c +++ b/src/options.c @@ -41,6 +41,7 @@ enum window_option_types { }; #define PILE_LIMIT_DFLT 5 +#define AUTOSAVE_DFLT -1 /* * NOTE: If you add (or delete) an option, please update the short @@ -273,6 +274,7 @@ static struct Comp_Opt { { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/ { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/ { "altkeyhandler", "alternate key handler", 20, SET_IN_GAME }, + { "autosave", "quit game after N turns", 20, DISP_IN_GAME }, /*WC*/ #ifdef BACKWARD_COMPAT { "boulder", "deprecated (use S_boulder in sym file instead)", 1, SET_IN_GAME }, @@ -735,6 +737,7 @@ initoptions_init() flags.end_around = 2; flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */ flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */ + iflags.autosave = -1; /* Disabled by default */ flags.runmode = RUN_LEAP; iflags.msg_history = 20; /* msg_window has conflicting defaults for multi-interface binary */ @@ -3078,6 +3081,30 @@ boolean tinitial, tfrom_file; return retval; } + /* autosave: play game for N turns, then automatically save-and-quit + when safe opportunity presents itself (-1 = no autosave) */ + fullname = "autosave"; + if (match_optname(opts, fullname, 5, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) + iflags.autosave = negated ? -1 : atoi(op); + else if (negated) { + bad_negation(fullname, TRUE); + return FALSE; + } else /* !op */ + iflags.autosave = AUTOSAVE_DFLT; + /* sanity check */ + if (iflags.autosave < -1) + iflags.autosave = AUTOSAVE_DFLT; + else if (iflags.autosave == 0) { + config_error_add("Autosave should be non-zero, assuming off"); + iflags.autosave = AUTOSAVE_DFLT; + } + return retval; + } + /* play mode: normal, explore/discovery, or debug/wizard */ fullname = "playmode"; if (match_optname(opts, fullname, 4, TRUE)) { @@ -5706,6 +5733,9 @@ char *buf; Sprintf(buf, "%s", ocl[0] ? ocl : "all"); } else if (!strcmp(optname, "pile_limit")) { Sprintf(buf, "%d", flags.pile_limit); + } else if (!strcmp(optname, "autosave")) { + (iflags.autosave < 0) ? Strcpy(buf, "off") + : Sprintf(buf, "%d", iflags.autosave); } else if (!strcmp(optname, "playmode")) { Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal"); } else if (!strcmp(optname, "race")) { diff --git a/src/save.c b/src/save.c index e0af4b8d2..eeaf750dc 100644 --- a/src/save.c +++ b/src/save.c @@ -80,7 +80,7 @@ dosave() if (iflags.debug_fuzzer) return 0; clear_nhwindow(WIN_MESSAGE); - if (yn("Really save?") == 'n') { + if (iflags.autosave && yn("Really save?") == 'n') { clear_nhwindow(WIN_MESSAGE); if (multi > 0) nomul(0);