Change the MAX_REROLLS option to be MAX_REROLL_RATE

Having a hard cap on the number of rerolls doesn't help save CPU
usage from excessive rerolling, because if the cap is set low
enough to keep the CPU usage reasonable it isn't high enough for
players to actually use the feature.

Instead, allow capping the number of rerolls per second. (Sensible
values seem to be in the 5-10 range.) If the player attempts more
rerolls than this, show a paranoid confirmation prompt: the need to
type the answer to the prompt will slow a human user down (and if
the prompt is filled in too quickly, it will simply just be shown
again, preventing attempts to use automation to skip the prompt).
This commit is contained in:
Alex Smith
2026-05-22 06:02:00 +01:00
parent 7d512fd8ce
commit 3f837939ba
7 changed files with 49 additions and 37 deletions
+1 -1
View File
@@ -141,7 +141,7 @@ tty: allow custom glyph colors if they're basic 16 nethack colors
General New Features
--------------------
sysconf option MAX_REROLLS to limit number of rerolls
sysconf option MAX_REROLL_RATE to limit rate at which rerolls are performed
Platform- and/or Interface-Specific New Features
+1 -1
View File
@@ -25,7 +25,7 @@ struct sysopt_s {
* -1: getenv() didn't find a value for DEBUGFILES.
*/
int maxplayers;
int maxrerolls;
int maxrerollrate;
int seduce;
int check_save_uid; /* restoring savefile checks UID? */
int check_plname; /* use plname for checking wizards/explorers/shellers */
+36
View File
@@ -769,6 +769,17 @@ newgame(void)
{
int i;
#ifdef SYSCF
time_t last_reroll_time;
time_t cur_reroll_time;
int rerolls_this_second = 0;
# if defined(BSD) && !defined(POSIX_TYPES)
# define GET_REROLL_TIME(t) (void) time((long *) t);
# else
# define GET_REROLL_TIME(t) (void) time(t);
# endif
#endif /* defined(SYSCF) */
/* make sure welcome messages are given before noticing monsters */
notice_mon_off();
disp.botlx = TRUE;
@@ -819,7 +830,32 @@ newgame(void)
docrt();
flush_screen(1);
bot();
#ifdef SYSCF
GET_REROLL_TIME(&last_reroll_time);
#endif
while (u.uroleplay.reroll && reroll_menu()) {
#ifdef SYSCF
if (sysopt.maxrerollrate > 0) {
check_reroll_time:
GET_REROLL_TIME(&cur_reroll_time);
if (last_reroll_time != cur_reroll_time) {
last_reroll_time = cur_reroll_time;
rerolls_this_second = 1;
} else {
if (rerolls_this_second >= sysopt.maxrerollrate) {
if (!paranoid_query(TRUE, "Continue rerolling?"))
break;
goto check_reroll_time;
}
++rerolls_this_second;
}
}
#endif
++u.uroleplay.numrerolls;
u_init_inventory_attrs();
bot();
}
+5 -5
View File
@@ -74,7 +74,7 @@ staticfn boolean cnf_line_CHECK_PLNAME(char *);
staticfn boolean cnf_line_SEDUCE(char *);
staticfn boolean cnf_line_HIDEUSAGE(char *);
staticfn boolean cnf_line_MAXPLAYERS(char *);
staticfn boolean cnf_line_MAX_REROLLS(char *);
staticfn boolean cnf_line_MAX_REROLL_RATE(char *);
staticfn boolean cnf_line_PERSMAX(char *);
staticfn boolean cnf_line_PERS_IS_UID(char *);
staticfn boolean cnf_line_ENTRYMAX(char *);
@@ -970,15 +970,15 @@ cnf_line_MAXPLAYERS(char *bufp)
}
staticfn boolean
cnf_line_MAX_REROLLS(char *bufp)
cnf_line_MAX_REROLL_RATE(char *bufp)
{
int n = atoi(bufp);
if (n < 0 || n > 255) {
config_error_add("Illegal value in MAX_REROLLS (maximum is 255)");
config_error_add("Illegal value in MAX_REROLL_RATE (maximum is 255)");
n = 10;
}
sysopt.maxrerolls = n;
sysopt.maxrerollrate = n;
return TRUE;
}
@@ -1363,7 +1363,7 @@ static const struct match_config_line_stmt {
CNFL_S(SEDUCE, 6),
CNFL_S(HIDEUSAGE, 9),
CNFL_S(MAXPLAYERS, 10),
CNFL_S(MAX_REROLLS, 10),
CNFL_S(MAX_REROLL_RATE, 10),
CNFL_S(PERSMAX, 7),
CNFL_S(PERS_IS_UID, 11),
CNFL_S(ENTRYMAX, 8),
+2 -26
View File
@@ -26,7 +26,6 @@ staticfn int ckvalidcat(struct obj *);
staticfn int ckunpaid(struct obj *);
staticfn char *safeq_xprname(struct obj *);
staticfn char *safeq_shortxprname(struct obj *);
staticfn boolean hit_reroll_limit(int);
staticfn char display_pickinv(const char *, const char *, const char *,
boolean, boolean, long *);
staticfn char display_used_invlets(char);
@@ -2541,16 +2540,6 @@ askchain(
return cnt;
}
staticfn boolean
hit_reroll_limit(int curr)
{
#ifdef SYSCF
if (sysopt.maxrerolls && curr >= (sysopt.maxrerolls-1))
return TRUE;
#endif /*SYSCF*/
return FALSE;
}
/* The menu for rerolling attributes and inventory.
This is similar to the other inventory menus, but simpler to help it fit on
@@ -2580,14 +2569,6 @@ reroll_menu(void)
MENU_ITEMFLAGS_NONE);
any.a_char = 'y';
Strcpy(buf, "reroll another character");
#ifdef SYSCF
if (sysopt.maxrerolls > 0) {
char *bp = eos(buf);
Sprintf(bp, " (%li/%i)",
u.uroleplay.numrerolls+1, sysopt.maxrerolls);
}
#endif /*SYSCF*/
add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : 'r', 0,
ATR_NONE, NO_COLOR, buf, MENU_ITEMFLAGS_NONE);
any.a_char = 0;
@@ -2618,7 +2599,7 @@ reroll_menu(void)
if (select_menu(win, PICK_ONE, &pick_list) > 0) {
option = pick_list[0].item.a_char;
free((genericptr_t) pick_list);
} else if (!hit_reroll_limit(u.uroleplay.numrerolls)) {
} else {
/* user closed the menu without selecting; unclear what their choice
is here so ask again; but (e.g. for hangup handling) stop asking if
the user cancels out again */
@@ -2626,12 +2607,7 @@ reroll_menu(void)
}
destroy_nhwindow(win);
if (option == 'y') {
++u.uroleplay.numrerolls;
if (!hit_reroll_limit(u.uroleplay.numrerolls))
return TRUE;
}
return FALSE;
return option == 'y';
}
/*
+1 -1
View File
@@ -59,7 +59,7 @@ sys_early_init(void)
sysopt.genericusers = (char *) 0;
sysopt.msghandler = (char *) 0;
sysopt.maxplayers = 0; /* XXX eventually replace MAX_NR_OF_PLAYERS */
sysopt.maxrerolls = 0;
sysopt.maxrerollrate = 0;
sysopt.bones_pools = 0;
sysopt.livelog = LL_NONE;
+3 -3
View File
@@ -55,9 +55,9 @@ GENERICUSERS=play player game games nethack nethacker ec2-user
# letter and "lock" (eg. alock, block, ...)
MAXPLAYERS=10
# Limit the number of rerolls
# Default is 0 (meaning infinite), valid values are 0-255
MAX_REROLLS=0
# Limit the number of rerolls per second when using the reroll option.
# 0 means that there is no limit on how quickly the user can reroll.
MAX_REROLL_RATE=0
# If not null, added to string "To get local support, " in the support
# information help.