diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 2de78d988..fb2cf76b0 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -194,6 +194,8 @@ avoid potential buffer overflow if object with very long name knocks other objects down stairs when dropped, thrown, or kicked there #wizintrinsic for 'flying' didn't update status line when flying ended #wizintrinsic for 'warn_of_mon' didn't set any type of monster (now grid bugs) +clairvoyance would show trap instead of a monster on/in that trap, which was + intentional, but when clairvoyance finished the monster wasn't shown Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 69ac1eec2..8fd85e91e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -390,7 +390,7 @@ E void FDECL(revive_mon, (ANY_P *, long)); E int NDECL(donull); E int NDECL(dowipe); E void FDECL(set_wounded_legs, (long, int)); -E void NDECL(heal_legs); +E void FDECL(heal_legs, (int)); /* ### do_name.c ### */ diff --git a/src/detect.c b/src/detect.c index ca6eda709..57ee916ce 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 detect.c $NHDT-Date: 1541144458 2018/11/02 07:40:58 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.85 $ */ +/* NetHack 3.6 detect.c $NHDT-Date: 1522891623 2018/04/05 01:27:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1300,25 +1300,82 @@ struct obj *sobj; /* scroll--actually fake spellbook--object */ { register int zx, zy; struct monst *mtmp; + struct obj *otmp; + long save_EDetect_mons; + char save_viz_uyux; boolean unconstrained, refresh = FALSE, mdetected = FALSE, - extended = (sobj && sobj->blessed); - int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5), + /* fake spellbook 'sobj' implies hero has cast the spell; + when book is blessed, casting is skilled or expert level; + if already clairvoyant, non-skilled spell acts like skilled */ + extended = (sobj && (sobj->blessed || Clairvoyant)); + int newglyph, oldglyph, + lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5), hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6), lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */ hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10), ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ; + /* + * 3.6.0 attempted to emphasize terrain over transient map + * properties (monsters and objects) but that led to problems. + * Notably, known trap would be displayed instead of a monster + * on or in it and then the display remained that way after the + * clairvoyant snapshot finished. That could have been fixed by + * issuing --More-- and then regular vision update, but we want + * to avoid that when having a clairvoyant episode every N turns + * (from donating to a temple priest or by carrying the Amulet). + * Unlike when casting the spell, it is much too intrustive when + * in the midst of walking around or combatting monsters. + * + * For 3.6.2, show terrain, then object, then monster like regular + * map updating, except in this case the map locations get marked + * as seen from every direction rather than just from direction of + * hero. Skilled spell marks revealed objects as 'seen up close' + * (but for piles, only the top item) and shows monsters as if + * detected. Non-skilled and timed clairvoyance reveals non-visible + * monsters as 'remembered, unseen'. + */ + + /* if hero is engulfed, show engulfer at */ + save_viz_uyux = viz_array[u.uy][u.ux]; + if (u.uswallow) + viz_array[u.uy][u.ux] |= IN_SIGHT; /* are reversed to [y][x] */ + save_EDetect_mons = EDetect_monsters; + /* for skilled spell, getpos() scanning of the map will display all + monsters within range; otherwise, "unseen creature" will be shown */ + EDetect_monsters |= I_SPECIAL; unconstrained = unconstrain_map(); for (zx = lo_x; zx <= hi_x; zx++) for (zy = lo_y; zy <= hi_y; zy++) { + oldglyph = glyph_at(zx, zy); + /* this will remove 'remembered, unseen mon' (and objects) */ show_map_spot(zx, zy); - - if (extended && (mtmp = m_at(zx, zy)) != 0 + /* if there are any objects here, see the top one */ + if (OBJ_AT(zx, zy)) { + /* not vobj_at(); this is not vision-based access; + unlike object detection, we don't notice buried items */ + otmp = level.objects[zx][zy]; + if (extended) + otmp->dknown = 1; + map_object(otmp, TRUE); + } + /* if there is a monster here, see or detect it, + possibly as "remembered, unseen monster" */ + if ((mtmp = m_at(zx, zy)) != 0 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */ - int oldglyph = glyph_at(zx, zy); - - map_monst(mtmp, FALSE); - if (glyph_at(zx, zy) != oldglyph) + /* if we're going to offer browse_map()/getpos() scanning of + the map and we're not doing extended/blessed clairvoyance + (hence must be swallowed or underwater), show "unseen + creature" unless map already displayed a monster here */ + if ((unconstrained || !level.flags.hero_memory) + && !extended && (zx != u.ux || zy != u.uy) + && !glyph_is_monster(oldglyph)) + map_invisible(zx, zy); + else + map_monst(mtmp, FALSE); + newglyph = glyph_at(zx, zy); + if (extended && newglyph != oldglyph + && !glyph_is_invisible(newglyph)) mdetected = TRUE; } } @@ -1331,13 +1388,26 @@ struct obj *sobj; /* scroll--actually fake spellbook--object */ You("sense your surroundings."); if (extended || glyph_is_monster(glyph_at(u.ux, u.uy))) ter_typ |= TER_MON; - if (extended) - EDetect_monsters |= I_SPECIAL; browse_map(ter_typ, "anything of interest"); - EDetect_monsters &= ~I_SPECIAL; refresh = TRUE; } reconstrain_map(); + EDetect_monsters = save_EDetect_mons; + viz_array[u.uy][u.ux] = save_viz_uyux; + + /* replace monsters with remembered,unseen monster, then run + see_monsters() to update visible ones and warned-of ones */ + for (zx = lo_x; zx <= hi_x; zx++) + for (zy = lo_y; zy <= hi_y; zy++) { + if (zx == u.ux && zy == u.uy) + continue; + newglyph = glyph_at(zx, zy); + if (glyph_is_monster(newglyph) + && glyph_to_mon(newglyph) != PM_LONG_WORM_TAIL) + map_invisible(zx, zy); + } + see_monsters(); + if (refresh) docrt(); } diff --git a/src/do.c b/src/do.c index bcae4ca43..00f0b8edd 100644 --- a/src/do.c +++ b/src/do.c @@ -1882,7 +1882,8 @@ register int timex; } void -heal_legs() +heal_legs(how) +int how; /* 0: ordinary, 1: dismounting steed, 2: limbs turn to stone */ { if (Wounded_legs) { if (ATEMP(A_DEX) < 0) { @@ -1890,7 +1891,11 @@ heal_legs() context.botl = 1; } - if (!u.usteed) { + /* when mounted, wounded legs applies to the steed; + during petrification countdown, "your limbs turn to stone" + before the final stages and that calls us (how==2) to cure + wounded legs, but we want to suppress the feel better message */ + if (!u.usteed && how != 2) { const char *legs = body_part(LEG); if ((EWounded_legs & BOTH_SIDES) == BOTH_SIDES) @@ -1900,7 +1905,7 @@ heal_legs() Your("%s %s better.", legs, vtense(legs, "feel")); } - HWounded_legs = EWounded_legs = 0; + HWounded_legs = EWounded_legs = 0L; /* Wounded_legs reduces carrying capacity, so we want an encumbrance check when they're healed. However, @@ -1912,7 +1917,7 @@ heal_legs() it might be immediately contradicted [able to carry more when steed becomes healthy, then possible floor feedback, then able to carry less when back on foot]. */ - if (!in_steed_dismounting) + if (how == 0) (void) encumber_msg(); } } diff --git a/src/drawing.c b/src/drawing.c index d81ffa2c9..68fdebdad 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -26,7 +26,8 @@ nhsym l_syms[SYM_MAX] = DUMMY; /* loaded symbols */ nhsym r_syms[SYM_MAX] = DUMMY; /* rogue symbols */ nhsym warnsyms[WARNCOUNT] = DUMMY; /* the current warning display symbols */ -const char invisexplain[] = "remembered, unseen, creature"; +const char invisexplain[] = "remembered, unseen, creature", + altinvisexplain[] = "unseen creature"; /* for clairvoyance */ /* Default object class symbols. See objclass.h. * {symbol, name, explain} diff --git a/src/eat.c b/src/eat.c index 40c7a4399..0d5e83ab2 100644 --- a/src/eat.c +++ b/src/eat.c @@ -2217,7 +2217,7 @@ struct obj *otmp; } } if (!otmp->cursed) - heal_legs(); + heal_legs(0); break; case EGG: if (flesh_petrifies(&mons[otmp->corpsenm])) { @@ -3181,7 +3181,8 @@ vomit() /* A good idea from David Neves */ dealing with some esoteric body_part() */ Your("jaw gapes convulsively."); } else { - make_sick(0L, (char *) 0, TRUE, SICK_VOMITABLE); + if (Sick && (u.usick_type & SICK_VOMITABLE) != 0) + make_sick(0L, (char *) 0, TRUE, SICK_VOMITABLE); /* if not enough in stomach to actually vomit then dry heave; vomiting_dialog() gives a vomit message when its countdown reaches 0, but only if u.uhs < FAINTING (and !cantvomit()) */ diff --git a/src/pager.c b/src/pager.c index c1824d2f8..305cfdc5a 100644 --- a/src/pager.c +++ b/src/pager.c @@ -895,12 +895,17 @@ struct permonst **for_supplement; } if (sym == DEF_INVISIBLE) { + extern const char altinvisexplain[]; /* drawing.c */ + /* for active clairvoyance, use alternate "unseen creature" */ + boolean usealt = (EDetect_monsters & I_SPECIAL) != 0L; + const char *unseen_explain = !usealt ? invisexplain : altinvisexplain; + if (!found) { - Sprintf(out_str, "%s%s", prefix, an(invisexplain)); - *firstmatch = invisexplain; + Sprintf(out_str, "%s%s", prefix, an(unseen_explain)); + *firstmatch = unseen_explain; found++; } else { - found += append_str(out_str, an(invisexplain)); + found += append_str(out_str, an(unseen_explain)); } } diff --git a/src/potion.c b/src/potion.c index 6904bc315..537cb064b 100644 --- a/src/potion.c +++ b/src/potion.c @@ -106,6 +106,9 @@ boolean talk; set_itimeout(&HStun, xtime); } +/* Sick is overloaded with both fatal illness and food poisoning (via + u.usick_type bit mask), but delayed killer can only support one or + the other at a time. They should become separate intrinsics.... */ void make_sick(xtime, cause, talk, type) long xtime; @@ -113,9 +116,10 @@ const char *cause; /* sickness cause */ boolean talk; int type; { + struct kinfo *kptr; long old = Sick; -#if 0 +#if 0 /* tell player even if hero is unconscious */ if (Unaware) talk = FALSE; #endif @@ -148,11 +152,20 @@ int type; context.botl = TRUE; } + kptr = find_delayed_killer(SICK); if (Sick) { exercise(A_CON, FALSE); - delayed_killer(SICK, KILLED_BY_AN, cause); + /* setting delayed_killer used to be unconditional, but that's + not right when make_sick(0) is called to cure food poisoning + if hero was also fatally ill; this is only approximate */ + if (xtime || !old || !kptr) { + int kpfx = ((cause && !strcmp(cause, "#wizintrinsic")) + ? KILLED_BY : KILLED_BY_AN); + + delayed_killer(SICK, kpfx, cause); + } } else - dealloc_killer(find_delayed_killer(SICK)); + dealloc_killer(kptr); } void @@ -162,7 +175,7 @@ const char *msg; { long old = Slimed; -#if 0 +#if 0 /* tell player even if hero is unconscious */ if (Unaware) msg = 0; #endif @@ -170,7 +183,7 @@ const char *msg; if ((xtime != 0L) ^ (old != 0L)) { context.botl = TRUE; if (msg) - pline1(msg); + pline("%s", msg); } if (!Slimed) dealloc_killer(find_delayed_killer(SLIMED)); @@ -186,7 +199,7 @@ const char *killername; { long old = Stoned; -#if 0 +#if 0 /* tell player even if hero is unconscious */ if (Unaware) msg = 0; #endif @@ -194,7 +207,7 @@ const char *killername; if ((xtime != 0L) ^ (old != 0L)) { context.botl = TRUE; if (msg) - pline1(msg); + pline("%s", msg); } if (!Stoned) dealloc_killer(find_delayed_killer(STONED)); @@ -892,9 +905,9 @@ register struct obj *otmp; } break; case POT_SPEED: + /* skip when mounted; heal_legs() would heal steed's legs */ if (Wounded_legs && !otmp->cursed && !u.usteed) { - /* heal_legs() would heal steeds legs */ - heal_legs(); + heal_legs(0); unkn++; break; } diff --git a/src/pray.c b/src/pray.c index 7ad09455a..e1f55cb49 100644 --- a/src/pray.c +++ b/src/pray.c @@ -524,7 +524,7 @@ int trouble; break; } case TROUBLE_WOUNDED_LEGS: - heal_legs(); + heal_legs(0); break; case TROUBLE_STUNNED: make_stunned(0L, TRUE); @@ -2181,6 +2181,7 @@ blocked_boulder(dx, dy) int dx, dy; { register struct obj *otmp; + int nx, ny; long count = 0L; for (otmp = level.objects[u.ux + dx][u.uy + dy]; otmp; @@ -2189,6 +2190,7 @@ int dx, dy; count += otmp->quan; } + nx = u.ux + 2 * dx, ny = u.uy + 2 * dy; /* next spot beyond boulder(s) */ switch (count) { case 0: /* no boulders--not blocked */ @@ -2196,17 +2198,24 @@ int dx, dy; case 1: /* possibly blocked depending on if it's pushable */ break; + case 2: + /* this is only approximate since multiple boulders might sink */ + if (is_pool_or_lava(nx, ny)) /* does its own isok() check */ + break; /* still need Sokoban check below */ + /*FALLTHRU*/ default: /* more than one boulder--blocked after they push the top one; don't force them to push it first to find out */ return TRUE; } - if (!isok(u.ux + 2 * dx, u.uy + 2 * dy)) + if (dx && dy && Sokoban) /* can't push boulder diagonally in Sokoban */ return TRUE; - if (IS_ROCK(levl[u.ux + 2 * dx][u.uy + 2 * dy].typ)) + if (!isok(nx, ny)) return TRUE; - if (sobj_at(BOULDER, u.ux + 2 * dx, u.uy + 2 * dy)) + if (IS_ROCK(levl[nx][ny].typ)) + return TRUE; + if (sobj_at(BOULDER, nx, ny)) return TRUE; return FALSE; diff --git a/src/sit.c b/src/sit.c index 0f8bedbb4..1aa8085bf 100644 --- a/src/sit.c +++ b/src/sit.c @@ -188,7 +188,7 @@ dosit() u.ucreamed = 0; make_blinded(0L, TRUE); make_sick(0L, (char *) 0, FALSE, SICK_ALL); - heal_legs(); + heal_legs(0); context.botl = 1; break; case 5: diff --git a/src/steed.c b/src/steed.c index 8cbf9d0ef..6b940c26e 100644 --- a/src/steed.c +++ b/src/steed.c @@ -221,7 +221,7 @@ boolean force; /* Quietly force this animal */ if (Wounded_legs) { Your("%s are in no shape for riding.", makeplural(body_part(LEG))); if (force && wizard && yn("Heal your legs?") == 'y') - HWounded_legs = EWounded_legs = 0; + HWounded_legs = EWounded_legs = 0L; else return (FALSE); } @@ -542,12 +542,8 @@ int reason; /* Player was thrown off etc. */ } /* While riding, Wounded_legs refers to the steed's legs; after dismounting, it reverts to the hero's legs. */ - if (repair_leg_damage) { - /* [TODO: make heal_legs() take a parameter to handle this] */ - in_steed_dismounting = TRUE; - heal_legs(); - in_steed_dismounting = FALSE; - } + if (repair_leg_damage) + heal_legs(1); /* Release the steed and saddle */ u.usteed = 0; diff --git a/src/timeout.c b/src/timeout.c index a75fc66b9..110aa03ad 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -136,10 +136,19 @@ stoned_dialogue() nomul(-3); /* can't move anymore */ multi_reason = "getting stoned"; nomovemsg = You_can_move_again; /* not unconscious */ + /* "your limbs have turned to stone" so terminate wounded legs */ + if (Wounded_legs && !u.usteed) + heal_legs(2); break; - case 2: + case 2: /* turned to stone */ if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L) set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */ + /* if also vomiting or turning into slime, stop those (no messages) */ + if (Vomiting) + make_vomiting(0L, FALSE); + if (Slimed) + make_slimed(0L, (char *) 0); + break; default: break; } @@ -318,15 +327,25 @@ slime_dialogue() } else pline1(buf); } - if (i == 3L) { /* limbs becoming oozy */ + + switch (i) { + case 3L: /* limbs becoming oozy */ HFast = 0L; /* lose intrinsic speed */ if (!Popeye(SLIMED)) stop_occupation(); if (multi > 0) nomul(0); + break; + case 2L: /* skin begins to peel */ + if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L) + set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */ + break; + case 1L: /* turning into slime */ + /* if also turning to stone, stop doing that (no message) */ + if (Stoned) + make_stoned(0L, (char *) 0, KILLED_BY_AN, (char *) 0); + break; } - if (i == 2L && (HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L) - set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */ exercise(A_DEX, FALSE); } @@ -534,7 +553,7 @@ nh_timeout() stop_occupation(); break; case WOUNDED_LEGS: - heal_legs(); + heal_legs(0); stop_occupation(); break; case HALLUC: diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 2967c2cd0..f86a67acc 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -1334,6 +1334,7 @@ $(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def $(O)nttty.o: $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h $(MSWSYS)\nttty.c @$(cc) $(cflagsBuild) -I$(WSHR) -Fo$@ $(MSWSYS)\nttty.c $(O)winnt.o: $(HACK_H) $(MSWSYS)\win32api.h $(MSWSYS)\winnt.c + @$(cc) $(cflagsBuild) -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWSYS)\win10.c @$(cc) $(cflagsBuild) -Fo$@ $(MSWSYS)\winnt.c $(O)ntsound.o: $(HACK_H) $(MSWSYS)\ntsound.c @$(cc) $(cflagsBuild) -Fo$@ $(MSWSYS)\ntsound.c @@ -1344,6 +1345,13 @@ $(O)ntsound.o: $(HACK_H) $(MSWSYS)\ntsound.c $(O)guistub.o: $(HACK_H) $(MSWSYS)\stubs.c @$(cc) $(cflagsBuild) -DGUISTUB -Fo$@ $(MSWSYS)\stubs.c +# +# WIN32 dependencies +# + +$(O)winhack.o: $(HACK_H) $(MSWIN)\winhack.c + @$(cc) $(cflagsBuild) -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWIN)\winhack.c + #if you aren't linking in the full tty then #include the following stub for proper linkage.