1 /* NetHack 3.7 light.c $NHDT-Date: 1726609514 2024/09/17 21:45:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */
2 /* Copyright (c) Dean Luick, 1994 */
3 /* NetHack may be freely redistributed. See license for details. */
8 * Mobile light sources.
10 * This implementation minimizes memory at the expense of extra
13 * Light sources are "things" that have a physical position and range.
14 * They have a type, which gives us information about them. Currently
15 * they are only attached to objects and monsters. Note well: the
16 * polymorphed-player handling assumes that gy.youmonst.m_id will
17 * always remain 1 and gy.youmonst.mx will always remain 0.
19 * Light sources, like timers, either follow game play (RANGE_GLOBAL) or
20 * stay on a level (RANGE_LEVEL). Light sources are unique by their
21 * (type, id) pair. For light sources attached to objects, this id
22 * is a pointer to the object.
24 * The major working function is do_light_sources(). It is called
25 * when the vision system is recreating its "could see" array. Here
26 * we add a flag (TEMP_LIT) to the array for all locations that are lit
27 * via a light source. The bad part of this is that we have to
28 * re-calculate the LOS of each light source every time the vision
29 * system runs. Even if the light sources and any topology (vision blocking
30 * positions) have not changed. The good part is that no extra memory
31 * is used, plus we don't have to figure out how far the sources have moved,
32 * or if the topology has changed.
34 * The structure of the save/restore mechanism is amazingly similar to
35 * the timer save/restore. This is because they both have the same
36 * principals of having pointers into objects that must be recalculated
37 * across saves and restores.
41 #define LSF_SHOW 0x1 /* display the light source */
42 #define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
43 #define LSF_IS_PROBLEMATIC 0x4 /* impossible situation encountered */
45 staticfn light_source
*new_light_core(coordxy
, coordxy
,
46 int, int, anything
*) NONNULLPTRS
;
47 staticfn
void delete_ls(light_source
*);
48 staticfn
void discard_flashes(void);
49 staticfn
void write_ls(NHFILE
*, light_source
*);
50 staticfn
int maybe_write_ls(NHFILE
*, int, boolean
);
51 staticfn
unsigned whereis_mon(struct monst
*, unsigned);
53 /* imported from vision.c, for small circles */
54 extern const coordxy circle_data
[];
55 extern const coordxy circle_start
[];
58 /* Create a new light source. Caller (and extern.h) doesn't need to know
59 anything about type 'light_source'. */
61 new_light_source(coordxy x
, coordxy y
, int range
, int type
, anything
*id
)
63 (void) new_light_core(x
, y
, range
, type
, id
);
66 /* Create a new light source and return it. Only used within this file. */
67 staticfn light_source
*
68 new_light_core(coordxy x
, coordxy y
, int range
, int type
, anything
*id
)
72 if (range
> MAX_RADIUS
|| range
< 0
73 /* camera flash uses radius 0 and passes Null object */
74 || (range
== 0 && (type
!= LS_OBJECT
|| id
->a_obj
!= 0))) {
75 impossible("new_light_source: illegal range %d", range
);
76 return (light_source
*) 0;
79 ls
= (light_source
*) alloc(sizeof *ls
);
81 (void) memset((genericptr_t
) ls
, 0, sizeof (light_source
));
82 ls
->next
= gl
.light_base
;
91 gv
.vision_full_recalc
= 1; /* make the source show up */
95 /* Find and delete a light source.
96 Assumes at most one light source is attached to an object at a time. */
98 del_light_source(int type
, anything
*id
)
104 /* need to be prepared for dealing a with light source which
105 has only been partially restored during a level change
106 (in particular: chameleon vs prot. from shape changers) */
109 impossible("del_light_source:type=none");
113 tmp_id
.a_uint
= id
->a_obj
? id
->a_obj
->o_id
: 0;
116 tmp_id
.a_uint
= id
->a_monst
->m_id
;
123 /* find the light source from its id */
124 for (curr
= gl
.light_base
; curr
; curr
= curr
->next
) {
125 if (curr
->type
!= type
)
128 == ((curr
->flags
& LSF_NEEDS_FIXUP
) ? tmp_id
.a_obj
: id
->a_obj
))
134 impossible("del_light_source: not found type=%d, id=%s", type
,
135 fmt_ptr((genericptr_t
) id
->a_obj
));
139 /* remove a light source from the light_base list and free it */
141 delete_ls(light_source
*ls
)
143 light_source
*curr
, *prev
;
145 for (prev
= 0, curr
= gl
.light_base
; curr
;
146 prev
= curr
, curr
= curr
->next
) {
149 prev
->next
= curr
->next
;
151 gl
.light_base
= curr
->next
;
157 (void) memset((genericptr_t
) ls
, 0, sizeof(light_source
));
158 free((genericptr_t
) ls
);
159 gv
.vision_full_recalc
= 1;
161 impossible("delete_ls not found, ls=%s", fmt_ptr((genericptr_t
) ls
));
166 /* Mark locations that are temporarily lit via mobile light sources. */
168 do_light_sources(seenV
**cs_rows
)
170 coordxy x
, y
, min_x
, max_x
, max_y
;
172 const coordxy
*limits
;
173 short at_hero_range
= 0;
177 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
178 ls
->flags
&= ~LSF_SHOW
;
181 * Check for moved light sources. It may be possible to
182 * save some effort if an object has not moved, but not in
183 * the current setup -- we need to recalculate for every
186 if (ls
->type
== LS_OBJECT
) {
187 if (ls
->range
== 0 /* camera flash; caller has set ls->{x,y} */
188 || get_obj_location(ls
->id
.a_obj
, &ls
->x
, &ls
->y
, 0))
189 ls
->flags
|= LSF_SHOW
;
190 } else if (ls
->type
== LS_MONSTER
) {
191 if (get_mon_location(ls
->id
.a_monst
, &ls
->x
, &ls
->y
, 0))
192 ls
->flags
|= LSF_SHOW
;
195 /* minor optimization: don't bother with duplicate light sources
197 if (u_at(ls
->x
, ls
->y
)) {
198 if (at_hero_range
>= ls
->range
)
199 ls
->flags
&= ~LSF_SHOW
;
201 at_hero_range
= ls
->range
;
204 if (ls
->flags
& LSF_SHOW
) {
206 * Walk the points in the circle and see if they are
207 * visible from the center. If so, mark'em.
209 * Kevin's tests indicated that doing this brute-force
210 * method is faster for radius <= 3 (or so).
212 limits
= circle_ptr(ls
->range
);
213 if ((max_y
= (ls
->y
+ ls
->range
)) >= ROWNO
)
215 if ((y
= (ls
->y
- ls
->range
)) < 0)
217 for (; y
<= max_y
; y
++) {
219 offset
= limits
[abs(y
- ls
->y
)];
220 if ((min_x
= (ls
->x
- offset
)) < 1)
222 if ((max_x
= (ls
->x
+ offset
)) >= COLNO
)
225 if (u_at(ls
->x
, ls
->y
)) {
227 * If the light source is located at the hero, then
228 * we can use the COULD_SEE bits already calculated
229 * by the vision system. More importantly than
230 * this optimization, is that it allows the vision
231 * system to correct problems with clear_path().
232 * The function clear_path() is a simple LOS
233 * path checker that doesn't go out of its way to
234 * make things look "correct". The vision system
237 for (x
= min_x
; x
<= max_x
; x
++)
238 if (row
[x
] & COULD_SEE
)
241 for (x
= min_x
; x
<= max_x
; x
++)
242 if ((ls
->x
== x
&& ls
->y
== y
)
243 || clear_path((int) ls
->x
, (int) ls
->y
, x
, y
))
251 /* lit 'obj' has been thrown or kicked and is passing through x,y on the
252 way to its destination; show its light so that hero has a chance to
253 remember terrain, objects, and monsters being revealed;
254 if 'obj' is Null, <x,y> is being hit by a camera's light flash */
256 show_transient_light(struct obj
*obj
, coordxy x
, coordxy y
)
258 light_source
*ls
= 0;
259 anything cameraflash
;
263 /* Null object indicates camera flash */
265 /* no need to temporarily light an already lit spot */
269 cameraflash
= cg
.zeroany
;
270 /* radius 0 will just light <x,y>; cameraflash.a_obj is Null */
271 ls
= new_light_core(x
, y
, 0, LS_OBJECT
, &cameraflash
);
272 /* pacify static analysis; 'ls' is never Null for
273 new_light_core(,,0,LS_OBJECT,&zeroany) */
276 /* thrown or kicked object which is emitting light; validate its
277 light source to obtain its radius (for monster sightings) */
278 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
279 if (ls
->type
!= LS_OBJECT
)
281 if (ls
->id
.a_obj
== obj
)
284 assert(obj
!= NULL
); /* necessary condition to get into this 'else' */
285 if (!ls
|| obj
->where
!= OBJ_FREE
) {
286 impossible("transient light %s %s %s not %s?",
287 obj
->lamplit
? "lit" : "unlit",
288 simpleonames(obj
), otense(obj
, "are"),
289 !ls
? "a light source" : "free");
294 if (obj
) /* put lit candle or lamp temporarily on the map */
295 place_object(obj
, gb
.bhitpos
.x
, gb
.bhitpos
.y
);
296 else /* camera flash: no object; directly set light source's location */
297 ls
->x
= x
, ls
->y
= y
;
299 /* full recalc; runs do_light_sources() */
303 radius_squared
= ls
->range
* ls
->range
;
304 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
305 if (DEADMONSTER(mon
) || (mon
->isgd
&& !mon
->mx
))
307 /* light range is the radius of a circle and we're limiting
308 canseemon() to a square enclosing that circle, but setting
309 mtemplit 'erroneously' for a seen monster is not a problem;
310 it just flags monsters for another canseemon() check when
311 'obj' has reached its destination after missile traversal */
312 if (dist2(mon
->mx
, mon
->my
, x
, y
) <= radius_squared
) {
316 /* [what about worm tails?] */
319 if (obj
) { /* take thrown/kicked candle or lamp off the map */
325 /* delete any camera flash light sources and draw "remembered, unseen
326 monster" glyph at locations where a monster was flagged for being
327 visible during transient light movement but can't be seen now */
329 transient_light_cleanup(void)
334 /* in case we're cleaning up a camera flash, remove all object light
335 sources which aren't associated with a specific object */
337 if (gv
.vision_full_recalc
) /* set by del_light_source() */
340 /* for thrown/kicked candle or lamp or for camera flash, some
341 monsters may have been mapped in light which has now gone away
342 so need to be replaced by "remembered, unseen monster" glyph */
344 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
345 if (DEADMONSTER(mon
))
350 if (!canspotmon(mon
))
351 map_invisible(mon
->mx
, mon
->my
);
358 /* camera flashes have Null object; caller wants to get rid of them now */
360 discard_flashes(void)
362 light_source
*ls
, *nxt_ls
;
364 for (ls
= gl
.light_base
; ls
; ls
= nxt_ls
) {
366 if (ls
->type
== LS_OBJECT
&& !ls
->id
.a_obj
)
371 /* (mon->mx == 0) implies migrating */
372 #define mon_is_local(mon) ((mon)->mx > 0)
375 find_mid(unsigned nid
, unsigned fmflags
)
379 if ((fmflags
& FM_YOU
) && nid
== 1)
381 if (fmflags
& FM_FMON
)
382 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
383 if (!DEADMONSTER(mtmp
) && mtmp
->m_id
== nid
)
385 if (fmflags
& FM_MIGRATE
)
386 for (mtmp
= gm
.migrating_mons
; mtmp
; mtmp
= mtmp
->nmon
)
387 if (mtmp
->m_id
== nid
)
389 if (fmflags
& FM_MYDOGS
)
390 for (mtmp
= gm
.mydogs
; mtmp
; mtmp
= mtmp
->nmon
)
391 if (mtmp
->m_id
== nid
)
393 return (struct monst
*) 0;
397 whereis_mon(struct monst
*mon
, unsigned fmflags
)
401 if ((fmflags
& FM_YOU
) && mon
== &gy
.youmonst
)
403 if (fmflags
& FM_FMON
)
404 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
407 if (fmflags
& FM_MIGRATE
)
408 for (mtmp
= gm
.migrating_mons
; mtmp
; mtmp
= mtmp
->nmon
)
411 if (fmflags
& FM_MYDOGS
)
412 for (mtmp
= gm
.mydogs
; mtmp
; mtmp
= mtmp
->nmon
)
418 /* Save all light sources of the given range. */
420 save_light_sources(NHFILE
*nhfp
, int range
)
422 int count
, actual
, is_global
;
423 light_source
**prev
, *curr
;
425 /* camera flash light sources have Null object and would trigger
426 impossible("no id!") below; they can only happen here if we're
427 in the midst of a panic save and they wouldn't be useful after
428 restore so just throw any that are present away */
430 gv
.vision_full_recalc
= 0;
432 if (perform_bwrite(nhfp
)) {
433 count
= maybe_write_ls(nhfp
, range
, FALSE
);
434 if (nhfp
->structlevel
) {
435 bwrite(nhfp
->fd
, (genericptr_t
) &count
, sizeof count
);
437 actual
= maybe_write_ls(nhfp
, range
, TRUE
);
439 panic("counted %d light sources, wrote %d! [range=%d]", count
,
443 if (release_data(nhfp
)) {
444 for (prev
= &gl
.light_base
; (curr
= *prev
) != 0; ) {
445 if (!curr
->id
.a_monst
) {
446 impossible("save_light_sources: no id! [range=%d]", range
);
449 switch (curr
->type
) {
451 is_global
= !obj_is_local(curr
->id
.a_obj
);
454 is_global
= !mon_is_local(curr
->id
.a_monst
);
458 impossible("save_light_sources: bad type (%d) [range=%d]",
462 /* if global and not doing local, or vice versa, remove it */
463 if (is_global
^ (range
== RANGE_LEVEL
)) {
465 (void) memset((genericptr_t
) curr
, 0, sizeof(light_source
));
466 free((genericptr_t
) curr
);
468 prev
= &(*prev
)->next
;
475 * Pull in the structures from disk, but don't recalculate the object
479 restore_light_sources(NHFILE
*nhfp
)
484 /* restore elements */
485 if (nhfp
->structlevel
)
486 mread(nhfp
->fd
, (genericptr_t
) &count
, sizeof count
);
488 while (count
-- > 0) {
489 ls
= (light_source
*) alloc(sizeof(light_source
));
490 if (nhfp
->structlevel
)
491 mread(nhfp
->fd
, (genericptr_t
) ls
, sizeof(light_source
));
492 ls
->next
= gl
.light_base
;
497 DISABLE_WARNING_FORMAT_NONLITERAL
499 /* to support '#stats' wizard-mode command */
501 light_stats(const char *hdrfmt
, char *hdrbuf
, long *count
, long *size
)
505 Sprintf(hdrbuf
, hdrfmt
, (long) sizeof (light_source
));
507 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
509 *size
+= (long) sizeof *ls
;
513 RESTORE_WARNING_FORMAT_NONLITERAL
515 /* Relink all lights that are so marked. */
517 relink_light_sources(boolean ghostly
)
525 * There has been at least one instance during to-be-3.7 development
526 * where the light_base linked list ended up with a circular link.
527 * If that happens, then once all the traversed elements have their
528 * LSF_NEEDS_FIXUP flag cleared, the traversal attempt will run wild.
530 * The circular list instance was blamed on attempting to restore
531 * a save file which should have been invalidated by version/patch/
532 * editlevel verification, but wasn't rejected because EDITLEVEL
533 * didn't get incremented when it should have been. Valid data should
534 * never produce the problem and it isn't possible in general to guard
535 * against code updates that neglect to set the verification info up
539 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
540 if (ls
->flags
& LSF_NEEDS_FIXUP
) {
541 if (ls
->type
== LS_OBJECT
|| ls
->type
== LS_MONSTER
) {
543 if (ghostly
&& !lookup_id_mapping(nid
, &nid
))
544 panic("relink_light_sources: no id mapping");
547 if (ls
->type
== LS_OBJECT
) {
548 if ((ls
->id
.a_obj
= find_oid(nid
)) == 0)
551 if ((ls
->id
.a_monst
= find_mid(nid
, FM_EVERYWHERE
)) == 0)
555 panic("relink_light_sources: can't find %c_id %u",
558 panic("relink_light_sources: bad type (%d)", ls
->type
);
560 ls
->flags
&= ~LSF_NEEDS_FIXUP
;
566 * Part of the light source save routine. Count up the number of light
567 * sources that would be written. If write_it is true, actually write
568 * the light source out.
571 maybe_write_ls(NHFILE
*nhfp
, int range
, boolean write_it
)
573 int count
= 0, is_global
;
576 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
577 if (!ls
->id
.a_monst
) {
578 impossible("maybe_write_ls: no id! [range=%d]", range
);
583 is_global
= !obj_is_local(ls
->id
.a_obj
);
586 is_global
= !mon_is_local(ls
->id
.a_monst
);
590 impossible("maybe_write_ls: bad type (%d) [range=%d]", ls
->type
,
594 /* if global and not doing local, or vice versa, count it */
595 if (is_global
^ (range
== RANGE_LEVEL
)) {
606 light_sources_sanity_check(void)
613 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
615 panic("insane light source: no id!");
616 if (ls
->type
== LS_OBJECT
) {
619 if (find_oid(auint
) != otmp
)
620 panic("insane light source: can't find obj #%u!", auint
);
621 } else if (ls
->type
== LS_MONSTER
) {
622 mtmp
= (struct monst
*) ls
->id
.a_monst
;
624 if (find_mid(auint
, FM_EVERYWHERE
) != mtmp
)
625 panic("insane light source: can't find mon #%u!", auint
);
627 panic("insane light source: bad ls type %d", ls
->type
);
632 /* Write a light source structure to disk. */
634 write_ls(NHFILE
*nhfp
, light_source
*ls
)
640 if (ls
->type
== LS_OBJECT
|| ls
->type
== LS_MONSTER
) {
641 if (ls
->flags
& LSF_NEEDS_FIXUP
) {
642 if (nhfp
->structlevel
)
643 bwrite(nhfp
->fd
, (genericptr_t
) ls
, sizeof(light_source
));
645 /* replace object pointer with id for write, then put back */
647 if (ls
->type
== LS_OBJECT
) {
650 ls
->id
.a_uint
= otmp
->o_id
;
651 if (find_oid((unsigned) ls
->id
.a_uint
) != otmp
) {
652 impossible("write_ls: can't find obj #%u!",
654 ls
->flags
|= LSF_IS_PROBLEMATIC
;
656 } else { /* ls->type == LS_MONSTER */
659 mtmp
= (struct monst
*) ls
->id
.a_monst
;
661 /* The monster pointer has been stashed in the light source
662 * for a while and while there is code meant to clean-up the
663 * light source aspects if a monster goes away, there have
664 * been some reports of light source issues, such as when
665 * going to the planes.
667 * Verify that the stashed monst pointer is still present
668 * in one of the monster chains before pulling subfield
669 * values such as m_id from it, to avoid any attempt to
670 * pull random m_id value from (now) freed memory.
672 * find_mid() disregards a DEADMONSTER, but whereis_mon()
675 if ((monloc
= whereis_mon(mtmp
, FM_EVERYWHERE
)) != 0) {
677 ls
->id
.a_uint
= mtmp
->m_id
;
678 if (find_mid((unsigned) ls
->id
.a_uint
, monloc
) != mtmp
) {
679 impossible("write_ls: can't find mon%s #%u!",
680 DEADMONSTER(mtmp
) ? " because it's dead"
683 ls
->flags
|= LSF_IS_PROBLEMATIC
;
687 "write_ls: stashed monst ptr not in any chain");
688 ls
->flags
|= LSF_IS_PROBLEMATIC
;
691 if (ls
->flags
& LSF_IS_PROBLEMATIC
) {
692 /* TODO: cleanup this ls, or skip writing it */
694 ls
->flags
|= LSF_NEEDS_FIXUP
;
695 if (nhfp
->structlevel
)
696 bwrite(nhfp
->fd
, (genericptr_t
) ls
, sizeof(light_source
));
698 ls
->flags
&= ~LSF_NEEDS_FIXUP
;
699 ls
->flags
&= ~LSF_IS_PROBLEMATIC
;
702 impossible("write_ls: bad type (%d)", ls
->type
);
706 /* Change light source's ID from src to dest. */
708 obj_move_light_source(struct obj
*src
, struct obj
*dest
)
712 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
)
713 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== src
)
719 /* return true if there exist any light sources */
721 any_light_source(void)
723 return (boolean
) (gl
.light_base
!= (light_source
*) 0);
727 * Snuff an object light source if at (x,y). This currently works
728 * only for burning light sources.
731 snuff_light_source(coordxy x
, coordxy y
)
736 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
)
738 * Is this position check valid??? Can I assume that the positions
739 * will always be correct because the objects would have been
740 * updated with the last vision update? [Is that recent enough???]
742 if (ls
->type
== LS_OBJECT
&& ls
->x
== x
&& ls
->y
== y
) {
744 if (obj_is_burning(obj
)) {
745 /* The only way to snuff Sunsword is to unwield it. Darkness
746 * scrolls won't affect it. (If we got here because it was
747 * dropped or thrown inside a monster, this won't matter
748 * anyway because it will go out when dropped.)
750 if (artifact_light(obj
))
752 end_burn(obj
, obj
->otyp
!= MAGIC_LAMP
);
754 * The current ls element has just been removed (and
755 * ls->next is now invalid). Return assuming that there
756 * is only one light source attached to each object.
763 /* Return TRUE if object sheds any light at all. */
765 obj_sheds_light(struct obj
*obj
)
767 /* so far, only burning objects shed light */
768 return obj_is_burning(obj
);
771 /* Return TRUE if sheds light AND will be snuffed by end_burn(). */
773 obj_is_burning(struct obj
*obj
)
775 return (boolean
) (obj
->lamplit
&& (ignitable(obj
)
776 || artifact_light(obj
)));
779 /* copy the light source(s) attached to src, and attach it/them to dest */
781 obj_split_light_source(struct obj
*src
, struct obj
*dest
)
783 light_source
*ls
, *new_ls
;
785 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
)
786 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== src
) {
788 * Insert the new source at beginning of list. This will
789 * never interfere us walking down the list - we are already
790 * past the insertion point.
792 new_ls
= (light_source
*) alloc(sizeof(light_source
));
794 if (Is_candle(src
)) {
795 /* split candles may emit less light than original group */
796 ls
->range
= candle_light_range(src
);
797 new_ls
->range
= candle_light_range(dest
);
798 gv
.vision_full_recalc
= 1; /* in case range changed */
800 new_ls
->id
.a_obj
= dest
;
801 new_ls
->next
= gl
.light_base
;
802 gl
.light_base
= new_ls
;
803 dest
->lamplit
= 1; /* now an active light source */
807 /* light source `src' has been folded into light source `dest';
808 used for merging lit candles and adding candle(s) to lit candelabrum */
810 obj_merge_light_sources(struct obj
*src
, struct obj
*dest
)
814 /* src == dest implies adding to candelabrum */
816 end_burn(src
, TRUE
); /* extinguish candles */
818 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
)
819 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== dest
) {
820 ls
->range
= candle_light_range(dest
);
821 gv
.vision_full_recalc
= 1; /* in case range changed */
826 /* light source `obj' is being made brighter or dimmer */
828 obj_adjust_light_radius(struct obj
*obj
, int new_radius
)
832 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
)
833 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== obj
) {
834 if (new_radius
!= ls
->range
)
835 gv
.vision_full_recalc
= 1;
836 ls
->range
= new_radius
;
839 impossible("obj_adjust_light_radius: can't find %s", xname(obj
));
842 /* Candlelight is proportional to the number of candles;
843 minimum range is 2 rather than 1 for playability. */
845 candle_light_range(struct obj
*obj
)
849 if (obj
->otyp
== CANDELABRUM_OF_INVOCATION
) {
851 * The special candelabrum emits more light than the
852 * corresponding number of candles would.
853 * 1..3 candles, range 2 (minimum range);
854 * 4..6 candles, range 3 (normal lamp range);
855 * 7 candles, range 4 (bright).
857 radius
= (obj
->spe
< 4) ? 2 : (obj
->spe
< 7) ? 3 : 4;
858 } else if (Is_candle(obj
)) {
860 * Range is incremented quadratically. You can get the same
861 * amount of light as from a lamp with 4 candles, and
862 * even better light with 9 candles, and so on.
863 * 1..3 candles, range 2;
864 * 4..8 candles, range 3;
865 * 9..15 candles, range 4; &c.
869 radius
= 1; /* always incremented at least once */
870 while(radius
*radius
<= n
&& radius
< MAX_RADIUS
) {
874 /* we're only called for lit candelabrum or candles */
875 /* impossible("candlelight for %d?", obj->otyp); */
876 radius
= 3; /* lamp's value */
881 /* light emitting artifact's range depends upon its curse/bless state */
883 arti_light_radius(struct obj
*obj
)
888 * Used by begin_burn() when setting up a new light source
889 * (obj->lamplit will already be set by this point) and
890 * also by bless()/unbless()/uncurse()/curse() to decide
891 * whether to call obj_adjust_light_radius().
894 /* sanity check [simplifies usage by bless()/curse()/&c] */
895 if (!obj
->lamplit
|| !artifact_light(obj
))
898 /* cursed radius of 1 is not noticeable for an item that's
899 carried by the hero but is if it's carried by a monster
900 or left lit on the floor (not applicable for Sunsword) */
901 res
= (obj
->blessed
? 3 : !obj
->cursed
? 2 : 1);
903 /* if poly'd into gold dragon with embedded scales, make the scales
904 have minimum radiance (hero as light source will use light radius
905 based on monster form); otherwise, worn gold DSM gives off more
906 light than other light sources */
909 else if (obj
->otyp
== GOLD_DRAGON_SCALE_MAIL
) /* DSM but not scales */
915 /* adverb describing lit artifact's light; radius varies depending upon
916 curse/bless state; also used for gold dragon scales/scale mail */
918 arti_light_description(struct obj
*obj
)
920 switch (arti_light_radius(obj
)) {
922 return "radiantly"; /* blessed gold dragon scale mail */
924 return "brilliantly"; /* blessed artifact, uncursed gold DSM */
926 return "brightly"; /* uncursed artifact, cursed gold DSM */
928 return "dimly"; /* cursed artifact, embedded scales */
935 /* the #lightsources command */
937 wiz_light_sources(void)
943 win
= create_nhwindow(NHW_MENU
); /* corner text window */
947 Sprintf(buf
, "Mobile light sources: hero @ (%2d,%2d)", u
.ux
, u
.uy
);
952 putstr(win
, 0, "location range flags type id");
953 putstr(win
, 0, "-------- ----- ------ ---- -------");
954 for (ls
= gl
.light_base
; ls
; ls
= ls
->next
) {
955 Sprintf(buf
, " %2d,%2d %2d 0x%04x %s %s", ls
->x
, ls
->y
,
956 ls
->range
, ls
->flags
,
957 (ls
->type
== LS_OBJECT
959 : ls
->type
== LS_MONSTER
960 ? (mon_is_local(ls
->id
.a_monst
)
962 : (ls
->id
.a_monst
== &gy
.youmonst
)
964 /* migrating monster */
967 fmt_ptr(ls
->id
.a_void
));
971 putstr(win
, 0, "<none>");
973 display_nhwindow(win
, FALSE
);
974 destroy_nhwindow(win
);
979 /* for 'onefile' processing where end of this file isn't necessarily the
980 end of the source code seen by the compiler */
982 #undef LSF_NEEDS_FIXUP