Extend #stats beyond just monsters and objects. Have it display memory usage for traps, engravings, light sources, timers, pending shop wall/floor repair, regions, bones tracking, named object types, and dungeon overview. No doubt there are other memory consumers that I've overlooked.
741 lines
23 KiB
C
741 lines
23 KiB
C
/* NetHack 3.6 light.c $NHDT-Date: 1446191876 2015/10/30 07:57:56 $ $NHDT-Branch: master $:$NHDT-Revision: 1.28 $ */
|
|
/* Copyright (c) Dean Luick, 1994 */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "lev.h" /* for checking save modes */
|
|
|
|
/*
|
|
* Mobile light sources.
|
|
*
|
|
* This implementation minimizes memory at the expense of extra
|
|
* recalculations.
|
|
*
|
|
* Light sources are "things" that have a physical position and range.
|
|
* They have a type, which gives us information about them. Currently
|
|
* they are only attached to objects and monsters. Note well: the
|
|
* polymorphed-player handling assumes that both youmonst.m_id and
|
|
* youmonst.mx will always remain 0.
|
|
*
|
|
* Light sources, like timers, either follow game play (RANGE_GLOBAL) or
|
|
* stay on a level (RANGE_LEVEL). Light sources are unique by their
|
|
* (type, id) pair. For light sources attached to objects, this id
|
|
* is a pointer to the object.
|
|
*
|
|
* The major working function is do_light_sources(). It is called
|
|
* when the vision system is recreating its "could see" array. Here
|
|
* we add a flag (TEMP_LIT) to the array for all locations that are lit
|
|
* via a light source. The bad part of this is that we have to
|
|
* re-calculate the LOS of each light source every time the vision
|
|
* system runs. Even if the light sources and any topology (vision blocking
|
|
* positions) have not changed. The good part is that no extra memory
|
|
* is used, plus we don't have to figure out how far the sources have moved,
|
|
* or if the topology has changed.
|
|
*
|
|
* The structure of the save/restore mechanism is amazingly similar to
|
|
* the timer save/restore. This is because they both have the same
|
|
* principals of having pointers into objects that must be recalculated
|
|
* across saves and restores.
|
|
*/
|
|
|
|
/* flags */
|
|
#define LSF_SHOW 0x1 /* display the light source */
|
|
#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
|
|
|
|
static light_source *light_base = 0;
|
|
|
|
STATIC_DCL void FDECL(write_ls, (int, light_source *));
|
|
STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P));
|
|
|
|
/* imported from vision.c, for small circles */
|
|
extern char circle_data[];
|
|
extern char circle_start[];
|
|
|
|
/* Create a new light source. */
|
|
void
|
|
new_light_source(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;
|
|
}
|
|
|
|
ls = (light_source *) alloc(sizeof(light_source));
|
|
|
|
ls->next = light_base;
|
|
ls->x = x;
|
|
ls->y = y;
|
|
ls->range = range;
|
|
ls->type = type;
|
|
ls->id = *id;
|
|
ls->flags = 0;
|
|
light_base = ls;
|
|
|
|
vision_full_recalc = 1; /* make the source show up */
|
|
}
|
|
|
|
/*
|
|
* Delete a light source. This assumes only one light source is attached
|
|
* to an object at a time.
|
|
*/
|
|
void
|
|
del_light_source(type, id)
|
|
int type;
|
|
anything *id;
|
|
{
|
|
light_source *curr, *prev;
|
|
anything tmp_id;
|
|
|
|
tmp_id = zeroany;
|
|
/* need to be prepared for dealing a with light source which
|
|
has only been partially restored during a level change
|
|
(in particular: chameleon vs prot. from shape changers) */
|
|
switch (type) {
|
|
case LS_OBJECT:
|
|
tmp_id.a_uint = id->a_obj->o_id;
|
|
break;
|
|
case LS_MONSTER:
|
|
tmp_id.a_uint = id->a_monst->m_id;
|
|
break;
|
|
default:
|
|
tmp_id.a_uint = 0;
|
|
break;
|
|
}
|
|
|
|
for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) {
|
|
if (curr->type != type)
|
|
continue;
|
|
if (curr->id.a_obj
|
|
== ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id.a_obj : id->a_obj)) {
|
|
if (prev)
|
|
prev->next = curr->next;
|
|
else
|
|
light_base = curr->next;
|
|
|
|
free((genericptr_t) curr);
|
|
vision_full_recalc = 1;
|
|
return;
|
|
}
|
|
}
|
|
impossible("del_light_source: not found type=%d, id=%s", type,
|
|
fmt_ptr((genericptr_t) id->a_obj));
|
|
}
|
|
|
|
/* Mark locations that are temporarily lit via mobile light sources. */
|
|
void
|
|
do_light_sources(cs_rows)
|
|
char **cs_rows;
|
|
{
|
|
int x, y, min_x, max_x, max_y, offset;
|
|
char *limits;
|
|
short at_hero_range = 0;
|
|
light_source *ls;
|
|
char *row;
|
|
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
ls->flags &= ~LSF_SHOW;
|
|
|
|
/*
|
|
* Check for moved light sources. It may be possible to
|
|
* save some effort if an object has not moved, but not in
|
|
* the current setup -- we need to recalculate for every
|
|
* vision recalc.
|
|
*/
|
|
if (ls->type == LS_OBJECT) {
|
|
if (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))
|
|
ls->flags |= LSF_SHOW;
|
|
}
|
|
|
|
/* 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;
|
|
else
|
|
at_hero_range = ls->range;
|
|
}
|
|
|
|
if (ls->flags & LSF_SHOW) {
|
|
/*
|
|
* Walk the points in the circle and see if they are
|
|
* visible from the center. If so, mark'em.
|
|
*
|
|
* Kevin's tests indicated that doing this brute-force
|
|
* method is faster for radius <= 3 (or so).
|
|
*/
|
|
limits = circle_ptr(ls->range);
|
|
if ((max_y = (ls->y + ls->range)) >= ROWNO)
|
|
max_y = ROWNO - 1;
|
|
if ((y = (ls->y - ls->range)) < 0)
|
|
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 ((max_x = (ls->x + offset)) >= COLNO)
|
|
max_x = COLNO - 1;
|
|
|
|
if (ls->x == u.ux && ls->y == u.uy) {
|
|
/*
|
|
* If the light source is located at the hero, then
|
|
* we can use the COULD_SEE bits already calculated
|
|
* by the vision system. More importantly than
|
|
* 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
|
|
* make things look "correct". The vision system
|
|
* does this.
|
|
*/
|
|
for (x = min_x; x <= max_x; x++)
|
|
if (row[x] & COULD_SEE)
|
|
row[x] |= TEMP_LIT;
|
|
} else {
|
|
for (x = min_x; x <= max_x; x++)
|
|
if ((ls->x == x && ls->y == y)
|
|
|| clear_path((int) ls->x, (int) ls->y, x, y))
|
|
row[x] |= TEMP_LIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* (mon->mx == 0) implies migrating */
|
|
#define mon_is_local(mon) ((mon)->mx > 0)
|
|
|
|
struct monst *
|
|
find_mid(nid, fmflags)
|
|
unsigned nid;
|
|
unsigned fmflags;
|
|
{
|
|
struct monst *mtmp;
|
|
|
|
if (!nid)
|
|
return &youmonst;
|
|
if (fmflags & FM_FMON)
|
|
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
|
|
if (!DEADMONSTER(mtmp) && mtmp->m_id == nid)
|
|
return mtmp;
|
|
if (fmflags & FM_MIGRATE)
|
|
for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
|
|
if (mtmp->m_id == nid)
|
|
return mtmp;
|
|
if (fmflags & FM_MYDOGS)
|
|
for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon)
|
|
if (mtmp->m_id == nid)
|
|
return mtmp;
|
|
return (struct monst *) 0;
|
|
}
|
|
|
|
/* Save all light sources of the given range. */
|
|
void
|
|
save_light_sources(fd, mode, range)
|
|
int fd, mode, range;
|
|
{
|
|
int count, actual, is_global;
|
|
light_source **prev, *curr;
|
|
|
|
if (perform_bwrite(mode)) {
|
|
count = maybe_write_ls(fd, range, FALSE);
|
|
bwrite(fd, (genericptr_t) &count, sizeof count);
|
|
actual = maybe_write_ls(fd, range, TRUE);
|
|
if (actual != count)
|
|
panic("counted %d light sources, wrote %d! [range=%d]", count,
|
|
actual, range);
|
|
}
|
|
|
|
if (release_data(mode)) {
|
|
for (prev = &light_base; (curr = *prev) != 0;) {
|
|
if (!curr->id.a_monst) {
|
|
impossible("save_light_sources: no id! [range=%d]", range);
|
|
is_global = 0;
|
|
} else
|
|
switch (curr->type) {
|
|
case LS_OBJECT:
|
|
is_global = !obj_is_local(curr->id.a_obj);
|
|
break;
|
|
case LS_MONSTER:
|
|
is_global = !mon_is_local(curr->id.a_monst);
|
|
break;
|
|
default:
|
|
is_global = 0;
|
|
impossible("save_light_sources: bad type (%d) [range=%d]",
|
|
curr->type, range);
|
|
break;
|
|
}
|
|
/* if global and not doing local, or vice versa, remove it */
|
|
if (is_global ^ (range == RANGE_LEVEL)) {
|
|
*prev = curr->next;
|
|
free((genericptr_t) curr);
|
|
} else {
|
|
prev = &(*prev)->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pull in the structures from disk, but don't recalculate the object
|
|
* pointers.
|
|
*/
|
|
void
|
|
restore_light_sources(fd)
|
|
int fd;
|
|
{
|
|
int count;
|
|
light_source *ls;
|
|
|
|
/* restore elements */
|
|
mread(fd, (genericptr_t) &count, sizeof count);
|
|
|
|
while (count-- > 0) {
|
|
ls = (light_source *) alloc(sizeof(light_source));
|
|
mread(fd, (genericptr_t) ls, sizeof(light_source));
|
|
ls->next = light_base;
|
|
light_base = ls;
|
|
}
|
|
}
|
|
|
|
/* to support '#stats' wizard-mode command */
|
|
void
|
|
light_stats(hdrfmt, hdrbuf, count, size)
|
|
const char *hdrfmt;
|
|
char *hdrbuf;
|
|
long *count, *size;
|
|
{
|
|
light_source *ls;
|
|
|
|
Sprintf(hdrbuf, hdrfmt, (long) sizeof (light_source));
|
|
*count = *size = 0L;
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
++*count;
|
|
*size += (long) sizeof *ls;
|
|
}
|
|
}
|
|
|
|
/* Relink all lights that are so marked. */
|
|
void
|
|
relink_light_sources(ghostly)
|
|
boolean ghostly;
|
|
{
|
|
char which;
|
|
unsigned nid;
|
|
light_source *ls;
|
|
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
if (ls->flags & LSF_NEEDS_FIXUP) {
|
|
if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
|
|
if (ghostly) {
|
|
if (!lookup_id_mapping(ls->id.a_uint, &nid))
|
|
impossible("relink_light_sources: no id mapping");
|
|
} else
|
|
nid = ls->id.a_uint;
|
|
if (ls->type == LS_OBJECT) {
|
|
which = 'o';
|
|
ls->id.a_obj = find_oid(nid);
|
|
} else {
|
|
which = 'm';
|
|
ls->id.a_monst = find_mid(nid, FM_EVERYWHERE);
|
|
}
|
|
if (!ls->id.a_monst)
|
|
impossible("relink_light_sources: cant find %c_id %d",
|
|
which, nid);
|
|
} else
|
|
impossible("relink_light_sources: bad type (%d)", ls->type);
|
|
|
|
ls->flags &= ~LSF_NEEDS_FIXUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Part of the light source save routine. Count up the number of light
|
|
* sources that would be written. If write_it is true, actually write
|
|
* the light source out.
|
|
*/
|
|
STATIC_OVL int
|
|
maybe_write_ls(fd, range, write_it)
|
|
int fd, range;
|
|
boolean write_it;
|
|
{
|
|
int count = 0, is_global;
|
|
light_source *ls;
|
|
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
if (!ls->id.a_monst) {
|
|
impossible("maybe_write_ls: no id! [range=%d]", range);
|
|
continue;
|
|
}
|
|
switch (ls->type) {
|
|
case LS_OBJECT:
|
|
is_global = !obj_is_local(ls->id.a_obj);
|
|
break;
|
|
case LS_MONSTER:
|
|
is_global = !mon_is_local(ls->id.a_monst);
|
|
break;
|
|
default:
|
|
is_global = 0;
|
|
impossible("maybe_write_ls: bad type (%d) [range=%d]", ls->type,
|
|
range);
|
|
break;
|
|
}
|
|
/* if global and not doing local, or vice versa, count it */
|
|
if (is_global ^ (range == RANGE_LEVEL)) {
|
|
count++;
|
|
if (write_it)
|
|
write_ls(fd, ls);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void
|
|
light_sources_sanity_check()
|
|
{
|
|
light_source *ls;
|
|
struct monst *mtmp;
|
|
struct obj *otmp;
|
|
unsigned int auint;
|
|
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
if (!ls->id.a_monst)
|
|
panic("insane light source: no id!");
|
|
if (ls->type == LS_OBJECT) {
|
|
otmp = (struct obj *) ls->id.a_obj;
|
|
auint = otmp->o_id;
|
|
if (find_oid(auint) != otmp)
|
|
panic("insane light source: can't find obj #%u!", auint);
|
|
} else if (ls->type == LS_MONSTER) {
|
|
mtmp = (struct monst *) ls->id.a_monst;
|
|
auint = mtmp->m_id;
|
|
if (find_mid(auint, FM_EVERYWHERE) != mtmp)
|
|
panic("insane light source: can't find mon #%u!", auint);
|
|
} else {
|
|
panic("insane light source: bad ls type %d", ls->type);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write a light source structure to disk. */
|
|
STATIC_OVL void
|
|
write_ls(fd, ls)
|
|
int fd;
|
|
light_source *ls;
|
|
{
|
|
anything arg_save;
|
|
struct obj *otmp;
|
|
struct monst *mtmp;
|
|
|
|
if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
|
|
if (ls->flags & LSF_NEEDS_FIXUP) {
|
|
bwrite(fd, (genericptr_t) ls, sizeof(light_source));
|
|
} else {
|
|
/* replace object pointer with id for write, then put back */
|
|
arg_save = ls->id;
|
|
if (ls->type == LS_OBJECT) {
|
|
otmp = ls->id.a_obj;
|
|
ls->id = zeroany;
|
|
ls->id.a_uint = otmp->o_id;
|
|
if (find_oid((unsigned) ls->id.a_uint) != otmp)
|
|
impossible("write_ls: can't find obj #%u!",
|
|
ls->id.a_uint);
|
|
} else { /* ls->type == LS_MONSTER */
|
|
mtmp = (struct monst *) ls->id.a_monst;
|
|
ls->id = zeroany;
|
|
ls->id.a_uint = mtmp->m_id;
|
|
if (find_mid((unsigned) ls->id.a_uint, FM_EVERYWHERE) != mtmp)
|
|
impossible("write_ls: can't find mon #%u!",
|
|
ls->id.a_uint);
|
|
}
|
|
ls->flags |= LSF_NEEDS_FIXUP;
|
|
bwrite(fd, (genericptr_t) ls, sizeof(light_source));
|
|
ls->id = arg_save;
|
|
ls->flags &= ~LSF_NEEDS_FIXUP;
|
|
}
|
|
} else {
|
|
impossible("write_ls: bad type (%d)", ls->type);
|
|
}
|
|
}
|
|
|
|
/* Change light source's ID from src to dest. */
|
|
void
|
|
obj_move_light_source(src, dest)
|
|
struct obj *src, *dest;
|
|
{
|
|
light_source *ls;
|
|
|
|
for (ls = light_base; ls; ls = ls->next)
|
|
if (ls->type == LS_OBJECT && ls->id.a_obj == src)
|
|
ls->id.a_obj = dest;
|
|
src->lamplit = 0;
|
|
dest->lamplit = 1;
|
|
}
|
|
|
|
/* return true if there exist any light sources */
|
|
boolean
|
|
any_light_source()
|
|
{
|
|
return (boolean) (light_base != (light_source *) 0);
|
|
}
|
|
|
|
/*
|
|
* Snuff an object light source if at (x,y). This currently works
|
|
* only for burning light sources.
|
|
*/
|
|
void
|
|
snuff_light_source(x, y)
|
|
int x, y;
|
|
{
|
|
light_source *ls;
|
|
struct obj *obj;
|
|
|
|
for (ls = light_base; ls; ls = ls->next)
|
|
/*
|
|
* Is this position check valid??? Can I assume that the positions
|
|
* will always be correct because the objects would have been
|
|
* updated with the last vision update? [Is that recent enough???]
|
|
*/
|
|
if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) {
|
|
obj = ls->id.a_obj;
|
|
if (obj_is_burning(obj)) {
|
|
/* The only way to snuff Sunsword is to unwield it. Darkness
|
|
* scrolls won't affect it. (If we got here because it was
|
|
* dropped or thrown inside a monster, this won't matter
|
|
* anyway because it will go out when dropped.)
|
|
*/
|
|
if (artifact_light(obj))
|
|
continue;
|
|
end_burn(obj, obj->otyp != MAGIC_LAMP);
|
|
/*
|
|
* The current ls element has just been removed (and
|
|
* ls->next is now invalid). Return assuming that there
|
|
* is only one light source attached to each object.
|
|
*/
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return TRUE if object sheds any light at all. */
|
|
boolean
|
|
obj_sheds_light(obj)
|
|
struct obj *obj;
|
|
{
|
|
/* so far, only burning objects shed light */
|
|
return obj_is_burning(obj);
|
|
}
|
|
|
|
/* Return TRUE if sheds light AND will be snuffed by end_burn(). */
|
|
boolean
|
|
obj_is_burning(obj)
|
|
struct obj *obj;
|
|
{
|
|
return (boolean) (obj->lamplit && (obj->otyp == MAGIC_LAMP
|
|
|| ignitable(obj)
|
|
|| artifact_light(obj)));
|
|
}
|
|
|
|
/* copy the light source(s) attached to src, and attach it/them to dest */
|
|
void
|
|
obj_split_light_source(src, dest)
|
|
struct obj *src, *dest;
|
|
{
|
|
light_source *ls, *new_ls;
|
|
|
|
for (ls = light_base; ls; ls = ls->next)
|
|
if (ls->type == LS_OBJECT && ls->id.a_obj == src) {
|
|
/*
|
|
* Insert the new source at beginning of list. This will
|
|
* never interfere us walking down the list - we are already
|
|
* past the insertion point.
|
|
*/
|
|
new_ls = (light_source *) alloc(sizeof(light_source));
|
|
*new_ls = *ls;
|
|
if (Is_candle(src)) {
|
|
/* split candles may emit less light than original group */
|
|
ls->range = candle_light_range(src);
|
|
new_ls->range = candle_light_range(dest);
|
|
vision_full_recalc = 1; /* in case range changed */
|
|
}
|
|
new_ls->id.a_obj = dest;
|
|
new_ls->next = light_base;
|
|
light_base = new_ls;
|
|
dest->lamplit = 1; /* now an active light source */
|
|
}
|
|
}
|
|
|
|
/* light source `src' has been folded into light source `dest';
|
|
used for merging lit candles and adding candle(s) to lit candelabrum */
|
|
void
|
|
obj_merge_light_sources(src, dest)
|
|
struct obj *src, *dest;
|
|
{
|
|
light_source *ls;
|
|
|
|
/* src == dest implies adding to candelabrum */
|
|
if (src != dest)
|
|
end_burn(src, TRUE); /* extinguish candles */
|
|
|
|
for (ls = light_base; ls; ls = ls->next)
|
|
if (ls->type == LS_OBJECT && ls->id.a_obj == dest) {
|
|
ls->range = candle_light_range(dest);
|
|
vision_full_recalc = 1; /* in case range changed */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* light source `obj' is being made brighter or dimmer */
|
|
void
|
|
obj_adjust_light_radius(obj, new_radius)
|
|
struct obj *obj;
|
|
int new_radius;
|
|
{
|
|
light_source *ls;
|
|
|
|
for (ls = light_base; ls; ls = ls->next)
|
|
if (ls->type == LS_OBJECT && ls->id.a_obj == obj) {
|
|
if (new_radius != ls->range)
|
|
vision_full_recalc = 1;
|
|
ls->range = new_radius;
|
|
return;
|
|
}
|
|
impossible("obj_adjust_light_radius: can't find %s", xname(obj));
|
|
}
|
|
|
|
/* Candlelight is proportional to the number of candles;
|
|
minimum range is 2 rather than 1 for playability. */
|
|
int
|
|
candle_light_range(obj)
|
|
struct obj *obj;
|
|
{
|
|
int radius;
|
|
|
|
if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
|
|
/*
|
|
* The special candelabrum emits more light than the
|
|
* corresponding number of candles would.
|
|
* 1..3 candles, range 2 (minimum range);
|
|
* 4..6 candles, range 3 (normal lamp range);
|
|
* 7 candles, range 4 (bright).
|
|
*/
|
|
radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4;
|
|
} else if (Is_candle(obj)) {
|
|
/*
|
|
* Range is incremented by powers of 7 so that it will take
|
|
* wizard mode quantities of candles to get more light than
|
|
* from a lamp, without imposing an arbitrary limit.
|
|
* 1..6 candles, range 2;
|
|
* 7..48 candles, range 3;
|
|
* 49..342 candles, range 4; &c.
|
|
*/
|
|
long n = obj->quan;
|
|
|
|
radius = 1; /* always incremented at least once */
|
|
do {
|
|
radius++;
|
|
n /= 7L;
|
|
} while (n > 0L);
|
|
} else {
|
|
/* we're only called for lit candelabrum or candles */
|
|
/* impossible("candlelight for %d?", obj->otyp); */
|
|
radius = 3; /* lamp's value */
|
|
}
|
|
return radius;
|
|
}
|
|
|
|
/* light emitting artifact's range depends upon its curse/bless state */
|
|
int
|
|
arti_light_radius(obj)
|
|
struct obj *obj;
|
|
{
|
|
/*
|
|
* Used by begin_burn() when setting up a new light source
|
|
* (obj->lamplit will already be set by this point) and
|
|
* also by bless()/unbless()/uncurse()/curse() to decide
|
|
* whether to call obj_adjust_light_radius().
|
|
*/
|
|
|
|
/* sanity check [simplifies usage by bless()/curse()/&c] */
|
|
if (!obj->lamplit || !artifact_light(obj))
|
|
return 0;
|
|
|
|
/* cursed radius of 1 is not noticeable for an item that's
|
|
carried by the hero but is if it's carried by a monster
|
|
or left lit on the floor (not applicable for Sunsword) */
|
|
return (obj->blessed ? 3 : !obj->cursed ? 2 : 1);
|
|
}
|
|
|
|
/* adverb describing lit artifact's light; depends on curse/bless state */
|
|
const char *
|
|
arti_light_description(obj)
|
|
struct obj *obj;
|
|
{
|
|
switch (arti_light_radius(obj)) {
|
|
case 3:
|
|
return "brilliantly"; /* blessed */
|
|
case 2:
|
|
return "brightly"; /* uncursed */
|
|
case 1:
|
|
return "dimly"; /* cursed */
|
|
default:
|
|
break;
|
|
}
|
|
return "strangely";
|
|
}
|
|
|
|
int
|
|
wiz_light_sources()
|
|
{
|
|
winid win;
|
|
char buf[BUFSZ];
|
|
light_source *ls;
|
|
|
|
win = create_nhwindow(NHW_MENU); /* corner text window */
|
|
if (win == WIN_ERR)
|
|
return 0;
|
|
|
|
Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy);
|
|
putstr(win, 0, buf);
|
|
putstr(win, 0, "");
|
|
|
|
if (light_base) {
|
|
putstr(win, 0, "location range flags type id");
|
|
putstr(win, 0, "-------- ----- ------ ---- -------");
|
|
for (ls = light_base; ls; ls = ls->next) {
|
|
Sprintf(buf, " %2d,%2d %2d 0x%04x %s %s", ls->x, ls->y,
|
|
ls->range, ls->flags,
|
|
(ls->type == LS_OBJECT
|
|
? "obj"
|
|
: ls->type == LS_MONSTER
|
|
? (mon_is_local(ls->id.a_monst)
|
|
? "mon"
|
|
: (ls->id.a_monst == &youmonst)
|
|
? "you"
|
|
/* migrating monster */
|
|
: "<m>")
|
|
: "???"),
|
|
fmt_ptr(ls->id.a_void));
|
|
putstr(win, 0, buf);
|
|
}
|
|
} else
|
|
putstr(win, 0, "<none>");
|
|
|
|
display_nhwindow(win, FALSE);
|
|
destroy_nhwindow(win);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*light.c*/
|