diff --git a/doc/fixes36.3 b/doc/fixes36.3 index f538bf3b5..2a267fe74 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.42 $ $NHDT-Date: 1559851954 2019/06/06 20:12:34 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.44 $ $NHDT-Date: 1559998716 2019/06/08 12:58:36 $ This fixes36.3 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.2 in May 2019. Please note, however, @@ -59,6 +59,13 @@ when finding a place to put a monster on the Plane of Water, don't pick a water location for flyers or floaters (levitate) or clingers (ceiling) #turn worked when unable to speak despite "you chant an arcane formula" #turn implicitly required sight due to wrong check for avoiding spanning walls +thrown or kicked light source (lit lamp, candle, oil) should emit light as it + traverses the map; dungeon features, objects, or monsters seen while + it's in transit will become part of hero's memory of the level, and + any messages delivered won't have stale light from it around the hero +unlike watching a monster trying to swap out a cursed weapon for some other + weapon and failing, watching it wield a cursed weapon didn't report + that weapon becoming welded to the monster's hand/claw/whatever Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/include/display.h b/include/display.h index 92c201a2a..4bece448b 100644 --- a/include/display.h +++ b/include/display.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 display.h $NHDT-Date: 1546212620 2018/12/30 23:30:20 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.29 $ */ +/* NetHack 3.6 display.h $NHDT-Date: 1559994621 2019/06/08 11:50:21 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.32 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -58,11 +58,18 @@ * vobj_at() returns a pointer to an object that the hero can see there. * Infravision is not taken into account. */ +#if 0 #define mon_visible(mon) \ (/* The hero can see the monster IF the monster */ \ (!mon->minvis || See_invisible) /* 1. is not invisible */ \ && !mon->mundetected /* AND 2. not an undetected hider */ \ && !(mon->mburied || u.uburied)) /* AND 3. neither you nor it is buried */ +#else /* without 'mburied' and 'uburied' */ +#define mon_visible(mon) \ + (/* The hero can see the monster IF the monster */ \ + (!mon->minvis || See_invisible) /* 1. is not invisible */ \ + && !mon->mundetected) /* AND 2. not an undetected hider */ +#endif /* * see_with_infrared() diff --git a/include/extern.h b/include/extern.h index 08a261861..15af73081 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1559670600 2019/06/04 17:50:00 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.709 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1559994622 2019/06/08 11:50:22 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.711 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1065,6 +1065,8 @@ E int NDECL(dosuspend); E void FDECL(new_light_source, (XCHAR_P, XCHAR_P, int, int, ANY_P *)); E void FDECL(del_light_source, (int, ANY_P *)); E void FDECL(do_light_sources, (char **)); +E void FDECL(show_transient_light, (struct obj *, int, int)); +E void NDECL(transient_light_cleanup); E struct monst *FDECL(find_mid, (unsigned, unsigned)); E void FDECL(save_light_sources, (int, int, int)); E void FDECL(restore_light_sources, (int)); diff --git a/include/monst.h b/include/monst.h index b15f89bd7..bf3b7498b 100644 --- a/include/monst.h +++ b/include/monst.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 monst.h $NHDT-Date: 1559422218 2019/06/01 20:50:18 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.31 $ */ +/* NetHack 3.6 monst.h $NHDT-Date: 1559994623 2019/06/08 11:50:23 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.32 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -97,11 +97,13 @@ struct monst { Bitfield(perminvis, 1); /* intrinsic minvis value */ Bitfield(mcan, 1); /* has been cancelled */ Bitfield(mburied, 1); /* has been buried */ +#define mtemplit mburied /* since buried isn't implemented, use bit for + * monsters shown by transcient light source; + * only valid during bhit() execution */ Bitfield(mundetected, 1); /* not seen in present hiding place; * implies one of M1_CONCEAL or M1_HIDE, * but not mimic (that is, snake, spider, - * trapper, piercer, eel) - */ + * trapper, piercer, eel) */ Bitfield(mcansee, 1); /* cansee 1, temp.blinded 0, blind 0 */ Bitfield(mspeed, 2); /* current speed */ diff --git a/include/rm.h b/include/rm.h index e001d8e0c..e104fc20f 100644 --- a/include/rm.h +++ b/include/rm.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 rm.h $NHDT-Date: 1547255911 2019/01/12 01:18:31 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.6 rm.h $NHDT-Date: 1559994624 2019/06/08 11:50:24 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.61 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -615,12 +615,16 @@ typedef struct { /* * Macros for encapsulation of level.monsters references. */ +#if 0 #define MON_AT(x, y) \ (g.level.monsters[x][y] != (struct monst *) 0 \ && !(g.level.monsters[x][y])->mburied) #define MON_BURIED_AT(x, y) \ (g.level.monsters[x][y] != (struct monst *) 0 \ && (g.level.monsters[x][y])->mburied) +#else /* without 'mburied' */ +#define MON_AT(x, y) (g.level.monsters[x][y] != (struct monst *) 0) +#endif #ifdef EXTRA_SANITY_CHECKS #define place_worm_seg(m, x, y) \ do { \ diff --git a/include/vision.h b/include/vision.h index 7858c84fa..f0ccd6f05 100644 --- a/include/vision.h +++ b/include/vision.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 vision.h $NHDT-Date: 1432512777 2015/05/25 00:12:57 $ $NHDT-Branch: master $:$NHDT-Revision: 1.9 $ */ +/* NetHack 3.6 vision.h $NHDT-Date: 1559994624 2019/06/08 11:50:24 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.10 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -37,11 +37,17 @@ */ #define m_cansee(mtmp, x2, y2) clear_path((mtmp)->mx, (mtmp)->my, (x2), (y2)) -#define m_canseeu(m) \ +#if 0 +#define m_canseeu(m) \ ((!Invis || perceives((m)->data)) \ - && !(Underwater || u.uburied || (m)->mburied) \ - ? couldsee((m)->mx, (m)->my) \ - : 0) + && !(Underwater || u.uburied || (m)->mburied) \ + && couldsee((m)->mx, (m)->my)) +#else /* without 'uburied' and 'mburied' */ +#define m_canseeu(m) \ + ((!Invis || perceives((m)->data)) \ + && !Underwater \ + && couldsee((m)->mx, (m)->my)) +#endif /* * Circle information @@ -52,12 +58,12 @@ #define circle_ptr(z) (&circle_data[(int) circle_start[z]]) /* howmonseen() bitmask values */ -#define MONSEEN_NORMAL 0x0001 /* normal vision */ +#define MONSEEN_NORMAL 0x0001 /* normal vision */ #define MONSEEN_SEEINVIS 0x0002 /* seeing invisible */ #define MONSEEN_INFRAVIS 0x0004 /* via infravision */ -#define MONSEEN_TELEPAT 0x0008 /* via telepathy */ -#define MONSEEN_XRAYVIS 0x0010 /* via Xray vision */ -#define MONSEEN_DETECT 0x0020 /* via extended monster detection */ -#define MONSEEN_WARNMON 0x0040 /* via type-specific warning */ +#define MONSEEN_TELEPAT 0x0008 /* via telepathy */ +#define MONSEEN_XRAYVIS 0x0010 /* via Xray vision */ +#define MONSEEN_DETECT 0x0020 /* via extended monster detection */ +#define MONSEEN_WARNMON 0x0040 /* via type-specific warning */ #endif /* VISION_H */ diff --git a/src/light.c b/src/light.c index 51e3a0d97..ab0e619f4 100644 --- a/src/light.c +++ b/src/light.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 light.c $NHDT-Date: 1446191876 2015/10/30 07:57:56 $ $NHDT-Branch: master $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.6 light.c $NHDT-Date: 1559994625 2019/06/08 11:50:25 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.30 $ */ /* Copyright (c) Dean Luick, 1994 */ /* NetHack may be freely redistributed. See license for details. */ @@ -63,7 +63,7 @@ anything *id; return; } - ls = (light_source *) alloc(sizeof(light_source)); + ls = (light_source *) alloc(sizeof *ls); ls->next = g.light_base; ls->x = x; @@ -152,8 +152,8 @@ char **cs_rows; ls->flags |= LSF_SHOW; } - /* minor optimization: don't bother with duplicate light sources */ - /* at hero */ + /* minor optimization: don't bother with duplicate light sources + at hero */ if (ls->x == u.ux && ls->y == u.uy) { if (at_hero_range >= ls->range) ls->flags &= ~LSF_SHOW; @@ -190,7 +190,7 @@ char **cs_rows; * this optimization, is that it allows the vision * system to correct problems with clear_path(). * The function clear_path() is a simple LOS - * path checker that doesn't go out of its way + * path checker that doesn't go out of its way to * make things look "correct". The vision system * does this. */ @@ -208,6 +208,80 @@ char **cs_rows; } } +/* lit 'obj' has been thrown or kicked and is passing through x,y on the + way to its destination; show its light so that hero has a chance to + remember terrain, objects, and monsters being revealed */ +void +show_transient_light(obj, x, y) +struct obj *obj; +int x, y; +{ + light_source *ls; + struct monst *mon; + int radius_squared; + + /* caller has verified obj->lamplit and that hero is not Blind; + validate light source and obtain its radius (for monster sightings) */ + for (ls = g.light_base; ls; ls = ls->next) { + if (ls->type != LS_OBJECT) + continue; + if (ls->id.a_obj == obj) + break; + } + if (!ls || obj->where != OBJ_FREE) { + impossible("transient light %s %s is not %s?", + obj->lamplit ? "lit" : "unlit", xname(obj), + !ls ? "a light source" : "free"); + } else { + /* "expensive" but rare */ + place_object(obj, g.bhitpos.x, g.bhitpos.y); /* temporarily put on map */ + vision_recalc(0); + flush_screen(0); + delay_output(); + remove_object(obj); /* take back off of map */ + + radius_squared = ls->range * ls->range; + for (mon = fmon; mon; mon = mon->nmon) { + if (DEADMONSTER(mon)) + continue; + /* light range is the radius of a circle and we're limiting + canseemon() to a square exclosing that circle, but setting + mtemplit 'erroneously' for a seen monster is not a problem; + it just flags monsters for another canseemon() check when + 'obj' has reached its destination after missile traversal */ + if (dist2(mon->mx, mon->my, x, y) <= radius_squared + && canseemon(mon)) + mon->mtemplit = 1; + /* [what about worm tails?] */ + } + } +} + +/* draw "remembered, unseen monster" glyph at locations where a monster + was flagged for being visible during transient light movement but can't + be seen now */ +void +transient_light_cleanup() +{ + struct monst *mon; + int mtempcount = 0; + + for (mon = fmon; mon; mon = mon->nmon) { + if (DEADMONSTER(mon)) + continue; + if (mon->mtemplit) { + mon->mtemplit = 0; + ++mtempcount; + if (!canseemon(mon)) + map_invisible(mon->mx, mon->my); + } + } + if (mtempcount) { + vision_recalc(0); + flush_screen(0); + } +} + /* (mon->mx == 0) implies migrating */ #define mon_is_local(mon) ((mon)->mx > 0) diff --git a/src/save.c b/src/save.c index d5c6003e4..ad5fd51f8 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 save.c $NHDT-Date: 1558880688 2019/05/26 14:24:48 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.120 $ */ +/* NetHack 3.6 save.c $NHDT-Date: 1559994625 2019/06/08 11:50:25 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.121 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1076,6 +1076,8 @@ struct monst *mtmp; { int buflen; + mtmp->mtemplit = 0; /* normally clear; if set here then a panic save + * is being written while bhit() was executing */ buflen = (int) sizeof (struct monst); bwrite(fd, (genericptr_t) &buflen, sizeof buflen); bwrite(fd, (genericptr_t) mtmp, buflen); diff --git a/src/weapon.c b/src/weapon.c index 6d7390928..8a3b22fa5 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 weapon.c $NHDT-Date: 1548209744 2019/01/23 02:15:44 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.69 $ */ +/* NetHack 3.6 weapon.c $NHDT-Date: 1559998716 2019/06/08 12:58:36 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.70 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -829,8 +829,18 @@ register struct monst *mon; setmnotwielded(mon, mw_tmp); mon->weapon_check = NEED_WEAPON; if (canseemon(mon)) { + boolean newly_welded; + pline("%s wields %s!", Monnam(mon), doname(obj)); - if (mwelded(mw_tmp)) { + /* 3.6.3: mwelded() predicate expects the object to have its + W_WEP bit set in owormmask, but the pline here and for + artifact_light don't want that because they'd have '(weapon + in hand/claw)' appended; so we set it for the mwelded test + and then clear it, until finally setting it for good below */ + obj->owornmask |= W_WEP; + newly_welded = mwelded(obj); + obj->owornmask &= ~W_WEP; + if (newly_welded) { pline("%s %s to %s %s!", Tobjnam(obj, "weld"), is_plural(obj) ? "themselves" : "itself", s_suffix(mon_nam(mon)), mbodypart(mon, HAND)); @@ -843,6 +853,12 @@ register struct monst *mon; pline("%s %s in %s %s!", Tobjnam(obj, "shine"), arti_light_description(obj), s_suffix(mon_nam(mon)), mbodypart(mon, HAND)); + /* 3.6.3: artifact might be getting wielded by invisible monst */ + else if (cansee(mon->mx, mon->my)) + pline("Light begins shining %s.", + (distu(mon->mx, mon->my) <= 5 * 5) + ? "nearby" + : "in the distance"); } obj->owornmask = W_WEP; return 1; diff --git a/src/zap.c b/src/zap.c index aa53e0f84..60bcee3a7 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1559685281 2019/06/04 21:54:41 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.310 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1559994626 2019/06/08 11:50:26 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.311 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3215,7 +3215,7 @@ int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */ struct obj **pobj; /* object tossed/used, set to NULL * if object is destroyed */ { - struct monst *mtmp; + struct monst *mtmp, *result = (struct monst *) 0; struct obj *obj = *pobj; uchar typ; boolean shopdoor = FALSE, point_blank = TRUE; @@ -3241,9 +3241,9 @@ struct obj **pobj; /* object tossed/used, set to NULL if (weapon == FLASHED_LIGHT) { tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam)); } else if (weapon == THROWN_TETHERED_WEAPON && obj) { - tethered_weapon = TRUE; - weapon = THROWN_WEAPON; /* simplify if's that follow below */ - tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng)); + tethered_weapon = TRUE; + weapon = THROWN_WEAPON; /* simplify 'if's that follow below */ + tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng)); } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng)); @@ -3264,21 +3264,25 @@ struct obj **pobj; /* object tossed/used, set to NULL if (is_pick(obj) && inside_shop(x, y) && (mtmp = shkcatch(obj, x, y)) != 0) { tmp_at(DISP_END, 0); - return mtmp; + result = mtmp; + goto bhit_done; } typ = levl[g.bhitpos.x][g.bhitpos.y].typ; - /* iron bars will block anything big enough */ - if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) - && typ == IRONBARS - && hits_bars(pobj, x - ddx, y - ddy, g.bhitpos.x, g.bhitpos.y, - point_blank ? 0 : !rn2(5), 1)) { - /* caveat: obj might now be null... */ - obj = *pobj; - g.bhitpos.x -= ddx; - g.bhitpos.y -= ddy; - break; + /* iron bars will block anything big enough and break some things */ + if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) { + if (typ == IRONBARS + && hits_bars(pobj, x - ddx, y - ddy, g.bhitpos.x, g.bhitpos.y, + point_blank ? 0 : !rn2(5), 1)) { + /* caveat: obj might now be null... */ + obj = *pobj; + g.bhitpos.x -= ddx; + g.bhitpos.y -= ddy; + break; + } else if (obj->lamplit && !Blind) { + show_transient_light(obj, g.bhitpos.x, g.bhitpos.y); + } } if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) { @@ -3358,7 +3362,8 @@ struct obj **pobj; /* object tossed/used, set to NULL (void) flash_hits_mon(mtmp, obj); } else { tmp_at(DISP_END, 0); - return mtmp; /* caller will call flash_hits_mon */ + result = mtmp; /* caller will call flash_hits_mon */ + goto bhit_done; } } else if (weapon == INVIS_BEAM) { /* Like FLASHED_LIGHT, INVIS_BEAM should continue @@ -3366,8 +3371,10 @@ struct obj **pobj; /* object tossed/used, set to NULL prepared for multiple hits so just get first one that's either visible or could see its invisible self. [No tmp_at() cleanup is needed here.] */ - if (!mtmp->minvis || perceives(mtmp->data)) - return mtmp; + if (!mtmp->minvis || perceives(mtmp->data)) { + result = mtmp; + goto bhit_done; + } } else if (weapon != ZAPPED_WAND) { /* THROWN_WEAPON, KICKED_WEAPON */ @@ -3376,7 +3383,8 @@ struct obj **pobj; /* object tossed/used, set to NULL if (cansee(g.bhitpos.x, g.bhitpos.y) && !canspotmon(mtmp)) map_invisible(g.bhitpos.x, g.bhitpos.y); - return mtmp; + result = mtmp; + goto bhit_done; } else { /* ZAPPED_WAND */ (*fhitm)(mtmp, obj); @@ -3399,7 +3407,7 @@ struct obj **pobj; /* object tossed/used, set to NULL || ship_object(obj, g.bhitpos.x, g.bhitpos.y, costly_spot(g.bhitpos.x, g.bhitpos.y)))) { tmp_at(DISP_END, 0); - return (struct monst *) 0; + goto bhit_done; /* result == (struct monst *) 0 */ } } if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) { @@ -3482,7 +3490,11 @@ struct obj **pobj; /* object tossed/used, set to NULL if (shopdoor) pay_for_damage("destroy", FALSE); - return (struct monst *) 0; + bhit_done: + if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) + transient_light_cleanup(); + + return result; } /* process thrown boomerang, which travels a curving path...