implement #986 - camera flash 'tweak'
Implement the suggested feature that a camera's flash actually update hero's memory of the map as it traverses across the level. Turned out to be more work than anticipated despite having the code for a thrown or kicked lit candle or lamp to build upon. Among other things it needed to update the circle code to handle previously unused radius 0 to operate on the center point only. I've never touched that before and hope this hasn't introduced any bugs. Also removes several instances of vision code operating on column #0. (At least one is still present.)
This commit is contained in:
158
src/light.c
158
src/light.c
@@ -41,6 +41,9 @@
|
||||
#define LSF_SHOW 0x1 /* display the light source */
|
||||
#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
|
||||
|
||||
static light_source *FDECL(new_light_core, (XCHAR_P, XCHAR_P, int, int,
|
||||
anything *));
|
||||
static void NDECL(discard_flashes);
|
||||
static void FDECL(write_ls, (NHFILE *, light_source *));
|
||||
static int FDECL(maybe_write_ls, (NHFILE *, int, BOOLEAN_P));
|
||||
|
||||
@@ -49,18 +52,31 @@ extern char circle_data[];
|
||||
extern char circle_start[];
|
||||
|
||||
|
||||
/* Create a new light source. */
|
||||
/* Create a new light source. Caller (and extern.h) doesn't need to know
|
||||
anything about type 'light_source'. */
|
||||
void
|
||||
new_light_source(x, y, range, type, id)
|
||||
xchar x, y;
|
||||
int range, type;
|
||||
anything *id;
|
||||
{
|
||||
(void) new_light_core(x, y, range, type, id);
|
||||
}
|
||||
|
||||
/* Create a new light source and return it. Only used within this file. */
|
||||
static light_source *
|
||||
new_light_core(x, y, range, type, id)
|
||||
xchar x, y;
|
||||
int range, type;
|
||||
anything *id;
|
||||
{
|
||||
light_source *ls;
|
||||
|
||||
if (range > MAX_RADIUS || range < 1) {
|
||||
impossible("new_light_source: illegal range %d", range);
|
||||
return;
|
||||
if (range > MAX_RADIUS || range < 0
|
||||
/* camera flash uses radius 0 and passes Null object */
|
||||
|| (range == 0 && (type != LS_OBJECT || id->a_obj != 0))) {
|
||||
impossible("new_light_source: illegal range %d", range);
|
||||
return (light_source *) 0;
|
||||
}
|
||||
|
||||
ls = (light_source *) alloc(sizeof *ls);
|
||||
@@ -75,6 +91,7 @@ new_light_source(x, y, range, type, id)
|
||||
g.light_base = ls;
|
||||
|
||||
g.vision_full_recalc = 1; /* make the source show up */
|
||||
return ls;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -95,7 +112,7 @@ anything *id;
|
||||
(in particular: chameleon vs prot. from shape changers) */
|
||||
switch (type) {
|
||||
case LS_OBJECT:
|
||||
tmp_id.a_uint = id->a_obj->o_id;
|
||||
tmp_id.a_uint = id->a_obj ? id->a_obj->o_id : 0;
|
||||
break;
|
||||
case LS_MONSTER:
|
||||
tmp_id.a_uint = id->a_monst->m_id;
|
||||
@@ -145,7 +162,8 @@ char **cs_rows;
|
||||
* vision recalc.
|
||||
*/
|
||||
if (ls->type == LS_OBJECT) {
|
||||
if (get_obj_location(ls->id.a_obj, &ls->x, &ls->y, 0))
|
||||
if (ls->range == 0 /* camera flash; caller has set ls->{x,y} */
|
||||
|| get_obj_location(ls->id.a_obj, &ls->x, &ls->y, 0))
|
||||
ls->flags |= LSF_SHOW;
|
||||
} else if (ls->type == LS_MONSTER) {
|
||||
if (get_mon_location(ls->id.a_monst, &ls->x, &ls->y, 0))
|
||||
@@ -177,8 +195,8 @@ char **cs_rows;
|
||||
for (; y <= max_y; y++) {
|
||||
row = cs_rows[y];
|
||||
offset = limits[abs(y - ls->y)];
|
||||
if ((min_x = (ls->x - offset)) < 0)
|
||||
min_x = 0;
|
||||
if ((min_x = (ls->x - offset)) < 1)
|
||||
min_x = 1;
|
||||
if ((max_x = (ls->x + offset)) >= COLNO)
|
||||
max_x = COLNO - 1;
|
||||
|
||||
@@ -210,62 +228,94 @@ 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 */
|
||||
remember terrain, objects, and monsters being revealed;
|
||||
if 'obj' is Null, <x,y> is being hit by a camera's light flash */
|
||||
void
|
||||
show_transient_light(obj, x, y)
|
||||
struct obj *obj;
|
||||
int x, y;
|
||||
{
|
||||
light_source *ls;
|
||||
light_source *ls = 0;
|
||||
anything cameraflash;
|
||||
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;
|
||||
/* Null object indicates camera flash */
|
||||
if (!obj) {
|
||||
/* no need to temporarily light an already lit spot */
|
||||
if (levl[x][y].lit)
|
||||
return;
|
||||
|
||||
cameraflash = cg.zeroany;
|
||||
/* radius 0 will just light <x,y>; cameraflash.a_obj is Null */
|
||||
ls = new_light_core(x, y, 0, LS_OBJECT, &cameraflash);
|
||||
} else {
|
||||
/* thrown or kicked object which is emitting light; validate its
|
||||
light source to 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) {
|
||||
if (!ls || (obj && 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 */
|
||||
return;
|
||||
}
|
||||
|
||||
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))
|
||||
if (obj) /* put lit candle or lamp temporarily on the map */
|
||||
place_object(obj, g.bhitpos.x, g.bhitpos.y);
|
||||
else /* camera flash: no object; directly set light source's location */
|
||||
ls->x = x, ls->y = y;
|
||||
|
||||
/* full recalc; runs do_light_sources() */
|
||||
vision_recalc(0);
|
||||
flush_screen(0);
|
||||
|
||||
radius_squared = ls->range * ls->range;
|
||||
for (mon = fmon; mon; mon = mon->nmon) {
|
||||
if (DEADMONSTER(mon) || (mon->isgd && !mon->mx))
|
||||
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) {
|
||||
if (canseemon(mon))
|
||||
mon->mtemplit = 1;
|
||||
/* [what about worm tails?] */
|
||||
}
|
||||
/* [what about worm tails?] */
|
||||
}
|
||||
|
||||
if (obj) { /* take thrown/kicked candle or lamp off the map */
|
||||
delay_output();
|
||||
remove_object(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
/* delete any camera flash light sources and 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;
|
||||
int mtempcount;
|
||||
|
||||
/* in case we're cleaning up a camera flash, remove all object light
|
||||
sources which aren't associated with a specific object */
|
||||
discard_flashes();
|
||||
if (g.vision_full_recalc) /* set by del_light_source() */
|
||||
vision_recalc(0);
|
||||
|
||||
/* for thrown/kicked candle or lamp or for camera flash, some
|
||||
monsters may have been mapped in light which has now gone away
|
||||
so need to be replaced by "remembered, unseen monster" glyph */
|
||||
mtempcount = 0;
|
||||
for (mon = fmon; mon; mon = mon->nmon) {
|
||||
if (DEADMONSTER(mon))
|
||||
continue;
|
||||
@@ -276,9 +326,22 @@ transient_light_cleanup()
|
||||
map_invisible(mon->mx, mon->my);
|
||||
}
|
||||
}
|
||||
if (mtempcount) {
|
||||
vision_recalc(0);
|
||||
if (mtempcount)
|
||||
flush_screen(0);
|
||||
}
|
||||
|
||||
/* camera flashes have Null object; caller wants to get rid of them now */
|
||||
static void
|
||||
discard_flashes()
|
||||
{
|
||||
light_source *ls, *nxt_ls;
|
||||
|
||||
for (ls = g.light_base; ls; ls = nxt_ls) {
|
||||
nxt_ls = ls->next;
|
||||
if (ls->type != LS_OBJECT)
|
||||
continue;
|
||||
if (!ls->id.a_obj)
|
||||
del_light_source(LS_OBJECT, &ls->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +381,13 @@ int range;
|
||||
int count, actual, is_global;
|
||||
light_source **prev, *curr;
|
||||
|
||||
/* camera flash light sources have Null object and would trigger
|
||||
impossible("no id!") below; they can only happen here if we're
|
||||
in the midst of a panic save and they wouldn't be useful after
|
||||
restore so just throw any that are present away */
|
||||
discard_flashes();
|
||||
g.vision_full_recalc = 0;
|
||||
|
||||
if (perform_bwrite(nhfp)) {
|
||||
count = maybe_write_ls(nhfp, range, FALSE);
|
||||
if (nhfp->structlevel) {
|
||||
@@ -330,7 +400,7 @@ int range;
|
||||
}
|
||||
|
||||
if (release_data(nhfp)) {
|
||||
for (prev = &g.light_base; (curr = *prev) != 0;) {
|
||||
for (prev = &g.light_base; (curr = *prev) != 0; ) {
|
||||
if (!curr->id.a_monst) {
|
||||
impossible("save_light_sources: no id! [range=%d]", range);
|
||||
is_global = 0;
|
||||
|
||||
Reference in New Issue
Block a user