diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 9466f72e5..c700b8a0d 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2097,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 7855d595b..2b7e2deba 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -2530,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 991c41011..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 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 7b808f30a..d11d78e6b 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*)); @@ -1373,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 9f4c6069b..6113343e3 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -102,6 +102,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 873ae53df..74b7e255c 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1170,8 +1170,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/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 d57448316..c27ec4f0d 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -1867,9 +1867,10 @@ donamelevel() if (!(mptr = find_mapseen(&u.uz))) return 0; if (mptr->custom) { - char qbuf[BUFSZ]; - Sprintf(qbuf, "Replace annotation \"%s\" with?", mptr->custom); - getlin(qbuf, nbuf); + 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; @@ -2205,12 +2206,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 f2ed4bedd..5af55af58 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -168,8 +168,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 b1aee908b..176d2b8d2 100644 --- a/src/invent.c +++ b/src/invent.c @@ -395,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"); @@ -411,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 */ diff --git a/src/mkmaze.c b/src/mkmaze.c index 1dd82a297..5ee58dbd2 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1156,12 +1156,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 c961cd53a..65ced725a 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2193,7 +2193,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 d8e138288..aba954425 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: 1430365894 2015/04/30 03:51:34 $ $NHDT-Branch: master $:$NHDT-Revision: 1.168 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1849,7 +1849,7 @@ register struct monst *mtmp; u.uy = mtmp->my; u.uswallow = 0; u.uswldtim = 0; - if (Punished) placebc(); + if (BALL_IN_MON) placebc(); vision_full_recalc = 1; docrt(); } @@ -3116,9 +3116,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++; @@ -3156,9 +3154,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 ccf0f9e17..b4c814fee 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: 1430192504 2015/04/28 03:41:44 $ $NHDT-Branch: master $:$NHDT-Revision: 1.189 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -282,7 +282,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 }, @@ -497,6 +497,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, (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 @@ -1162,13 +1169,116 @@ 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) +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; @@ -1211,20 +1321,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 @@ -1251,11 +1348,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) @@ -2617,14 +2751,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; } @@ -3086,6 +3218,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; @@ -3137,6 +3274,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 ? "!" : "", @@ -3176,6 +3317,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 */ @@ -3453,79 +3645,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]); @@ -3545,6 +3728,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) { @@ -3869,10 +4054,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 5e4cf3096..ed18d3eba 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 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -307,8 +307,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) @@ -316,10 +318,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'; } @@ -344,26 +355,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 a2a238dc4..7b2981cd5 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 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2869,9 +2869,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; }