From 6857372f451d2c4ecfbf52c6d1f098e881ac6298 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 24 Dec 2017 14:00:49 -0800 Subject: [PATCH 01/11] fix #H6624 - missile miss message redundancy Excess verbosity for multi-shot throwing/shooting by monsters. The Green-elf shoots 2 elven arrows. You are almost hit by the 1st elven arrow. The 1st elven arrow misses. You are almost hit by the 2nd elven arrow. The 2nd elven arrow misses. Just give one or the other of the miss messages. If it reaches the hero's location, give the first. If it lands somewhere else, give the second. (It might be possible to get both if hero is displaced and the monster thinks he/she is behind his/her actual location. I'm not sure.) Also, only say "you are almost hit" if it is true: the dieroll nearly got past your armor. Otherwise, say "The Nth arrow misses you." --- doc/fixes36.1 | 1 + src/mthrowu.c | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 69724485b..c27a3b795 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -492,6 +492,7 @@ hero poly'd into paper or straw golem reverts to human if burned up even when Unchanging without any explanation given hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' +make multi-shot missiles fired by monsters be less verbose when they miss Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mthrowu.c b/src/mthrowu.c index 3d2830b96..6806feef4 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mthrowu.c $NHDT-Date: 1446887531 2015/11/07 09:12:11 $ $NHDT-Branch: master $:$NHDT-Revision: 1.63 $ */ +/* NetHack 3.6 mthrowu.c $NHDT-Date: 1514152830 2017/12/24 22:00:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.73 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,6 +26,7 @@ STATIC_OVL NEARDATA const char *breathwep[] = { }; extern boolean notonhead; /* for long worms */ +STATIC_VAR int mesg_given; /* for m_throw()/thitu() 'miss' message */ /* hero is hit by something other than a monster */ int @@ -37,7 +38,7 @@ const char *name; /* if null, then format `*objp' */ struct obj *obj = objp ? *objp : 0; const char *onm, *knm; boolean is_acid; - int kprefix = KILLED_BY_AN; + int kprefix = KILLED_BY_AN, dieroll; char onmbuf[BUFSZ], knmbuf[BUFSZ]; if (!name) { @@ -59,10 +60,15 @@ const char *name; /* if null, then format `*objp' */ : an(name); is_acid = (obj && obj->otyp == ACID_VENOM); - if (u.uac + tlev <= rnd(20)) { - if (Blind || !flags.verbose) + if (u.uac + tlev <= (dieroll = rnd(20))) { + ++mesg_given; + if (Blind || !flags.verbose) { pline("It misses."); - else + } else if (u.uac + tlev <= dieroll - 2) { + if (onm != onmbuf) + Strcpy(onmbuf, onm); /* [modifiable buffer for upstart()] */ + pline("%s %s you.", upstart(onmbuf), vtense(onmbuf, "miss")); + } else You("are almost hit by %s.", onm); return 0; } else { @@ -518,6 +524,7 @@ struct obj *obj; /* missile (or stack providing it) */ (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } + mesg_given = 0; /* a 'missile misses' message has not yet been shown */ /* Note: drop_throw may destroy singleobj. Since obj must be destroyed * early to avoid the dagger bug, anyone who modifies this code should @@ -646,8 +653,10 @@ struct obj *obj; /* missile (or stack providing it) */ if (!range /* reached end of path */ || MT_FLIGHTCHECK(FALSE)) { if (singleobj) { /* hits_bars might have destroyed it */ - if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y) - || (archer && canseemon(archer)))) + if (m_shot.n > 1 + && (!mesg_given || bhitpos.x != u.ux || bhitpos.y != u.uy) + && (cansee(bhitpos.x, bhitpos.y) + || (archer && canseemon(archer)))) pline("%s misses.", The(mshot_xname(singleobj))); (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); } @@ -659,6 +668,7 @@ struct obj *obj; /* missile (or stack providing it) */ tmp_at(bhitpos.x, bhitpos.y); delay_output(); tmp_at(DISP_END, 0); + mesg_given = 0; /* reset */ if (blindinc) { u.ucreamed += blindinc; From 860cdf6625bdbbc4133f31084d27140a7ae25fd8 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 28 Dec 2017 15:40:11 -0800 Subject: [PATCH 02/11] playing music while impaired Newsgroup discussion mentioned that it was possible to open the castle drawbridge with musical notes even while confused. There was already some handling for confusion: improvisation treats magical instruments as their mundane equivalents. This takes if farther: when stunned or confused or hallucinating you'll always improvise instead of being given a chance to choose notes. Being stunned now behaves the same as being confused in regards to magical instruments (possibly/probably it should prevent playing music altogether). Hallucination gives different feedback at start but still allows magical playing. --- doc/fixes36.1 | 1 + src/music.c | 138 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 83 insertions(+), 56 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c27a3b795..05216acec 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -493,6 +493,7 @@ hero poly'd into paper or straw golem reverts to human if burned up even when hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' make multi-shot missiles fired by monsters be less verbose when they miss +can no longer play controlled notes on musical instrument if impaired Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/music.c b/src/music.c index 75ab39d4d..68c9e97d5 100644 --- a/src/music.c +++ b/src/music.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 music.c $NHDT-Date: 1452660194 2016/01/13 04:43:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.42 $ */ +/* NetHack 3.6 music.c $NHDT-Date: 1514504228 2017/12/28 23:37:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.46 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,7 +6,7 @@ * This file contains the different functions designed to manipulate the * musical instruments and their various effects. * - * Actually the list of instruments / effects is : + * The list of instruments / effects is : * * (wooden) flute may calm snakes if player has enough dexterity * magic flute may put monsters to sleep: area of effect depends @@ -450,8 +450,7 @@ STATIC_OVL int do_improvisation(instr) struct obj *instr; { - int damage, do_spec = !Confusion; -#if defined(MAC) || defined(AMIGA) || defined(VPIX_MUSIC) || defined(PCMUSIC) + int damage, mode, do_spec = !(Stunned || Confusion); struct obj itmp; itmp = *instr; @@ -476,23 +475,55 @@ struct obj *instr; #ifdef PCMUSIC pc_speaker(&itmp, "C"); #endif -#endif /* MAC || AMIGA || VPIX_MUSIC || PCMUSIC */ - if (!do_spec) +#define PLAY_NORMAL 0 +#define PLAY_STUNNED 1 +#define PLAY_CONFUSED 2 +#define PLAY_HALLU 4 + mode = PLAY_NORMAL; + if (Stunned) + mode |= PLAY_STUNNED; + if (Confusion) + mode |= PLAY_CONFUSED; + if (Hallucination) + mode |= PLAY_HALLU; + + switch (mode) { + case PLAY_NORMAL: + You("start playing %s.", yname(instr)); + break; + case PLAY_STUNNED: + You("produce an obnoxious droning sound."); + break; + case PLAY_CONFUSED: + You("produce a raucous noise."); + break; + case PLAY_HALLU: + You("produce a kaleidoscopic display of floating butterfiles."); + break; + /* TODO? give some or all of these combinations their own feedback; + hallucination ones should reference senses other than hearing... */ + case PLAY_STUNNED | PLAY_CONFUSED: + case PLAY_STUNNED | PLAY_HALLU: + case PLAY_CONFUSED | PLAY_HALLU: + case PLAY_STUNNED | PLAY_CONFUSED | PLAY_HALLU: + default: pline("What you produce is quite far from music..."); - else - You("start playing %s.", the(xname(instr))); + break; + } +#undef PLAY_NORMAL +#undef PLAY_STUNNED +#undef PLAY_CONFUSED +#undef PLAY_HALLU - switch (instr->otyp) { + switch (itmp.otyp) { /* note: itmp.otyp might differ from instr->otyp */ case MAGIC_FLUTE: /* Make monster fall asleep */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - You("produce %s music.", Hallucination ? "piped" : "soft"); - put_monsters_to_sleep(u.ulevel * 5); - exercise(A_DEX, TRUE); - break; - } /* else FALLTHRU */ + You("produce %s music.", Hallucination ? "piped" : "soft"); + put_monsters_to_sleep(u.ulevel * 5); + exercise(A_DEX, TRUE); + break; case WOODEN_FLUTE: /* May charm snakes */ do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); pline("%s.", Tobjnam(instr, do_spec ? "trill" : "toot")); @@ -502,66 +533,59 @@ struct obj *instr; break; case FIRE_HORN: /* Idem wand of fire */ case FROST_HORN: /* Idem wand of cold */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - if (!getdir((char *) 0)) { - pline("%s.", Tobjnam(instr, "vibrate")); - break; - } else if (!u.dx && !u.dy && !u.dz) { - if ((damage = zapyourself(instr, TRUE)) != 0) { - char buf[BUFSZ]; - - Sprintf(buf, "using a magical horn on %sself", uhim()); - losehp(damage, buf, KILLED_BY); /* fire or frost damage */ - } - } else { - buzz((instr->otyp == FROST_HORN) ? AD_COLD - 1 : AD_FIRE - 1, - rn1(6, 6), u.ux, u.uy, u.dx, u.dy); - } - makeknown(instr->otyp); + if (!getdir((char *) 0)) { + pline("%s.", Tobjnam(instr, "vibrate")); break; - } /* else FALLTHRU */ + } else if (!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(instr, TRUE)) != 0) { + char buf[BUFSZ]; + + Sprintf(buf, "using a magical horn on %sself", uhim()); + losehp(damage, buf, KILLED_BY); /* fire or frost damage */ + } + } else { + buzz((instr->otyp == FROST_HORN) ? AD_COLD - 1 : AD_FIRE - 1, + rn1(6, 6), u.ux, u.uy, u.dx, u.dy); + } + makeknown(instr->otyp); + break; case TOOLED_HORN: /* Awaken or scare monsters */ You("produce a frightful, grave sound."); awaken_monsters(u.ulevel * 30); exercise(A_WIS, FALSE); break; case BUGLE: /* Awaken & attract soldiers */ - You("extract a loud noise from %s.", the(xname(instr))); + You("extract a loud noise from %s.", yname(instr)); awaken_soldiers(&youmonst); exercise(A_WIS, FALSE); break; case MAGIC_HARP: /* Charm monsters */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - pline("%s very attractive music.", Tobjnam(instr, "produce")); - charm_monsters((u.ulevel - 1) / 3 + 1); - exercise(A_DEX, TRUE); - break; - } /* else FALLTHRU */ + pline("%s very attractive music.", Tobjnam(instr, "produce")); + charm_monsters((u.ulevel - 1) / 3 + 1); + exercise(A_DEX, TRUE); + break; case WOODEN_HARP: /* May calm Nymph */ do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); - pline("%s %s.", The(xname(instr)), + pline("%s %s.", Yname2(instr), do_spec ? "produces a lilting melody" : "twangs"); if (do_spec) calm_nymphs(u.ulevel * 3); exercise(A_DEX, TRUE); break; case DRUM_OF_EARTHQUAKE: /* create several pits */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - You("produce a heavy, thunderous rolling!"); - pline_The("entire %s is shaking around you!", - generic_lvl_desc()); - do_earthquake((u.ulevel - 1) / 3 + 1); - /* shake up monsters in a much larger radius... */ - awaken_monsters(ROWNO * COLNO); - makeknown(DRUM_OF_EARTHQUAKE); - break; - } /* else FALLTHRU */ + You("produce a heavy, thunderous rolling!"); + pline_The("entire %s is shaking around you!", generic_lvl_desc()); + do_earthquake((u.ulevel - 1) / 3 + 1); + /* shake up monsters in a much larger radius... */ + awaken_monsters(ROWNO * COLNO); + makeknown(DRUM_OF_EARTHQUAKE); + break; case LEATHER_DRUM: /* Awaken monsters */ You("beat a deafening row!"); awaken_monsters(u.ulevel * 40); @@ -571,7 +595,7 @@ struct obj *instr; break; default: impossible("What a weird instrument (%d)!", instr->otyp); - break; + return 0; } return 2; /* That takes time */ } @@ -598,11 +622,13 @@ struct obj *instr; You("are incapable of playing %s.", the(distant_name(instr, xname))); return 0; } - if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE) { + if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE + && !(Stunned || Confusion || Hallucination)) { c = ynq("Improvise?"); if (c == 'q') goto nevermind; } + if (c == 'n') { if (u.uevent.uheard_tune == 2) c = ynq("Play the passtune?"); From 9f12aeb8abe3b91e9714e72b89d5a754007c211b Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Dec 2017 16:20:05 -0800 Subject: [PATCH 03/11] uncursing prayer vs helm of opposite alignment Implement the suggestion that hero's current god not uncurse a worn helm of opposite alignment when prayer result is fix-worst-cursed-item or uncurse-all-cursed-items since doing so makes it easy for hero to switch to another god. The second boon will still uncurse non-worn helms of opposite alignment since that has no effect on how easy or hard it is for the hero to change alignments. (The first boon only applies to worn items plus luckstones and loadstones; non-worn helms aren't applicable.) --- doc/fixes36.1 | 5 ++++- src/pray.c | 30 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 05216acec..3a3e33464 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -739,11 +739,14 @@ Master Key of Thievery always finds door and chest traps if used to lock or blessed (for non-rogues); player is offered the opportunity to disarm "Elbereth" must now be the only engraved text on a square to function "Elbereth" now erodes based on attacks by the player, not monsters scared -option herecmd_menu to make a mouse click on your character pop up +add option herecmd_menu to make a mouse click on your character pop up a context menu, and extended command #herecmdmenu to do the same change #adjust's behavior when collecting compatible stacks; that used to occur for any #adjust which lacked a split count, now it only happens when 'adjusting' into a stack's own inventory slot +a prayer result which results in uncursing some or all of the hero's items + won't uncurse a worn helm of opposite alignment since that would + facilitate the hero switching to another god by taking it off Platform- and/or Interface-Specific New Features diff --git a/src/pray.c b/src/pray.c index 51dfbb5bb..0e74bb98a 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pray.c $NHDT-Date: 1450577672 2015/12/20 02:14:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.89 $ */ +/* NetHack 3.6 pray.c $NHDT-Date: 1514593198 2017/12/30 00:19:58 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -265,18 +265,21 @@ worst_cursed_item() with taking off a ring or putting on a shield */ if (welded(uwep) && (uright || bimanual(uwep))) { /* weapon */ otmp = uwep; - /* gloves come next, due to rings */ + /* gloves come next, due to rings */ } else if (uarmg && uarmg->cursed) { /* gloves */ otmp = uarmg; - /* then shield due to two handed weapons and spells */ + /* then shield due to two handed weapons and spells */ } else if (uarms && uarms->cursed) { /* shield */ otmp = uarms; - /* then cloak due to body armor */ + /* then cloak due to body armor */ } else if (uarmc && uarmc->cursed) { /* cloak */ otmp = uarmc; } else if (uarm && uarm->cursed) { /* suit */ otmp = uarm; - } else if (uarmh && uarmh->cursed) { /* helmet */ + /* if worn helmet of opposite alignment is making you an adherent + of the current god, he/she/it won't uncurse that for you */ + } else if (uarmh && uarmh->cursed /* helmet */ + && uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT) { otmp = uarmh; } else if (uarmf && uarmf->cursed) { /* boots */ otmp = uarmf; @@ -290,13 +293,13 @@ worst_cursed_item() otmp = uright; } else if (ublindf && ublindf->cursed) { /* eyewear */ otmp = ublindf; /* must be non-blinding lenses */ - /* if weapon wasn't handled above, do it now */ + /* if weapon wasn't handled above, do it now */ } else if (welded(uwep)) { /* weapon */ otmp = uwep; - /* active secondary weapon even though it isn't welded */ + /* active secondary weapon even though it isn't welded */ } else if (uswapwep && uswapwep->cursed && u.twoweap) { otmp = uswapwep; - /* all worn items ought to be handled by now */ + /* all worn items ought to be handled by now */ } else { for (otmp = invent; otmp; otmp = otmp->nobj) { if (!otmp->cursed) @@ -336,9 +339,7 @@ int trouble; break; case TROUBLE_LAVA: You("are back on solid ground."); - /* teleport should always succeed, but if not, - * just untrap them. - */ + /* teleport should always succeed, but if not, just untrap them */ if (!safe_teleds(FALSE)) u.utrap = 0; break; @@ -386,8 +387,7 @@ int trouble; if ((otmp = stuck_ring(uleft, RIN_SUSTAIN_ABILITY)) != 0) { if (otmp == uleft) what = leftglow; - } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) - != 0) { + } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) != 0) { if (otmp == uright) what = rightglow; } @@ -1070,7 +1070,9 @@ aligntyp g_align; else You("are surrounded by %s aura.", an(hcolor(NH_LIGHT_BLUE))); for (otmp = invent; otmp; otmp = otmp->nobj) { - if (otmp->cursed) { + if (otmp->cursed + && (otmp != uarmh /* [see worst_cursed_item()] */ + || uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT)) { if (!Blind) { pline("%s %s.", Yobjnam2(otmp, "softly glow"), hcolor(NH_AMBER)); From 46da4b5e9045e66afef6cf1bbc8a47bc95ddd1e5 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Dec 2017 03:38:29 -0800 Subject: [PATCH 04/11] fix #H6704 - appearance of mimic's replacement If mimics were genocided before loading a special level which contained mimics with specific appearances, whatever random monsters took their place also end up having their intended appearance. monst->cham uses NON_PM rather than 0 to mean "not a shapechanger". --- doc/fixes36.1 | 3 +++ src/sp_lev.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 3a3e33464..77e524f7d 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -494,6 +494,9 @@ hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' make multi-shot missiles fired by monsters be less verbose when they miss can no longer play controlled notes on musical instrument if impaired +if a special level specified the appearance of a mimic and mimics had been + genocided prior to creating the level, whatever random monster took + the mimic's place got its intended appearance Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/sp_lev.c b/src/sp_lev.c index 5981a1b5e..170a6e251 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1513879435 2017/12/21 18:03:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514720301 2017/12/31 11:38:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.94 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1593,7 +1593,7 @@ struct mkroom *croom; * eventually be expanded. */ if (m->appear_as.str - && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham) + && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham >= LOW_PM) && !Protection_from_shape_changers) { int i; From bb9738ff0ea3722b8cdac03bc712260a7cbbfb33 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Dec 2017 17:19:38 -0800 Subject: [PATCH 05/11] special level mimics The special level loader would allow the level description to specify an alternate monster appearance for any type of monster, and if one was specified for a mimic then that mimic would be polymorphed into the appearance instead of masquerading as it. This changes it to only use an appearance for mimics, the Wizard, vampires, and general shapeshifters (chameleons, doppelgangers, sandestins). The mimic case doesn't work as expected: map display shows the symbol for the specified shape but farlook describes it as a mimic. The Wizard case hasn't been tested. The chameleon and vampshifter cases seem to work. It also allowed shapechangers (including vampires) to be given an object or furniture appearance. I didn't try things out to find out what what their behavior would be if/when that happened. I'm not sure whether the farlook issue for mimics-as-monsters is with the pager code or the monster name formatting code. (Possibly the mimic just needs to be flagged has 'hidden' as well has having an alternate appearance.) I'm not going to worry about it since none of our special levels attempt to give mimics a monster shape. Mimicking a monster is a feature for clones of the Wizard, not for mimics, although it might be nice if the latter worked correctly someday. --- include/extern.h | 3 ++- src/mon.c | 5 ++--- src/sp_lev.c | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/include/extern.h b/include/extern.h index 6fd1df12f..6f355fa07 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1513130012 2017/12/13 01:53:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.621 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1514769568 2018/01/01 01:19:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.622 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1397,6 +1397,7 @@ E void FDECL(restore_cham, (struct monst *)); E boolean FDECL(hideunder, (struct monst *)); E void FDECL(hide_monst, (struct monst *)); E void FDECL(mon_animal_list, (BOOLEAN_P)); +E boolean FDECL(validvamp, (struct monst *, int *, int)); E int FDECL(select_newcham_form, (struct monst *)); E void FDECL(mgender_from_permonst, (struct monst *, struct permonst *)); E int FDECL(newcham, diff --git a/src/mon.c b/src/mon.c index a872b93a9..62fb34a8f 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1505266804 2017/09/13 01:40:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.244 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1514769571 2018/01/01 01:19:31 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.246 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,7 +23,6 @@ STATIC_DCL void FDECL(kill_eggs, (struct obj *)); STATIC_DCL int FDECL(pickvampshape, (struct monst *)); STATIC_DCL boolean FDECL(isspecmon, (struct monst *)); STATIC_DCL boolean FDECL(validspecmon, (struct monst *, int)); -STATIC_DCL boolean FDECL(validvamp, (struct monst *, int *, int)); STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int)); STATIC_DCL struct obj *FDECL(make_corpse, (struct monst *, unsigned)); STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *)); @@ -3162,7 +3161,7 @@ int mndx; } /* prevent wizard mode user from specifying invalid vampshifter shape */ -STATIC_OVL boolean +boolean validvamp(mon, mndx_p, monclass) struct monst *mon; int *mndx_p, monclass; diff --git a/src/sp_lev.c b/src/sp_lev.c index 170a6e251..e2d362c6c 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514720301 2017/12/31 11:38:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514769572 2018/01/01 01:19:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1589,11 +1589,14 @@ struct mkroom *croom; mtmp = christen_monst(mtmp, m->name.str); /* - * This is currently hardwired for mimics only. It should - * eventually be expanded. + * This doesn't complain if an attempt is made to give a + * non-mimic/non-shapechanger an appearance or to give a + * shapechanger a non-monster shape, it just refuses to comply. */ if (m->appear_as.str - && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham >= LOW_PM) + && ((mtmp->data->mlet == S_MIMIC) + /* shapechanger (chameleons, et al, and vampires) */ + || (mtmp->cham >= LOW_PM && m->appear == M_AP_MONSTER)) && !Protection_from_shape_changers) { int i; @@ -1658,7 +1661,29 @@ struct mkroom *croom; mndx = select_newcham_form(mtmp); else mndx = name_to_mon(m->appear_as.str); - if ((mndx != NON_PM) && (&mons[mndx] != mtmp->data)) { + + if (mndx == NON_PM || (is_vampshifter(mtmp) + && !validvamp(mtmp, &mndx, S_HUMAN))) { + impossible("create_monster: invalid %s (\"%s\")", + (mtmp->data->mlet == S_MIMIC) + ? "mimic appearance" + : (mtmp->data == &mons[PM_WIZARD_OF_YENDOR]) + ? "Wizard appearance" + : is_vampshifter(mtmp) + ? "vampire shape" + : "chameleon shape", + m->appear_as.str); + } else if (&mons[mndx] == mtmp->data) { + /* explicitly forcing a mimic to appear as itself */ + mtmp->m_ap_type = M_AP_NOTHING; + mtmp->mappearance = 0; + } else if (mtmp->data->mlet == S_MIMIC + || mtmp->data == &mons[PM_WIZARD_OF_YENDOR]) { + /* this is ordinarily only used for Wizard clones + and hasn't been exhaustively tested for mimics */ + mtmp->m_ap_type = M_AP_MONSTER; + mtmp->mappearance = mndx; + } else { /* chameleon or vampire */ struct permonst *mdat = &mons[mndx]; struct permonst *olddata = mtmp->data; From 66242a0691e41a3104a696a5087c2c95a9aa6a7f Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Jan 2018 17:14:37 -0800 Subject: [PATCH 06/11] fix #H6707 - double "gush of water hits" messages When polymorphed into an iron golem (or gremlin with 2/3 chance), triggering a rust trap would give "a gush of water hits " and then give a second "a gush of water hits you" when dealing with golem or gremlin effects. That made it seem as if the trap was hitting twice. This removes the redundant messages. (Rust trap against monster iron golem or gremlin didn't have them.) --- doc/fixes36.1 | 1 + src/trap.c | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 77e524f7d..2b0c439e8 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -497,6 +497,7 @@ can no longer play controlled notes on musical instrument if impaired if a special level specified the appearance of a mimic and mimics had been genocided prior to creating the level, whatever random monster took the mimic's place got its intended appearance +redundant "hit by gush of water" message if poly'd into iron golem or gremlin Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/trap.c b/src/trap.c index 401d5a7fc..df0e1e883 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 trap.c $NHDT-Date: 1494107206 2017/05/06 21:46:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.278 $ */ +/* NetHack 3.6 trap.c $NHDT-Date: 1514855666 2018/01/02 01:14:26 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.284 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1103,7 +1103,7 @@ unsigned trflags; if (uarmc) (void) water_damage(uarmc, cloak_simple_name(uarmc), TRUE); else if (uarm) - (void) water_damage(uarm, "armor", TRUE); + (void) water_damage(uarm, suit_simple_name(uarm), TRUE); else if (uarmu) (void) water_damage(uarmu, "shirt", TRUE); } @@ -1112,11 +1112,9 @@ unsigned trflags; if (u.umonnum == PM_IRON_GOLEM) { int dam = u.mhmax; - pline("%s you!", A_gush_of_water_hits); You("are covered with rust!"); losehp(Maybe_Half_Phys(dam), "rusting away", KILLED_BY); } else if (u.umonnum == PM_GREMLIN && rn2(3)) { - pline("%s you!", A_gush_of_water_hits); (void) split_mon(&youmonst, (struct monst *) 0); } @@ -2292,7 +2290,8 @@ register struct monst *mtmp; (void) water_damage(target, cloak_simple_name(target), TRUE); else if ((target = which_armor(mtmp, W_ARM)) != 0) - (void) water_damage(target, "armor", TRUE); + (void) water_damage(target, suit_simple_name(target), + TRUE); else if ((target = which_armor(mtmp, W_ARMU)) != 0) (void) water_damage(target, "shirt", TRUE); } From 0c5155584975731f2c37ace88acd046a54ae5aa6 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 5 Jan 2018 01:23:56 -0800 Subject: [PATCH 07/11] fix #H6713 - unpaid_cost: object not on any bill Stealing a shop object from outside the shop with a grappling hook would result in that item being left marked 'unpaid' after the shop's bill was treated as being bought and not yet paid for. This led to "unpaid_cost: object wasn't on any bill" every time inventory was examined. The problem was caused by handling the shop robbery after removing the object from the floor but before adding it to inventory, so it couldn't be found to have its unpaid bit cleared. When investigating this I came across a more severe bug: if the hero had never entered the shop, the shopkeeper's bill wasn't initialized properly and add_one_tobill() could crash while attempting to execute bp->bo_id = obj->o_id; because 'bp' was Null. --- doc/fixes36.1 | 5 +++++ src/pickup.c | 26 ++++++++++++++++++++------ src/shk.c | 8 +++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 2b0c439e8..d33e86598 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -498,6 +498,11 @@ if a special level specified the appearance of a mimic and mimics had been genocided prior to creating the level, whatever random monster took the mimic's place got its intended appearance redundant "hit by gush of water" message if poly'd into iron golem or gremlin +a shop object stolen from outside the shop (via grappling hook) would be left + marked as 'unpaid' after the shop robbery took place, resulting in + "unpaid_cost: object wasn't on any bill" when looking at inventory +a shop object stolen from outside the shop could trigger a crash if that shop + had never been entered by the hero Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/pickup.c b/src/pickup.c index aa147080b..d09bbbfdb 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1508549438 2017/10/21 01:30:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.192 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1515144225 2018/01/05 09:23:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.193 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1495,15 +1495,30 @@ struct obj * pick_obj(otmp) struct obj *otmp; { + struct obj *result; + int ox = otmp->ox, oy = otmp->oy; + boolean robshop = (!u.uswallow && otmp != uball && costly_spot(ox, oy)); + obj_extract_self(otmp); - if (!u.uswallow && otmp != uball && costly_spot(otmp->ox, otmp->oy)) { + otmp->nomerge = 1; + result = addinv(otmp); + otmp->nomerge = 0; + newsym(ox, oy); + + /* this used to be done before addinv(), but remote_burglary() + calls rob_shop() which calls setpaid() after moving costs of + unpaid items to shop debt; setpaid() calls clear_unpaid() for + lots of object chains, but 'otmp' wasn't on any of those so + remained flagged as an unpaid item in inventory, triggering + impossible() every time inventory was examined... */ + if (robshop) { char saveushops[5], fakeshop[2]; /* addtobill cares about your location rather than the object's; usually they'll be the same, but not when using telekinesis (if ever implemented) or a grappling hook */ Strcpy(saveushops, u.ushops); - fakeshop[0] = *in_rooms(otmp->ox, otmp->oy, SHOPBASE); + fakeshop[0] = *in_rooms(ox, oy, SHOPBASE); fakeshop[1] = '\0'; Strcpy(u.ushops, fakeshop); /* sets obj->unpaid if necessary */ @@ -1511,10 +1526,9 @@ struct obj *otmp; Strcpy(u.ushops, saveushops); /* if you're outside the shop, make shk notice */ if (!index(u.ushops, *fakeshop)) - remote_burglary(otmp->ox, otmp->oy); + remote_burglary(ox, oy); } - newsym(otmp->ox, otmp->oy); - return addinv(otmp); /* might merge it with other objects */ + return result; } /* diff --git a/src/shk.c b/src/shk.c index 62052fbe7..c81c788b2 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 shk.c $NHDT-Date: 1464138042 2016/05/25 01:00:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.132 $ */ +/* NetHack 3.6 shk.c $NHDT-Date: 1515144230 2018/01/05 09:23:50 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.136 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2354,6 +2354,12 @@ struct monst *shkp; return; } + /* normally bill_p gets set up whenever you enter the shop, but obj + might be going onto the bill because hero just snagged it with + a grappling hook from outside without ever having been inside */ + if (!eshkp->bill_p) + eshkp->bill_p = &(eshkp->bill[0]); + bct = eshkp->billct; bp = &(eshkp->bill_p[bct]); bp->bo_id = obj->o_id; From 2b7b2af9eb1f5dcefa601e6813fe5fd308b1067b Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 6 Jan 2018 00:12:49 +0000 Subject: [PATCH 08/11] Give feedback when oilskin sacks get wet We can identify them by elimination in this case (they're the only bag-like container that doesn't produce a message, the others all do), so it's probably best to be more explicit as to what's going on (for user interfaces and TDTTOE purposes). --- src/trap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/trap.c b/src/trap.c index 401d5a7fc..27914fba6 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3431,6 +3431,11 @@ boolean force; water_damage_chain(obj->cobj, FALSE); return ER_NOTHING; + } else if (obj->otyp == OILSKIN_SACK) { + if (carried(obj)) + pline("Some water slides right off your %s.", ostr); + makeknown(OILSKIN_SACK); + return ER_NOTHING; } else if (!force && (Luck + 5) > rn2(20)) { /* chance per item of sustaining damage: * max luck: 10% From bad36c8672e8132fbdf98bec1109b2b049c62690 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 6 Jan 2018 00:31:11 +0000 Subject: [PATCH 09/11] Fix an exploit involving bags and potions of water Discovered while writing the previous commit. If you dipped a sack full of potions into an uncursed potion of water, the potions would dilute but you wouldn't lose the original potion, letting you repeat until all were diluted. Allowing people to do this trick to blank multiple potions from one potion of water seems like it's not an abuse, given that it can be done in a more tedious way with water walking or the like and it costs resources, but it's definitely abusive to make it possible entirely for free. --- src/trap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/trap.c b/src/trap.c index 27914fba6..66ab163f8 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3430,12 +3430,16 @@ boolean force; pline("Water gets into your %s!", ostr); water_damage_chain(obj->cobj, FALSE); - return ER_NOTHING; + return ER_DAMAGED; /* contents were damaged */ } else if (obj->otyp == OILSKIN_SACK) { if (carried(obj)) pline("Some water slides right off your %s.", ostr); makeknown(OILSKIN_SACK); - return ER_NOTHING; + /* not actually damaged, but because we /didn't/ get the "water + gets into!" message, the player now has more information and + thus we need to waste any potion they may have used (also, + flavourwise the water is now on the floor) */ + return ER_DAMAGED; } else if (!force && (Luck + 5) > rn2(20)) { /* chance per item of sustaining damage: * max luck: 10% From f9144fa57658db9b5893434f511e64e28b78139b Mon Sep 17 00:00:00 2001 From: keni Date: Tue, 9 Jan 2018 20:59:26 -0500 Subject: [PATCH 10/11] build fixes for MacOSX: - fix generation of sysconf when WANT_SOURCE_INSTALL=1 - use xcrun as main search for gdb - turn gdb off in most cases --- sys/unix/hints/macosx.sh | 17 ++++++++++++----- sys/unix/hints/macosx10.10 | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) mode change 100755 => 100644 sys/unix/hints/macosx.sh diff --git a/sys/unix/hints/macosx.sh b/sys/unix/hints/macosx.sh old mode 100755 new mode 100644 index df71033b8..2bf2360d9 --- a/sys/unix/hints/macosx.sh +++ b/sys/unix/hints/macosx.sh @@ -1,5 +1,5 @@ #!/bin/sh -# NetHack 3.6 macosx.sh $NHDT-Date: 1455930387 2016/02/20 01:06:27 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.16 $ +# NetHack 3.6 macosx.sh $NHDT-Date: 1515549543 2018/01/10 01:59:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.18 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. # NetHack may be freely redistributed. See license for details. # @@ -94,16 +94,23 @@ xgroup2) xeditsysconf) src=$2 dest=$3 - # we should traverse the elements of $PATH instead - if [ -f /usr/bin/gdb ]; then + ptg=1 + # We don't need an LLDB module because any MacOSX new enough to + # have no Apple supported gdb is also new enough to get good + # stack traces through libc. + # NB: xcrun will check $PATH + if [[ -x /usr/bin/xcrun && `/usr/bin/xcrun -f gdb 2>/dev/null` ]] ; then + gdbpath="GDBPATH="`/usr/bin/xcrun -f gdb` + elif [ -f /usr/bin/gdb ]; then gdbpath='GDBPATH=/usr/bin/gdb' #traditional location elif [ -f /opt/local/bin/ggdb ]; then gdbpath='GDBPATH=/opt/local/bin/ggdb' #macports gdb elif [ -f /Developer/usr/bin/gdb ]; then # this one seems to be broken with Xcode 5.1.1 on Mountain Lion - gdbpath='GDBPATH=/Developer/usr/bin/gdb' #recent Xcode tools + gdbpath='GDBPATH=/Developer/usr/bin/gdb' #older Xcode tools else gdbpath='#GDBPATH' #none of the above + ptg=0 fi if [ -f /bin/grep ]; then greppath='GREPPATH=/bin/grep' @@ -116,7 +123,7 @@ xeditsysconf) if ! [ -e $dest ]; then sed -e "s:^GDBPATH=.*:$gdbpath:" \ -e "s:^GREPPATH=.*:$greppath:" \ - -e 's/^PANICTRACE_GDB=[12]/PANICTRACE_GDB=0/' \ + -e "s/^PANICTRACE_GDB=./PANICTRACE_GDB=$ptg/" \ -e 's/^#OPTIONS=.*/&\ OPTIONS=!use_darkgray/' \ $src > $dest diff --git a/sys/unix/hints/macosx10.10 b/sys/unix/hints/macosx10.10 index 19737f0c1..f320e25fb 100644 --- a/sys/unix/hints/macosx10.10 +++ b/sys/unix/hints/macosx10.10 @@ -1,5 +1,5 @@ # -# NetHack 3.6 macosx10.11 $NHDT-Date: 1445622451 2015/10/23 17:47:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.0 $ +# NetHack 3.6 macosx10.11 $NHDT-Date: 1515549543 2018/01/10 01:59:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.48 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. # NetHack may be freely redistributed. See license for details. # @@ -172,6 +172,7 @@ CHGRP=/usr/bin/true GAMEPERM = 0700 VARFILEPERM = 0600 VARDIRPERM = 0700 +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; # We can use "make all" to build the whole thing - but it misses some things: MOREALL=$(MAKE) install CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE From c59f9512c79a464a74c6e29cd3934c326b801ee7 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 12 Jan 2018 16:13:14 -0800 Subject: [PATCH 11/11] T-shirt punctuation 'It reads: "foo bar quux"' is a sentence so should have a terminating period. Technically that ought to be placed inside the quotes, but putting it after distinguishes slogans which have their own punctuation from ones which don't. A couple of entries contain multiple sentences. Some used two-space separation between those sentences, some only one; make all use two. Add a few new T-shirt messages, including a couple with pop culture references which are only 10 years old instead of 20 or more.... --- src/read.c | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/read.c b/src/read.c index e452bf33a..34dd9a32e 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 read.c $NHDT-Date: 1513130018 2017/12/13 01:53:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.149 $ */ +/* NetHack 3.6 read.c $NHDT-Date: 1515802375 2018/01/13 00:12:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.150 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -102,10 +102,10 @@ char *buf; "Go team ant!", "Got newt?", "Hello, my darlings!", /* Charlie Drake */ - "Hey! Nymphs! Steal This T-Shirt!", + "Hey! Nymphs! Steal This T-Shirt!", "I <3 Dungeon of Doom", "I <3 Maud", - "I am a Valkyrie. If you see me running, try to keep up.", + "I am a Valkyrie. If you see me running, try to keep up.", "I am not a pack rat - I am a collector", "I bounced off a rubber tree", /* Monkey Island */ "Plunder Island Brimstone Beach Club", /* Monkey Island */ @@ -141,6 +141,16 @@ char *buf; "Pudding farmer", "Vegetarian", "Hello, I'm War!", + "It is better to light a candle than to curse the darkness", + "It is easier to curse the darkness than to light a candle", + /* expanded "rock--paper--scissors" featured in TV show "Big Bang + Theory" although they didn't create it (and an actual T-shirt + with pentagonal diagram showing which choices defeat which) */ + "rock--paper--scissors--lizard--Spock!", + /* "All men must die -- all men must serve" challange and response + from book series _A_Song_of_Ice_and_Fire_ by George R.R. Martin, + TV show "Game of Thrones" (probably an actual T-shirt too...) */ + "/Valar morghulis/ -- /Valar dohaeris/", }; Strcpy(buf, shirt_msgs[tshirt->o_id % SIZE(shirt_msgs)]); @@ -191,7 +201,8 @@ doread() useup(scroll); return 1; } else if (scroll->otyp == T_SHIRT || scroll->otyp == ALCHEMY_SMOCK) { - char buf[BUFSZ]; + char buf[BUFSZ], *mesg; + const char *endpunct; if (Blind) { You_cant("feel any Braille writing."); @@ -205,15 +216,25 @@ doread() return 0; } u.uconduct.literate++; - if (flags.verbose) + /* populate 'buf[]' */ + mesg = (scroll->otyp == T_SHIRT) ? tshirt_text(scroll, buf) + : apron_text(scroll, buf); + endpunct = ""; + if (flags.verbose) { + int ln = (int) strlen(mesg); + + /* we will be displaying a sentence; need ending punctuation */ + if (ln > 0 && !index(".!?", mesg[ln - 1])) + endpunct = "."; pline("It reads:"); - pline("\"%s\"", (scroll->otyp == T_SHIRT) ? tshirt_text(scroll, buf) - : apron_text(scroll, buf)); + } + pline("\"%s\"%s", mesg, endpunct); return 1; } else if (scroll->otyp == CREDIT_CARD) { static const char *card_msgs[] = { "Leprechaun Gold Tru$t - Shamrock Card", - "Magic Memory Vault Charge Card", "Larn National Bank", /* Larn */ + "Magic Memory Vault Charge Card", + "Larn National Bank", /* Larn */ "First Bank of Omega", /* Omega */ "Bank of Zork - Frobozz Magic Card", /* Zork */ "Ankh-Morpork Merchant's Guild Barter Card", @@ -238,13 +259,14 @@ doread() : card_msgs[scroll->o_id % (SIZE(card_msgs) - 1)]); } /* Make a credit card number */ - pline("\"%d0%d %ld%d1 0%d%d0\"", + pline("\"%d0%d %ld%d1 0%d%d0\"%s", (((int) scroll->o_id % 89) + 10), ((int) scroll->o_id % 4), ((((long) scroll->o_id * 499L) % 899999L) + 100000L), ((int) scroll->o_id % 10), (!((int) scroll->o_id % 3)), - (((int) scroll->o_id * 7) % 10)); + (((int) scroll->o_id * 7) % 10), + (flags.verbose || Blind) ? "." : ""); u.uconduct.literate++; return 1; } else if (scroll->otyp == CAN_OF_GREASE) { @@ -257,7 +279,7 @@ doread() } if (flags.verbose) pline("It reads:"); - pline("\"Magic Marker(TM) Red Ink Marker Pen. Water Soluble.\""); + pline("\"Magic Marker(TM) Red Ink Marker Pen. Water Soluble.\""); u.uconduct.literate++; return 1; } else if (scroll->oclass == COIN_CLASS) { @@ -265,7 +287,7 @@ doread() You("feel the embossed words:"); else if (flags.verbose) You("read:"); - pline("\"1 Zorkmid. 857 GUE. In Frobs We Trust.\""); + pline("\"1 Zorkmid. 857 GUE. In Frobs We Trust.\""); u.uconduct.literate++; return 1; } else if (scroll->oartifact == ART_ORB_OF_FATE) { @@ -291,7 +313,7 @@ doread() You_cant("feel any Braille writing."); return 0; } - pline("The wrapper reads: \"%s\"", + pline("The wrapper reads: \"%s\".", wrapper_msgs[scroll->o_id % SIZE(wrapper_msgs)]); u.uconduct.literate++; return 1;