record amount of gold in hero's possession in xlogfile
new objects: amulets of flying and guarding
new monsters: displacer beast ('f') and genetic engineer ('Q')
+make camera flash which reveals previously unseen map features or objects or
+ monsters record those on the hero's map; monsters revert to 'unseen'
Platform- and/or Interface-Specific New Features
/*
* Circle information
*/
-#define MAX_RADIUS 15 /* this is in points from the source */
+#define MAX_RADIUS 16 /* this is in points from the source */
/* Use this macro to get a list of distances of the edges (see vision.c). */
#define circle_ptr(z) (&circle_data[(int) circle_start[z]])
(u.dz > 0) ? surface(u.ux, u.uy) : ceiling(u.ux, u.uy));
} else if (!u.dx && !u.dy) {
(void) zapyourself(obj, TRUE);
- } else if ((mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT,
- (int FDECL((*), (MONST_P, OBJ_P))) 0,
- (int FDECL((*), (OBJ_P, OBJ_P))) 0, &obj)) != 0) {
- obj->ox = u.ux, obj->oy = u.uy;
- (void) flash_hits_mon(mtmp, obj);
+ } else {
+ mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT,
+ (int FDECL((*), (MONST_P, OBJ_P))) 0,
+ (int FDECL((*), (OBJ_P, OBJ_P))) 0, &obj);
+ obj->ox = u.ux, obj->oy = u.uy; /* flash_hits_mon() wants this */
+ if (mtmp)
+ (void) flash_hits_mon(mtmp, obj);
+ /* normally bhit() would do this but for FLASHED_LIGHT we want it
+ to be deferred until after flash_hits_mon() */
+ transient_light_cleanup();
}
return 1;
}
#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));
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);
g.light_base = ls;
g.vision_full_recalc = 1; /* make the source show up */
+ return ls;
}
/*
(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;
* 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))
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;
/* 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;
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);
}
}
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) {
}
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;
u.umconf--;
}
+/* returns 1 if light flash has noticeable effect on 'mtmp', 0 otherwise */
int
flash_hits_mon(mtmp, otmp)
struct monst *mtmp;
struct obj *otmp; /* source of flash */
{
- int tmp, amt, res = 0, useeit = canseemon(mtmp);
+ struct rm *lev;
+ int tmp, amt, useeit, res = 0;
- if (mtmp->msleeping) {
+ if (g.notonhead)
+ return 0;
+ lev = &levl[mtmp->mx][mtmp->my];
+ useeit = canseemon(mtmp);
+
+ if (mtmp->msleeping && haseyes(mtmp->data)) {
mtmp->msleeping = 0;
if (useeit) {
pline_The("flash awakens %s.", mon_nam(mtmp));
mtmp->mcansee = 0;
mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50 / tmp);
}
+ } else if (flags.verbose && useeit) {
+ if (lev->lit)
+ pline("The flash of light shines on %s.", mon_nam(mtmp));
+ else
+ pline("%s is illuminated.", Monnam(mtmp));
+ res = 2; /* 'message has been given' temporary value */
}
}
+ if (res) {
+ if (!lev->lit)
+ display_nhwindow(WIN_MESSAGE, TRUE);
+ res &= 1; /* change temporary 2 back to 0 */
+ }
return res;
}
*
*/
const char circle_data[] = {
- /* 0*/ 1, 1,
- /* 2*/ 2, 2, 1,
- /* 5*/ 3, 3, 2, 1,
- /* 9*/ 4, 4, 4, 3, 2,
- /* 14*/ 5, 5, 5, 4, 3, 2,
- /* 20*/ 6, 6, 6, 5, 5, 4, 2,
- /* 27*/ 7, 7, 7, 6, 6, 5, 4, 2,
- /* 35*/ 8, 8, 8, 7, 7, 6, 6, 4, 2,
- /* 44*/ 9, 9, 9, 9, 8, 8, 7, 6, 5, 3,
- /* 54*/ 10, 10, 10, 10, 9, 9, 8, 7, 6, 5, 3,
- /* 65*/ 11, 11, 11, 11, 10, 10, 9, 9, 8, 7, 5, 3,
- /* 77*/ 12, 12, 12, 12, 11, 11, 10, 10, 9, 8, 7, 5, 3,
- /* 90*/ 13, 13, 13, 13, 12, 12, 12, 11, 10, 10, 9, 7, 6, 3,
- /*104*/ 14, 14, 14, 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 6, 3,
- /*119*/ 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 11, 10, 9, 8, 6, 3,
- /*135*/ 16 /* MAX_RADIUS+1; used to terminate range loops -dlc */
+ /* 0*/ 0,
+ /* 1*/ 1, 1,
+ /* 3*/ 2, 2, 1,
+ /* 6*/ 3, 3, 2, 1,
+ /* 10*/ 4, 4, 4, 3, 2,
+ /* 15*/ 5, 5, 5, 4, 3, 2,
+ /* 21*/ 6, 6, 6, 5, 5, 4, 2,
+ /* 28*/ 7, 7, 7, 6, 6, 5, 4, 2,
+ /* 36*/ 8, 8, 8, 7, 7, 6, 6, 4, 2,
+ /* 45*/ 9, 9, 9, 9, 8, 8, 7, 6, 5, 3,
+ /* 55*/ 10, 10, 10, 10, 9, 9, 8, 7, 6, 5, 3,
+ /* 66*/ 11, 11, 11, 11, 10, 10, 9, 9, 8, 7, 5, 3,
+ /* 78*/ 12, 12, 12, 12, 11, 11, 10, 10, 9, 8, 7, 5, 3,
+ /* 91*/ 13, 13, 13, 13, 12, 12, 12, 11, 10, 10, 9, 7, 6, 3,
+ /*105*/ 14, 14, 14, 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 6, 3,
+ /*120*/ 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 11, 10, 9, 8, 6, 3,
+ /*136*/ 16 /* MAX_RADIUS+1; used to terminate range loops -dlc */
};
/*
* These are the starting indexes into the circle_data[] array for a
- * circle of a given radius.
+ * circle of a given radius. Radius 0 used to be unused, but is now
+ * used for a single point: temporary light source of a camera flash
+ * as it traverses its path.
*/
const char circle_start[] = {
- /* */ 0, /* circles of radius zero are not used */
- /* 1*/ 0,
- /* 2*/ 2,
- /* 3*/ 5,
- /* 4*/ 9,
- /* 5*/ 14,
- /* 6*/ 20,
- /* 7*/ 27,
- /* 8*/ 35,
- /* 9*/ 44,
- /*10*/ 54,
- /*11*/ 65,
- /*12*/ 77,
- /*13*/ 90,
- /*14*/ 104,
- /*15*/ 119,
+ /* 0*/ 0,
+ /* 1*/ 1,
+ /* 2*/ 3,
+ /* 3*/ 6,
+ /* 4*/ 10,
+ /* 5*/ 15,
+ /* 6*/ 21,
+ /* 7*/ 38,
+ /* 8*/ 36,
+ /* 9*/ 45,
+ /*10*/ 55,
+ /*11*/ 66,
+ /*12*/ 78,
+ /*13*/ 91,
+ /*14*/ 105,
+ /*15*/ 120,
};
/*==========================================================================*/
nrmin = *rmin;
nrmax = *rmax;
- (void) memset((genericptr_t) * *rows, 0,
- ROWNO * COLNO); /* we see nothing */
+ (void) memset((genericptr_t) **rows, 0, ROWNO * COLNO); /* see nothing */
for (row = 0; row < ROWNO; row++) { /* set row min & max */
*nrmin++ = COLNO - 1;
- *nrmax++ = 0;
+ *nrmax++ = 1;
}
}
vision_recalc(control)
int control;
{
+ extern unsigned char seenv_matrix[3][3]; /* from display.c */
+ static unsigned char colbump[COLNO + 1]; /* cols to bump sv */
char **temp_array; /* points to the old vision array */
char **next_array; /* points to the new vision array */
char *next_row; /* row pointer for the new array */
char *old_row; /* row pointer for the old array */
char *next_rmin; /* min pointer for the new array */
char *next_rmax; /* max pointer for the new array */
- const char *ranges; /* circle ranges -- used for xray & night vision */
+ const char *ranges; /* circle ranges -- used for xray & night vision */
int row = 0; /* row counter (outer loop) */
int start, stop; /* inner loop starting/stopping index */
int dx, dy; /* one step from a lit door or lit wall (see below) */
register int col; /* inner loop counter */
register struct rm *lev; /* pointer to current pos */
- struct rm *flev; /* pointer to position in "front" of current pos */
- extern unsigned char seenv_matrix[3][3]; /* from display.c */
- static unsigned char colbump[COLNO + 1]; /* cols to bump sv */
- unsigned char *sv; /* ptr to seen angle bits */
- int oldseenv; /* previous seenv value */
+ struct rm *flev; /* pointer to position in "front" of current pos */
+ unsigned char *sv; /* ptr to seen angle bits */
+ int oldseenv; /* previous seenv value */
g.vision_full_recalc = 0; /* reset flag */
if (g.in_mklev || !iflags.vision_inited)
*
* + Monsters to see with the "new" vision, even on the rogue
* level.
- *
* + Monsters can see you even when you're in a pit.
*/
view_from(u.uy, u.ux, next_array, next_rmin, next_rmax, 0,
} else if (Is_rogue_level(&u.uz)) {
rogue_vision(next_array, next_rmin, next_rmax);
} else {
- int has_night_vision = 1; /* hero has night vision */
+ int lo_col, has_night_vision = 1; /* hero has night vision */
if (Underwater && !Is_waterlevel(&u.uz)) {
/*
*/
has_night_vision = 0;
+ lo_col = max(u.ux - 1, 1);
for (row = u.uy - 1; row <= u.uy + 1; row++)
- for (col = u.ux - 1; col <= u.ux + 1; col++) {
+ for (col = lo_col; col <= u.ux + 1; col++) {
if (!isok(col, row) || !is_pool(col, row))
continue;
if (row >= ROWNO)
break;
- next_rmin[row] = max(0, u.ux - 1);
+ next_rmin[row] = max(1, u.ux - 1);
next_rmax[row] = min(COLNO - 1, u.ux + 1);
next_row = next_array[row];
dy = v_abs(u.uy - row);
next_row = next_array[row];
- start = max(0, u.ux - ranges[dy]);
+ start = max(1, u.ux - ranges[dy]);
stop = min(COLNO - 1, u.ux + ranges[dy]);
for (col = start; col <= stop; col++) {
char old_row_val = next_row[col];
+
next_row[col] |= IN_SIGHT;
oldseenv = levl[col][row].seenv;
levl[col][row].seenv = SVALL; /* see all! */
dy = v_abs(u.uy - row);
next_row = next_array[row];
- start = max(0, u.ux - ranges[dy]);
+ start = max(1, u.ux - ranges[dy]);
stop = min(COLNO - 1, u.ux + ranges[dy]);
for (col = start; col <= stop; col++)
/* iron bars will block anything big enough and break some things */
if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) {
+ if (obj->lamplit && !Blind)
+ show_transient_light(obj, g.bhitpos.x, g.bhitpos.y);
if (typ == IRONBARS
&& hits_bars(pobj, x - ddx, y - ddy, g.bhitpos.x, g.bhitpos.y,
point_blank ? 0 : !rn2(5), 1)) {
g.bhitpos.x -= ddx;
g.bhitpos.y -= ddy;
break;
- } else if (obj->lamplit && !Blind) {
- show_transient_light(obj, g.bhitpos.x, g.bhitpos.y);
}
+ } else if (weapon == FLASHED_LIGHT) {
+ if (!Blind)
+ show_transient_light((struct obj *) 0,
+ g.bhitpos.x, g.bhitpos.y);
}
if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
pay_for_damage("destroy", FALSE);
bhit_done:
+ /* note: for FLASHED_LIGHT, _caller_ must call transient_light_cleanup()
+ after possibly calling flash_hits_mon() */
if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
transient_light_cleanup();