OSDN Git Service

finalize changelog
[jnethack/source.git] / src / light.c
1 /* NetHack 3.6  light.c $NHDT-Date: 1559994625 2019/06/08 11:50:25 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.30 $ */
2 /* Copyright (c) Dean Luick, 1994                                       */
3 /* NetHack may be freely redistributed.  See license for details.       */
4
5 /* JNetHack Copyright */
6 /* For 3.6-, Copyright (c) SHIRAKATA Kentaro, 2002-2022            */
7 /* JNetHack may be freely redistributed.  See license for details. */
8
9 #include "hack.h"
10 #include "lev.h" /* for checking save modes */
11
12 /*
13  * Mobile light sources.
14  *
15  * This implementation minimizes memory at the expense of extra
16  * recalculations.
17  *
18  * Light sources are "things" that have a physical position and range.
19  * They have a type, which gives us information about them.  Currently
20  * they are only attached to objects and monsters.  Note well:  the
21  * polymorphed-player handling assumes that both youmonst.m_id and
22  * youmonst.mx will always remain 0.
23  *
24  * Light sources, like timers, either follow game play (RANGE_GLOBAL) or
25  * stay on a level (RANGE_LEVEL).  Light sources are unique by their
26  * (type, id) pair.  For light sources attached to objects, this id
27  * is a pointer to the object.
28  *
29  * The major working function is do_light_sources(). It is called
30  * when the vision system is recreating its "could see" array.  Here
31  * we add a flag (TEMP_LIT) to the array for all locations that are lit
32  * via a light source.  The bad part of this is that we have to
33  * re-calculate the LOS of each light source every time the vision
34  * system runs.  Even if the light sources and any topology (vision blocking
35  * positions) have not changed.  The good part is that no extra memory
36  * is used, plus we don't have to figure out how far the sources have moved,
37  * or if the topology has changed.
38  *
39  * The structure of the save/restore mechanism is amazingly similar to
40  * the timer save/restore.  This is because they both have the same
41  * principals of having pointers into objects that must be recalculated
42  * across saves and restores.
43  */
44
45 /* flags */
46 #define LSF_SHOW 0x1        /* display the light source */
47 #define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
48
49 static light_source *light_base = 0;
50
51 STATIC_DCL void FDECL(write_ls, (int, light_source *));
52 STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P));
53
54 /* imported from vision.c, for small circles */
55 extern char circle_data[];
56 extern char circle_start[];
57
58 /* Create a new light source.  */
59 void
60 new_light_source(x, y, range, type, id)
61 xchar x, y;
62 int range, type;
63 anything *id;
64 {
65     light_source *ls;
66
67     if (range > MAX_RADIUS || range < 1) {
68         impossible("new_light_source:  illegal range %d", range);
69         return;
70     }
71
72     ls = (light_source *) alloc(sizeof *ls);
73
74     ls->next = light_base;
75     ls->x = x;
76     ls->y = y;
77     ls->range = range;
78     ls->type = type;
79     ls->id = *id;
80     ls->flags = 0;
81     light_base = ls;
82
83     vision_full_recalc = 1; /* make the source show up */
84 }
85
86 /*
87  * Delete a light source. This assumes only one light source is attached
88  * to an object at a time.
89  */
90 void
91 del_light_source(type, id)
92 int type;
93 anything *id;
94 {
95     light_source *curr, *prev;
96     anything tmp_id;
97
98     tmp_id = zeroany;
99     /* need to be prepared for dealing a with light source which
100        has only been partially restored during a level change
101        (in particular: chameleon vs prot. from shape changers) */
102     switch (type) {
103     case LS_OBJECT:
104         tmp_id.a_uint = id->a_obj->o_id;
105         break;
106     case LS_MONSTER:
107         tmp_id.a_uint = id->a_monst->m_id;
108         break;
109     default:
110         tmp_id.a_uint = 0;
111         break;
112     }
113
114     for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) {
115         if (curr->type != type)
116             continue;
117         if (curr->id.a_obj
118             == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id.a_obj : id->a_obj)) {
119             if (prev)
120                 prev->next = curr->next;
121             else
122                 light_base = curr->next;
123
124             free((genericptr_t) curr);
125             vision_full_recalc = 1;
126             return;
127         }
128     }
129     impossible("del_light_source: not found type=%d, id=%s", type,
130                fmt_ptr((genericptr_t) id->a_obj));
131 }
132
133 /* Mark locations that are temporarily lit via mobile light sources. */
134 void
135 do_light_sources(cs_rows)
136 char **cs_rows;
137 {
138     int x, y, min_x, max_x, max_y, offset;
139     char *limits;
140     short at_hero_range = 0;
141     light_source *ls;
142     char *row;
143
144     for (ls = light_base; ls; ls = ls->next) {
145         ls->flags &= ~LSF_SHOW;
146
147         /*
148          * Check for moved light sources.  It may be possible to
149          * save some effort if an object has not moved, but not in
150          * the current setup -- we need to recalculate for every
151          * vision recalc.
152          */
153         if (ls->type == LS_OBJECT) {
154             if (get_obj_location(ls->id.a_obj, &ls->x, &ls->y, 0))
155                 ls->flags |= LSF_SHOW;
156         } else if (ls->type == LS_MONSTER) {
157             if (get_mon_location(ls->id.a_monst, &ls->x, &ls->y, 0))
158                 ls->flags |= LSF_SHOW;
159         }
160
161         /* minor optimization: don't bother with duplicate light sources
162            at hero */
163         if (ls->x == u.ux && ls->y == u.uy) {
164             if (at_hero_range >= ls->range)
165                 ls->flags &= ~LSF_SHOW;
166             else
167                 at_hero_range = ls->range;
168         }
169
170         if (ls->flags & LSF_SHOW) {
171             /*
172              * Walk the points in the circle and see if they are
173              * visible from the center.  If so, mark'em.
174              *
175              * Kevin's tests indicated that doing this brute-force
176              * method is faster for radius <= 3 (or so).
177              */
178             limits = circle_ptr(ls->range);
179             if ((max_y = (ls->y + ls->range)) >= ROWNO)
180                 max_y = ROWNO - 1;
181             if ((y = (ls->y - ls->range)) < 0)
182                 y = 0;
183             for (; y <= max_y; y++) {
184                 row = cs_rows[y];
185                 offset = limits[abs(y - ls->y)];
186                 if ((min_x = (ls->x - offset)) < 0)
187                     min_x = 0;
188                 if ((max_x = (ls->x + offset)) >= COLNO)
189                     max_x = COLNO - 1;
190
191                 if (ls->x == u.ux && ls->y == u.uy) {
192                     /*
193                      * If the light source is located at the hero, then
194                      * we can use the COULD_SEE bits already calculated
195                      * by the vision system.  More importantly than
196                      * this optimization, is that it allows the vision
197                      * system to correct problems with clear_path().
198                      * The function clear_path() is a simple LOS
199                      * path checker that doesn't go out of its way to
200                      * make things look "correct".  The vision system
201                      * does this.
202                      */
203                     for (x = min_x; x <= max_x; x++)
204                         if (row[x] & COULD_SEE)
205                             row[x] |= TEMP_LIT;
206                 } else {
207                     for (x = min_x; x <= max_x; x++)
208                         if ((ls->x == x && ls->y == y)
209                             || clear_path((int) ls->x, (int) ls->y, x, y))
210                             row[x] |= TEMP_LIT;
211                 }
212             }
213         }
214     }
215 }
216
217 /* lit 'obj' has been thrown or kicked and is passing through x,y on the
218    way to its destination; show its light so that hero has a chance to
219    remember terrain, objects, and monsters being revealed */
220 void
221 show_transient_light(obj, x, y)
222 struct obj *obj;
223 int x, y;
224 {
225     light_source *ls;
226     struct monst *mon;
227     int radius_squared;
228
229     /* caller has verified obj->lamplit and that hero is not Blind;
230        validate light source and obtain its radius (for monster sightings) */
231     for (ls = light_base; ls; ls = ls->next) {
232         if (ls->type != LS_OBJECT)
233             continue;
234         if (ls->id.a_obj == obj)
235             break;
236     }
237     if (!ls || obj->where != OBJ_FREE) {
238         impossible("transient light %s %s is not %s?",
239                    obj->lamplit ? "lit" : "unlit", xname(obj),
240                    !ls ? "a light source" : "free");
241     } else {
242         /* "expensive" but rare */
243         place_object(obj, bhitpos.x, bhitpos.y); /* temporarily put on map */
244         vision_recalc(0);
245         flush_screen(0);
246         delay_output();
247         remove_object(obj); /* take back off of map */
248
249         radius_squared = ls->range * ls->range;
250         for (mon = fmon; mon; mon = mon->nmon) {
251             if (DEADMONSTER(mon))
252                 continue;
253             /* light range is the radius of a circle and we're limiting
254                canseemon() to a square exclosing that circle, but setting
255                mtemplit 'erroneously' for a seen monster is not a problem;
256                it just flags monsters for another canseemon() check when
257                'obj' has reached its destination after missile traversal */
258             if (dist2(mon->mx, mon->my, x, y) <= radius_squared
259                 && canseemon(mon))
260                 mon->mtemplit = 1;
261             /* [what about worm tails?] */
262         }
263     }
264 }
265
266 /* draw "remembered, unseen monster" glyph at locations where a monster
267    was flagged for being visible during transient light movement but can't
268    be seen now */
269 void
270 transient_light_cleanup()
271 {
272     struct monst *mon;
273     int mtempcount = 0;
274
275     for (mon = fmon; mon; mon = mon->nmon) {
276         if (DEADMONSTER(mon))
277             continue;
278         if (mon->mtemplit) {
279             mon->mtemplit = 0;
280             ++mtempcount;
281             if (!canseemon(mon))
282                 map_invisible(mon->mx, mon->my);
283         }
284     }
285     if (mtempcount) {
286         vision_recalc(0);
287         flush_screen(0);
288     }
289 }
290
291 /* (mon->mx == 0) implies migrating */
292 #define mon_is_local(mon) ((mon)->mx > 0)
293
294 struct monst *
295 find_mid(nid, fmflags)
296 unsigned nid;
297 unsigned fmflags;
298 {
299     struct monst *mtmp;
300
301     if (!nid)
302         return &youmonst;
303     if (fmflags & FM_FMON)
304         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
305             if (!DEADMONSTER(mtmp) && mtmp->m_id == nid)
306                 return mtmp;
307     if (fmflags & FM_MIGRATE)
308         for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
309             if (mtmp->m_id == nid)
310                 return mtmp;
311     if (fmflags & FM_MYDOGS)
312         for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon)
313             if (mtmp->m_id == nid)
314                 return mtmp;
315     return (struct monst *) 0;
316 }
317
318 /* Save all light sources of the given range. */
319 void
320 save_light_sources(fd, mode, range)
321 int fd, mode, range;
322 {
323     int count, actual, is_global;
324     light_source **prev, *curr;
325
326     if (perform_bwrite(mode)) {
327         count = maybe_write_ls(fd, range, FALSE);
328         bwrite(fd, (genericptr_t) &count, sizeof count);
329         actual = maybe_write_ls(fd, range, TRUE);
330         if (actual != count)
331             panic("counted %d light sources, wrote %d! [range=%d]", count,
332                   actual, range);
333     }
334
335     if (release_data(mode)) {
336         for (prev = &light_base; (curr = *prev) != 0;) {
337             if (!curr->id.a_monst) {
338                 impossible("save_light_sources: no id! [range=%d]", range);
339                 is_global = 0;
340             } else
341                 switch (curr->type) {
342                 case LS_OBJECT:
343                     is_global = !obj_is_local(curr->id.a_obj);
344                     break;
345                 case LS_MONSTER:
346                     is_global = !mon_is_local(curr->id.a_monst);
347                     break;
348                 default:
349                     is_global = 0;
350                     impossible("save_light_sources: bad type (%d) [range=%d]",
351                                curr->type, range);
352                     break;
353                 }
354             /* if global and not doing local, or vice versa, remove it */
355             if (is_global ^ (range == RANGE_LEVEL)) {
356                 *prev = curr->next;
357                 free((genericptr_t) curr);
358             } else {
359                 prev = &(*prev)->next;
360             }
361         }
362     }
363 }
364
365 /*
366  * Pull in the structures from disk, but don't recalculate the object
367  * pointers.
368  */
369 void
370 restore_light_sources(fd)
371 int fd;
372 {
373     int count;
374     light_source *ls;
375
376     /* restore elements */
377     mread(fd, (genericptr_t) &count, sizeof count);
378
379     while (count-- > 0) {
380         ls = (light_source *) alloc(sizeof(light_source));
381         mread(fd, (genericptr_t) ls, sizeof(light_source));
382         ls->next = light_base;
383         light_base = ls;
384     }
385 }
386
387 /* to support '#stats' wizard-mode command */
388 void
389 light_stats(hdrfmt, hdrbuf, count, size)
390 const char *hdrfmt;
391 char *hdrbuf;
392 long *count, *size;
393 {
394     light_source *ls;
395
396     Sprintf(hdrbuf, hdrfmt, (long) sizeof (light_source));
397     *count = *size = 0L;
398     for (ls = light_base; ls; ls = ls->next) {
399         ++*count;
400         *size += (long) sizeof *ls;
401     }
402 }
403
404 /* Relink all lights that are so marked. */
405 void
406 relink_light_sources(ghostly)
407 boolean ghostly;
408 {
409     char which;
410     unsigned nid;
411     light_source *ls;
412
413     for (ls = light_base; ls; ls = ls->next) {
414         if (ls->flags & LSF_NEEDS_FIXUP) {
415             if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
416                 if (ghostly) {
417                     if (!lookup_id_mapping(ls->id.a_uint, &nid))
418                         impossible("relink_light_sources: no id mapping");
419                 } else
420                     nid = ls->id.a_uint;
421                 if (ls->type == LS_OBJECT) {
422                     which = 'o';
423                     ls->id.a_obj = find_oid(nid);
424                 } else {
425                     which = 'm';
426                     ls->id.a_monst = find_mid(nid, FM_EVERYWHERE);
427                 }
428                 if (!ls->id.a_monst)
429                     impossible("relink_light_sources: cant find %c_id %d",
430                                which, nid);
431             } else
432                 impossible("relink_light_sources: bad type (%d)", ls->type);
433
434             ls->flags &= ~LSF_NEEDS_FIXUP;
435         }
436     }
437 }
438
439 /*
440  * Part of the light source save routine.  Count up the number of light
441  * sources that would be written.  If write_it is true, actually write
442  * the light source out.
443  */
444 STATIC_OVL int
445 maybe_write_ls(fd, range, write_it)
446 int fd, range;
447 boolean write_it;
448 {
449     int count = 0, is_global;
450     light_source *ls;
451
452     for (ls = light_base; ls; ls = ls->next) {
453         if (!ls->id.a_monst) {
454             impossible("maybe_write_ls: no id! [range=%d]", range);
455             continue;
456         }
457         switch (ls->type) {
458         case LS_OBJECT:
459             is_global = !obj_is_local(ls->id.a_obj);
460             break;
461         case LS_MONSTER:
462             is_global = !mon_is_local(ls->id.a_monst);
463             break;
464         default:
465             is_global = 0;
466             impossible("maybe_write_ls: bad type (%d) [range=%d]", ls->type,
467                        range);
468             break;
469         }
470         /* if global and not doing local, or vice versa, count it */
471         if (is_global ^ (range == RANGE_LEVEL)) {
472             count++;
473             if (write_it)
474                 write_ls(fd, ls);
475         }
476     }
477
478     return count;
479 }
480
481 void
482 light_sources_sanity_check()
483 {
484     light_source *ls;
485     struct monst *mtmp;
486     struct obj *otmp;
487     unsigned int auint;
488
489     for (ls = light_base; ls; ls = ls->next) {
490         if (!ls->id.a_monst)
491             panic("insane light source: no id!");
492         if (ls->type == LS_OBJECT) {
493             otmp = (struct obj *) ls->id.a_obj;
494             auint = otmp->o_id;
495             if (find_oid(auint) != otmp)
496                 panic("insane light source: can't find obj #%u!", auint);
497         } else if (ls->type == LS_MONSTER) {
498             mtmp = (struct monst *) ls->id.a_monst;
499             auint = mtmp->m_id;
500             if (find_mid(auint, FM_EVERYWHERE) != mtmp)
501                 panic("insane light source: can't find mon #%u!", auint);
502         } else {
503             panic("insane light source: bad ls type %d", ls->type);
504         }
505     }
506 }
507
508 /* Write a light source structure to disk. */
509 STATIC_OVL void
510 write_ls(fd, ls)
511 int fd;
512 light_source *ls;
513 {
514     anything arg_save;
515     struct obj *otmp;
516     struct monst *mtmp;
517
518     if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
519         if (ls->flags & LSF_NEEDS_FIXUP) {
520             bwrite(fd, (genericptr_t) ls, sizeof(light_source));
521         } else {
522             /* replace object pointer with id for write, then put back */
523             arg_save = ls->id;
524             if (ls->type == LS_OBJECT) {
525                 otmp = ls->id.a_obj;
526                 ls->id = zeroany;
527                 ls->id.a_uint = otmp->o_id;
528                 if (find_oid((unsigned) ls->id.a_uint) != otmp)
529                     impossible("write_ls: can't find obj #%u!",
530                                ls->id.a_uint);
531             } else { /* ls->type == LS_MONSTER */
532                 mtmp = (struct monst *) ls->id.a_monst;
533                 ls->id = zeroany;
534                 ls->id.a_uint = mtmp->m_id;
535                 if (find_mid((unsigned) ls->id.a_uint, FM_EVERYWHERE) != mtmp)
536                     impossible("write_ls: can't find mon #%u!",
537                                ls->id.a_uint);
538             }
539             ls->flags |= LSF_NEEDS_FIXUP;
540             bwrite(fd, (genericptr_t) ls, sizeof(light_source));
541             ls->id = arg_save;
542             ls->flags &= ~LSF_NEEDS_FIXUP;
543         }
544     } else {
545         impossible("write_ls: bad type (%d)", ls->type);
546     }
547 }
548
549 /* Change light source's ID from src to dest. */
550 void
551 obj_move_light_source(src, dest)
552 struct obj *src, *dest;
553 {
554     light_source *ls;
555
556     for (ls = light_base; ls; ls = ls->next)
557         if (ls->type == LS_OBJECT && ls->id.a_obj == src)
558             ls->id.a_obj = dest;
559     src->lamplit = 0;
560     dest->lamplit = 1;
561 }
562
563 /* return true if there exist any light sources */
564 boolean
565 any_light_source()
566 {
567     return (boolean) (light_base != (light_source *) 0);
568 }
569
570 /*
571  * Snuff an object light source if at (x,y).  This currently works
572  * only for burning light sources.
573  */
574 void
575 snuff_light_source(x, y)
576 int x, y;
577 {
578     light_source *ls;
579     struct obj *obj;
580
581     for (ls = light_base; ls; ls = ls->next)
582         /*
583          * Is this position check valid??? Can I assume that the positions
584          * will always be correct because the objects would have been
585          * updated with the last vision update?  [Is that recent enough???]
586          */
587         if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) {
588             obj = ls->id.a_obj;
589             if (obj_is_burning(obj)) {
590                 /* The only way to snuff Sunsword is to unwield it.  Darkness
591                  * scrolls won't affect it.  (If we got here because it was
592                  * dropped or thrown inside a monster, this won't matter
593                  * anyway because it will go out when dropped.)
594                  */
595                 if (artifact_light(obj))
596                     continue;
597                 end_burn(obj, obj->otyp != MAGIC_LAMP);
598                 /*
599                  * The current ls element has just been removed (and
600                  * ls->next is now invalid).  Return assuming that there
601                  * is only one light source attached to each object.
602                  */
603                 return;
604             }
605         }
606 }
607
608 /* Return TRUE if object sheds any light at all. */
609 boolean
610 obj_sheds_light(obj)
611 struct obj *obj;
612 {
613     /* so far, only burning objects shed light */
614     return obj_is_burning(obj);
615 }
616
617 /* Return TRUE if sheds light AND will be snuffed by end_burn(). */
618 boolean
619 obj_is_burning(obj)
620 struct obj *obj;
621 {
622     return (boolean) (obj->lamplit && (obj->otyp == MAGIC_LAMP
623                                        || ignitable(obj)
624                                        || artifact_light(obj)));
625 }
626
627 /* copy the light source(s) attached to src, and attach it/them to dest */
628 void
629 obj_split_light_source(src, dest)
630 struct obj *src, *dest;
631 {
632     light_source *ls, *new_ls;
633
634     for (ls = light_base; ls; ls = ls->next)
635         if (ls->type == LS_OBJECT && ls->id.a_obj == src) {
636             /*
637              * Insert the new source at beginning of list.  This will
638              * never interfere us walking down the list - we are already
639              * past the insertion point.
640              */
641             new_ls = (light_source *) alloc(sizeof(light_source));
642             *new_ls = *ls;
643             if (Is_candle(src)) {
644                 /* split candles may emit less light than original group */
645                 ls->range = candle_light_range(src);
646                 new_ls->range = candle_light_range(dest);
647                 vision_full_recalc = 1; /* in case range changed */
648             }
649             new_ls->id.a_obj = dest;
650             new_ls->next = light_base;
651             light_base = new_ls;
652             dest->lamplit = 1; /* now an active light source */
653         }
654 }
655
656 /* light source `src' has been folded into light source `dest';
657    used for merging lit candles and adding candle(s) to lit candelabrum */
658 void
659 obj_merge_light_sources(src, dest)
660 struct obj *src, *dest;
661 {
662     light_source *ls;
663
664     /* src == dest implies adding to candelabrum */
665     if (src != dest)
666         end_burn(src, TRUE); /* extinguish candles */
667
668     for (ls = light_base; ls; ls = ls->next)
669         if (ls->type == LS_OBJECT && ls->id.a_obj == dest) {
670             ls->range = candle_light_range(dest);
671             vision_full_recalc = 1; /* in case range changed */
672             break;
673         }
674 }
675
676 /* light source `obj' is being made brighter or dimmer */
677 void
678 obj_adjust_light_radius(obj, new_radius)
679 struct obj *obj;
680 int new_radius;
681 {
682     light_source *ls;
683
684     for (ls = light_base; ls; ls = ls->next)
685         if (ls->type == LS_OBJECT && ls->id.a_obj == obj) {
686             if (new_radius != ls->range)
687                 vision_full_recalc = 1;
688             ls->range = new_radius;
689             return;
690         }
691     impossible("obj_adjust_light_radius: can't find %s", xname(obj));
692 }
693
694 /* Candlelight is proportional to the number of candles;
695    minimum range is 2 rather than 1 for playability. */
696 int
697 candle_light_range(obj)
698 struct obj *obj;
699 {
700     int radius;
701
702     if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
703         /*
704          *      The special candelabrum emits more light than the
705          *      corresponding number of candles would.
706          *       1..3 candles, range 2 (minimum range);
707          *       4..6 candles, range 3 (normal lamp range);
708          *          7 candles, range 4 (bright).
709          */
710         radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4;
711     } else if (Is_candle(obj)) {
712         /*
713          *      Range is incremented by powers of 7 so that it will take
714          *      wizard mode quantities of candles to get more light than
715          *      from a lamp, without imposing an arbitrary limit.
716          *       1..6   candles, range 2;
717          *       7..48  candles, range 3;
718          *      49..342 candles, range 4; &c.
719          */
720         long n = obj->quan;
721
722         radius = 1; /* always incremented at least once */
723         do {
724             radius++;
725             n /= 7L;
726         } while (n > 0L);
727     } else {
728         /* we're only called for lit candelabrum or candles */
729         /* impossible("candlelight for %d?", obj->otyp); */
730         radius = 3; /* lamp's value */
731     }
732     return radius;
733 }
734
735 /* light emitting artifact's range depends upon its curse/bless state */
736 int
737 arti_light_radius(obj)
738 struct obj *obj;
739 {
740     /*
741      * Used by begin_burn() when setting up a new light source
742      * (obj->lamplit will already be set by this point) and
743      * also by bless()/unbless()/uncurse()/curse() to decide
744      * whether to call obj_adjust_light_radius().
745      */
746
747     /* sanity check [simplifies usage by bless()/curse()/&c] */
748     if (!obj->lamplit || !artifact_light(obj))
749         return 0;
750
751     /* cursed radius of 1 is not noticeable for an item that's
752        carried by the hero but is if it's carried by a monster
753        or left lit on the floor (not applicable for Sunsword) */
754     return (obj->blessed ? 3 : !obj->cursed ? 2 : 1);
755 }
756
757 /* adverb describing lit artifact's light; depends on curse/bless state */
758 const char *
759 arti_light_description(obj)
760 struct obj *obj;
761 {
762     switch (arti_light_radius(obj)) {
763     case 3:
764 #if 0 /*JP:T*/
765         return "brilliantly"; /* blessed */
766 #else
767         return "\83L\83\89\83L\83\89\82Æ"; /* blessed */
768 #endif
769     case 2:
770 #if 0 /*JP:T*/
771         return "brightly"; /* uncursed */
772 #else
773         return "\96¾\82é\82­"; /* uncursed */
774 #endif
775     case 1:
776 #if 0 /*JP:T*/
777         return "dimly"; /* cursed */
778 #else
779         return "\94\96\88Ã\82­"; /* cursed */
780 #endif
781     default:
782         break;
783     }
784 /*JP
785     return "strangely";
786 */
787     return "\95s\8ev\8bc\82É";
788 }
789
790 int
791 wiz_light_sources()
792 {
793     winid win;
794     char buf[BUFSZ];
795     light_source *ls;
796
797     win = create_nhwindow(NHW_MENU); /* corner text window */
798     if (win == WIN_ERR)
799         return 0;
800
801     Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy);
802     putstr(win, 0, buf);
803     putstr(win, 0, "");
804
805     if (light_base) {
806         putstr(win, 0, "location range flags  type    id");
807         putstr(win, 0, "-------- ----- ------ ----  -------");
808         for (ls = light_base; ls; ls = ls->next) {
809             Sprintf(buf, "  %2d,%2d   %2d   0x%04x  %s  %s", ls->x, ls->y,
810                     ls->range, ls->flags,
811                     (ls->type == LS_OBJECT
812                        ? "obj"
813                        : ls->type == LS_MONSTER
814                           ? (mon_is_local(ls->id.a_monst)
815                              ? "mon"
816                              : (ls->id.a_monst == &youmonst)
817                                 ? "you"
818                                 /* migrating monster */
819                                 : "<m>")
820                           : "???"),
821                     fmt_ptr(ls->id.a_void));
822             putstr(win, 0, buf);
823         }
824     } else
825         putstr(win, 0, "<none>");
826
827     display_nhwindow(win, FALSE);
828     destroy_nhwindow(win);
829
830     return 0;
831 }
832
833 /*light.c*/