missile light sources

Throwing or kicking a lit lamp, lit candle, or lit potion of oil
wasn't giving off any light as it travelled to its destination.
Now it does, and dungeon features, objects, or monsters that are
temporarily seen as it moves from square to square till appear on
the map.  In the monster case, they go away as soon as the light
moves beyond range, but when it finishes moving the "remembered,
unseen monster" glyph will be drawn at their location.  I think that
part has some room for improvement, but mapping temporarily seen
terrain features is the primary impetus for this change.

Also, any message delivery while the "lit missile" travelled still
showed its light around the hero.  Noticeable for lamps or stacks
of sufficient candles if hero has no other light source.

This cannibalizes the monst->mburied bit for temporarily seeing a
monster.  It has been present but unused for ages.  I needed to
replace a couple of vision macros to make sure they didn't examine
it any more so that overloading for transient lighting doesn't
introduce any vision oddities.  For version $NEXT, monst->mtemplit
can be given its own bit.  It is only set during bhit() execution
and cleared by the time that returns, so has no effect on save files.
This commit is contained in:
PatR
2019-06-08 04:50:40 -07:00
parent fabfe276fe
commit c741d6c7c9
9 changed files with 158 additions and 45 deletions

View File

@@ -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. */
@@ -65,7 +65,7 @@ anything *id;
return;
}
ls = (light_source *) alloc(sizeof(light_source));
ls = (light_source *) alloc(sizeof *ls);
ls->next = light_base;
ls->x = x;
@@ -154,8 +154,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;
@@ -192,7 +192,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.
*/
@@ -210,6 +210,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 = 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, bhitpos.x, 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)

View File

@@ -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. */
@@ -1078,6 +1078,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);

View File

@@ -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. */
@@ -3223,7 +3223,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;
@@ -3249,9 +3249,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));
@@ -3272,21 +3272,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[bhitpos.x][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, bhitpos.x, bhitpos.y,
point_blank ? 0 : !rn2(5), 1)) {
/* caveat: obj might now be null... */
obj = *pobj;
bhitpos.x -= ddx;
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, bhitpos.x, bhitpos.y,
point_blank ? 0 : !rn2(5), 1)) {
/* caveat: obj might now be null... */
obj = *pobj;
bhitpos.x -= ddx;
bhitpos.y -= ddy;
break;
} else if (obj->lamplit && !Blind) {
show_transient_light(obj, bhitpos.x, bhitpos.y);
}
}
if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
@@ -3366,7 +3370,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
@@ -3374,8 +3379,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 */
@@ -3384,7 +3391,8 @@ struct obj **pobj; /* object tossed/used, set to NULL
if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
map_invisible(bhitpos.x, bhitpos.y);
return mtmp;
result = mtmp;
goto bhit_done;
} else {
/* ZAPPED_WAND */
(*fhitm)(mtmp, obj);
@@ -3407,7 +3415,7 @@ struct obj **pobj; /* object tossed/used, set to NULL
|| ship_object(obj, bhitpos.x, bhitpos.y,
costly_spot(bhitpos.x, 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)) {
@@ -3490,7 +3498,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...