diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 8b32a50aa..c700b8a0d 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch$:$NHDT-Revision$ $NHDT-Date$ +.\" $NHDT-Branch: master $:$NHDT-Revision: 1.160 $ $NHDT-Date: 1430043650 2015/04/26 10:20:50 $ .\" $Revision: 1.130 $ $Date: 2015/03/27 00:38:30 $ .ds h0 "NetHack Guidebook .ds h1 @@ -607,12 +607,17 @@ is true. .lp i List your inventory (everything you're carrying). .lp I -List selected parts of your inventory. +List selected parts of your inventory, usually be specifying the character +for a particular set of objects, like `[' for armor or `!' for potions. .sd .si I* - list all gems in inventory; Iu - list all unpaid items; Ix - list all used up items that are on your shopping bill; +IB - list all items known to be blessed; +IU - list all items known to be uncursed; +IC - list all items known to be cursed; +IX - list all items whose bless/curse status is known; I$ - count your money. .ei .ed @@ -1256,6 +1261,11 @@ Objects can also be blessed. Blessed items usually work better or more beneficially than normal uncursed items. For example, a blessed weapon will do more damage against demons. .pg +Objects which are neither cursed nor blessed are referred to as uncursed. +They could just as easily have been described as unblessed, but the +uncursed designation is what you will see within the game. A ``glass +half full versus glass half empty'' situation; make of that what you will. +.pg There are magical means of bestowing or removing curses upon objects, so even if you are stuck with one, you can still have the curse lifted and the item removed. Priests and Priestesses have an innate @@ -1266,6 +1276,8 @@ An item with unknown status will be reported in your inventory with no prefix. An item which you know the state of will be distinguished in your inventory by the presence of the word ``cursed'', ``uncursed'' or ``blessed'' in the description of the item. +In some cases ``uncursed'' will be omitted as being redundant when +enough other information is displayed. .hn 2 Weapons (`)') .pg @@ -2085,8 +2097,8 @@ Implemented by the Amiga, Gem and tty ports. Default '^'. .lp menu_headings Controls how the headings in a menu are highlighted. -Values are 'bold', 'inverse', or 'underline'. -Not all ports can actually display all three types. +Values are 'none', 'bold', 'dim', 'underline', 'blink', or 'inverse'. +Not all ports can actually display all types. .lp menu_invert_all Menu character accelerator to invert all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 057fbc8e9..2b7e2deba 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -742,12 +742,18 @@ something appropriate if {\it autoquiver\/} is true. List your inventory (everything you're carrying). %.lp \item[\tb{I}] -List selected parts of your inventory.\\ +List selected parts of your inventory, usually be specifying the character +for a particular set of objects, like `{\tt [}' for armor or `{\tt !}' +for potions.\\ %.sd %.si {\tt I*} --- list all gems in inventory;\\ {\tt Iu} --- list all unpaid items;\\ {\tt Ix} --- list all used up items that are on your shopping bill;\\ +{\tt IB} --- list all items known to be blessed;\\ +{\tt IU} --- list all items known to be uncursed;\\ +{\tt IC} --- list all items known to be cursed;\\ +{\tt IX} --- list all items whose bless/curse status is unknown;\\ {\tt I\$} --- count your money. %.ei %.ed @@ -1543,6 +1549,12 @@ Objects can also be blessed. Blessed items usually work better or more beneficially than normal uncursed items. For example, a blessed weapon will do more damage against demons. +%.pg +Objects which are neither cursed nor blessed are referred to as uncursed. +They could just as easily have been described as unblessed, but the +uncursed designation is what you will see within the game. A ``glass +half full versus glass half empty'' situation; make of that what you will. + %.pg There are magical means of bestowing or removing curses upon objects, so even if you are stuck with one, you can still have the curse @@ -1555,6 +1567,8 @@ An item with unknown status will be reported in your inventory with no prefix. An item which you know the state of will be distinguished in your inventory by the presence of the word ``cursed'', ``uncursed'' or ``blessed'' in the description of the item. +In some cases ``uncursed'' will be omitted as being redundant when +enough other information is displayed. %.hn 2 \subsection*{Weapons (`{\tt )}')} @@ -2516,8 +2530,9 @@ Implemented by the Amiga, Gem and tty ports. Default `\verb+^+'. \item[\ib{menu\_headings}] Controls how the headings in a menu are highlighted. -Values are ``{\tt bold}'', ``{\tt inverse}'', or ``{\tt underline}''. -Not all ports can actually display all three types. +Values are ``{\tt none}'', ``{\tt bold}'', ``{\tt dim}'', +``{\tt underline}'', ``{\tt blink}'', or ``{\tt inverse}''. +Not all ports can actually display all types. \item[\ib{menu\_invert\_all}] Menu character accelerator to invert all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 072dbc6f3..02f3e4e8d 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -903,6 +903,9 @@ some monsters can eat through iron bars inaccessible niches occasionally have iron bars in front sinks may teleport or polymorph shopkeepers give honorifics to vampires and elves +when commands (D, A, object identify) mix object class filtering with BUCX + filtering, take the intersection rather than the union (so ?B picks + blessed scrolls rather than all scrolls plus blessed everything) Platform- and/or Interface-Specific Fixes @@ -1128,6 +1131,8 @@ Some levels in Gehennom now use the old corridor-style maze instead of "beetle legs" are restored. gnomes will occasionally have a candle stop travel or run when you get hungry +'I' command can accept 'B','U','C',or 'X' as an alternative to normal object + class character to show inventory of items known to be blessed,&c Platform- and/or Interface-Specific New Features diff --git a/include/color.h b/include/color.h index a772dd84c..5b4b966f5 100644 --- a/include/color.h +++ b/include/color.h @@ -53,6 +53,7 @@ struct menucoloring { struct nhregex *match; + char *origstr; int color, attr; struct menucoloring *next; }; diff --git a/include/extern.h b/include/extern.h index 821f2a41c..a185be710 100644 --- a/include/extern.h +++ b/include/extern.h @@ -222,6 +222,7 @@ E boolean FDECL(is_lava, (int,int)); E boolean FDECL(is_pool_or_lava, (int,int)); E boolean FDECL(is_ice, (int,int)); E boolean FDECL(is_moat, (int,int)); +E schar FDECL(db_under_typ, (int)); E int FDECL(is_drawbridge_wall, (int,int)); E boolean FDECL(is_db_wall, (int,int)); E boolean FDECL(find_drawbridge, (int *,int*)); @@ -570,6 +571,7 @@ E boolean FDECL(Invocation_lev, (d_level *)); E xchar NDECL(level_difficulty); E schar FDECL(lev_by_name, (const char *)); E schar FDECL(print_dungeon, (BOOLEAN_P,schar *,xchar *)); +E char *FDECL(get_annotation, (d_level *)); E int NDECL(donamelevel); E int NDECL(dooverview); E void FDECL(show_overview, (int,int)); @@ -1372,6 +1374,7 @@ E void FDECL(mon_regen, (struct monst *,BOOLEAN_P)); E int FDECL(dochugw, (struct monst *)); E boolean FDECL(onscary, (int,int,struct monst *)); E void FDECL(monflee, (struct monst *, int, BOOLEAN_P, BOOLEAN_P)); +E void FDECL(mon_yells, (struct monst *, const char *)); E int FDECL(dochug, (struct monst *)); E int FDECL(m_move, (struct monst *,int)); E void FDECL(dissolve_bars, (int,int)); diff --git a/include/mondata.h b/include/mondata.h index 1a4d8eb9c..5043f8aaa 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -134,6 +134,8 @@ #define is_displacer(ptr) (((ptr)->mflags3 & M3_DISPLACES) != 0L) #define is_mplayer(ptr) (((ptr) >= &mons[PM_ARCHEOLOGIST]) && \ ((ptr) <= &mons[PM_WIZARD])) +#define is_watch(ptr) ((ptr) == &mons[PM_WATCHMAN] || \ + (ptr) == &mons[PM_WATCH_CAPTAIN]) #define is_rider(ptr) ((ptr) == &mons[PM_DEATH] || \ (ptr) == &mons[PM_FAMINE] || \ (ptr) == &mons[PM_PESTILENCE]) diff --git a/src/ball.c b/src/ball.c index e1fa4b72a..3cd8e0eae 100644 --- a/src/ball.c +++ b/src/ball.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 ball.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* NetHack 3.5 ball.c $NHDT-Date: 1430365884 2015/04/30 03:51:24 $ $NHDT-Branch: master $:$NHDT-Revision: 1.22 $ */ /* NetHack 3.5 ball.c $Date: 2011/08/30 22:13:26 $ $Revision: 1.17 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -102,7 +102,7 @@ ballfall() * It is assumed that when this is called, the ball and chain are NOT * attached to the object list. * - * Should not be called while swallowed. + * Should not be called while swallowed except on waterlevel. */ void placebc() @@ -133,7 +133,19 @@ placebc() void unplacebc() { - if (u.uswallow) return; /* ball&chain not placed while swallowed */ + if (u.uswallow) { + if (Is_waterlevel(&u.uz)) { + /* we need to proceed with the removal from the floor + * so that movebubbles() processing will disregard it as + * intended. Ignore all the vision stuff. + */ + if (!carried(uball)) + obj_extract_self(uball); + obj_extract_self(uchain); + } + /* ball&chain not unplaced while swallowed */ + return; + } if (!carried(uball)) { obj_extract_self(uball); diff --git a/src/dbridge.c b/src/dbridge.c index 8c4b90031..e2aa105ab 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -103,6 +103,18 @@ int x, y; return FALSE; } +schar +db_under_typ(mask) +int mask; +{ + switch (mask & DB_UNDER) { + case DB_ICE: return ICE; break; + case DB_LAVA: return LAVAPOOL; break; + case DB_MOAT: return MOAT; break; + default: return STONE; break; + } +} + /* * We want to know whether a wall (or a door) is the portcullis (passageway) * of an eventual drawbridge. diff --git a/src/dig.c b/src/dig.c index 1d7d5dd7a..9f031f1c5 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1171,8 +1171,7 @@ watch_dig(mtmp, x, y, zap) if (!mtmp) { for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if ((mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + if (is_watch(mtmp->data) && mtmp->mcansee && m_canseeu(mtmp) && couldsee(mtmp->mx, mtmp->my) && mtmp->mpeaceful) break; diff --git a/src/display.c b/src/display.c index f2e18df61..3cd92f916 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 display.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* NetHack 3.5 display.c $NHDT-Date: 1430365890 2015/04/30 03:51:30 $ $NHDT-Branch: master $:$NHDT-Revision: 1.49 $ */ /* NetHack 3.5 display.c $Date: 2011/12/05 03:17:36 $ $Revision: 1.34 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ @@ -1363,11 +1363,16 @@ row_refresh(start,stop,y) void cls() { + static boolean in_cls = 0; + + if (in_cls) return; + in_cls = TRUE; display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ context.botlx = 1; /* force update of botl window */ clear_nhwindow(WIN_MAP); /* clear physical screen */ clear_glyph_buffer(); /* this is sort of an extra effort, but OK */ + in_cls = FALSE; } /* diff --git a/src/do.c b/src/do.c index 97c11a8ef..3962e6d01 100644 --- a/src/do.c +++ b/src/do.c @@ -1069,6 +1069,7 @@ boolean at_stairs, falling, portal; boolean new = FALSE; /* made a new level? */ struct monst *mtmp; char whynot[BUFSZ]; + char *annotation; if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel)) newlevel->dlevel = dunlevs_in_dungeon(newlevel); @@ -1458,6 +1459,9 @@ boolean at_stairs, falling, portal; save_currentstate(); #endif + if ((annotation = get_annotation(&u.uz))) + You("remember this level as %s.", annotation); + /* assume this will always return TRUE when changing level */ (void) in_out_region(u.ux, u.uy); (void) pickup(1); diff --git a/src/do_name.c b/src/do_name.c index bd1dc6f49..06d305401 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -596,6 +596,9 @@ docallcmd() any.a_char = 'd'; /* entry 'd' (or 'b'), group accelator 'd' */ add_menu(win, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, "the type of an object on discoveries list", MENU_UNSELECTED); + any.a_char = 'e'; + add_menu(win, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, + "the current level", MENU_UNSELECTED); #if 0 any.a_char = 'f'; /* entry 'e' (or 'c'), group accelator 'f' */ add_menu(win, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, @@ -643,6 +646,9 @@ docallcmd() case 'd': /* name a type of object on the discoveries list */ rename_disco(); break; + case 'e': /* annotate level */ + donamelevel(); + break; #if 0 case 'f': /* name a type of object visible on the floor */ /* [not implemented] */ diff --git a/src/dokick.c b/src/dokick.c index d558bdd38..5a380fc91 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1198,15 +1198,10 @@ dumb: if (in_town(x, y)) for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if((mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my) && mtmp->mpeaceful) { - if (canspotmon(mtmp)) - pline("%s yells:", Amonnam(mtmp)); - else - You_hear("someone yell:"); - verbalize("Halt, thief! You're under arrest!"); + mon_yells(mtmp, "Halt, thief! You're under arrest!"); (void) angry_guards(FALSE); break; } @@ -1218,18 +1213,13 @@ dumb: if (in_town(x, y)) for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if ((mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + if (is_watch(mtmp->data) && mtmp->mpeaceful && couldsee(mtmp->mx, mtmp->my)) { - if (canspotmon(mtmp)) - pline("%s yells:", Amonnam(mtmp)); - else - You_hear("someone yell:"); if(levl[x][y].looted & D_WARNED) { - verbalize("Halt, vandal! You're under arrest!"); + mon_yells(mtmp, "Halt, vandal! You're under arrest!"); (void) angry_guards(FALSE); } else { - verbalize("Hey, stop damaging that door!"); + mon_yells(mtmp, "Hey, stop damaging that door!"); levl[x][y].looted |= D_WARNED; } break; diff --git a/src/dungeon.c b/src/dungeon.c index d02f3aec6..95d6c0946 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -1848,6 +1848,16 @@ d_level *dest; } } +char * +get_annotation(lev) +d_level *lev; +{ + mapseen *mptr; + if ((mptr = find_mapseen(lev))) + return mptr->custom; + return NULL; +} + /* #annotate command - add a custom name to the current level */ int donamelevel() @@ -1857,8 +1867,15 @@ donamelevel() if (!(mptr = find_mapseen(&u.uz))) return 0; - getlin("What do you want to call this dungeon level?", nbuf); + if (mptr->custom) { + char tmpbuf[BUFSZ]; + Sprintf(tmpbuf, "Replace annotation \"%.30s%s\" with?", + mptr->custom, strlen(mptr->custom) > 30 ? "..." : ""); + getlin(tmpbuf, nbuf); + } else + getlin("What do you want to call this dungeon level?", nbuf); if (index(nbuf, '\033')) return 0; + (void)mungspaces(nbuf); /* discard old annotation, if any */ if (mptr->custom) { @@ -2190,12 +2207,7 @@ recalc_mapseen() if (cansee(x, y) || (x == u.ux && y == u.uy && !Levitation)) { ltyp = levl[x][y].typ; if (ltyp == DRAWBRIDGE_UP) - switch (levl[x][y].drawbridgemask & DB_UNDER) { - case DB_ICE: ltyp = ICE; break; - case DB_LAVA: ltyp = LAVAPOOL; break; - case DB_MOAT: ltyp = MOAT; break; - default: ltyp = STONE; break; - } + ltyp = db_under_typ(levl[x][y].drawbridgemask); if ((mtmp = m_at(x, y)) != 0 && mtmp->m_ap_type == M_AP_FURNITURE && canseemon(mtmp)) ltyp = cmap_to_type(mtmp->mappearance); diff --git a/src/fountain.c b/src/fountain.c index 405fac4a3..03a373843 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -169,8 +169,7 @@ boolean isyou; /* Warn about future fountain use. */ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if ((mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my) && mtmp->mpeaceful) { pline("%s yells:", Amonnam(mtmp)); diff --git a/src/invent.c b/src/invent.c index 2cc65798d..176d2b8d2 100644 --- a/src/invent.c +++ b/src/invent.c @@ -24,6 +24,7 @@ STATIC_PTR char *FDECL(safeq_xprname, (struct obj *)); STATIC_PTR char *FDECL(safeq_shortxprname, (struct obj *)); STATIC_DCL char FDECL(display_pickinv, (const char *,BOOLEAN_P, long *)); STATIC_DCL char FDECL(display_used_invlets, (CHAR_P)); +STATIC_DCL void FDECL(tally_BUCX, (struct obj *,int *,int *,int *,int *,int *)); STATIC_DCL boolean FDECL(this_type_only, (struct obj *)); STATIC_DCL void NDECL(dounpaid); STATIC_DCL struct obj *FDECL(find_unpaid,(struct obj *,struct obj **)); @@ -394,6 +395,7 @@ addinv(obj) struct obj *obj; { struct obj *otmp, *prev; + int saved_otyp = (int)obj->otyp; /* for panic */ if (obj->where != OBJ_FREE) panic("addinv: obj not free"); @@ -410,12 +412,16 @@ struct obj *obj; extra to quivered stack is more useful than to wielded one */ if (uquiver && merged(&uquiver, &obj)) { obj = uquiver; + if (!obj) + panic("addinv: null obj after quiver merge otyp=%d", saved_otyp); goto added; } /* merge if possible; find end of chain in the process */ for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj) if (merged(&otmp, &obj)) { obj = otmp; + if (!obj) + panic("addinv: null obj after merge otyp=%d", saved_otyp); goto added; } /* didn't merge, so insert into chain */ @@ -2067,6 +2073,33 @@ count_buc(list, type) return count; } +/* similar to count_buc(), but tallies all states at once + rather than looking for a specific type */ +STATIC_OVL void +tally_BUCX(list, bcp, ucp, ccp, xcp, ocp) +struct obj *list; +int *bcp, *ucp, *ccp, *xcp, *ocp; +{ + *bcp = *ucp = *ccp = *xcp = *ocp = 0; + for ( ; list; list = list->nobj) { + if (list->oclass == COIN_CLASS) { + ++(*ocp); /* "other" */ + continue; + } + /* priests always know bless/curse state */ + if (Role_if(PM_PRIEST)) list->bknown = 1; + + if (!list->bknown) + ++(*xcp); + else if (list->blessed) + ++(*bcp); + else if (list->cursed) + ++(*ccp); + else /* neither blessed nor cursed => uncursed */ + ++(*ucp); + } +} + long count_contents(container, nested, quantity, everything) struct obj *container; @@ -2189,7 +2222,18 @@ STATIC_OVL boolean this_type_only(obj) struct obj *obj; { - return (obj->oclass == this_type); + boolean res = (obj->oclass == this_type); + + if (obj->oclass != COIN_CLASS) { + switch (this_type) { + case 'B': res = (obj->bknown && obj->blessed); break; + case 'U': res = (obj->bknown && !(obj->blessed || obj->cursed)); break; + case 'C': res = (obj->bknown && obj->cursed); break; + case 'X': res = !obj->bknown; break; + default: break; /* use 'res' as-is */ + } + } + return res; } /* the 'I' command */ @@ -2200,6 +2244,7 @@ dotypeinv() int n, i = 0; char *extra_types, types[BUFSZ]; int class_count, oclass, unpaid_count, itemcount; + int bcnt, ccnt, ucnt, xcnt, ocnt; boolean billx = *u.ushops && doinvbill(0); menu_item *pick_list; boolean traditional = TRUE; @@ -2210,12 +2255,18 @@ dotypeinv() return 0; } unpaid_count = count_unpaid(invent); + tally_BUCX(invent, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt); + if (flags.menu_style != MENU_TRADITIONAL) { if (flags.menu_style == MENU_FULL || flags.menu_style == MENU_PARTIAL) { traditional = FALSE; i = UNPAID_TYPES; if (billx) i |= BILLED_TYPES; + if (bcnt) i |= BUC_BLESSED; + if (ucnt) i |= BUC_UNCURSED; + if (ccnt) i |= BUC_CURSED; + if (xcnt) i |= BUC_UNKNOWN; n = query_category(prompt, invent, i, &pick_list, PICK_ONE); if (!n) return 0; this_type = c = pick_list[0].item.a_int; @@ -2225,73 +2276,96 @@ dotypeinv() if (traditional) { /* collect a list of classes of objects carried, for use as a prompt */ types[0] = 0; - class_count = collect_obj_classes(types, invent, - FALSE, - (boolean FDECL((*),(OBJ_P))) 0, &itemcount); - if (unpaid_count) { - Strcat(types, "u"); - class_count++; - } - if (billx) { - Strcat(types, "x"); - class_count++; - } + class_count = collect_obj_classes(types, invent, FALSE, + (boolean FDECL((*),(OBJ_P))) 0, + &itemcount); + if (unpaid_count || billx || (bcnt + ccnt + ucnt + xcnt) != 0) + types[class_count++] = ' '; + if (unpaid_count) types[class_count++] = 'u'; + if (billx) types[class_count++] = 'x'; + if (bcnt) types[class_count++] = 'B'; + if (ucnt) types[class_count++] = 'U'; + if (ccnt) types[class_count++] = 'C'; + if (xcnt) types[class_count++] = 'X'; + types[class_count] = '\0'; /* add everything not already included; user won't see these */ extra_types = eos(types); *extra_types++ = '\033'; if (!unpaid_count) *extra_types++ = 'u'; if (!billx) *extra_types++ = 'x'; + if (!bcnt) *extra_types++ = 'B'; + if (!ucnt) *extra_types++ = 'U'; + if (!ccnt) *extra_types++ = 'C'; + if (!xcnt) *extra_types++ = 'X'; *extra_types = '\0'; /* for index() */ for (i = 0; i < MAXOCLASSES; i++) - if (!index(types, def_oc_syms[i].sym)) { - *extra_types++ = def_oc_syms[i].sym; - *extra_types = '\0'; - } + if (!index(types, def_oc_syms[i].sym)) { + *extra_types++ = def_oc_syms[i].sym; + *extra_types = '\0'; + } - if(class_count > 1) { - c = yn_function(prompt, types, '\0'); - savech(c); - if(c == '\0') { - clear_nhwindow(WIN_MESSAGE); - return 0; - } + if (class_count > 1) { + c = yn_function(prompt, types, '\0'); + savech(c); + if (c == '\0') { + clear_nhwindow(WIN_MESSAGE); + return 0; + } } else { - /* only one thing to itemize */ - if (unpaid_count) - c = 'u'; - else if (billx) - c = 'x'; - else - c = types[0]; + /* only one thing to itemize */ + if (unpaid_count) + c = 'u'; + else if (billx) + c = 'x'; + else + c = types[0]; } } - if (c == 'x') { + if (c == 'x' || (c == 'X' && billx && !xcnt)) { if (billx) - (void) doinvbill(1); - else - pline("No used-up objects on your shopping bill."); + (void) doinvbill(1); + else + pline("No used-up objects%s.", + unpaid_count ? " on your shopping bill" : ""); return 0; } - if (c == 'u') { + if (c == 'u' || (c == 'U' && unpaid_count && !ucnt)) { if (unpaid_count) - dounpaid(); + dounpaid(); else - You("are not carrying any unpaid objects."); + You("are not carrying any unpaid objects."); return 0; } if (traditional) { - oclass = def_char_to_objclass(c); /* change to object class */ - if (oclass == COIN_CLASS) { - return doprgold(); - } else if (index(types, c) > index(types, '\033')) { - You("have no such objects."); - return 0; + if (index("BUCX", c)) + oclass = c; /* not a class but understood by this_type_only() */ + else + oclass = def_char_to_objclass(c); /* change to object class */ + + if (oclass == COIN_CLASS) + return doprgold(); + if (index(types, c) > index(types, '\033')) { + /* '> ESC' => "hidden choice", something known not to be carried */ + const char *which = 0; + + switch (c) { + case 'B': which = "known to be blessed"; break; + case 'U': which = "known to be uncursed"; break; + case 'C': which = "known to be cursed"; break; + case 'X': You( + "have no objects whose blessed/uncursed/cursed status is unknown."); + break; /* better phrasing is desirable */ + default: which = "such"; break; + } + if (which) + You("have no %s objects.", which); + return 0; } this_type = oclass; } if (query_objlist((char *) 0, invent, - (flags.invlet_constant ? USE_INVLET : 0)|INVORDER_SORT, - &pick_list, PICK_NONE, this_type_only) > 0) + (flags.invlet_constant ? USE_INVLET : 0)|INVORDER_SORT, + &pick_list, PICK_NONE, this_type_only) > 0) free((genericptr_t)pick_list); return 0; } diff --git a/src/mkmaze.c b/src/mkmaze.c index 42851c0df..923a51f1d 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1157,12 +1157,7 @@ xchar x,y; lev = &levl[x][y]; ltyp = lev->typ; if (ltyp == DRAWBRIDGE_UP) - switch (lev->drawbridgemask & DB_UNDER) { - case DB_ICE: ltyp = ICE; break; - case DB_LAVA: ltyp = LAVAPOOL; break; - case DB_MOAT: ltyp = MOAT; break; - default: ltyp = STONE; break; - } + ltyp = db_under_typ(lev->drawbridgemask); if (ltyp == LAVAPOOL) return "lava"; diff --git a/src/mkobj.c b/src/mkobj.c index a9d0ebfe8..699c5dbdb 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2194,7 +2194,7 @@ unsigned oid; for (fx = ex; abs(fx - ex) < 3; fx += dx) { for (fy = ey; abs(fy - ey) < 3; fy += dy) { /* 0, 0 was checked above */ - if (fx != x || fy != y) { + if (isok(fx,fy) && (fx != x || fy != y)) { if ((otmp = sobj_at(otyp, fx, fy)) != 0) { return otmp; } diff --git a/src/mon.c b/src/mon.c index 3a2fc402b..7acc26670 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 mon.c $NHDT-Date: 1429666918 2015/04/22 01:41:58 $ $NHDT-Branch: master $:$NHDT-Revision: 1.165 $ */ +/* NetHack 3.5 mon.c $NHDT-Date: 1430396792 2015/04/30 12:26:32 $ $NHDT-Branch: master $:$NHDT-Revision: 1.169 $ */ /* NetHack 3.5 mon.c $Date: 2012/05/16 02:15:10 $ $Revision: 1.126 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1850,7 +1850,7 @@ register struct monst *mtmp; u.uy = mtmp->my; u.uswallow = 0; u.uswldtim = 0; - if (Punished) placebc(); + if (Punished && uball->where == OBJ_FREE) placebc(); vision_full_recalc = 1; docrt(); } @@ -3117,9 +3117,7 @@ register boolean silent; for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if((mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) - && mtmp->mpeaceful) { + if (is_watch(mtmp->data) && mtmp->mpeaceful) { ct++; if(cansee(mtmp->mx, mtmp->my) && mtmp->mcanmove) { if (distu(mtmp->mx, mtmp->my) == 2) nct++; @@ -3157,9 +3155,8 @@ pacify_guards() for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if (mtmp->data == &mons[PM_WATCHMAN] || - mtmp->data == &mons[PM_WATCH_CAPTAIN]) - mtmp->mpeaceful = 1; + if (is_watch(mtmp->data)) + mtmp->mpeaceful = 1; } } diff --git a/src/monmove.c b/src/monmove.c index a7a3ca46f..9b4d47cce 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -50,6 +50,18 @@ boolean for_unlocking; /* true => credit card ok, false => not ok */ return m_carrying(mon, SKELETON_KEY) || m_carrying(mon, LOCK_PICK); } +void +mon_yells(mon, shout) +struct monst *mon; +const char *shout; +{ + if (canspotmon(mon)) + pline("%s yells:", Amonnam(mon)); + else + You_hear("someone yell:"); + verbalize(shout); +} + STATIC_OVL void watch_on_duty(mtmp) register struct monst *mtmp; @@ -63,13 +75,11 @@ register struct monst *mtmp; (levl[x][y].doormask & D_LOCKED)) { if(couldsee(mtmp->mx, mtmp->my)) { - - pline("%s yells:", Amonnam(mtmp)); if(levl[x][y].looted & D_WARNED) { - verbalize("Halt, thief! You're under arrest!"); + mon_yells(mtmp, "Halt, thief! You're under arrest!"); (void) angry_guards(!!Deaf); } else { - verbalize("Hey, stop picking that lock!"); + mon_yells(mtmp, "Hey, stop picking that lock!"); levl[x][y].looted |= D_WARNED; } stop_occupation(); @@ -400,7 +410,7 @@ register struct monst *mtmp; } /* the watch will look around and see if you are up to no good :-) */ - if (mdat == &mons[PM_WATCHMAN] || mdat == &mons[PM_WATCH_CAPTAIN]) + if (is_watch(mdat)) watch_on_duty(mtmp); else if (is_mind_flayer(mdat) && !rn2(20)) { @@ -1295,15 +1305,10 @@ register int x, y; { int levtyp = levl[x][y].typ; - if (levtyp == DRAWBRIDGE_UP) { - /* use underlying terrain in front of closed drawbridge */ - switch (levl[x][y].drawbridgemask & DB_UNDER) { - case DB_MOAT: levtyp = MOAT; break; - case DB_LAVA: levtyp = LAVAPOOL; break; - case DB_ICE: levtyp = ICE; break; - case DB_FLOOR: levtyp = ROOM; break; - } - } + /* use underlying terrain in front of closed drawbridge */ + if (levtyp == DRAWBRIDGE_UP) + levtyp = db_under_typ(levl[x][y].drawbridgemask); + return (boolean)(ACCESSIBLE(levtyp) && !closed_door(x, y)); } diff --git a/src/mthrowu.c b/src/mthrowu.c index 1ff6cddc3..e8095332c 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -421,7 +421,8 @@ struct obj *obj; /* missile (or stack providing it) */ (void) drop_throw(singleobj, hitu, u.ux, u.uy); break; } - } else if (!range /* reached end of path */ + } + if (!range /* reached end of path */ /* missile hits edge of screen */ || !isok(bhitpos.x+dx,bhitpos.y+dy) /* missile hits the wall */ @@ -440,7 +441,7 @@ struct obj *obj; /* missile (or stack providing it) */ tmp_at(bhitpos.x, bhitpos.y); delay_output(); } - if (isok(bhitpos.x, bhitpos.y)) tmp_at(bhitpos.x, bhitpos.y); + tmp_at(bhitpos.x, bhitpos.y); delay_output(); tmp_at(DISP_END, 0); diff --git a/src/options.c b/src/options.c index 1e3d18338..8a5e65254 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 options.c $NHDT-Date: 1429953065 2015/04/25 09:11:05 $ $NHDT-Branch: master $:$NHDT-Revision: 1.186 $ */ +/* NetHack 3.5 options.c $NHDT-Date: 1430441885 2015/05/01 00:58:05 $ $NHDT-Branch: master $:$NHDT-Revision: 1.191 $ */ /* NetHack 3.5 options.c $Date: 2012/04/09 02:56:30 $ $Revision: 1.153 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -283,7 +283,7 @@ static struct Comp_Opt 4, SET_IN_FILE }, { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE }, - { "menu_headings", "bold, inverse, or underline headings", 9, SET_IN_GAME }, + { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME }, { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE }, { "menu_invert_page", "invert all items on this page of a menu", 4, SET_IN_FILE }, @@ -498,6 +498,13 @@ STATIC_OVL boolean FDECL(is_wc2_option, (const char *)); STATIC_OVL boolean FDECL(wc2_supported, (const char *)); STATIC_DCL void FDECL(remove_autopickup_exception, (struct autopickup_exception *)); STATIC_OVL int FDECL(count_ape_maps, (int *, int *)); +STATIC_DCL const char *FDECL(clr2colorname, (int)); +STATIC_DCL const char *FDECL(attr2attrname, (int)); +STATIC_DCL int NDECL(query_color); +STATIC_DCL int FDECL(query_attr, (const char *)); +STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int)); +STATIC_DCL void FDECL(free_one_menu_coloring, (int)); +STATIC_DCL int NDECL(count_menucolors); void @@ -1163,13 +1170,117 @@ static const struct { {"inverse", ATR_INVERSE} }; +const char * +clr2colorname(clr) +int clr; +{ + int i; + for (i = 0; i < SIZE(colornames); i++) + if (colornames[i].color == clr) + return colornames[i].name; + return NULL; +} + +const char * +attr2attrname(attr) +int attr; +{ + int i; + for (i = 0; i < SIZE(attrnames); i++) + if (attrnames[i].attr == attr) + return attrnames[i].name; + return NULL; +} + +int +query_color() +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *)0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + for (i = 0; i < SIZE(colornames); i++) { + if (!strcmp(colornames[i].name, "grey")) continue; + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name, MENU_UNSELECTED); + } + end_menu(tmpwin, "Pick a color"); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + i = colornames[picks->item.a_int - 1].color; + free((genericptr_t)picks); + return i; + } + return -1; +} + +int +query_attr(prompt) +const char *prompt; +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *)0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + for (i = 0; i < SIZE(attrnames); i++) { + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + attrnames[i].attr, attrnames[i].name, MENU_UNSELECTED); + } + end_menu(tmpwin, prompt ? prompt : "Pick an attribute"); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + i = attrnames[picks->item.a_int - 1].attr; + free((genericptr_t)picks); + return i; + } + return -1; +} + +boolean +add_menu_coloring_parsed(str, c, a) +char *str; +int c, a; +{ + struct menucoloring *tmp; + if (!str) return FALSE; + tmp = (struct menucoloring *)alloc(sizeof(struct menucoloring)); + tmp->match = regex_init(); + if (!regex_compile(str, tmp->match)) { + static const char *re_error = "Menucolor regex error"; + if (!iflags.window_inited) + raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->match)); + else + pline("%s: %s", re_error, regex_error_desc(tmp->match)); + wait_synch(); + free(tmp); + return FALSE; + } else { + tmp->next = menu_colorings; + tmp->origstr = dupstr(str); + tmp->color = c; + tmp->attr = a; + menu_colorings = tmp; + return TRUE; + } +} + /* parse '"regex_string"=color&attr' and add it to menucoloring */ boolean add_menu_coloring(str) char *str; { int i, c = NO_COLOR, a = ATR_NONE; - struct menucoloring *tmp; char *tmps, *cs = strchr(str, '='); if (!cs || !str) return FALSE; @@ -1212,20 +1323,7 @@ char *str; } } - tmp = (struct menucoloring *)alloc(sizeof(struct menucoloring)); - tmp->match = regex_init(); - if (!regex_compile(tmps, tmp->match)) { - raw_printf("\nMenucolor regex error: %s\n", regex_error_desc(tmp->match)); - wait_synch(); - free(tmp); - return FALSE; - } else { - tmp->next = menu_colorings; - tmp->color = c; - tmp->attr = a; - menu_colorings = tmp; - return TRUE; - } + return add_menu_coloring_parsed(tmps, c, a); } boolean @@ -1252,11 +1350,48 @@ free_menu_coloring() while (tmp) { struct menucoloring *tmp2 = tmp->next; regex_free(tmp->match); + free(tmp->origstr); free(tmp); tmp = tmp2; } } +void +free_one_menu_coloring(idx) +int idx; /* 0 .. */ +{ + struct menucoloring *tmp = menu_colorings; + struct menucoloring *prev = NULL; + + while (tmp) { + if (idx == 0) { + struct menucoloring *next = tmp->next; + regex_free(tmp->match); + free(tmp->origstr); + free(tmp); + if (prev) prev->next = next; + else menu_colorings = next; + return; + } + idx--; + prev = tmp; + tmp = tmp->next; + } +} + + +int +count_menucolors() +{ + int count = 0; + struct menucoloring *tmp = menu_colorings; + + while (tmp) { + count++; + tmp = tmp->next; + } + return count; +} void parseoptions(opts, tinitial, tfrom_file) @@ -2618,14 +2753,12 @@ goodfruit: else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return; } - if (!strcmpi(opts,"bold")) - iflags.menu_headings = ATR_BOLD; - else if (!strcmpi(opts,"inverse")) - iflags.menu_headings = ATR_INVERSE; - else if (!strcmpi(opts,"underline")) - iflags.menu_headings = ATR_ULINE; - else - badoption(opts); + for (i = 0; i < SIZE(attrnames); i++) + if (!strcmpi(opts, attrnames[i].name)) { + iflags.menu_headings = attrnames[i].attr; + return; + } + badoption(opts); return; } @@ -3087,6 +3220,11 @@ doset() doset_add_menu(tmpwin, compopt[i].name, (pass == DISP_IN_GAME) ? 0 : indexoffset); } + any.a_int = -3; + Sprintf(buf2, "(%d currently set)", count_menucolors()); + Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : " ", + "menucolors", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); #ifdef STATUS_VIA_WINDOWPORT # ifdef STATUS_HILITES any.a_int = -2; @@ -3138,6 +3276,10 @@ doset() } else # endif #endif + if (opt_indx == -4) { + (void)special_handling("menucolors", + setinitial, fromfile); + } else if (opt_indx < boolcount) { /* boolean option */ Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "", @@ -3177,6 +3319,57 @@ doset() return 0; } +int +handle_add_list_remove(optname, numtotal) +char *optname; +int numtotal; +{ + winid tmpwin; + anything any; + int i, pick_cnt, pick_idx, opt_idx; + menu_item *pick_list = (menu_item *)0; + static const struct action { + char letr; + const char *desc; + } action_titles[] = { + { 'a', "add new %s" }, /* [0] */ + { 'l', "list %s" }, /* [1] */ + { 'r', "remove existing %s" }, /* [2] */ + { 'x', "exit this menu" }, /* [3] */ + }; + + opt_idx = 0; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + for (i = 0; i < SIZE(action_titles); i++) { + char tmpbuf[BUFSZ]; + any.a_int++; + /* omit list and remove if there aren't any yet */ + if (!numtotal && (i == 1 || i == 2)) continue; + Sprintf(tmpbuf, action_titles[i].desc, (i == 1) ? makeplural(optname) : optname); + add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, + 0, ATR_NONE, tmpbuf, +#if 0 /* this ought to work but doesn't... */ + (action_titles[i].letr == 'x') ? MENU_SELECTED : +#endif + MENU_UNSELECTED); + } + end_menu(tmpwin, "Do what?"); + if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) { + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { + opt_idx = pick_list[pick_idx].item.a_int - 1; + } + free((genericptr_t)pick_list); + pick_list = (menu_item *)0; + } + destroy_nhwindow(tmpwin); + + if (pick_cnt < 1) opt_idx = 3; /* none selected, exit menu */ + return opt_idx; +} + + struct symsetentry *symset_list = 0; /* files.c will populate this with list of available sets */ @@ -3454,79 +3647,70 @@ boolean setinitial,setfromfile; } destroy_nhwindow(tmpwin); } else if (!strcmp("menu_headings", optname)) { - static const char *mhchoices[3] = {"bold", "inverse", "underline"}; - const char *npletters = "biu"; - menu_item *mode_pick = (menu_item *)0; - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin); - any = zeroany; - for (i = 0; i < SIZE(mhchoices); i++) { - any.a_int = i + 1; - add_menu(tmpwin, NO_GLYPH, &any, npletters[i], 0, - ATR_NONE, mhchoices[i], MENU_UNSELECTED); - } - end_menu(tmpwin, "How to highlight menu headings:"); - if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) { - int mode = mode_pick->item.a_int - 1; - switch(mode) { - case 2: - iflags.menu_headings = ATR_ULINE; - break; - case 0: - iflags.menu_headings = ATR_BOLD; - break; - case 1: - default: - iflags.menu_headings = ATR_INVERSE; + int mhattr = query_attr("How to highlight menu headings:"); + if (mhattr != -1) iflags.menu_headings = mhattr; + } else if (!strcmp("menucolors", optname)) { + int opt_idx, nmc, mcclr, mcattr; + char mcbuf[BUFSZ]; +menucolors_again: + nmc = count_menucolors(); + opt_idx = handle_add_list_remove("menucolor", nmc); + if (opt_idx == 3) { + ; /* done--fall through to function exit */ + } else if (opt_idx == 0) { /* add new */ + getlin("What new menucolor pattern?", mcbuf); + if (*mcbuf == '\033' || !*mcbuf) goto menucolors_again; + mcclr = query_color(); + if (mcclr == -1) goto menucolors_again; + mcattr = query_attr(NULL); + if (mcattr == -1) goto menucolors_again; + if (!add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) { + pline("Error adding the menu color."); + wait_synch(); + goto menucolors_again; } - free((genericptr_t)mode_pick); - } - destroy_nhwindow(tmpwin); - } else if (!strcmp("autopickup_exception", optname)) { - int pick_cnt, pick_idx, opt_idx, pass; - int totalapes = 0, numapes[2] = {0,0}; - menu_item *pick_list = (menu_item *)0; - char apebuf[1+BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */ - struct autopickup_exception *ape; - static const struct ape_action { - char letr; - const char *desc; - } action_titles[] = { - { 'a', "add new autopickup exception" }, /* [0] */ - { 'l', "list autopickup exceptions" }, /* [1] */ - { 'r', "remove existing autopickup exception" }, /* [2] */ - { 'x', "exit this menu" }, /* [3] */ - }; - - ape_again: - opt_idx = 0; - totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]); - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin); - any = zeroany; - for (i = 0; i < SIZE(action_titles); i++) { - any.a_int++; - /* omit list and remove if there aren't any yet */ - if (!totalapes && (i == 1 || i == 2)) continue; - add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, - 0, ATR_NONE, action_titles[i].desc, -#if 0 /* this ought to work but doesn't... */ - (action_titles[i].letr == 'x') ? MENU_SELECTED : -#endif - MENU_UNSELECTED); - } - end_menu(tmpwin, "Do what?"); - if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) { - for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { - opt_idx = pick_list[pick_idx].item.a_int - 1; + } else { /* list or remove */ + int pick_idx, pick_cnt; + int mc_idx; + menu_item *pick_list = (menu_item *)0; + struct menucoloring *tmp = menu_colorings; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + mc_idx = 0; + while (tmp) { + const char *sattr = attr2attrname(tmp->attr); + const char *sclr = clr2colorname(tmp->color); + any.a_int = (++mc_idx); + Sprintf(mcbuf, "\"%s\"=%s%s%s", tmp->origstr, sclr, + (tmp->attr != ATR_NONE) ? " & " : "", + (tmp->attr != ATR_NONE) ? sattr : ""); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf, MENU_UNSELECTED); + tmp = tmp->next; + } + Sprintf(mcbuf, "%s menu colors", + (opt_idx == 1) ? "List of" : "Remove which"); + end_menu(tmpwin, mcbuf); + pick_cnt = select_menu(tmpwin, + (opt_idx == 1) ? PICK_NONE : PICK_ANY, + &pick_list); + if (pick_cnt > 0) { + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) + free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1 - pick_idx); } free((genericptr_t)pick_list); pick_list = (menu_item *)0; + destroy_nhwindow(tmpwin); + if (pick_cnt >= 0) goto menucolors_again; } - destroy_nhwindow(tmpwin); - - if (pick_cnt < 1 || opt_idx == 3) { + } else if (!strcmp("autopickup_exception", optname)) { + int opt_idx, pass, totalapes = 0, numapes[2] = {0,0}; + char apebuf[1+BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */ + struct autopickup_exception *ape; +ape_again: + totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]); + opt_idx = handle_add_list_remove("autopickup exception", totalapes); + if (opt_idx == 3) { ; /* done--fall through to function exit */ } else if (opt_idx == 0) { /* add new */ getlin("What new autopickup exception pattern?", &apebuf[1]); @@ -3546,6 +3730,8 @@ boolean setinitial,setfromfile; goto ape_again; } } else { /* list or remove */ + int pick_idx, pick_cnt; + menu_item *pick_list = (menu_item *)0; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) { @@ -3870,10 +4056,7 @@ char *buf; else if (!strcmp(optname, "menu_invert_all")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_headings")) { - Sprintf(buf, "%s", (iflags.menu_headings == ATR_BOLD) ? - "bold" : (iflags.menu_headings == ATR_INVERSE) ? - "inverse" : (iflags.menu_headings == ATR_ULINE) ? - "underline" : "unknown"); + Sprintf(buf, "%s", attr2attrname(iflags.menu_headings)); } else if (!strcmp(optname, "menu_invert_page")) Sprintf(buf, "%s", to_be_done); diff --git a/src/pickup.c b/src/pickup.c index 3866c2287..4277a3ea2 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 pickup.c $NHDT-Date: 1426558927 2015/03/17 02:22:07 $ $NHDT-Branch: master $:$NHDT-Revision: 1.131 $ */ +/* NetHack 3.5 pickup.c $NHDT-Date: 1430122768 2015/04/27 08:19:28 $ $NHDT-Branch: master $:$NHDT-Revision: 1.150 $ */ /* NetHack 3.5 pickup.c $Date: 2012/02/16 03:01:38 $ $Revision: 1.123 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -308,8 +308,10 @@ struct obj *obj; return (obj->quan >= val_for_n_or_more); } -/* List of valid menu classes for query_objlist() and allow_category callback */ -static char valid_menu_classes[MAXOCLASSES + 2]; +/* list of valid menu classes for query_objlist() and allow_category callback + (with room for all object classes, 'u'npaid, BUCX, and terminator) */ +static char valid_menu_classes[MAXOCLASSES + 1 + 4 + 1]; +static boolean class_filter, bucx_filter, shop_filter; void add_valid_menu_class(c) @@ -317,10 +319,19 @@ int c; { static int vmc_count = 0; - if (c == 0) /* reset */ - vmc_count = 0; - else - valid_menu_classes[vmc_count++] = (char)c; + if (c == 0) { /* reset */ + vmc_count = 0; + class_filter = bucx_filter = shop_filter = FALSE; + } else { + valid_menu_classes[vmc_count++] = (char)c; + /* categorize the new class */ + switch (c) { + case 'B': case 'U': case 'C': /*FALLTHRU*/ + case 'X': bucx_filter = TRUE; break; + case 'u': shop_filter = TRUE; break; + default: class_filter = TRUE; break; + } + } valid_menu_classes[vmc_count] = '\0'; } @@ -345,26 +356,47 @@ boolean allow_category(obj) struct obj *obj; { - if (Role_if(PM_PRIEST)) obj->bknown = TRUE; - - /* if obj's class is in the list, then obj is acceptable */ - if (index(valid_menu_classes, obj->oclass)) - return TRUE; /* unpaid and BUC checks don't apply to coins */ if (obj->oclass == COIN_CLASS) + return index(valid_menu_classes, COIN_CLASS) ? TRUE : FALSE; + + if (Role_if(PM_PRIEST)) obj->bknown = TRUE; + /* + * There are three types of filters possible and the first and + * third can have more than one entry: + * 1) object class (armor, potion, &c); + * 2) unpaid shop item; + * 3) bless/curse state (blessed, uncursed, cursed, BUC-unknown). + * When only one type is present, the situation is simple: + * to be accepted, obj's status must match one of the entries. + * When more than one type is present, the obj will now only + * be accepted when it matches one entry of each type. + * So ?!B will accept blessed scrolls or potions, and [u will + * accept unpaid armor. (In 3.4.3, an object was accepted by + * this filter if it met any entry of any type, so ?!B resulted + * in accepting all scrolls and potions regardless of bless/curse + * state plus all blessed non-scroll, non-potion objects.) + */ + /* if class is expected but obj's class is not in the list, reject */ + if (class_filter && !index(valid_menu_classes, obj->oclass)) + return FALSE; + /* if unpaid is expected and obj isn't unpaid, reject (treat a container + holding any unpaid object as unpaid even if isn't unpaid itself) */ + if (shop_filter && !obj->unpaid + && !(Has_contents(obj) && count_unpaid(obj) > 0)) return FALSE; - /* check for unpaid item */ - if (index(valid_menu_classes, 'u') && - (obj->unpaid || (Has_contents(obj) && count_unpaid(obj)))) - return TRUE; /* check for particular bless/curse state */ - if (!obj->bknown ? index(valid_menu_classes, 'X') : /* unknown BUC state */ - obj->blessed ? index(valid_menu_classes, 'B') : /* known blessed */ - !obj->cursed ? index(valid_menu_classes, 'U') : /* known uncursed */ - index(valid_menu_classes, 'C')) /* known cursed */ - return TRUE; - /* obj isn't acceptable */ - return FALSE; + if (bucx_filter) { + /* first categorize this object's bless/curse state */ + char bucx = !obj->bknown ? 'X' + : obj->blessed ? 'B' : obj->cursed ? 'C' : 'U'; + + /* if its category is not in the list, reject */ + if (!index(valid_menu_classes, bucx)) + return FALSE; + } + /* obj didn't fail any of the filter checks, so accept */ + return TRUE; } #if 0 /* not used */ diff --git a/src/sit.c b/src/sit.c index fd4207af5..9f2c1d9e6 100644 --- a/src/sit.c +++ b/src/sit.c @@ -15,6 +15,7 @@ take_gold() nobj = otmp->nobj; if (otmp->oclass == COIN_CLASS) { lost_money = 1; + remove_worn_item(otmp, FALSE); delobj(otmp); } } diff --git a/src/spell.c b/src/spell.c index 3de41b5ec..bccb6a590 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1401,7 +1401,7 @@ int *spell_no; Sprintf(buf, "Name\tLevel\tCategory\tFail\tRetention"); fmt = "%s\t%-d\t%s\t%-d%%\t%s"; } - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, buf, MENU_UNSELECTED); for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) { splnum = !spl_orderindx ? i : spl_orderindx[i]; Sprintf(buf, fmt, diff --git a/src/uhitm.c b/src/uhitm.c index 19361c261..a7ecbed9b 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -516,8 +516,7 @@ int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ anger_guards = (mon->mpeaceful && (mon->ispriest || mon->isshk || - mon->data == &mons[PM_WATCHMAN] || - mon->data == &mons[PM_WATCH_CAPTAIN])); + is_watch(mon->data))); result = hmon_hitmon(mon, obj, thrown); if (mon->ispriest && !rn2(2)) ghod_hitsu(mon); if (anger_guards) (void)angry_guards(!!Deaf); diff --git a/src/zap.c b/src/zap.c index bec35c8dc..9d74271dc 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 zap.c $NHDT-Date: 1428207622 2015/04/05 04:20:22 $ $NHDT-Branch: nhmall-booktribute $:$NHDT-Revision: 1.215 $ */ +/* NetHack 3.5 zap.c $NHDT-Date: 1430355196 2015/04/30 00:53:16 $ $NHDT-Branch: master $:$NHDT-Revision: 1.218 $ */ /* NetHack 3.5 zap.c $Date: 2013/11/05 00:57:56 $ $Revision: 1.183 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2870,9 +2870,11 @@ int skill; else if (dex < 8) hit_bon -= 1; else if (dex < 14) - hit_bon -= 0; /* Will change when print stuff below removed */ + /* Will change when print stuff below removed */ + hit_bon -= 0; else - hit_bon += dex - 14; /* Even increment for dextrous heroes (see weapon.c abon) */ + /* Even increment for dextrous heroes (see weapon.c abon) */ + hit_bon += dex - 14; return hit_bon; } diff --git a/win/X11/winX.c b/win/X11/winX.c index 85852598d..203822595 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 winX.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* NetHack 3.5 winX.c $NHDT-Date: 1430040327 2015/04/26 09:25:27 $ $NHDT-Branch: master $:$NHDT-Revision: 1.28 $ */ /* NetHack 3.5 winX.c $Date: 2012/01/24 04:26:26 $ $Revision: 1.27 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -1532,6 +1532,7 @@ static char yn_esc_map; /* ESC maps to this char. */ static Widget yn_popup; /* popup for the yn fuction (created once) */ static Widget yn_label; /* label for yn function (created once) */ static boolean yn_getting_num; /* TRUE if accepting digits */ +static boolean yn_preserve_case; /* default is to force yn to lower case */ static int yn_ndigits; /* digit count */ static long yn_val; /* accumulated value */ @@ -1604,7 +1605,8 @@ yn_key(w, event, params, num_params) if (!yn_choices) { /* accept any input */ yn_return = ch; } else { - ch = lowc(ch); /* move to lower case */ + if (!yn_preserve_case) + ch = lowc(ch); /* move to lower case */ if (ch == '\033') { yn_getting_num = FALSE; @@ -1664,6 +1666,7 @@ X11_yn_function(ques, choices, def) yn_choices = choices; /* set up globals for callback to use */ yn_def = def; + yn_preserve_case = !choices; /* preserve when arbitrary response allowed */ /* * This is sort of a kludge. There are quite a few places in the main @@ -1679,6 +1682,14 @@ X11_yn_function(ques, choices, def) char *cb, choicebuf[QBUFSZ]; Strcpy(choicebuf, choices); /* anything beyond is hidden */ + /* default when choices are present is to force yn answer to + lowercase unless one or more choices are explicitly uppercase; + check this before stripping the hidden choices */ + for (cb = choicebuf; *cb; ++cb) + if ('A' <= *cb && *cb <= 'Z') { + yn_preserve_case = TRUE; + break; + } if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0'; /* ques [choices] (def) */ if ((int)(1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ) diff --git a/win/tty/topl.c b/win/tty/topl.c index cc4df15d5..dbeea75dd 100644 --- a/win/tty/topl.c +++ b/win/tty/topl.c @@ -1,4 +1,4 @@ -/* NetHack 3.5 topl.c $NHDT-Date: 1425081315 2015/02/27 23:55:15 $ $NHDT-Branch: master $:$NHDT-Revision: 1.24 $ */ +/* NetHack 3.5 topl.c $NHDT-Date: 1430040322 2015/04/26 09:25:22 $ $NHDT-Branch: master $:$NHDT-Revision: 1.29 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -333,7 +333,7 @@ char def; { register char q; char rtmp[40]; - boolean digit_ok, allow_num; + boolean digit_ok, allow_num, preserve_case = FALSE; struct WinDesc *cw = wins[WIN_MESSAGE]; boolean doprev = 0; char prompt[BUFSZ]; @@ -347,6 +347,14 @@ char def; allow_num = (index(resp, '#') != 0); Strcpy(respbuf, resp); + /* normally we force lowercase, but if any uppercase letters + are present in the allowed response, preserve case; + check this before stripping the hidden choices */ + for (rb = respbuf; *rb; ++rb) + if ('A' <= *rb && *rb <= 'Z') { + preserve_case = TRUE; + break; + } /* any acceptable responses that follow aren't displayed */ if ((rb = index(respbuf, '\033')) != 0) *rb = '\0'; (void)strncpy(prompt, query, QBUFSZ-1); @@ -358,13 +366,16 @@ char def; Strcat(prompt, " "); pline("%s", prompt); } else { + /* no restriction on allowed response, so always preserve case */ + /* preserve_case = TRUE; -- moot since we're jumping to the end */ pline("%s ", query); q = readchar(); goto clean_up; } do { /* loop until we get valid input */ - q = lowc(readchar()); + q = readchar(); + if (!preserve_case) q = lowc(q); if (q == '\020') { /* ctrl-P */ if (iflags.prevmsg_window != 's') { int sav = ttyDisplay->inread; @@ -422,7 +433,8 @@ char def; q = '#'; } do { /* loop until we get a non-digit */ - z = lowc(readchar()); + z = readchar(); + if (!preserve_case) z = lowc(z); if (digit(z)) { value = (10 * value) + (z - '0'); if (value < 0) break; /* overflow: try again */