cmdhelp revamp
[aNetHack.git] / src / light.c
blob446dc390eb81cc2c07b1feda45ac7510574f3482
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. */
5 #include "hack.h"
6 #include "lev.h" /* for checking save modes */
8 /*
9 * Mobile light sources.
11 * This implementation minimizes memory at the expense of extra
12 * recalculations.
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.
41 /* flags */
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. */
55 void
56 new_light_source(x, y, range, type, id)
57 xchar x, y;
58 int range, type;
59 anything *id;
61 light_source *ls;
63 if (range > MAX_RADIUS || range < 1) {
64 impossible("new_light_source: illegal range %d", range);
65 return;
68 ls = (light_source *) alloc(sizeof(light_source));
70 ls->next = light_base;
71 ls->x = x;
72 ls->y = y;
73 ls->range = range;
74 ls->type = type;
75 ls->id = *id;
76 ls->flags = 0;
77 light_base = ls;
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.
86 void
87 del_light_source(type, id)
88 int type;
89 anything *id;
91 light_source *curr, *prev;
92 anything tmp_id;
94 tmp_id = zeroany;
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) */
98 switch (type) {
99 case LS_OBJECT:
100 tmp_id.a_uint = id->a_obj->o_id;
101 break;
102 case LS_MONSTER:
103 tmp_id.a_uint = id->a_monst->m_id;
104 break;
105 default:
106 tmp_id.a_uint = 0;
107 break;
110 for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) {
111 if (curr->type != type)
112 continue;
113 if (curr->id.a_obj
114 == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id.a_obj : id->a_obj)) {
115 if (prev)
116 prev->next = curr->next;
117 else
118 light_base = curr->next;
120 free((genericptr_t) curr);
121 vision_full_recalc = 1;
122 return;
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. */
130 void
131 do_light_sources(cs_rows)
132 char **cs_rows;
134 int x, y, min_x, max_x, max_y, offset;
135 char *limits;
136 short at_hero_range = 0;
137 light_source *ls;
138 char *row;
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
147 * vision recalc.
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 */
158 /* at hero */
159 if (ls->x == u.ux && ls->y == u.uy) {
160 if (at_hero_range >= ls->range)
161 ls->flags &= ~LSF_SHOW;
162 else
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)
176 max_y = ROWNO - 1;
177 if ((y = (ls->y - ls->range)) < 0)
178 y = 0;
179 for (; y <= max_y; y++) {
180 row = cs_rows[y];
181 offset = limits[abs(y - ls->y)];
182 if ((min_x = (ls->x - offset)) < 0)
183 min_x = 0;
184 if ((max_x = (ls->x + offset)) >= COLNO)
185 max_x = COLNO - 1;
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
197 * does this.
199 for (x = min_x; x <= max_x; x++)
200 if (row[x] & COULD_SEE)
201 row[x] |= TEMP_LIT;
202 } else {
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))
206 row[x] |= TEMP_LIT;
213 /* (mon->mx == 0) implies migrating */
214 #define mon_is_local(mon) ((mon)->mx > 0)
216 struct monst *
217 find_mid(nid, fmflags)
218 unsigned nid;
219 unsigned fmflags;
221 struct monst *mtmp;
223 if (!nid)
224 return &youmonst;
225 if (fmflags & FM_FMON)
226 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
227 if (!DEADMONSTER(mtmp) && mtmp->m_id == nid)
228 return mtmp;
229 if (fmflags & FM_MIGRATE)
230 for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
231 if (mtmp->m_id == nid)
232 return mtmp;
233 if (fmflags & FM_MYDOGS)
234 for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon)
235 if (mtmp->m_id == nid)
236 return mtmp;
237 return (struct monst *) 0;
240 /* Save all light sources of the given range. */
241 void
242 save_light_sources(fd, mode, range)
243 int 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);
252 if (actual != count)
253 panic("counted %d light sources, wrote %d! [range=%d]", count,
254 actual, range);
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);
261 is_global = 0;
262 } else
263 switch (curr->type) {
264 case LS_OBJECT:
265 is_global = !obj_is_local(curr->id.a_obj);
266 break;
267 case LS_MONSTER:
268 is_global = !mon_is_local(curr->id.a_monst);
269 break;
270 default:
271 is_global = 0;
272 impossible("save_light_sources: bad type (%d) [range=%d]",
273 curr->type, range);
274 break;
276 /* if global and not doing local, or vice versa, remove it */
277 if (is_global ^ (range == RANGE_LEVEL)) {
278 *prev = curr->next;
279 free((genericptr_t) curr);
280 } else {
281 prev = &(*prev)->next;
288 * Pull in the structures from disk, but don't recalculate the object
289 * pointers.
291 void
292 restore_light_sources(fd)
293 int fd;
295 int count;
296 light_source *ls;
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;
305 light_base = ls;
309 /* to support '#stats' wizard-mode command */
310 void
311 light_stats(hdrfmt, hdrbuf, count, size)
312 const char *hdrfmt;
313 char *hdrbuf;
314 long *count, *size;
316 light_source *ls;
318 Sprintf(hdrbuf, hdrfmt, (long) sizeof (light_source));
319 *count = *size = 0L;
320 for (ls = light_base; ls; ls = ls->next) {
321 ++*count;
322 *size += (long) sizeof *ls;
326 /* Relink all lights that are so marked. */
327 void
328 relink_light_sources(ghostly)
329 boolean ghostly;
331 char which;
332 unsigned nid;
333 light_source *ls;
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) {
338 if (ghostly) {
339 if (!lookup_id_mapping(ls->id.a_uint, &nid))
340 impossible("relink_light_sources: no id mapping");
341 } else
342 nid = ls->id.a_uint;
343 if (ls->type == LS_OBJECT) {
344 which = 'o';
345 ls->id.a_obj = find_oid(nid);
346 } else {
347 which = 'm';
348 ls->id.a_monst = find_mid(nid, FM_EVERYWHERE);
350 if (!ls->id.a_monst)
351 impossible("relink_light_sources: cant find %c_id %d",
352 which, nid);
353 } else
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.
366 STATIC_OVL int
367 maybe_write_ls(fd, range, write_it)
368 int fd, range;
369 boolean write_it;
371 int count = 0, is_global;
372 light_source *ls;
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);
377 continue;
379 switch (ls->type) {
380 case LS_OBJECT:
381 is_global = !obj_is_local(ls->id.a_obj);
382 break;
383 case LS_MONSTER:
384 is_global = !mon_is_local(ls->id.a_monst);
385 break;
386 default:
387 is_global = 0;
388 impossible("maybe_write_ls: bad type (%d) [range=%d]", ls->type,
389 range);
390 break;
392 /* if global and not doing local, or vice versa, count it */
393 if (is_global ^ (range == RANGE_LEVEL)) {
394 count++;
395 if (write_it)
396 write_ls(fd, ls);
400 return count;
403 void
404 light_sources_sanity_check()
406 light_source *ls;
407 struct monst *mtmp;
408 struct obj *otmp;
409 unsigned int auint;
411 for (ls = light_base; ls; ls = ls->next) {
412 if (!ls->id.a_monst)
413 panic("insane light source: no id!");
414 if (ls->type == LS_OBJECT) {
415 otmp = (struct obj *) ls->id.a_obj;
416 auint = otmp->o_id;
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;
421 auint = mtmp->m_id;
422 if (find_mid(auint, FM_EVERYWHERE) != mtmp)
423 panic("insane light source: can't find mon #%u!", auint);
424 } else {
425 panic("insane light source: bad ls type %d", ls->type);
430 /* Write a light source structure to disk. */
431 STATIC_OVL void
432 write_ls(fd, ls)
433 int fd;
434 light_source *ls;
436 anything arg_save;
437 struct obj *otmp;
438 struct monst *mtmp;
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));
443 } else {
444 /* replace object pointer with id for write, then put back */
445 arg_save = ls->id;
446 if (ls->type == LS_OBJECT) {
447 otmp = ls->id.a_obj;
448 ls->id = zeroany;
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!",
452 ls->id.a_uint);
453 } else { /* ls->type == LS_MONSTER */
454 mtmp = (struct monst *) ls->id.a_monst;
455 ls->id = zeroany;
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!",
459 ls->id.a_uint);
461 ls->flags |= LSF_NEEDS_FIXUP;
462 bwrite(fd, (genericptr_t) ls, sizeof(light_source));
463 ls->id = arg_save;
464 ls->flags &= ~LSF_NEEDS_FIXUP;
466 } else {
467 impossible("write_ls: bad type (%d)", ls->type);
471 /* Change light source's ID from src to dest. */
472 void
473 obj_move_light_source(src, dest)
474 struct obj *src, *dest;
476 light_source *ls;
478 for (ls = light_base; ls; ls = ls->next)
479 if (ls->type == LS_OBJECT && ls->id.a_obj == src)
480 ls->id.a_obj = dest;
481 src->lamplit = 0;
482 dest->lamplit = 1;
485 /* return true if there exist any light sources */
486 boolean
487 any_light_source()
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.
496 void
497 snuff_light_source(x, y)
498 int x, y;
500 light_source *ls;
501 struct obj *obj;
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) {
510 obj = ls->id.a_obj;
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))
518 continue;
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.
525 return;
530 /* Return TRUE if object sheds any light at all. */
531 boolean
532 obj_sheds_light(obj)
533 struct obj *obj;
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(). */
540 boolean
541 obj_is_burning(obj)
542 struct obj *obj;
544 return (boolean) (obj->lamplit && (obj->otyp == MAGIC_LAMP
545 || ignitable(obj)
546 || artifact_light(obj)));
549 /* copy the light source(s) attached to src, and attach it/them to dest */
550 void
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));
564 *new_ls = *ls;
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;
573 light_base = new_ls;
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 */
580 void
581 obj_merge_light_sources(src, dest)
582 struct obj *src, *dest;
584 light_source *ls;
586 /* src == dest implies adding to candelabrum */
587 if (src != dest)
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 */
594 break;
598 /* light source `obj' is being made brighter or dimmer */
599 void
600 obj_adjust_light_radius(obj, new_radius)
601 struct obj *obj;
602 int new_radius;
604 light_source *ls;
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;
611 return;
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)
620 struct obj *obj;
622 int radius;
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.
642 long n = obj->quan;
644 radius = 1; /* always incremented at least once */
645 do {
646 radius++;
647 n /= 7L;
648 } while (n > 0L);
649 } else {
650 /* we're only called for lit candelabrum or candles */
651 /* impossible("candlelight for %d?", obj->otyp); */
652 radius = 3; /* lamp's value */
654 return radius;
657 /* light emitting artifact's range depends upon its curse/bless state */
659 arti_light_radius(obj)
660 struct obj *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))
671 return 0;
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 */
680 const char *
681 arti_light_description(obj)
682 struct obj *obj;
684 switch (arti_light_radius(obj)) {
685 case 3:
686 return "brilliantly"; /* blessed */
687 case 2:
688 return "brightly"; /* uncursed */
689 case 1:
690 return "dimly"; /* cursed */
691 default:
692 break;
694 return "strangely";
698 wiz_light_sources()
700 winid win;
701 char buf[BUFSZ];
702 light_source *ls;
704 win = create_nhwindow(NHW_MENU); /* corner text window */
705 if (win == WIN_ERR)
706 return 0;
708 Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy);
709 putstr(win, 0, buf);
710 putstr(win, 0, "");
712 if (light_base) {
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
719 ? "obj"
720 : ls->type == LS_MONSTER
721 ? (mon_is_local(ls->id.a_monst)
722 ? "mon"
723 : (ls->id.a_monst == &youmonst)
724 ? "you"
725 /* migrating monster */
726 : "<m>")
727 : "???"),
728 fmt_ptr(ls->id.a_void));
729 putstr(win, 0, buf);
731 } else
732 putstr(win, 0, "<none>");
734 display_nhwindow(win, FALSE);
735 destroy_nhwindow(win);
737 return 0;
740 /*light.c*/