From: PatR Date: Fri, 16 Apr 2021 22:35:25 +0000 (-0700) Subject: OPTIONS=scores:own X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=563ed2f7db875bb899fa628cb3ff7e92edc870fb;p=nethack OPTIONS=scores:own From a beta tester six years ago: specifying 'scores:own' resulted in an option setting of 'scores:3 top/2 around/own' when player wanted 'scores:0 top/0 around/own'. Change it so that when fewer than all three fields are given new values, the others are reset rather than having their old values merge with new settings. Also, 'scores:none' can be used to get 'scores:0 top/0 around/!own' to skip the scores at the end without skipping notification of whether the ending game's score made it into the top N list. Options parsing accepts '!scores' and then ignores the negation. Changing the optlist flags for 'scores' to allow negation resulted in a complaint about missing value; I gave up instead of pursuing that. 'scores:none' should suffice. Setting 'scores:!top/own' or 'scores:!around/own' would behave as 'scores:1 top/!own' or 'scores:1 around/!own', respectively. 'scores:!top/!around/own' behaved as 'scores:1 top/1 around/own' (note affect of two prior negations on final field compared to single negation in the earlier two variations). This fixes those. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index a9b39b928..c98096082 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -455,6 +455,11 @@ if a branch has only one level (Fort Ludios), prevent creation of any level opening/unlocking magic zapped at monster holding the hero will release hold (zap at engulfer already expels hero); zapping at self has same effect when riding, allow scroll of remove curse to affect to affect steed's saddle +the 'scores' option for final top ten display left default values in place if + only some of the three settings were set; 'scores:own' should have + produced '0 top/0 around/own' but ended up as '3 top/2 around/own'; + also, allow 'scores:none' as shorthand for 'scores:0 t/0 a/!o' (player + will to told whether new score made the list but no scores get shown) Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/options.c b/src/options.c index 4865ed675..d04ab4ac4 100644 --- a/src/options.c +++ b/src/options.c @@ -2721,44 +2721,61 @@ optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) if ((op = string_for_opt(opts, FALSE)) == empty_optstr) return optn_err; + /* 3.7: earlier versions left old values for unspecified arguments + if player's scores:foo option only specified some of the three; + in particular, attempting to use 'scores:own' rather than + 'scores:0 top/0 around/own' didn't work as intended */ + flags.end_top = flags.end_around = 0, flags.end_own = FALSE; + + if (negated) + op = eos(op); + while (*op) { int inum = 1; + negated = (*op == '!'); + if (negated) + op++; + if (digit(*op)) { inum = atoi(op); while (digit(*op)) op++; - } else if (*op == '!') { - negated = !negated; - op++; } while (*op == ' ') op++; - switch (*op) { + switch (lowc(*op)) { case 't': - case 'T': - flags.end_top = inum; + flags.end_top = negated ? 0 : inum; break; case 'a': - case 'A': - flags.end_around = inum; + flags.end_around = negated ? 0 : inum; break; case 'o': - case 'O': - flags.end_own = !negated; + flags.end_own = (negated || !inum) ? FALSE : TRUE; break; + case 'n': /* none */ + flags.end_top = flags.end_around = 0, flags.end_own = FALSE; + break; + case '-': + if (digit(*(op + 1))) { + config_error_add( + "Values for %s:top and %s:around must not be negative", + allopt[optidx].name, + allopt[optidx].name); + return optn_silenterr; + } + /*FALLTHRU*/ default: config_error_add("Unknown %s parameter '%s'", allopt[optidx].name, op); - return optn_err; + return optn_silenterr; } - /* "3a" is sufficient but accept "3around" (or "3abracadabra") - */ + /* "3a" is sufficient but accept "3around" (or "3abracadabra") */ while (letter(*op)) op++; - /* t, a, and o can be separated by space(s) or slash or both - */ + /* t, a, and o can be separated by space(s) or slash or both */ while (*op == ' ') op++; if (*op == '/') @@ -2769,8 +2786,17 @@ optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) if (req == get_val) { if (!opts) return optn_err; - Sprintf(opts, "%d top/%d around%s", flags.end_top, flags.end_around, - flags.end_own ? "/own" : ""); + *opts = '\0'; + if (flags.end_top > 0) + Sprintf(opts, "%d top", flags.end_top); + if (flags.end_around > 0) + Sprintf(eos(opts), "%s%d around", + (flags.end_top > 0) ? "/" : "", flags.end_around); + if (flags.end_own) + Sprintf(eos(opts), "%sown", + (flags.end_top > 0 || flags.end_around > 0) ? "/" : ""); + if (!*opts) + Strcpy(opts, "none"); return optn_ok; } return optn_ok; diff --git a/src/topten.c b/src/topten.c index 430cd39ea..51a782c40 100644 --- a/src/topten.c +++ b/src/topten.c @@ -49,11 +49,13 @@ struct toptenentry { char plalign[ROLESZ + 1]; char name[NAMSZ + 1]; char death[DTHSZ + 1]; -} * tt_head; +} *tt_head; /* size big enough to read in all the string fields at once; includes room for separating space or trailing newline plus string terminator */ #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1) +static struct toptenentry zerott; + static void topten_print(const char *); static void topten_print_bold(const char *); static void outheader(void); @@ -162,7 +164,7 @@ topten_print(const char *x) } static void -topten_print_bold(const char* x) +topten_print_bold(const char *x) { if (g.toptenwin == WIN_ERR) raw_print_bold(x); @@ -605,28 +607,20 @@ free_ttlist(struct toptenentry* tt) void topten(int how, time_t when) { - int uid = getuid(); - int rank, rank0 = -1, rank1 = 0; - int occ_cnt = sysopt.persmax; register struct toptenentry *t0, *tprev; struct toptenentry *t1; FILE *rfile; - register int flg = 0; - boolean t0_used; #ifdef LOGFILE FILE *lfile; -#endif /* LOGFILE */ +#endif #ifdef XLOGFILE FILE *xlfile; -#endif /* XLOGFILE */ - -#ifdef _DCC - /* Under DICE 3.0, this crashes the system consistently, apparently due to - * corruption of *rfile somewhere. Until I figure this out, just cut out - * topten support entirely - at least then the game exits cleanly. --AC - */ - return; #endif + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = sysopt.persmax; + int flg = 0; + boolean t0_used, skip_scores; /* If we are in the midst of a panic, cut out topten entirely. * topten uses alloc() several times, which will lead to @@ -651,6 +645,7 @@ topten(int how, time_t when) /* create a new 'topten' entry */ t0_used = FALSE; t0 = newttentry(); + *t0 = zerott; t0->ver_major = VERSION_MAJOR; t0->ver_minor = VERSION_MINOR; t0->patchlevel = PATCHLEVEL; @@ -742,7 +737,7 @@ topten(int how, time_t when) t1 = tt_head = newttentry(); tprev = 0; /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ - for (rank = 1;;) { + for (rank = 1; ; ) { readentry(rfile, t1); if (t1->points < sysopt.pointsmin) t1->points = 0; @@ -774,7 +769,7 @@ topten(int how, time_t when) char pbuf[BUFSZ]; Sprintf(pbuf, - "You didn't beat your previous score of %ld points.", + "You didn't beat your previous score of %ld points.", t1->points); topten_print(pbuf); topten_print(""); @@ -823,43 +818,45 @@ topten(int how, time_t when) topten_print(""); } } + skip_scores = !flags.end_top && !flags.end_around && !flags.end_own; if (rank0 == 0) rank0 = rank1; if (rank0 <= 0) rank0 = rank; - if (!done_stopprint) + if (!skip_scores && !done_stopprint) outheader(); - t1 = tt_head; - for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + for (t1 = tt_head, rank = 1; t1->points != 0; t1 = t1->tt_next, ++rank) { if (flg #ifdef UPDATE_RECORD_IN_PLACE && rank >= rank0 #endif ) writeentry(rfile, t1); - if (done_stopprint) - continue; - if (rank > flags.end_top && (rank < rank0 - flags.end_around - || rank > rank0 + flags.end_around) - && (!flags.end_own - || (sysopt.pers_is_uid - ? t1->uid == t0->uid - : strncmp(t1->name, t0->name, NAMSZ) == 0))) + if (skip_scores || done_stopprint) continue; - if (rank == rank0 - flags.end_around - && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own) - topten_print(""); - if (rank != rank0) - outentry(rank, t1, FALSE); - else if (!rank1) - outentry(rank, t1, TRUE); - else { - outentry(rank, t1, TRUE); - outentry(0, t0, TRUE); + if (rank <= flags.end_top + || (rank >= rank0 - flags.end_around + && rank <= rank0 + flags.end_around) + || (flags.end_own && (sysopt.pers_is_uid + ? t1->uid == t0->uid + : !strncmp(t1->name, t0->name, NAMSZ)))) { + if (rank == rank0 - flags.end_around + && rank0 > flags.end_top + flags.end_around + 1 + && !flags.end_own) + topten_print(""); + + if (rank != rank0) { + outentry(rank, t1, FALSE); + } else if (!rank1) { + outentry(rank, t1, TRUE); + } else { + outentry(rank, t1, TRUE); + outentry(0, t0, TRUE); + } } } if (rank0 >= rank) - if (!done_stopprint) + if (!skip_scores && !done_stopprint) outentry(0, t0, TRUE); #ifdef UPDATE_RECORD_IN_PLACE if (flg) { @@ -868,16 +865,16 @@ topten(int how, time_t when) truncate_file(rfile); #else /* use sentinel record rather than relying on truncation */ - t1->points = 0L; /* terminates file when read back in */ - t1->ver_major = t1->ver_minor = t1->patchlevel = 0; - t1->uid = t1->deathdnum = t1->deathlev = 0; - t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0; + *t1 = zerott; + t1->points = 0L; /* [redundant] terminates file when read back in */ t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-'; - t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0; t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L); Strcpy(t1->name, "@"); - Strcpy(t1->death, "\n"); + Strcpy(t1->death, "\n"); /* end of data */ writeentry(rfile, t1); + /* note: there might be junk (if file has shrunk due to shorter + entries supplanting longer ones) after this dummy entry, but + reading and/or updating will ignore it */ (void) fflush(rfile); #endif /* TRUNCATE_FILE */ } @@ -886,10 +883,10 @@ topten(int how, time_t when) unlock_file(RECORD); free_ttlist(tt_head); -showwin: + showwin: if (iflags.toptenwin && !done_stopprint) display_nhwindow(g.toptenwin, 1); -destroywin: + destroywin: if (!t0_used) dealloc_ttentry(t0); if (iflags.toptenwin) { @@ -1310,7 +1307,7 @@ get_rnd_toptenentry(void) tt = &tt_buf; rank = rnd(sysopt.tt_oname_maxrank); -pickentry: + pickentry: for (i = rank; i; i--) { readentry(rfile, tt); if (tt->points == 0)