1 /* NetHack 3.6 light.c $NHDT-Date: 1446191876 2015/10/30 07:57:56 $ $NHDT-Branch: master $:$NHDT-Revision: 1.28 $ */
2 /* Copyright (c) Dean Luick, 1994 */
3 /* NetHack may be freely redistributed. See license for details. */
6 #include "lev.h" /* for checking save modes */
9 * Mobile light sources.
11 * This implementation minimizes memory at the expense of extra
14 * Light sources are "things" that have a physical position and range.
15 * They have a type, which gives us information about them. Currently
16 * they are only attached to objects and monsters. Note well: the
17 * polymorphed-player handling assumes that both youmonst.m_id and
18 * youmonst.mx will always remain 0.
20 * Light sources, like timers, either follow game play (RANGE_GLOBAL) or
21 * stay on a level (RANGE_LEVEL). Light sources are unique by their
22 * (type, id) pair. For light sources attached to objects, this id
23 * is a pointer to the object.
25 * The major working function is do_light_sources(). It is called
26 * when the vision system is recreating its "could see" array. Here
27 * we add a flag (TEMP_LIT) to the array for all locations that are lit
28 * via a light source. The bad part of this is that we have to
29 * re-calculate the LOS of each light source every time the vision
30 * system runs. Even if the light sources and any topology (vision blocking
31 * positions) have not changed. The good part is that no extra memory
32 * is used, plus we don't have to figure out how far the sources have moved,
33 * or if the topology has changed.
35 * The structure of the save/restore mechanism is amazingly similar to
36 * the timer save/restore. This is because they both have the same
37 * principals of having pointers into objects that must be recalculated
38 * across saves and restores.
42 #define LSF_SHOW 0x1 /* display the light source */
43 #define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
45 static light_source
*light_base
= 0;
47 STATIC_DCL
void FDECL(write_ls
, (int, light_source
*));
48 STATIC_DCL
int FDECL(maybe_write_ls
, (int, int, BOOLEAN_P
));
50 /* imported from vision.c, for small circles */
51 extern char circle_data
[];
52 extern char circle_start
[];
54 /* Create a new light source. */
56 new_light_source(x
, y
, range
, type
, id
)
63 if (range
> MAX_RADIUS
|| range
< 1) {
64 impossible("new_light_source: illegal range %d", range
);
68 ls
= (light_source
*) alloc(sizeof(light_source
));
70 ls
->next
= light_base
;
79 vision_full_recalc
= 1; /* make the source show up */
83 * Delete a light source. This assumes only one light source is attached
84 * to an object at a time.
87 del_light_source(type
, id
)
91 light_source
*curr
, *prev
;
95 /* need to be prepared for dealing a with light source which
96 has only been partially restored during a level change
97 (in particular: chameleon vs prot. from shape changers) */
100 tmp_id
.a_uint
= id
->a_obj
->o_id
;
103 tmp_id
.a_uint
= id
->a_monst
->m_id
;
110 for (prev
= 0, curr
= light_base
; curr
; prev
= curr
, curr
= curr
->next
) {
111 if (curr
->type
!= type
)
114 == ((curr
->flags
& LSF_NEEDS_FIXUP
) ? tmp_id
.a_obj
: id
->a_obj
)) {
116 prev
->next
= curr
->next
;
118 light_base
= curr
->next
;
120 free((genericptr_t
) curr
);
121 vision_full_recalc
= 1;
125 impossible("del_light_source: not found type=%d, id=%s", type
,
126 fmt_ptr((genericptr_t
) id
->a_obj
));
129 /* Mark locations that are temporarily lit via mobile light sources. */
131 do_light_sources(cs_rows
)
134 int x
, y
, min_x
, max_x
, max_y
, offset
;
136 short at_hero_range
= 0;
140 for (ls
= light_base
; ls
; ls
= ls
->next
) {
141 ls
->flags
&= ~LSF_SHOW
;
144 * Check for moved light sources. It may be possible to
145 * save some effort if an object has not moved, but not in
146 * the current setup -- we need to recalculate for every
149 if (ls
->type
== LS_OBJECT
) {
150 if (get_obj_location(ls
->id
.a_obj
, &ls
->x
, &ls
->y
, 0))
151 ls
->flags
|= LSF_SHOW
;
152 } else if (ls
->type
== LS_MONSTER
) {
153 if (get_mon_location(ls
->id
.a_monst
, &ls
->x
, &ls
->y
, 0))
154 ls
->flags
|= LSF_SHOW
;
157 /* minor optimization: don't bother with duplicate light sources */
159 if (ls
->x
== u
.ux
&& ls
->y
== u
.uy
) {
160 if (at_hero_range
>= ls
->range
)
161 ls
->flags
&= ~LSF_SHOW
;
163 at_hero_range
= ls
->range
;
166 if (ls
->flags
& LSF_SHOW
) {
168 * Walk the points in the circle and see if they are
169 * visible from the center. If so, mark'em.
171 * Kevin's tests indicated that doing this brute-force
172 * method is faster for radius <= 3 (or so).
174 limits
= circle_ptr(ls
->range
);
175 if ((max_y
= (ls
->y
+ ls
->range
)) >= ROWNO
)
177 if ((y
= (ls
->y
- ls
->range
)) < 0)
179 for (; y
<= max_y
; y
++) {
181 offset
= limits
[abs(y
- ls
->y
)];
182 if ((min_x
= (ls
->x
- offset
)) < 0)
184 if ((max_x
= (ls
->x
+ offset
)) >= COLNO
)
187 if (ls
->x
== u
.ux
&& ls
->y
== u
.uy
) {
189 * If the light source is located at the hero, then
190 * we can use the COULD_SEE bits already calculated
191 * by the vision system. More importantly than
192 * this optimization, is that it allows the vision
193 * system to correct problems with clear_path().
194 * The function clear_path() is a simple LOS
195 * path checker that doesn't go out of its way
196 * make things look "correct". The vision system
199 for (x
= min_x
; x
<= max_x
; x
++)
200 if (row
[x
] & COULD_SEE
)
203 for (x
= min_x
; x
<= max_x
; x
++)
204 if ((ls
->x
== x
&& ls
->y
== y
)
205 || clear_path((int) ls
->x
, (int) ls
->y
, x
, y
))
213 /* (mon->mx == 0) implies migrating */
214 #define mon_is_local(mon) ((mon)->mx > 0)
217 find_mid(nid
, fmflags
)
225 if (fmflags
& FM_FMON
)
226 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
227 if (!DEADMONSTER(mtmp
) && mtmp
->m_id
== nid
)
229 if (fmflags
& FM_MIGRATE
)
230 for (mtmp
= migrating_mons
; mtmp
; mtmp
= mtmp
->nmon
)
231 if (mtmp
->m_id
== nid
)
233 if (fmflags
& FM_MYDOGS
)
234 for (mtmp
= mydogs
; mtmp
; mtmp
= mtmp
->nmon
)
235 if (mtmp
->m_id
== nid
)
237 return (struct monst
*) 0;
240 /* Save all light sources of the given range. */
242 save_light_sources(fd
, mode
, range
)
245 int count
, actual
, is_global
;
246 light_source
**prev
, *curr
;
248 if (perform_bwrite(mode
)) {
249 count
= maybe_write_ls(fd
, range
, FALSE
);
250 bwrite(fd
, (genericptr_t
) &count
, sizeof count
);
251 actual
= maybe_write_ls(fd
, range
, TRUE
);
253 panic("counted %d light sources, wrote %d! [range=%d]", count
,
257 if (release_data(mode
)) {
258 for (prev
= &light_base
; (curr
= *prev
) != 0;) {
259 if (!curr
->id
.a_monst
) {
260 impossible("save_light_sources: no id! [range=%d]", range
);
263 switch (curr
->type
) {
265 is_global
= !obj_is_local(curr
->id
.a_obj
);
268 is_global
= !mon_is_local(curr
->id
.a_monst
);
272 impossible("save_light_sources: bad type (%d) [range=%d]",
276 /* if global and not doing local, or vice versa, remove it */
277 if (is_global
^ (range
== RANGE_LEVEL
)) {
279 free((genericptr_t
) curr
);
281 prev
= &(*prev
)->next
;
288 * Pull in the structures from disk, but don't recalculate the object
292 restore_light_sources(fd
)
298 /* restore elements */
299 mread(fd
, (genericptr_t
) &count
, sizeof count
);
301 while (count
-- > 0) {
302 ls
= (light_source
*) alloc(sizeof(light_source
));
303 mread(fd
, (genericptr_t
) ls
, sizeof(light_source
));
304 ls
->next
= light_base
;
309 /* to support '#stats' wizard-mode command */
311 light_stats(hdrfmt
, hdrbuf
, count
, size
)
318 Sprintf(hdrbuf
, hdrfmt
, (long) sizeof (light_source
));
320 for (ls
= light_base
; ls
; ls
= ls
->next
) {
322 *size
+= (long) sizeof *ls
;
326 /* Relink all lights that are so marked. */
328 relink_light_sources(ghostly
)
335 for (ls
= light_base
; ls
; ls
= ls
->next
) {
336 if (ls
->flags
& LSF_NEEDS_FIXUP
) {
337 if (ls
->type
== LS_OBJECT
|| ls
->type
== LS_MONSTER
) {
339 if (!lookup_id_mapping(ls
->id
.a_uint
, &nid
))
340 impossible("relink_light_sources: no id mapping");
343 if (ls
->type
== LS_OBJECT
) {
345 ls
->id
.a_obj
= find_oid(nid
);
348 ls
->id
.a_monst
= find_mid(nid
, FM_EVERYWHERE
);
351 impossible("relink_light_sources: cant find %c_id %d",
354 impossible("relink_light_sources: bad type (%d)", ls
->type
);
356 ls
->flags
&= ~LSF_NEEDS_FIXUP
;
362 * Part of the light source save routine. Count up the number of light
363 * sources that would be written. If write_it is true, actually write
364 * the light source out.
367 maybe_write_ls(fd
, range
, write_it
)
371 int count
= 0, is_global
;
374 for (ls
= light_base
; ls
; ls
= ls
->next
) {
375 if (!ls
->id
.a_monst
) {
376 impossible("maybe_write_ls: no id! [range=%d]", range
);
381 is_global
= !obj_is_local(ls
->id
.a_obj
);
384 is_global
= !mon_is_local(ls
->id
.a_monst
);
388 impossible("maybe_write_ls: bad type (%d) [range=%d]", ls
->type
,
392 /* if global and not doing local, or vice versa, count it */
393 if (is_global
^ (range
== RANGE_LEVEL
)) {
404 light_sources_sanity_check()
411 for (ls
= light_base
; ls
; ls
= ls
->next
) {
413 panic("insane light source: no id!");
414 if (ls
->type
== LS_OBJECT
) {
415 otmp
= (struct obj
*) ls
->id
.a_obj
;
417 if (find_oid(auint
) != otmp
)
418 panic("insane light source: can't find obj #%u!", auint
);
419 } else if (ls
->type
== LS_MONSTER
) {
420 mtmp
= (struct monst
*) ls
->id
.a_monst
;
422 if (find_mid(auint
, FM_EVERYWHERE
) != mtmp
)
423 panic("insane light source: can't find mon #%u!", auint
);
425 panic("insane light source: bad ls type %d", ls
->type
);
430 /* Write a light source structure to disk. */
440 if (ls
->type
== LS_OBJECT
|| ls
->type
== LS_MONSTER
) {
441 if (ls
->flags
& LSF_NEEDS_FIXUP
) {
442 bwrite(fd
, (genericptr_t
) ls
, sizeof(light_source
));
444 /* replace object pointer with id for write, then put back */
446 if (ls
->type
== LS_OBJECT
) {
449 ls
->id
.a_uint
= otmp
->o_id
;
450 if (find_oid((unsigned) ls
->id
.a_uint
) != otmp
)
451 impossible("write_ls: can't find obj #%u!",
453 } else { /* ls->type == LS_MONSTER */
454 mtmp
= (struct monst
*) ls
->id
.a_monst
;
456 ls
->id
.a_uint
= mtmp
->m_id
;
457 if (find_mid((unsigned) ls
->id
.a_uint
, FM_EVERYWHERE
) != mtmp
)
458 impossible("write_ls: can't find mon #%u!",
461 ls
->flags
|= LSF_NEEDS_FIXUP
;
462 bwrite(fd
, (genericptr_t
) ls
, sizeof(light_source
));
464 ls
->flags
&= ~LSF_NEEDS_FIXUP
;
467 impossible("write_ls: bad type (%d)", ls
->type
);
471 /* Change light source's ID from src to dest. */
473 obj_move_light_source(src
, dest
)
474 struct obj
*src
, *dest
;
478 for (ls
= light_base
; ls
; ls
= ls
->next
)
479 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== src
)
485 /* return true if there exist any light sources */
489 return (boolean
) (light_base
!= (light_source
*) 0);
493 * Snuff an object light source if at (x,y). This currently works
494 * only for burning light sources.
497 snuff_light_source(x
, y
)
503 for (ls
= light_base
; ls
; ls
= ls
->next
)
505 * Is this position check valid??? Can I assume that the positions
506 * will always be correct because the objects would have been
507 * updated with the last vision update? [Is that recent enough???]
509 if (ls
->type
== LS_OBJECT
&& ls
->x
== x
&& ls
->y
== y
) {
511 if (obj_is_burning(obj
)) {
512 /* The only way to snuff Sunsword is to unwield it. Darkness
513 * scrolls won't affect it. (If we got here because it was
514 * dropped or thrown inside a monster, this won't matter
515 * anyway because it will go out when dropped.)
517 if (artifact_light(obj
))
519 end_burn(obj
, obj
->otyp
!= MAGIC_LAMP
);
521 * The current ls element has just been removed (and
522 * ls->next is now invalid). Return assuming that there
523 * is only one light source attached to each object.
530 /* Return TRUE if object sheds any light at all. */
535 /* so far, only burning objects shed light */
536 return obj_is_burning(obj
);
539 /* Return TRUE if sheds light AND will be snuffed by end_burn(). */
544 return (boolean
) (obj
->lamplit
&& (obj
->otyp
== MAGIC_LAMP
546 || artifact_light(obj
)));
549 /* copy the light source(s) attached to src, and attach it/them to dest */
551 obj_split_light_source(src
, dest
)
552 struct obj
*src
, *dest
;
554 light_source
*ls
, *new_ls
;
556 for (ls
= light_base
; ls
; ls
= ls
->next
)
557 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== src
) {
559 * Insert the new source at beginning of list. This will
560 * never interfere us walking down the list - we are already
561 * past the insertion point.
563 new_ls
= (light_source
*) alloc(sizeof(light_source
));
565 if (Is_candle(src
)) {
566 /* split candles may emit less light than original group */
567 ls
->range
= candle_light_range(src
);
568 new_ls
->range
= candle_light_range(dest
);
569 vision_full_recalc
= 1; /* in case range changed */
571 new_ls
->id
.a_obj
= dest
;
572 new_ls
->next
= light_base
;
574 dest
->lamplit
= 1; /* now an active light source */
578 /* light source `src' has been folded into light source `dest';
579 used for merging lit candles and adding candle(s) to lit candelabrum */
581 obj_merge_light_sources(src
, dest
)
582 struct obj
*src
, *dest
;
586 /* src == dest implies adding to candelabrum */
588 end_burn(src
, TRUE
); /* extinguish candles */
590 for (ls
= light_base
; ls
; ls
= ls
->next
)
591 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== dest
) {
592 ls
->range
= candle_light_range(dest
);
593 vision_full_recalc
= 1; /* in case range changed */
598 /* light source `obj' is being made brighter or dimmer */
600 obj_adjust_light_radius(obj
, new_radius
)
606 for (ls
= light_base
; ls
; ls
= ls
->next
)
607 if (ls
->type
== LS_OBJECT
&& ls
->id
.a_obj
== obj
) {
608 if (new_radius
!= ls
->range
)
609 vision_full_recalc
= 1;
610 ls
->range
= new_radius
;
613 impossible("obj_adjust_light_radius: can't find %s", xname(obj
));
616 /* Candlelight is proportional to the number of candles;
617 minimum range is 2 rather than 1 for playability. */
619 candle_light_range(obj
)
624 if (obj
->otyp
== CANDELABRUM_OF_INVOCATION
) {
626 * The special candelabrum emits more light than the
627 * corresponding number of candles would.
628 * 1..3 candles, range 2 (minimum range);
629 * 4..6 candles, range 3 (normal lamp range);
630 * 7 candles, range 4 (bright).
632 radius
= (obj
->spe
< 4) ? 2 : (obj
->spe
< 7) ? 3 : 4;
633 } else if (Is_candle(obj
)) {
635 * Range is incremented by powers of 7 so that it will take
636 * wizard mode quantities of candles to get more light than
637 * from a lamp, without imposing an arbitrary limit.
638 * 1..6 candles, range 2;
639 * 7..48 candles, range 3;
640 * 49..342 candles, range 4; &c.
644 radius
= 1; /* always incremented at least once */
650 /* we're only called for lit candelabrum or candles */
651 /* impossible("candlelight for %d?", obj->otyp); */
652 radius
= 3; /* lamp's value */
657 /* light emitting artifact's range depends upon its curse/bless state */
659 arti_light_radius(obj
)
663 * Used by begin_burn() when setting up a new light source
664 * (obj->lamplit will already be set by this point) and
665 * also by bless()/unbless()/uncurse()/curse() to decide
666 * whether to call obj_adjust_light_radius().
669 /* sanity check [simplifies usage by bless()/curse()/&c] */
670 if (!obj
->lamplit
|| !artifact_light(obj
))
673 /* cursed radius of 1 is not noticeable for an item that's
674 carried by the hero but is if it's carried by a monster
675 or left lit on the floor (not applicable for Sunsword) */
676 return (obj
->blessed
? 3 : !obj
->cursed
? 2 : 1);
679 /* adverb describing lit artifact's light; depends on curse/bless state */
681 arti_light_description(obj
)
684 switch (arti_light_radius(obj
)) {
686 return "brilliantly"; /* blessed */
688 return "brightly"; /* uncursed */
690 return "dimly"; /* cursed */
704 win
= create_nhwindow(NHW_MENU
); /* corner text window */
708 Sprintf(buf
, "Mobile light sources: hero @ (%2d,%2d)", u
.ux
, u
.uy
);
713 putstr(win
, 0, "location range flags type id");
714 putstr(win
, 0, "-------- ----- ------ ---- -------");
715 for (ls
= light_base
; ls
; ls
= ls
->next
) {
716 Sprintf(buf
, " %2d,%2d %2d 0x%04x %s %s", ls
->x
, ls
->y
,
717 ls
->range
, ls
->flags
,
718 (ls
->type
== LS_OBJECT
720 : ls
->type
== LS_MONSTER
721 ? (mon_is_local(ls
->id
.a_monst
)
723 : (ls
->id
.a_monst
== &youmonst
)
725 /* migrating monster */
728 fmt_ptr(ls
->id
.a_void
));
732 putstr(win
, 0, "<none>");
734 display_nhwindow(win
, FALSE
);
735 destroy_nhwindow(win
);