diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 8e84445b0..30c7e9dbc 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4522,9 +4522,12 @@ The official NetHack web site is maintained by \fBKen Lorber\fP at http://www.ne SPECIAL THANKS .pg On behalf of the NetHack community, thank you very much once -again to \fBM. Drew Streib\fP, \fBPasi Kallinen\fP and \fBRobin Bandy\fP -for providing public NetHack servers at nethack.alt.org and devnull.net -and/or for hosting annual NetHack tournaments. +again to \fBM. Drew Streib\fP, \fBPasi Kallinen\fP for providing a +public NetHack server at nethack.alt.org. Thanks to \fBKeith Simpson\fP +and \fBAndy Thomson\fP for hardfought.org. Thanks to all those +unnamed dungeoneers who invest their time and effort into annual +NetHack tournaments such as Junehack and in days past, +devnull.net (gone for now, but not forgotten). .pg .ce - - - - - - - - - -\ \ \ \ \" when centered, the dashes look a little too far diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 3859b4e68..048c81021 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -5297,9 +5297,12 @@ http:{\tt /}{\tt /}www.nethack.org{\tt /}. \subsection*{Special Thanks} \nd On behalf of the {\it NetHack\/} community, thank you very much once -again to {\it M. Drew Streib}, {\it Pasi Kallinen} and {\it Robin Bandy} for -providing public {\it NetHack\/} servers at {\it nethack.alt.org\/} and -{\it devnull.net\/} and/or for hosting annual {\it NetHack\/} tournaments. +again to {\it M. Drew Streib}, {\it Pasi Kallinen} for providing a +public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} +and {\it Andy Thomson} for hardfought.org. Thanks to all those +unnamed dungeoneers who invest their time and effort into annual +{\it NetHack\/} tournaments such as {\it Junethack} and in days past, +{\it devnull.net\/} (gone for now, but not forgotten). \clearpage %.hn diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4547d4273..802ce2507 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -12,12 +12,31 @@ list MSGTYPE values shows empty strings as reported in H7140 Killing Vlad while he was in bat/fog cloud/wolf form gave poorly worded feedback when he reverted to vampire form spaces in hilite_status option title text field not working -numeric hilite_status values didn't allow negative numbers (needed for AC) +numeric hilite_status values didn't allow negative numbers (needed for AC); + change them to accept leading '-', also accept unary '+' as a no-op +permanent inventory window was updated too soon when a scroll of charging + was used to [re]charge an item, not reflecting the item's change(s) +for starting inventory, don't give an orc hero lembas wafers or cram rations +targetting with a polearm could give away location of hidden monster +static prototype could be left orphaned depending on #defines in rip.c +config file error handling routines were calling xx_wait_synch early + even before the window system was initialized; add a default routine +status_finish() in botl.c would unconditionally invoke the window port's + win_status_finish() routine which was problematic if the windowport + wasn't initialized yet +using 'O' to set up a hilite_status rule for string comparison, the menu for + color was titled "choose attribute for when is 'bar'" and the + one prompting for attribute used the default "pick an attribute" +when finishing using 'O' to examine or set hilite_status rules, if the + 'statushilites' option is 0 and there is at least one rule, give a + reminder about setting it to non-zero to activate highlighting Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository ------------------------------------------------------------------ fix access violation when --debug:xxxx has no other args after it +Setting the inverse attribute for gold had the space before "$:" + getting highlighted along with the gold field Platform- and/or Interface-Specific Fixes @@ -40,11 +59,21 @@ windows-tty: Use nhraykey by default if the players keyboard layout is windows-tty: We now support changing altkeyhandler in game windows: Added ntassert() mechanism for Windows based port use tty: significant optimizations for performance and per field rendering +unix: Makefile.src and Makefile.utl inadvertently relied on a 'gnu make' + extension when using $(VERBOSEMAKE) to reduce build-time feedback; + replace with $(QUIETCC) which operates the same but defaults to + verbose so doesn't use '$<' for multi-prerequisite targets unless + specifically requested; use 'make QUIETCC=1 ' to get the + 3.6.1 behavior back General New Features -------------------- integrate aklys feature introduced in 3.6.1 into display +status_hilite options which use comparisons may now use <= and >= in + addition to previous < and >; in 3.6.1 the latter operated as if + they were <= and >= but now behave as conventional less than and + greater than; old highlight rules using them should be updated Code Cleanup and Reorganization diff --git a/include/botl.h b/include/botl.h index ce2a94496..4d9347088 100644 --- a/include/botl.h +++ b/include/botl.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.h $NHDT-Date: 1452660165 2016/01/13 04:42:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.15 $ */ +/* NetHack 3.6 botl.h $NHDT-Date: 1526907469 2018/05/21 12:57:49 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.19 $ */ /* Copyright (c) Michael Allison, 2003 */ /* NetHack may be freely redistributed. See license for details. */ @@ -32,11 +32,13 @@ enum statusfields { BL_FLUSH = -1, BL_TITLE = 0, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /* 1..6 */ BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, /* 7..12 */ - BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, BL_LEVELDESC, /* 13..20 */ - BL_EXP, BL_CONDITION + BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, /* 13..18 */ + BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION /* 19..22 */ }; -enum relationships { LT_VALUE = -1, EQ_VALUE, GT_VALUE, TXT_VALUE }; +enum relationships { NO_LTEQGT = -1, + EQ_VALUE, LT_VALUE, LE_VALUE, + GE_VALUE, GT_VALUE, TXT_VALUE }; #define MAXBLSTATS (BL_CONDITION + 1) diff --git a/src/apply.c b/src/apply.c index c40d1ac62..d8cbb7a87 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 apply.c $NHDT-Date: 1519598527 2018/02/25 22:42:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.243 $ */ +/* NetHack 3.6 apply.c $NHDT-Date: 1526769961 2018/05/19 22:46:01 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.246 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2865,21 +2865,41 @@ coord *pos; int min_range, max_range; { struct monst *mtmp; - struct monst *selmon = (struct monst *) 0; + coord mpos; + boolean impaired; + int x, y, lo_x, hi_x, lo_y, hi_y, rt, glyph; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - if (mtmp && !DEADMONSTER(mtmp) && !mtmp->mtame - && cansee(mtmp->mx, mtmp->my) - && distu(mtmp->mx, mtmp->my) <= max_range - && distu(mtmp->mx, mtmp->my) >= min_range) { - if (selmon) - return FALSE; - selmon = mtmp; + if (Blind) + return FALSE; /* must be able to see target location */ + impaired = (Confusion || Stunned || Hallucination); + mpos.x = mpos.y = 0; /* no candidate location yet */ + rt = isqrt(max_range); + lo_x = max(u.ux - rt, 1), hi_x = min(u.ux + rt, COLNO - 1); + lo_y = max(u.uy - rt, 0), hi_y = min(u.uy + rt, ROWNO - 1); + for (x = lo_x; x <= hi_x; ++x) { + for (y = lo_y; y <= hi_y; ++y) { + if (distu(x, y) < min_range || distu(x, y) > max_range + || !isok(x, y) || !cansee(x, y)) + continue; + glyph = glyph_at(x, y); + if (!impaired + && glyph_is_monster(glyph) + && (mtmp = m_at(x, y)) != 0 + && (mtmp->mtame || (mtmp->mpeaceful && flags.confirm))) + continue; + if (glyph_is_monster(glyph) + || glyph_is_warning(glyph) + || glyph_is_invisible(glyph) + || (glyph_is_statue(glyph) && impaired)) { + if (mpos.x) + return FALSE; /* more than one candidate location */ + mpos.x = x, mpos.y = y; + } } - if (!selmon) - return FALSE; - pos->x = selmon->mx; - pos->y = selmon->my; + } + if (!mpos.x) + return FALSE; /* no candidate location */ + *pos = mpos; return TRUE; } @@ -2887,8 +2907,8 @@ static int polearm_range_min = -1; static int polearm_range_max = -1; STATIC_OVL boolean -get_valid_polearm_position(x,y) -int x,y; +get_valid_polearm_position(x, y) +int x, y; { return (isok(x, y) && ACCESSIBLE(levl[x][y].typ) && distu(x, y) >= polearm_range_min @@ -2998,7 +3018,7 @@ struct obj *obj; return res; } - context.polearm.hitmon = NULL; + context.polearm.hitmon = (struct monst *) 0; /* Attack the monster there */ bhitpos = cc; if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != (struct monst *) 0) { diff --git a/src/botl.c b/src/botl.c index 2bd11768c..1548aef70 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526597284 2018/05/17 22:48:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1527042178 2018/05/23 02:22:58 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.101 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -446,21 +446,25 @@ STATIC_DCL boolean FDECL(is_ltgt_percentnumber, (const char *)); STATIC_DCL boolean FDECL(has_ltgt_percentnumber, (const char *)); STATIC_DCL boolean FDECL(parse_status_hl2, (char (*)[QBUFSZ],BOOLEAN_P)); STATIC_DCL boolean FDECL(parse_condition, (char (*)[QBUFSZ], int)); +STATIC_DCL boolean FDECL(noneoftheabove, (const char *)); STATIC_DCL void FDECL(merge_bestcolor, (int *, int)); STATIC_DCL void FDECL(get_hilite_color, (int, int, genericptr_t, int, - int, int *)); + int, int *)); STATIC_DCL unsigned long FDECL(match_str2conditionbitmask, (const char *)); STATIC_DCL unsigned long FDECL(str2conditionbitmask, (char *)); STATIC_DCL void FDECL(split_clridx, (int, int *, int *)); STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int)); STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *, - unsigned long, const char *)); - + unsigned long, const char *)); STATIC_DCL void NDECL(status_hilite_linestr_done); STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int)); STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions); STATIC_DCL void NDECL(status_hilite_linestr_gather); STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *)); +STATIC_DCL int NDECL(status_hilite_menu_choose_field); +STATIC_DCL int FDECL(status_hilite_menu_choose_behavior, (int)); +STATIC_DCL int FDECL(status_hilite_menu_choose_updownboth, (int, const char *, + BOOLEAN_P, BOOLEAN_P)); STATIC_DCL boolean FDECL(status_hilite_menu_add, (int)); #define has_hilite(i) (blstats[0][(i)].thresholds) #endif @@ -491,7 +495,8 @@ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC), INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD), INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME), - INIT_BLSTAT("hunger", " %s", ANY_UINT, 40, BL_HUNGER), + /* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */ + INIT_BLSTAT("hunger", " %s", ANY_INT, 40, BL_HUNGER), INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP), INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX), INIT_BLSTAT("dungeon-level", "%s", ANY_STR, 80, BL_LEVELDESC), @@ -636,7 +641,11 @@ bot_via_windowport() blstats[idx][BL_TIME].a.a_long = moves; /* Hunger */ - blstats[idx][BL_HUNGER].a.a_uint = u.uhs; + /* note: u.uhs is unsigned, and 3.6.1's STATUS_HILITE defined + BL_HUNGER to be ANY_UINT, but that was the only non-int/non-long + numeric field so it's far simpler to treat it as plain int and + not need ANY_UINT handling at all */ + blstats[idx][BL_HUNGER].a.a_int = (int) u.uhs; Strcpy(blstats[idx][BL_HUNGER].val, (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : ""); valset[BL_HUNGER] = TRUE; @@ -873,7 +882,8 @@ status_finish() int i; /* call the window port cleanup routine first */ - (*windowprocs.win_status_finish)(); + if (windowprocs.win_status_finish) + (*windowprocs.win_status_finish)(); /* free memory that we alloc'd now */ for (i = 0; i < MAXBLSTATS; ++i) { @@ -1216,6 +1226,10 @@ static struct fieldid_t { {0, BL_FLUSH} }; +/* format arguments */ +static const char threshold_value[] = "hilite_status threshold ", + is_out_of_range[] = " is out of range"; + /* field name to bottom line index */ STATIC_OVL enum statusfields fldname_to_bl_indx(name) @@ -1295,6 +1309,20 @@ reset_status_hilites() context.botlx = TRUE; } +/* test whether the text from a title rule matches the string for + title-while-polymorphed in the 'textmatch' menu */ +STATIC_OVL boolean +noneoftheabove(hl_text) +const char *hl_text; +{ + if (fuzzymatch(hl_text, "none of the above", "\" -_", TRUE) + || fuzzymatch(hl_text, "(polymorphed)", "\"()", TRUE) + || fuzzymatch(hl_text, "none of the above (polymorphed)", + "\" -_()", TRUE)) + return TRUE; + return FALSE; +} + STATIC_OVL void merge_bestcolor(bestcolor, newcolor) int *bestcolor; @@ -1349,79 +1377,132 @@ int *colorptr; { int bestcolor = NO_COLOR; struct hilite_s *hl; - anything *value = (anything *)vp; - char *txtstr, *cmpstr; + anything *value = (anything *) vp; + char *txtstr; if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS) return; if (blstats[idx][fldidx].thresholds) { /* there are hilites set here */ - int max_pc = 0, min_pc = 100; - int max_val = 0, min_val = LARGEST_INT; - boolean exactmatch = FALSE; + int max_pc = -1, min_pc = 101; + int max_val = -LARGEST_INT, min_val = LARGEST_INT; + boolean exactmatch = FALSE, updown = FALSE, changed = FALSE, + perc_or_abs = FALSE; - hl = blstats[idx][fldidx].thresholds; + /* min_/max_ are used to track best fit */ + for (hl = blstats[idx][fldidx].thresholds; hl; hl = hl->next) { + /* if we've already matched a temporary highlight, it takes + precedence over all persistent ones; we still process + updown rules to get the last one which qualifies */ + if ((updown || changed) && hl->behavior != BL_TH_UPDOWN) + continue; + /* among persistent highlights, if a 'percentage' or 'absolute' + rule has been matched, it takes precedence over 'always' */ + if (perc_or_abs && hl->behavior == BL_TH_ALWAYS_HILITE) + continue; - while (hl) { switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: if (hl->rel == EQ_VALUE && pc == hl->value.a_int) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = max_pc = hl->value.a_int; - exactmatch = TRUE; - } else if (hl->rel == LT_VALUE && !exactmatch - && (hl->value.a_int >= pc) + exactmatch = perc_or_abs = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip lt,ge,&c */ + } else if (hl->rel == LT_VALUE + && (pc < hl->value.a_int) && (hl->value.a_int <= min_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = hl->value.a_int; - } else if (hl->rel == GT_VALUE && !exactmatch - && (hl->value.a_int <= pc) + perc_or_abs = TRUE; + } else if (hl->rel == LE_VALUE + && (pc <= hl->value.a_int) + && (hl->value.a_int <= min_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_pc = hl->value.a_int; + perc_or_abs = TRUE; + } else if (hl->rel == GT_VALUE + && (pc > hl->value.a_int) && (hl->value.a_int >= max_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); max_pc = hl->value.a_int; + perc_or_abs = TRUE; + } else if (hl->rel == GE_VALUE + && (pc >= hl->value.a_int) + && (hl->value.a_int >= max_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_pc = hl->value.a_int; + perc_or_abs = TRUE; } break; case BL_TH_UPDOWN: + /* specific 'up' or 'down' takes precedence over general + 'changed' regardless of their order in the rule set */ if (chg < 0 && hl->rel == LT_VALUE) { merge_bestcolor(&bestcolor, hl->coloridx); + updown = TRUE; } else if (chg > 0 && hl->rel == GT_VALUE) { merge_bestcolor(&bestcolor, hl->coloridx); - } else if (hl->rel == EQ_VALUE && chg) { + updown = TRUE; + } else if (chg != 0 && hl->rel == EQ_VALUE && !updown) { merge_bestcolor(&bestcolor, hl->coloridx); - min_val = max_val = hl->value.a_int; + changed = TRUE; } break; case BL_TH_VAL_ABSOLUTE: + /* + * TODO: + * This covers data type ANY_INT. We need to handle ANY_LONG + * separately using a_long and new min_lval, max_lval. + */ if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = max_val = hl->value.a_int; - exactmatch = TRUE; - } else if (hl->rel == LT_VALUE && !exactmatch - && (hl->value.a_int >= value->a_int) - && (hl->value.a_int < min_val)) { + exactmatch = perc_or_abs = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip lt,ge,&c */ + } else if (hl->rel == LT_VALUE + && (value->a_int < hl->value.a_int) + && (hl->value.a_int <= min_val)) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = hl->value.a_int; - } else if (hl->rel == GT_VALUE && !exactmatch - && (hl->value.a_int <= value->a_int) - && (hl->value.a_int > max_val)) { + perc_or_abs = TRUE; + } else if (hl->rel == LE_VALUE + && (value->a_int <= hl->value.a_int) + && (hl->value.a_int <= min_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_val = hl->value.a_int; + perc_or_abs = TRUE; + } else if (hl->rel == GT_VALUE + && (value->a_int > hl->value.a_int) + && (hl->value.a_int >= max_val)) { merge_bestcolor(&bestcolor, hl->coloridx); max_val = hl->value.a_int; + perc_or_abs = TRUE; + } else if (hl->rel == GE_VALUE + && (value->a_int >= hl->value.a_int) + && (hl->value.a_int >= max_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_val = hl->value.a_int; + perc_or_abs = TRUE; } break; case BL_TH_TEXTMATCH: - txtstr = dupstr(blstats[idx][fldidx].val); - cmpstr = txtstr; - if (fldidx == BL_TITLE) { - int len = (strlen(plname) + sizeof(" the")); - cmpstr += len; + txtstr = blstats[idx][fldidx].val; + if (fldidx == BL_TITLE) + txtstr += (strlen(plname) + sizeof " the " - sizeof ""); + if (hl->rel == TXT_VALUE && hl->textmatch[0]) { + if (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE)) { + merge_bestcolor(&bestcolor, hl->coloridx); + exactmatch = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip "noneoftheabove" */ + } else if (fldidx == BL_TITLE + && Upolyd && noneoftheabove(hl->textmatch)) { + merge_bestcolor(&bestcolor, hl->coloridx); + } } - (void) trimspaces(cmpstr); - if (hl->rel == TXT_VALUE && hl->textmatch[0] && - !strcmpi(hl->textmatch, cmpstr)) { - merge_bestcolor(&bestcolor, hl->coloridx); - } - free(txtstr); break; case BL_TH_ALWAYS_HILITE: merge_bestcolor(&bestcolor, hl->coloridx); @@ -1431,7 +1512,6 @@ int *colorptr; default: break; } - hl = hl->next; } } *colorptr = bestcolor; @@ -1443,9 +1523,9 @@ split_clridx(idx, coloridx, attrib) int idx; int *coloridx, *attrib; { - if (idx && coloridx && attrib) { - *coloridx = idx & 0x00FF; - *attrib = (idx & 0xFF00) >> 8; + if (coloridx && attrib) { + *coloridx = idx & 0x00FF; + *attrib = (idx >> 8) & 0x00FF; } } @@ -1483,12 +1563,11 @@ boolean from_configfile; hsbuf[fldnum][ccount] = '\0'; op++; continue; - } else { - rslt = parse_status_hl2(hsbuf, from_configfile); - if (!rslt) { - badopt = TRUE; - break; - } + } + rslt = parse_status_hl2(hsbuf, from_configfile); + if (!rslt) { + badopt = TRUE; + break; } } for (i = 0; i < MAX_THRESH; ++i) { @@ -1515,7 +1594,7 @@ boolean from_configfile; return TRUE; } -/* is str in the format of "[<>]?-?[0-9]+%?" regex */ +/* is str in the format of "[<>]?=?[-+]?[0-9]+%?" regex */ STATIC_OVL boolean is_ltgt_percentnumber(str) const char *str; @@ -1524,10 +1603,12 @@ const char *str; if (*s == '<' || *s == '>') s++; - if (*s == '-') + if (*s == '=') s++; - /* note: this doesn't match the regexp shown above since it doesn't - require at least one digit; but it's adequate for how it gets used */ + if (*s == '-' || *s == '+') + s++; + if (!digit(*s)) + return FALSE; while (digit(*s)) s++; if (*s == '%') @@ -1535,7 +1616,7 @@ const char *str; return (*s == '\0'); } -/* does str only contain "<>0-9%" chars */ +/* does str only contain "<>=-+0-9%" chars */ STATIC_OVL boolean has_ltgt_percentnumber(str) const char *str; @@ -1543,7 +1624,7 @@ const char *str; const char *s = str; while (*s) { - if (!index("<>-0123456789%", *s)) + if (!index("<>=-+0123456789%", *s)) return FALSE; s++; } @@ -1660,22 +1741,15 @@ struct hilite_s *hilite; return; /* alloc and initialize a new hilite_s struct */ - new_hilite = (struct hilite_s *) alloc(sizeof(struct hilite_s)); + new_hilite = (struct hilite_s *) alloc(sizeof (struct hilite_s)); *new_hilite = *hilite; /* copy struct */ new_hilite->set = TRUE; new_hilite->fld = fld; - new_hilite->next = (struct hilite_s *)0; + new_hilite->next = blstats[0][fld].thresholds; + blstats[0][fld].thresholds = new_hilite; + /* sort_hilites(fld) */ - /* Does that status field currently have any hilite thresholds? */ - if (!blstats[0][fld].thresholds) { - blstats[0][fld].thresholds = new_hilite; - } else { - struct hilite_s *temp_hilite = blstats[0][fld].thresholds; - new_hilite->next = temp_hilite; - blstats[0][fld].thresholds = new_hilite; - /* sort_hilites(fld) */ - } /* current and prev must both point at the same hilites */ blstats[1][fld].thresholds = blstats[0][fld].thresholds; } @@ -1690,19 +1764,16 @@ boolean from_configfile; int sidx = 0, i = -1, dt = -1; int coloridx = -1, successes = 0; int disp_attrib = 0; - boolean percent = FALSE, changed = FALSE, numeric = FALSE; - boolean down= FALSE, up = FALSE; - boolean gt = FALSE, lt = FALSE, eq = FALSE, neq = FALSE; - boolean txtval = FALSE; - boolean always = FALSE; + boolean percent, changed, numeric, down, up, + gt, lt, ge, le, eq, txtval, always; const char *txt; enum statusfields fld = BL_FLUSH; struct hilite_s hilite; char tmpbuf[BUFSZ]; - const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + static const char *aligntxt[] = { "chaotic", "neutral", "lawful" }; /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */ - const char *hutxt[] = {"Satiated", "", "Hungry", "Weak", - "Fainting", "Fainted", "Starved"}; + static const char *hutxt[] = { "Satiated", "", "Hungry", "Weak", + "Fainting", "Fainted", "Starved" }; /* Examples: 3.6.1: @@ -1737,112 +1808,117 @@ boolean from_configfile; return parse_condition(s, sidx); ++sidx; - while(s[sidx]) { + while (s[sidx]) { char buf[BUFSZ], **subfields; int sf = 0; /* subfield count */ int kidx; txt = (const char *)0; - percent = changed = numeric = FALSE; - down = up = FALSE; - gt = eq = lt = neq = txtval = FALSE; - always = FALSE; + percent = numeric = always = FALSE; + down = up = changed = FALSE; + gt = ge = eq = le = lt = txtval = FALSE; /* threshold value */ if (!s[sidx][0]) return TRUE; - memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s)); hilite.set = FALSE; /* mark it "unset" */ hilite.fld = fld; - if (*s[sidx+1] == '\0' || !strcmpi(s[sidx], "always")) { + if (*s[sidx + 1] == '\0' || !strcmpi(s[sidx], "always")) { /* "field/always/color" OR "field/color" */ always = TRUE; - if (*s[sidx+1] == '\0') + if (*s[sidx + 1] == '\0') sidx--; - goto do_rel; } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) { if (!strcmpi(s[sidx], "down")) down = TRUE; else up = TRUE; changed = TRUE; - goto do_rel; } else if (fld == BL_CAP && is_fld_arrayvalues(s[sidx], enc_stat, - SLT_ENCUMBER, OVERLOADED+1, &kidx)) { + SLT_ENCUMBER, OVERLOADED + 1, + &kidx)) { txt = enc_stat[kidx]; txtval = TRUE; - goto do_rel; } else if (fld == BL_ALIGN && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) { txt = aligntxt[kidx]; txtval = TRUE; - goto do_rel; } else if (fld == BL_HUNGER && is_fld_arrayvalues(s[sidx], hutxt, - SATIATED, STARVED+1, &kidx)) { + SATIATED, STARVED + 1, &kidx)) { txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */ txtval = TRUE; - goto do_rel; } else if (!strcmpi(s[sidx], "changed")) { changed = TRUE; - goto do_rel; } else if (is_ltgt_percentnumber(s[sidx])) { - tmp = s[sidx]; + const char *op; + + tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?=?[-+]?[0-9]+%? */ if (strchr(tmp, '%')) percent = TRUE; - if (strchr(tmp, '<')) - lt = TRUE; - if (strchr(tmp, '>')) - gt = TRUE; - (void) stripchars(tmpbuf, "%<>", tmp); - tmp = tmpbuf; - while (*tmp) { - if (!index("0123456789", *tmp) - && (*tmp != '-' || tmp > tmpbuf)) - return FALSE; - tmp++; + if (*tmp == '<') { + if (tmp[1] == '=') + le = TRUE; + else + lt = TRUE; + } else if (*tmp == '>') { + if (tmp[1] == '=') + ge = TRUE; + else + gt = TRUE; } + /* '%', '<', '>' have served their purpose, '=' is either + part of '<' or '>' or optional for '=N', unary '+' is + just decorative, so get rid of them, leaving -?[0-9]+ */ + tmp = stripchars(tmpbuf, "%<>=+", tmp); numeric = TRUE; - tmp = tmpbuf; - if (strlen(tmp) > 0) { - dt = initblstats[fld].anytype; - if (percent) - dt = ANY_INT; - (void) s_to_anything(&hilite.value, tmp, dt); - } else + dt = percent ? ANY_INT : initblstats[fld].anytype; + (void) s_to_anything(&hilite.value, tmp, dt); + + op = gt ? ">" : ge ? ">=" : lt ? "<" : le ? "<=" : "="; + if (dt == ANY_INT + /* AC is the only field where negative values make sense but + accept >-1 for other fields; reject <0 for non-AC */ + && (hilite.value.a_int + < ((fld == BL_AC) ? -128 : gt ? -1 : lt ? 1 : 0) + /* percentages have another more comprehensive check below */ + || hilite.value.a_int > (percent ? (lt ? 101 : 100) + : LARGEST_INT))) { + config_error_add("%s'%s%d%s'%s", threshold_value, + op, hilite.value.a_int, percent ? "%" : "", + is_out_of_range); return FALSE; - if (!hilite.value.a_void && (strcmp(tmp, "0") != 0)) - return FALSE; + } else if (dt == ANY_LONG + && (hilite.value.a_long < (gt ? -1L : lt ? 1L : 0L))) { + config_error_add("%s'%s%ld'%s", threshold_value, + op, hilite.value.a_long, is_out_of_range); + return FALSE; + } } else if (initblstats[fld].anytype == ANY_STR) { txt = s[sidx]; txtval = TRUE; - goto do_rel; } else { config_error_add(has_ltgt_percentnumber(s[sidx]) ? "Wrong format '%s', expected a threshold number or percent" - : "Unknown behavior '%s'", s[sidx]); + : "Unknown behavior '%s'", + s[sidx]); return FALSE; } -do_rel: - /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */ - if (gt) + + /* relationships {LT_VALUE, LE_VALUE, EQ_VALUE, GE_VALUE, GT_VALUE} */ + if (gt || up) hilite.rel = GT_VALUE; - else if (eq) - hilite.rel = EQ_VALUE; - else if (lt) + else if (lt || down) hilite.rel = LT_VALUE; - else if (percent) - hilite.rel = EQ_VALUE; - else if (numeric) - hilite.rel = EQ_VALUE; - else if (down) - hilite.rel = LT_VALUE; - else if (up) - hilite.rel = GT_VALUE; - else if (changed) + else if (ge) + hilite.rel = GE_VALUE; + else if (le) + hilite.rel = LE_VALUE; + else if (eq || percent || numeric || changed) hilite.rel = EQ_VALUE; else if (txtval) hilite.rel = TXT_VALUE; @@ -1860,13 +1936,24 @@ do_rel: config_error_add("Cannot use percent with '%s'", initblstats[fld].fldname); return FALSE; - } else if ((hilite.value.a_int < 0) + } else if ((hilite.value.a_int < -1) + || (hilite.value.a_int == -1 + && hilite.value.a_int != GT_VALUE) || (hilite.value.a_int == 0 && hilite.rel == LT_VALUE) - || (hilite.value.a_int > 100) || (hilite.value.a_int == 100 - && hilite.rel == GT_VALUE)) { - config_error_add("Illegal percentage value"); + && hilite.rel == GT_VALUE) + || (hilite.value.a_int == 101 + && hilite.value.a_int != LT_VALUE) + || (hilite.value.a_int > 101)) { + config_error_add( + "hilite_status: invalid percentage value '%s%d%%'", + (hilite.rel == LT_VALUE) ? "<" + : (hilite.rel == LE_VALUE) ? "<=" + : (hilite.rel == GT_VALUE) ? ">" + : (hilite.rel == GE_VALUE) ? ">=" + : "=", + hilite.value.a_int); return FALSE; } } @@ -1889,6 +1976,7 @@ do_rel: for (i = 0; i < sf; ++i) { int a = match_str2attr(subfields[i], FALSE); + if (a == ATR_DIM) disp_attrib |= HL_DIM; else if (a == ATR_BLINK) @@ -1933,7 +2021,7 @@ do_rel: hilite.anytype = dt; if (hilite.behavior == BL_TH_TEXTMATCH && txt - && strlen(txt) < QBUFSZ-1) { + && strlen(txt) < QBUFSZ - 1) { Strcpy(hilite.textmatch, txt); (void) trimspaces(hilite.textmatch); } @@ -2281,8 +2369,7 @@ struct _status_hilite_line_str { struct _status_hilite_line_str *next; }; -struct _status_hilite_line_str *status_hilite_str = - (struct _status_hilite_line_str *) 0; +static struct _status_hilite_line_str *status_hilite_str = 0; static int status_hilite_str_id = 0; STATIC_OVL void @@ -2292,22 +2379,23 @@ struct hilite_s *hl; unsigned long mask; const char *str; { - struct _status_hilite_line_str *tmp = (struct _status_hilite_line_str *) - alloc(sizeof(struct _status_hilite_line_str)); - struct _status_hilite_line_str *nxt = status_hilite_str; + struct _status_hilite_line_str *tmp, *nxt; - (void) memset(tmp, 0, sizeof(struct _status_hilite_line_str)); + tmp = (struct _status_hilite_line_str *) alloc(sizeof *tmp); + (void) memset(tmp, 0, sizeof *tmp); ++status_hilite_str_id; tmp->fld = fld; tmp->hl = hl; tmp->mask = mask; - (void) stripchars(tmp->str, " ", str); - + if (fld == BL_TITLE) + Strcpy(tmp->str, str); + else + (void) stripchars(tmp->str, " ", str); tmp->id = status_hilite_str_id; - if (nxt) { - while (nxt && nxt->next) + if ((nxt = status_hilite_str) != 0) { + while (nxt->next) nxt = nxt->next; nxt->next = tmp; } else { @@ -2319,8 +2407,7 @@ const char *str; STATIC_OVL void status_hilite_linestr_done() { - struct _status_hilite_line_str *tmp = status_hilite_str; - struct _status_hilite_line_str *nxt; + struct _status_hilite_line_str *nxt, *tmp = status_hilite_str; while (tmp) { nxt = tmp->next; @@ -2350,6 +2437,7 @@ int count_status_hilites(VOID_ARGS) { int count; + status_hilite_linestr_gather(); count = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); @@ -2365,13 +2453,14 @@ status_hilite_linestr_gather_conditions() unsigned long clratr; } cond_maps[SIZE(valid_conditions)]; - (void)memset(cond_maps, 0, - sizeof(struct _cond_map) * SIZE(valid_conditions)); + (void) memset(cond_maps, 0, + SIZE(valid_conditions) * sizeof (struct _cond_map)); for (i = 0; i < SIZE(valid_conditions); i++) { int clr = NO_COLOR; int atr = HL_NONE; int j; + for (j = 0; j < CLR_MAX; j++) if (cond_hilites[j] & valid_conditions[i].bitmask) clr = j; @@ -2389,6 +2478,7 @@ status_hilite_linestr_gather_conditions() if (clr != NO_COLOR || atr != HL_NONE) { unsigned long ca = clr | (atr << 8); boolean added_condmap = FALSE; + for (j = 0; j < SIZE(valid_conditions); j++) if (cond_maps[j].clratr == ca) { cond_maps[j].bm |= valid_conditions[i].bitmask; @@ -2409,12 +2499,14 @@ status_hilite_linestr_gather_conditions() for (i = 0; i < SIZE(valid_conditions); i++) if (cond_maps[i].bm) { int clr = NO_COLOR, atr = HL_NONE; + split_clridx(cond_maps[i].clratr, &clr, &atr); if (clr != NO_COLOR || atr != HL_NONE) { char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char condbuf[BUFSZ]; char *tmpattr; + (void) stripchars(clrbuf, " ", clr2colorname(clr)); tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ); if (tmpattr) @@ -2457,21 +2549,24 @@ struct hilite_s *hl; char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char *tmpattr; + const char *op; if (!hl) return (char *) 0; behavebuf[0] = '\0'; clrbuf[0] = '\0'; + op = (hl->rel == LT_VALUE) ? "<" + : (hl->rel == LE_VALUE) ? "<=" + : (hl->rel == GT_VALUE) ? ">" + : (hl->rel == GE_VALUE) ? ">=" + : (hl->rel == EQ_VALUE) ? "=" + : 0; switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: - if (hl->rel == LT_VALUE) - Sprintf(behavebuf, "<%i%%", hl->value.a_int); - else if (hl->rel == GT_VALUE) - Sprintf(behavebuf, ">%i%%", hl->value.a_int); - else if (hl->rel == EQ_VALUE) - Sprintf(behavebuf, "%i%%", hl->value.a_int); + if (op) + Sprintf(behavebuf, "%s%d%%", op, hl->value.a_int); else impossible("hl->behavior=percentage, rel error"); break; @@ -2486,12 +2581,8 @@ struct hilite_s *hl; impossible("hl->behavior=updown, rel error"); break; case BL_TH_VAL_ABSOLUTE: - if (hl->rel == LT_VALUE) - Sprintf(behavebuf, "<%i", hl->value.a_int); - else if (hl->rel == GT_VALUE) - Sprintf(behavebuf, ">%i", hl->value.a_int); - else if (hl->rel == EQ_VALUE) - Sprintf(behavebuf, "%i", hl->value.a_int); + if (op) + Sprintf(behavebuf, "%s%d", op, hl->value.a_int); else impossible("hl->behavior=absolute, rel error"); break; @@ -2531,7 +2622,7 @@ struct hilite_s *hl; return buf; } -int +STATIC_OVL int status_hilite_menu_choose_field() { winid tmpwin; @@ -2560,7 +2651,7 @@ status_hilite_menu_choose_field() return fld; } -int +STATIC_OVL int status_hilite_menu_choose_behavior(fld) int fld; { @@ -2606,7 +2697,8 @@ int fld; nopts++; } - if (fld != BL_CAP && fld != BL_HUNGER && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { + if (fld != BL_CAP && fld != BL_HUNGER + && (at == ANY_INT || at == ANY_LONG)) { any = zeroany; any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE; add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, @@ -2622,7 +2714,8 @@ int fld; nopts++; } - if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) { + if (initblstats[fld].anytype == ANY_STR + || fld == BL_CAP || fld == BL_HUNGER) { any = zeroany; any.a_int = onlybeh = BL_TH_TEXTMATCH; Sprintf(buf, "%s text match", initblstats[fld].fldname); @@ -2650,12 +2743,13 @@ int fld; return beh; } -int -status_hilite_menu_choose_updownboth(fld, str) +STATIC_OVL int +status_hilite_menu_choose_updownboth(fld, str, ltok, gtok) int fld; const char *str; +boolean ltok, gtok; { - int res, ret = -2; + int res, ret = NO_LTEQGT; winid tmpwin; char buf[BUFSZ]; anything any; @@ -2664,14 +2758,26 @@ const char *str; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); - if (str) - Sprintf(buf, "%s or less", str); - else - Sprintf(buf, "Value goes down"); - any = zeroany; - any.a_int = 10 + LT_VALUE; - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - buf, MENU_UNSELECTED); + if (ltok) { + if (str) + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Better (lower)" : "Less", str); + else + Sprintf(buf, "Value goes down"); + any = zeroany; + any.a_int = 10 + LT_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "better (lower)" : "less"); + any = zeroany; + any.a_int = 10 + LE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + } if (str) Sprintf(buf, "Exactly %s", str); @@ -2682,15 +2788,26 @@ const char *str; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); - if (str) - Sprintf(buf, "%s or more", str); - else - Sprintf(buf, "Value goes up"); - any = zeroany; - any.a_int = 10 + GT_VALUE; - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - buf, MENU_UNSELECTED); + if (gtok) { + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "worse (higher)" : "more"); + any = zeroany; + any.a_int = 10 + GE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + if (str) + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Worse (higher)" : "More", str); + else + Sprintf(buf, "Value goes up"); + any = zeroany; + any.a_int = 10 + GT_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } Sprintf(buf, "Select field %s value:", initblstats[fld].fldname); end_menu(tmpwin, buf); @@ -2710,7 +2827,7 @@ int origfld; { int fld; int behavior; - int lt_gt_eq = 0; + int lt_gt_eq; int clr = NO_COLOR, atr = HL_UNDEF; struct hilite_s hilite; unsigned long cond = 0UL; @@ -2731,55 +2848,63 @@ choose_field: colorqry[0] = '\0'; attrqry[0] = '\0'; - memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s)); hilite.set = FALSE; /* mark it "unset" */ hilite.fld = fld; choose_behavior: - behavior = status_hilite_menu_choose_behavior(fld); - if (behavior == (BL_TH_NONE-1)) { + if (behavior == (BL_TH_NONE - 1)) { return FALSE; } else if (behavior == BL_TH_NONE) { if (origfld == BL_FLUSH) goto choose_field; - else - return FALSE; + return FALSE; } hilite.behavior = behavior; choose_value: - if (behavior == BL_TH_VAL_PERCENTAGE || behavior == BL_TH_VAL_ABSOLUTE) { - char inbuf[BUFSZ] = DUMMY, buf[BUFSZ]; - int val; - boolean skipltgt = FALSE; - boolean gotnum = FALSE; - char *inp = inbuf; - char *numstart = inbuf; + char inbuf[BUFSZ], buf[BUFSZ]; + anything aval; + int val, dt; + boolean gotnum = FALSE, percent = (behavior == BL_TH_VAL_PERCENTAGE); + char *inp, *numstart; + const char *op; + lt_gt_eq = NO_LTEQGT; /* not set up yet */ inbuf[0] = '\0'; Sprintf(buf, "Enter %svalue for %s threshold:", - (behavior == BL_TH_VAL_PERCENTAGE) ? "percentage " : "", + percent ? "percentage " : "", initblstats[fld].fldname); getlin(buf, inbuf); if (inbuf[0] == '\0' || inbuf[0] == '\033') goto choose_behavior; - inp = trimspaces(inbuf); + inp = numstart = trimspaces(inbuf); if (!*inp) goto choose_behavior; - /* allow user to enter "<50%" or ">50" or just "50" */ + /* allow user to enter "<50%" or ">50" or just "50" + or <=50% or >=50 or =50 */ if (*inp == '>' || *inp == '<' || *inp == '=') { - lt_gt_eq = (*inp == '>') ? GT_VALUE - : (*inp == '<') ? LT_VALUE : EQ_VALUE; - skipltgt = TRUE; - *inp = ' '; + lt_gt_eq = (*inp == '>') ? ((inp[1] == '=') ? GE_VALUE : GT_VALUE) + : (*inp == '<') ? ((inp[1] == '=') ? LE_VALUE : LT_VALUE) + : EQ_VALUE; + *inp++ = ' '; + numstart++; + if (lt_gt_eq == GE_VALUE || lt_gt_eq == LE_VALUE) { + *inp++ = ' '; + numstart++; + } + } + if (*inp == '-') { inp++; + } else if (*inp == '+') { + *inp++ = ' '; numstart++; } while (digit(*inp)) { @@ -2787,11 +2912,12 @@ choose_value: gotnum = TRUE; } if (*inp == '%') { - behavior = BL_TH_VAL_PERCENTAGE; - *inp = '\0'; - } else if (!*inp) { - behavior = BL_TH_VAL_ABSOLUTE; - } else { + if (!percent) { + pline("Not expecting a percentage."); + goto choose_behavior; + } + *inp = '\0'; /* strip '%' [this accepts trailing junk!] */ + } else if (*inp) { /* some random characters */ pline("\"%s\" is not a recognized number.", inp); goto choose_value; @@ -2800,65 +2926,114 @@ choose_value: pline("Is that an invisible number?"); goto choose_value; } + op = (lt_gt_eq == LT_VALUE) ? "<" + : (lt_gt_eq == LE_VALUE) ? "<=" + : (lt_gt_eq == GT_VALUE) ? ">" + : (lt_gt_eq == GE_VALUE) ? ">=" + : (lt_gt_eq == EQ_VALUE) ? "=" + : ""; /* didn't specify lt_gt_eq with number */ - val = atoi(numstart); - if (behavior == BL_TH_VAL_PERCENTAGE) { + aval = zeroany; + dt = percent ? ANY_INT : initblstats[fld].anytype; + (void) s_to_anything(&aval, numstart, dt); + + if (percent) { + val = aval.a_int; if (initblstats[fld].idxmax == -1) { pline("Field '%s' does not support percentage values.", initblstats[fld].fldname); behavior = BL_TH_VAL_ABSOLUTE; goto choose_value; } - if (val < 0 || val > 100) { - pline("Not a valid percent value."); + /* if player only specified a number then lt_gt_eq isn't set + up yet and the >-1 and <101 exceptions can't be honored; + deliberate use of those should be uncommon enough for + that to be palatable; for 0 and 100, choose_updown_both() + will prevent useless operations */ + if ((val < 0 && (val != -1 || lt_gt_eq != GT_VALUE)) + || (val == 0 && lt_gt_eq == LT_VALUE) + || (val == 100 && lt_gt_eq == GT_VALUE) + || (val > 100 && (val != 101 || lt_gt_eq != LT_VALUE))) { + pline("'%s%d%%' is not a valid percent value.", op, val); goto choose_value; } + /* restore suffix for use in color and attribute prompts */ + if (!index(numstart, '%')) + Strcat(numstart, "%"); + + /* reject negative values except for AC and >-1; reject 0 for < */ + } else if (dt == ANY_INT + && (aval.a_int < ((fld == BL_AC) ? -128 + : (lt_gt_eq == GT_VALUE) ? -1 + : (lt_gt_eq == LT_VALUE) ? 1 : 0))) { + pline("%s'%s%d'%s", threshold_value, + op, aval.a_int, is_out_of_range); + goto choose_value; + } else if (dt == ANY_LONG + && (aval.a_long < (lt_gt_eq == GT_VALUE) ? -1L + : (lt_gt_eq == LT_VALUE) ? 1L : 0L)) { + pline("%s'%s%ld'%s", threshold_value, + op, aval.a_long, is_out_of_range); + goto choose_value; } - if (!skipltgt) { - lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf); - if (lt_gt_eq == -2) + if (lt_gt_eq == NO_LTEQGT) { + boolean ltok = ((dt == ANY_INT) + ? (aval.a_int > 0 || fld == BL_AC) + : (aval.a_long > 0L)), + gtok = (!percent || aval.a_long < 100); + + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf, + ltok, gtok); + if (lt_gt_eq == NO_LTEQGT) goto choose_value; } - Sprintf(colorqry, "Choose a color for when %s is %s%s:", + Sprintf(colorqry, "Choose a color for when %s is %s%s%s:", initblstats[fld].fldname, + (lt_gt_eq == LT_VALUE) ? "less than " + : (lt_gt_eq == GT_VALUE) ? "more than " + : "", numstart, - (lt_gt_eq == EQ_VALUE) ? "" - : (lt_gt_eq == LT_VALUE) ? " or less" - : " or more"); - - Sprintf(attrqry, "Choose attribute for when %s is %s%s:", + (lt_gt_eq == LE_VALUE) ? " or less" + : (lt_gt_eq == GE_VALUE) ? " or more" + : ""); + Sprintf(attrqry, "Choose attribute for when %s is %s%s%s:", initblstats[fld].fldname, - inbuf, - (lt_gt_eq == EQ_VALUE) ? "" - : (lt_gt_eq == LT_VALUE) ? " or less" - : " or more"); + (lt_gt_eq == LT_VALUE) ? "less than " + : (lt_gt_eq == GT_VALUE) ? "more than " + : "", + numstart, + (lt_gt_eq == LE_VALUE) ? " or less" + : (lt_gt_eq == GE_VALUE) ? " or more" + : ""); hilite.rel = lt_gt_eq; - hilite.value.a_int = val; + hilite.value = aval; } else if (behavior == BL_TH_UPDOWN) { - lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0); - if (lt_gt_eq == -2) + boolean ltok = (fld != BL_TIME), gtok = TRUE; + + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0, + ltok, gtok); + if (lt_gt_eq == NO_LTEQGT) goto choose_behavior; Sprintf(colorqry, "Choose a color for when %s %s:", initblstats[fld].fldname, (lt_gt_eq == EQ_VALUE) ? "changes" - : (lt_gt_eq == LT_VALUE) ? "decreases" - : "increases"); + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); Sprintf(attrqry, "Choose attribute for when %s %s:", initblstats[fld].fldname, (lt_gt_eq == EQ_VALUE) ? "changes" - : (lt_gt_eq == LT_VALUE) ? "decreases" - : "increases"); + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); hilite.rel = lt_gt_eq; } else if (behavior == BL_TH_CONDITION) { cond = query_conditions(); if (!cond) { if (origfld == BL_FLUSH) goto choose_field; - else - return FALSE; + return FALSE; } Sprintf(colorqry, "Choose a color for conditions %s:", conditionbitmask2str(cond)); @@ -2866,6 +3041,7 @@ choose_value: conditionbitmask2str(cond)); } else if (behavior == BL_TH_TEXTMATCH) { char qry_buf[BUFSZ]; + Sprintf(qry_buf, "%s %s text value to match:", (fld == BL_CAP || fld == BL_ALIGN @@ -2875,48 +3051,78 @@ choose_value: if (fld == BL_CAP) { int rv = query_arrayvalue(qry_buf, enc_stat, - SLT_ENCUMBER, OVERLOADED+1); + SLT_ENCUMBER, OVERLOADED + 1); + if (rv < SLT_ENCUMBER) goto choose_behavior; hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, enc_stat[rv]); } else if (fld == BL_ALIGN) { - const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + static const char *aligntxt[] = { "chaotic", "neutral", "lawful" }; int rv = query_arrayvalue(qry_buf, - aligntxt, 0, 3); + aligntxt, 0, 2 + 1); + if (rv < 0) goto choose_behavior; hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, aligntxt[rv]); } else if (fld == BL_HUNGER) { - const char *hutxt[] = {"Satiated", (char *)0, "Hungry", "Weak", - "Fainting", "Fainted", "Starved"}; + static const char *hutxt[] = { "Satiated", (char *) 0, "Hungry", + "Weak", "Fainting", "Fainted", + "Starved" }; int rv = query_arrayvalue(qry_buf, hutxt, - SATIATED, STARVED+1); + SATIATED, STARVED + 1); + if (rv < SATIATED) goto choose_behavior; hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, hutxt[rv]); } else if (fld == BL_TITLE) { - const char *rolelist[9]; - int i, rv; + const char *rolelist[3 * 9 + 1]; + char mbuf[QBUFSZ], fbuf[QBUFSZ], obuf[QBUFSZ]; + int i, j, rv; - for (i = 0; i < 9; i++) - rolelist[i] = (flags.female && urole.rank[i].f) - ? urole.rank[i].f : urole.rank[i].m; + for (i = j = 0; i < 9; i++) { + Sprintf(mbuf, "\"%s\"", urole.rank[i].m); + if (urole.rank[i].f) { + Sprintf(fbuf, "\"%s\"", urole.rank[i].f); + Sprintf(obuf, "%s or %s", + flags.female ? fbuf : mbuf, + flags.female ? mbuf : fbuf); + } else { + fbuf[0] = obuf[0] = '\0'; + } + if (flags.female) { + if (*fbuf) + rolelist[j++] = dupstr(fbuf); + rolelist[j++] = dupstr(mbuf); + if (*obuf) + rolelist[j++] = dupstr(obuf); + } else { + rolelist[j++] = dupstr(mbuf); + if (*fbuf) + rolelist[j++] = dupstr(fbuf); + if (*obuf) + rolelist[j++] = dupstr(obuf); + } + } + rolelist[j++] = dupstr("\"none of the above (polymorphed)\""); - rv = query_arrayvalue(qry_buf, rolelist, 0, 9); + rv = query_arrayvalue(qry_buf, rolelist, 0, j); + if (rv >= 0) { + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, rolelist[rv]); + } + for (i = 0; i < j; i++) + free((genericptr_t) rolelist[i]), rolelist[i] = 0; if (rv < 0) goto choose_behavior; - - hilite.rel = TXT_VALUE; - Strcpy(hilite.textmatch, rolelist[rv]); } else { - char inbuf[BUFSZ] = DUMMY; + char inbuf[BUFSZ]; inbuf[0] = '\0'; getlin(qry_buf, inbuf); @@ -2924,14 +3130,14 @@ choose_value: goto choose_behavior; hilite.rel = TXT_VALUE; - if (strlen(inbuf) < QBUFSZ-1) + if (strlen(inbuf) < QBUFSZ - 1) Strcpy(hilite.textmatch, inbuf); else return FALSE; } Sprintf(colorqry, "Choose a color for when %s is '%s':", initblstats[fld].fldname, hilite.textmatch); - Sprintf(colorqry, "Choose attribute for when %s is '%s':", + Sprintf(attrqry, "Choose attribute for when %s is '%s':", initblstats[fld].fldname, hilite.textmatch); } else if (behavior == BL_TH_ALWAYS_HILITE) { Sprintf(colorqry, "Choose a color to always hilite %s:", @@ -2941,7 +3147,6 @@ choose_value: } choose_color: - clr = query_color(colorqry); if (clr == -1) { if (behavior != BL_TH_ALWAYS_HILITE) @@ -2975,6 +3180,7 @@ choose_color: char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char *tmpattr; + if (atr == HL_DIM) cond_hilites[HL_ATTCLR_DIM] |= cond; else if (atr == HL_BLINK) @@ -3000,9 +3206,25 @@ choose_color: pline("Added hilite condition/%s/%s", conditionbitmask2str(cond), clrbuf); } else { + char *p, *q; + hilite.coloridx = clr | (atr << 8); hilite.anytype = initblstats[fld].anytype; + if (fld == BL_TITLE && (p = strstri(hilite.textmatch, " or ")) != 0) { + /* split menu choice "male-rank or female-rank" into two distinct + but otherwise identical rules, "male-rank" and "female-rank" */ + *p = '\0'; /* chop off " or female-rank" */ + /* new rule for male-rank */ + status_hilite_add_threshold(fld, &hilite); + pline("Added hilite %s", status_hilite2str(&hilite)); + /* transfer female-rank to start of hilite.textmatch buffer */ + p += sizeof " or " - sizeof ""; + q = hilite.textmatch; + while ((*q++ = *p++) != '\0') + continue; + /* proceed with normal addition of new rule */ + } status_hilite_add_threshold(fld, &hilite); pline("Added hilite %s", status_hilite2str(&hilite)); } @@ -3177,7 +3399,7 @@ status_hilites_viewall() while (hlstr) { Sprintf(buf, "OPTIONS=hilite_status: %.*s", - (int)(BUFSZ - sizeof "OPTIONS=hilite_status: " - 1), + (int) (BUFSZ - sizeof "OPTIONS=hilite_status: " - 1), hlstr->str); putstr(datawin, 0, buf); hlstr = hlstr->next; @@ -3204,9 +3426,7 @@ shlmenu_redo: start_menu(tmpwin); status_hilite_linestr_gather(); - countall = status_hilite_linestr_countfield(BL_FLUSH); - if (countall) { any = zeroany; any.a_int = -1; @@ -3222,12 +3442,10 @@ shlmenu_redo: char buf[BUFSZ]; any = zeroany; - any.a_int = (i+1); + any.a_int = i + 1; + Sprintf(buf, "%-18s", initblstats[i].fldname); if (count) - Sprintf(buf, "%-18s (%i defined)", - initblstats[i].fldname, count); - else - Sprintf(buf, "%-18s", initblstats[i].fldname); + Sprintf(eos(buf), " (%d defined)", count); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); } @@ -3240,17 +3458,24 @@ shlmenu_redo: status_hilites_viewall(); else (void) status_hilite_menu_fld(i); - free((genericptr_t) picks); + free((genericptr_t) picks), picks = (menu_item *) 0; redo = TRUE; } - picks = (menu_item *) 0; destroy_nhwindow(tmpwin); + countall = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); if (redo) goto shlmenu_redo; + /* hilite_delta=='statushilites' does double duty: it is the + number of turns for temporary highlights to remain visible + and also when non-zero it is the flag to enable highlighting */ + if (countall > 0 && !iflags.hilite_delta) + pline( + "To have highlights become active, set 'statushilites' option to non-zero."); + return TRUE; } diff --git a/src/cmd.c b/src/cmd.c index 9c4739a42..d7303ec18 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -5354,8 +5354,10 @@ dotravel(VOID_ARGS) } #ifdef PORT_DEBUG +#if defined(WIN32) && defined(TTY_GRAPHICS) extern void NDECL(win32con_debug_keystrokes); extern void NDECL(win32con_handler_info); +#endif int wiz_port_debug() @@ -5369,7 +5371,7 @@ wiz_port_debug() char *menutext; void NDECL((*fn)); } menu_selections[] = { -#ifdef WIN32 +#if defined(WIN32) && defined(TTY_GRAPHICS) { "test win32 keystrokes (tty only)", win32con_debug_keystrokes }, { "show keystroke handler information (tty only)", win32con_handler_info }, diff --git a/src/options.c b/src/options.c index e61eaa906..fbe568fb2 100644 --- a/src/options.c +++ b/src/options.c @@ -2651,7 +2651,7 @@ boolean tinitial, tfrom_file; bad_negation(fullname, FALSE); return FALSE; } else if ((op = string_for_opt(opts, negated)) != 0) { -#if defined(WIN32CON) +#if defined(WIN32) && defined(TTY_GRAPHICS) set_altkeyhandler(op); #endif } else @@ -3304,7 +3304,9 @@ boolean tinitial, tfrom_file; op = string_for_opt(opts, 0); if (!op) return FALSE; +#ifdef TTY_GRAPHICS map_subkeyvalue(op); +#endif #endif } return retval; diff --git a/src/read.c b/src/read.c index 02cb79b91..f86afe04a 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 read.c $NHDT-Date: 1515802375 2018/01/13 00:12:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.150 $ */ +/* NetHack 3.6 read.c $NHDT-Date: 1526728750 2018/05/19 11:19:10 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.155 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,19 +19,23 @@ static NEARDATA const char readable[] = { ALL_CLASSES, SCROLL_CLASS, static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; STATIC_DCL boolean FDECL(learnscrolltyp, (SHORT_P)); -STATIC_DCL char * FDECL(erode_obj_text, (struct obj *, char *)); -STATIC_DCL void NDECL(do_class_genocide); +STATIC_DCL char *FDECL(erode_obj_text, (struct obj *, char *)); +STATIC_DCL char *FDECL(apron_text, (struct obj *, char *buf)); STATIC_DCL void FDECL(stripspe, (struct obj *)); STATIC_DCL void FDECL(p_glow1, (struct obj *)); STATIC_DCL void FDECL(p_glow2, (struct obj *, const char *)); -STATIC_DCL void FDECL(randomize, (int *, int)); STATIC_DCL void FDECL(forget_single_object, (int)); +#if 0 /* not used */ +STATIC_DCL void FDECL(forget_objclass, (int)); +#endif +STATIC_DCL void FDECL(randomize, (int *, int)); STATIC_DCL void FDECL(forget, (int)); STATIC_DCL int FDECL(maybe_tame, (struct monst *, struct obj *)); -STATIC_DCL boolean FDECL(is_valid_stinking_cloud_pos, (int, int, BOOLEAN_P)); -STATIC_DCL void FDECL(display_stinking_cloud_positions, (int)); STATIC_DCL boolean FDECL(get_valid_stinking_cloud_pos, (int, int)); +STATIC_DCL boolean FDECL(is_valid_stinking_cloud_pos, (int, int, BOOLEAN_P)); +STATIC_PTR void FDECL(display_stinking_cloud_positions, (int)); STATIC_PTR void FDECL(set_lit, (int, int, genericptr)); +STATIC_DCL void NDECL(do_class_genocide); STATIC_OVL boolean learnscrolltyp(scrolltyp) @@ -56,7 +60,7 @@ struct obj *sobj; (void) learnscrolltyp(sobj->otyp); } -char * +STATIC_OVL char * erode_obj_text(otmp, buf) struct obj *otmp; char *buf; @@ -158,7 +162,7 @@ char *buf; return erode_obj_text(tshirt, buf); } -char * +STATIC_OVL char * apron_text(apron, buf) struct obj *apron; char *buf; @@ -324,6 +328,7 @@ doread() return 0; } else if (Blind && (scroll->otyp != SPE_BOOK_OF_THE_DEAD)) { const char *what = 0; + if (scroll->oclass == SPBOOK_CLASS) what = "mystic runes"; else if (!scroll->dknown) @@ -953,7 +958,7 @@ int x,y; || distu(x, y) >= 32)); } -boolean +STATIC_OVL boolean is_valid_stinking_cloud_pos(x, y, showmsg) int x, y; boolean showmsg; @@ -966,7 +971,7 @@ boolean showmsg; return TRUE; } -void +STATIC_PTR void display_stinking_cloud_positions(state) int state; { @@ -1621,7 +1626,8 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ pline("This is a scroll of fire!"); dam *= 5; pline("Where do you want to center the explosion?"); - getpos_sethilite(display_stinking_cloud_positions, get_valid_stinking_cloud_pos); + getpos_sethilite(display_stinking_cloud_positions, + get_valid_stinking_cloud_pos); (void) getpos(&cc, TRUE, "the desired position"); if (!is_valid_stinking_cloud_pos(cc.x, cc.y, FALSE)) { /* try to reach too far, get burned */ @@ -1693,7 +1699,8 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ already_known ? "stinking " : ""); cc.x = u.ux; cc.y = u.uy; - getpos_sethilite(display_stinking_cloud_positions, get_valid_stinking_cloud_pos); + getpos_sethilite(display_stinking_cloud_positions, + get_valid_stinking_cloud_pos); if (getpos(&cc, TRUE, "the desired position") < 0) { pline1(Never_mind); break; @@ -1707,6 +1714,11 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ default: impossible("What weird effect is this? (%u)", otyp); } + /* if sobj is gone, we've already called useup() above and the + update_inventory() that it performs might have come too soon + (before charging an item, for instance) */ + if (!sobj) + update_inventory(); return sobj ? 0 : 1; } diff --git a/src/rip.c b/src/rip.c index ee2a51642..6e48dde45 100644 --- a/src/rip.c +++ b/src/rip.c @@ -5,8 +5,6 @@ #include "hack.h" -STATIC_DCL void FDECL(center, (int, char *)); - #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) #define TEXT_TOMBSTONE @@ -18,6 +16,7 @@ STATIC_DCL void FDECL(center, (int, char *)); #endif #ifdef TEXT_TOMBSTONE +STATIC_DCL void FDECL(center, (int, char *)); #ifndef NH320_DEDICATION /* A normal tombstone for end of game display. */ diff --git a/src/u_init.c b/src/u_init.c index 3087f70d3..44fb9142a 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 u_init.c $NHDT-Date: 1503960969 2017/08/28 22:56:09 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.40 $ */ +/* NetHack 3.6 u_init.c $NHDT-Date: 1526755625 2018/05/19 18:47:05 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.42 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -219,11 +219,14 @@ static struct inv_sub { { PM_ORC, SMALL_SHIELD, ORCISH_SHIELD }, { PM_ORC, RING_MAIL, ORCISH_RING_MAIL }, { PM_ORC, CHAIN_MAIL, ORCISH_CHAIN_MAIL }, + { PM_ORC, CRAM_RATION, TRIPE_RATION }, + { PM_ORC, LEMBAS_WAFER, TRIPE_RATION }, { PM_DWARF, SPEAR, DWARVISH_SPEAR }, { PM_DWARF, SHORT_SWORD, DWARVISH_SHORT_SWORD }, { PM_DWARF, HELMET, DWARVISH_IRON_HELM }, /* { PM_DWARF, SMALL_SHIELD, DWARVISH_ROUNDSHIELD }, */ /* { PM_DWARF, PICK_AXE, DWARVISH_MATTOCK }, */ + { PM_DWARF, LEMBAS_WAFER, CRAM_RATION }, { PM_GNOME, BOW, CROSSBOW }, { PM_GNOME, ARROW, CROSSBOW_BOLT }, { NON_PM, STRANGE_OBJECT, STRANGE_OBJECT } @@ -972,17 +975,8 @@ register struct trobj *trop; int otyp, i; while (trop->trclass) { - if (trop->trotyp != UNDEF_TYP) { - otyp = (int) trop->trotyp; - if (urace.malenum != PM_HUMAN) { - /* substitute specific items for generic ones */ - for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) - if (inv_subs[i].race_pm == urace.malenum - && otyp == inv_subs[i].item_otyp) { - otyp = inv_subs[i].subs_otyp; - break; - } - } + otyp = (int) trop->trotyp; + if (otyp != UNDEF_TYP) { obj = mksobj(otyp, TRUE, FALSE); } else { /* UNDEF_TYP */ static NEARDATA short nocreate = STRANGE_OBJECT; @@ -1057,6 +1051,23 @@ register struct trobj *trop; nocreate4 = otyp; } + if (urace.malenum != PM_HUMAN) { + /* substitute race-specific items; this used to be in + the 'if (otyp != UNDEF_TYP) { }' block above, but then + substitutions didn't occur for randomly generated items + (particularly food) which have racial substitutes */ + for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) + if (inv_subs[i].race_pm == urace.malenum + && otyp == inv_subs[i].item_otyp) { + debugpline3("ini_inv: substituting %s for %s%s", + OBJ_NAME(objects[inv_subs[i].subs_otyp]), + (trop->trotyp == UNDEF_TYP) ? "random " : "", + OBJ_NAME(objects[otyp])); + otyp = obj->otyp = inv_subs[i].subs_otyp; + break; + } + } + /* nudist gets no armor */ if (u.uroleplay.nudist && obj->oclass == ARMOR_CLASS) { dealloc_obj(obj); diff --git a/src/windows.c b/src/windows.c index 9a82325d9..96d9a218d 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 windows.c $NHDT-Date: 1495232365 2017/05/19 22:19:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 windows.c $NHDT-Date: 1526933747 2018/05/21 20:15:47 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.48 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -56,6 +56,7 @@ extern void *FDECL(trace_procs_chain, (int, int, void *, void *, void *)); #endif STATIC_DCL void FDECL(def_raw_print, (const char *s)); +STATIC_DCL void NDECL(def_wait_synch); #ifdef DUMPLOG STATIC_DCL winid FDECL(dump_create_nhwindow, (int)); @@ -191,6 +192,22 @@ const char *s; puts(s); } +STATIC_OVL +void +def_wait_synch(VOID_ARGS) +{ + /* Config file error handling routines + * call wait_sync() without checking to + * see if it actually has a value, + * leading to spectacular violations + * when you try to execute address zero. + * The existence of this allows early + * processing to have something to execute + * even though it essentially does nothing + */ + return; +} + #ifdef WINCHAIN static struct win_choices * win_choices_find(s) @@ -232,6 +249,9 @@ const char *s; if (!windowprocs.win_raw_print) windowprocs.win_raw_print = def_raw_print; + if (!windowprocs.win_wait_synch) + /* early config file error processing routines call this */ + windowprocs.win_wait_synch = def_wait_synch; if (!winchoices[0].procs) { raw_printf("No window types?"); diff --git a/src/worn.c b/src/worn.c index 612f71bac..cf45277a9 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worn.c $NHDT-Date: 1496959481 2017/06/08 22:04:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ +/* NetHack 3.6 worn.c $NHDT-Date: 1526728754 2018/05/19 11:19:14 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.51 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -105,7 +105,8 @@ long mask; } } } - update_inventory(); + if (!restoring) + update_inventory(); } /* called e.g. when obj is destroyed */ @@ -136,7 +137,9 @@ register struct obj *obj; if ((p = w_blocks(obj, wp->w_mask)) != 0) u.uprops[p].blocked &= ~wp->w_mask; } - update_inventory(); + /* setnotworn() isn't called during restore but parallel setworn() */ + if (!restoring) + update_inventory(); } /* return a bitmask of the equipment slot(s) a given item might be worn in */ diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 1b38ab951..cfb7dbf8a 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -310,7 +310,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif #ifdef WIN32 save_getreturn_status = getreturn_enabled; +#ifdef TTY_GRAPHICS raw_clear_screen(); +#endif getreturn_enabled = TRUE; check_recordfile((char *) 0); #endif @@ -484,6 +486,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ NHWinMainInit(); else */ +#ifdef TTY_GRAPHICS if (!strncmpi(windowprocs.name, "tty", 3)) { iflags.use_background_glyph = FALSE; nttty_open(1); @@ -492,6 +495,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif #endif +#endif #if defined(MSDOS) || defined(WIN32) /* Player didn't specify any symbol set so use IBM defaults */ @@ -510,7 +514,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ process_options(argc, argv); #endif -#ifdef WIN32 +#if defined(WIN32) && defined(TTY_GRAPHICS) toggle_mouse_support(); /* must come after process_options */ #endif @@ -791,14 +795,11 @@ char *argv[]; #endif #ifdef WIN32 case 'w': /* windowtype */ +#ifdef TTY_GRAPHICS if (strncmpi(&argv[0][2], "tty", 3)) { nttty_open(1); } - /* - else { - NHWinMainInit(); - } - */ +#endif config_error_init(FALSE, "command line", FALSE); choose_windows(&argv[0][2]); config_error_done(); diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 71b62c6e0..cb5650af6 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -501,6 +501,15 @@ int code; exit(code); } +#ifdef WIN32 +#ifndef TTY_GRAPHICS +void +synch_cursor() +{ +} +#endif +#endif + /* Chdir back to original directory */ #ifdef TOS diff --git a/sys/share/pcunix.c b/sys/share/pcunix.c index 72daafe10..22bc4ae4e 100644 --- a/sys/share/pcunix.c +++ b/sys/share/pcunix.c @@ -15,9 +15,10 @@ #if defined(WIN32) || defined(MSDOS) extern char orgdir[]; -#ifdef WIN32 -extern void NDECL(backsp); #endif + +#if defined(WIN32) && defined(TTY_GRAPHICS) +extern void NDECL(backsp); extern void NDECL(clear_screen); #endif @@ -196,7 +197,7 @@ getlock() if (c == 'y' || c == 'Y') #ifndef SELF_RECOVER if (eraseoldlocks()) { -#if defined(WIN32) +#if defined(WIN32) && defined(TTY_GRAPHICS) if (!strncmpi(windowprocs.name, "tty", 3)) clear_screen(); /* display gets fouled up otherwise */ #endif @@ -210,7 +211,7 @@ getlock() } #else /*SELF_RECOVER*/ if (recover_savefile()) { -#if defined(WIN32) +#if defined(WIN32) && defined(TTY_GRAPHICS) if (!strncmpi(windowprocs.name, "tty", 3)) clear_screen(); /* display gets fouled up otherwise */ #endif diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 47af438b4..98c80a2fd 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -1,5 +1,5 @@ # NetHack Makefile. -# NetHack 3.6 Makefile.src $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.54 $ +# NetHack 3.6 Makefile.src $NHDT-Date: 1526687360 2018/05/18 23:49:20 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.55 $ # Copyright (c) 2018 by Pasi Kallinen # NetHack may be freely redistributed. See license for details. @@ -343,7 +343,13 @@ AWK = nawk # will proceed without it GITINFO=1 -#VERBOSEMAKE = 1 +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 # ---------------------------------------- # @@ -353,7 +359,7 @@ GITINFO=1 # {unixconf.h, pcconf.h, tosconf.h}, and possibly system.h # Verbosity definitions, begin -# Set VERBOSEMAKE=1 to output more stuff. +# Set QUIETCC=1 above to output less feedback while building. # CC and CXX obey verbosity, LD and LINK don't. # AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) @@ -361,23 +367,25 @@ ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = $(AT_V$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end MAKEDEFS = ../util/makedefs diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 9119cef82..78deeb23e 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -1,5 +1,5 @@ # Makefile for NetHack's utility programs. -# NetHack 3.6 Makefile.utl $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.35 $ +# NetHack 3.6 Makefile.utl $NHDT-Date: 1526687364 2018/05/18 23:49:24 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.36 $ # Copyright (c) 2018 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. @@ -127,34 +127,50 @@ LEXYYC = lex.yy.c # LEXYYC = lexyy.c +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 + +# TODO? the link/load commands below are handled differently from the ones +# in Makefile.src; these use '$(CC) $(LFLAGS)' and ought to be changed to use +# $(LD) or $(LINK) as appropriate [quiet mode echoes a misleading $< value] # ---------------------------------------- # # Nothing below this line should have to be changed. # Verbosity definitions, begin +# Set QUIETCC=1 above to output less feedback while building. +# CC and CXX obey verbosity, LD and LINK don't. +# AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = V$(AT_$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end # timestamps for primary header files, matching src/Makefile diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index f7417adbe..e983d36fb 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -13,6 +13,7 @@ * */ + #ifdef WIN32 #define NEED_VARARGS /* Uses ... */ #include "hack.h" @@ -21,6 +22,10 @@ #include #include "win32api.h" +extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +extern int redirect_stdout; + +#ifdef TTY_GRAPHICS /* * Console Buffer Flipping Support * @@ -84,9 +89,6 @@ INPUT_RECORD ir; /* Support for changing console font if existing glyph widths are too wide */ -extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ -extern int redirect_stdout; - /* Flag for whether NetHack was launched via the GUI, not the command line. * The reason we care at all, is so that we can get * a final RETURN at the end of the game when launched from the GUI @@ -1078,40 +1080,6 @@ void set_altkeyhandler(const char * inName) return; } -/* this is used as a printf() replacement when the window - * system isn't initialized yet - */ -void msmsg -VA_DECL(const char *, fmt) -{ - char buf[ROWNO * COLNO]; /* worst case scenario */ - VA_START(fmt); - VA_INIT(fmt, const char *); - Vsprintf(buf, fmt, VA_ARGS); - if (redirect_stdout) - fprintf(stdout, "%s", buf); - else { - if(!init_ttycolor_completed) - init_ttycolor(); - - /* if we have generated too many messages ... ask the user to - * confirm and then clear. - */ - if (console.cursor.Y > console.height - 4) { - xputs("Hit to continue."); - while (pgetchar() != '\n') - ; - raw_clear_screen(); - set_console_cursor(1, 0); - } - - xputs(buf); - if (ttyDisplay) - curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); - } - VA_END(); - return; -} /* fatal error */ /*VARARGS1*/ @@ -1912,5 +1880,43 @@ void nethack_enter_nttty() error("Unable to load nhraykey.dll"); } } +#endif TTY_GRAPHICS + +/* this is used as a printf() replacement when the window + * system isn't initialized yet + */ +void msmsg +VA_DECL(const char *, fmt) +{ + char buf[ROWNO * COLNO]; /* worst case scenario */ + VA_START(fmt); + VA_INIT(fmt, const char *); + Vsprintf(buf, fmt, VA_ARGS); + if (redirect_stdout) + fprintf(stdout, "%s", buf); + else { +#ifdef TTY_GRAPHICS + if(!init_ttycolor_completed) + init_ttycolor(); + /* if we have generated too many messages ... ask the user to + * confirm and then clear. + */ + if (console.cursor.Y > console.height - 4) { + xputs("Hit to continue."); + while (pgetchar() != '\n') + ; + raw_clear_screen(); + set_console_cursor(1, 0); + } + xputs(buf); + if (ttyDisplay) + curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); +#else + fprintf(stdout, "%s", buf); +#endif + } + VA_END(); + return; +} #endif /* WIN32 */ diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index ef161ae9b..2a0d10e79 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -96,11 +96,13 @@ clear_screen() return; } +#ifdef TTY_GRAPHICS void backsp() { return; } +#endif int has_color(int color) @@ -158,11 +160,13 @@ VA_DECL(const char *, s) return; } +#ifdef TTY_GRAPHICS void synch_cursor() { return; } +#endif void more() diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 067ba5abd..451aae2f0 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -17,7 +17,9 @@ #endif #include #include "win32api.h" +#ifdef TTY_GRAPHICS #include "wintty.h" +#endif #ifdef WIN32 /* @@ -227,7 +229,14 @@ Delay(int ms) (void) Sleep(ms); } +#ifdef TTY_GRAPHICS extern void NDECL(backsp); +#else +void +backsp() +{ +} +#endif void win32_abort() @@ -481,11 +490,10 @@ void ntassert_failed(const char * exp, const char * file, int line) /* nethack_enter_winnt() is the first thing called from main */ void nethack_enter_winnt() { -#ifdef WIN32CON +#ifdef TTY_GRAPHICS nethack_enter_nttty(); #endif } - #endif /* WIN32 */ /*winnt.c*/ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index fc40dcc75..42dad00c2 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1526429383 2018/05/16 00:09:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.166 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1526909614 2018/05/21 13:33:34 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.167 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -4175,6 +4175,10 @@ render_status(VOID_ARGS) * +-----------+ */ if (iflags.hilite_delta) { + if (*text == ' ') { + tty_putstatusfield(nullfield, " ", x++, y); + text++; + } /* multiple attributes can be in effect concurrently */ Begin_Attr(attridx); if (coloridx != NO_COLOR && coloridx != CLR_MAX)