OSDN Git Service

patch artifact
[jnethack/source.git] / src / light.c
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.       */
4
5 #include "hack.h"
6 #include "lev.h" /* for checking save modes */
7
8 /*
9  * Mobile light sources.
10  *
11  * This implementation minimizes memory at the expense of extra
12  * recalculations.
13  *
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.
19  *
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.
24  *
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.
34  *
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.
39  */
40
41 /* flags */
42 #define LSF_SHOW 0x1        /* display the light source */
43 #define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */
44
45 static light_source *light_base = 0;
46
47 STATIC_DCL void FDECL(write_ls, (int, light_source *));
48 STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P));
49
50 /* imported from vision.c, for small circles */
51 extern char circle_data[];
52 extern char circle_start[];
53
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;
60 {
61     light_source *ls;
62
63     if (range > MAX_RADIUS || range < 1) {
64         impossible("new_light_source:  illegal range %d", range);
65         return;
66     }
67
68     ls = (light_source *) alloc(sizeof(light_source));
69
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;
78
79     vision_full_recalc = 1; /* make the source show up */
80 }
81
82 /*
83  * Delete a light source. This assumes only one light source is attached
84  * to an object at a time.
85  */
86 void
87 del_light_source(type, id)
88 int type;
89 anything *id;
90 {
91     light_source *curr, *prev;
92     anything tmp_id;
93
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;
108     }
109
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;
119
120             free((genericptr_t) curr);
121             vision_full_recalc = 1;
122             return;
123         }
124     }
125     impossible("del_light_source: not found type=%d, id=%s", type,
126                fmt_ptr((genericptr_t) id->a_obj));
127 }
128
129 /* Mark locations that are temporarily lit via mobile light sources. */
130 void
131 do_light_sources(cs_rows)
132 char **cs_rows;
133 {
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;
139
140     for (ls = light_base; ls; ls = ls->next) {
141         ls->flags &= ~LSF_SHOW;
142
143         /*
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.
148          */
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;
155         }
156
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;
164         }
165
166         if (ls->flags & LSF_SHOW) {
167             /*
168              * Walk the points in the circle and see if they are
169              * visible from the center.  If so, mark'em.
170              *
171              * Kevin's tests indicated that doing this brute-force
172              * method is faster for radius <= 3 (or so).
173              */
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;
186
187                 if (ls->x == u.ux && ls->y == u.uy) {
188                     /*
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.
198                      */
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;
207                 }
208             }
209         }
210     }
211 }
212
213 /* (mon->mx == 0) implies migrating */
214 #define mon_is_local(mon) ((mon)->mx > 0)
215
216 struct monst *
217 find_mid(nid, fmflags)
218 unsigned nid;
219 unsigned fmflags;
220 {
221     struct monst *mtmp;
222
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;
238 }
239
240 /* Save all light sources of the given range. */
241 void
242 save_light_sources(fd, mode, range)
243 int fd, mode, range;
244 {
245     int count, actual, is_global;
246     light_source **prev, *curr;
247
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);
255     }
256
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;
275                 }
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;
282             }
283         }
284     }
285 }
286
287 /*
288  * Pull in the structures from disk, but don't recalculate the object
289  * pointers.
290  */
291 void
292 restore_light_sources(fd)
293 int fd;
294 {
295     int count;
296     light_source *ls;
297
298     /* restore elements */
299     mread(fd, (genericptr_t) &count, sizeof count);
300
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;
306     }
307 }
308
309 /* Relink all lights that are so marked. */
310 void
311 relink_light_sources(ghostly)
312 boolean ghostly;
313 {
314     char which;
315     unsigned nid;
316     light_source *ls;
317
318     for (ls = light_base; ls; ls = ls->next) {
319         if (ls->flags & LSF_NEEDS_FIXUP) {
320             if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
321                 if (ghostly) {
322                     if (!lookup_id_mapping(ls->id.a_uint, &nid))
323                         impossible("relink_light_sources: no id mapping");
324                 } else
325                     nid = ls->id.a_uint;
326                 if (ls->type == LS_OBJECT) {
327                     which = 'o';
328                     ls->id.a_obj = find_oid(nid);
329                 } else {
330                     which = 'm';
331                     ls->id.a_monst = find_mid(nid, FM_EVERYWHERE);
332                 }
333                 if (!ls->id.a_monst)
334                     impossible("relink_light_sources: cant find %c_id %d",
335                                which, nid);
336             } else
337                 impossible("relink_light_sources: bad type (%d)", ls->type);
338
339             ls->flags &= ~LSF_NEEDS_FIXUP;
340         }
341     }
342 }
343
344 /*
345  * Part of the light source save routine.  Count up the number of light
346  * sources that would be written.  If write_it is true, actually write
347  * the light source out.
348  */
349 STATIC_OVL int
350 maybe_write_ls(fd, range, write_it)
351 int fd, range;
352 boolean write_it;
353 {
354     int count = 0, is_global;
355     light_source *ls;
356
357     for (ls = light_base; ls; ls = ls->next) {
358         if (!ls->id.a_monst) {
359             impossible("maybe_write_ls: no id! [range=%d]", range);
360             continue;
361         }
362         switch (ls->type) {
363         case LS_OBJECT:
364             is_global = !obj_is_local(ls->id.a_obj);
365             break;
366         case LS_MONSTER:
367             is_global = !mon_is_local(ls->id.a_monst);
368             break;
369         default:
370             is_global = 0;
371             impossible("maybe_write_ls: bad type (%d) [range=%d]", ls->type,
372                        range);
373             break;
374         }
375         /* if global and not doing local, or vice versa, count it */
376         if (is_global ^ (range == RANGE_LEVEL)) {
377             count++;
378             if (write_it)
379                 write_ls(fd, ls);
380         }
381     }
382
383     return count;
384 }
385
386 void
387 light_sources_sanity_check()
388 {
389     light_source *ls;
390     struct monst *mtmp;
391     struct obj *otmp;
392     unsigned int auint;
393
394     for (ls = light_base; ls; ls = ls->next) {
395         if (!ls->id.a_monst)
396             panic("insane light source: no id!");
397         if (ls->type == LS_OBJECT) {
398             otmp = (struct obj *) ls->id.a_obj;
399             auint = otmp->o_id;
400             if (find_oid(auint) != otmp)
401                 panic("insane light source: can't find obj #%u!", auint);
402         } else if (ls->type == LS_MONSTER) {
403             mtmp = (struct monst *) ls->id.a_monst;
404             auint = mtmp->m_id;
405             if (find_mid(auint, FM_EVERYWHERE) != mtmp)
406                 panic("insane light source: can't find mon #%u!", auint);
407         } else {
408             panic("insane light source: bad ls type %d", ls->type);
409         }
410     }
411 }
412
413 /* Write a light source structure to disk. */
414 STATIC_OVL void
415 write_ls(fd, ls)
416 int fd;
417 light_source *ls;
418 {
419     anything arg_save;
420     struct obj *otmp;
421     struct monst *mtmp;
422
423     if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
424         if (ls->flags & LSF_NEEDS_FIXUP) {
425             bwrite(fd, (genericptr_t) ls, sizeof(light_source));
426         } else {
427             /* replace object pointer with id for write, then put back */
428             arg_save = ls->id;
429             if (ls->type == LS_OBJECT) {
430                 otmp = ls->id.a_obj;
431                 ls->id = zeroany;
432                 ls->id.a_uint = otmp->o_id;
433                 if (find_oid((unsigned) ls->id.a_uint) != otmp)
434                     impossible("write_ls: can't find obj #%u!",
435                                ls->id.a_uint);
436             } else { /* ls->type == LS_MONSTER */
437                 mtmp = (struct monst *) ls->id.a_monst;
438                 ls->id = zeroany;
439                 ls->id.a_uint = mtmp->m_id;
440                 if (find_mid((unsigned) ls->id.a_uint, FM_EVERYWHERE) != mtmp)
441                     impossible("write_ls: can't find mon #%u!",
442                                ls->id.a_uint);
443             }
444             ls->flags |= LSF_NEEDS_FIXUP;
445             bwrite(fd, (genericptr_t) ls, sizeof(light_source));
446             ls->id = arg_save;
447             ls->flags &= ~LSF_NEEDS_FIXUP;
448         }
449     } else {
450         impossible("write_ls: bad type (%d)", ls->type);
451     }
452 }
453
454 /* Change light source's ID from src to dest. */
455 void
456 obj_move_light_source(src, dest)
457 struct obj *src, *dest;
458 {
459     light_source *ls;
460
461     for (ls = light_base; ls; ls = ls->next)
462         if (ls->type == LS_OBJECT && ls->id.a_obj == src)
463             ls->id.a_obj = dest;
464     src->lamplit = 0;
465     dest->lamplit = 1;
466 }
467
468 /* return true if there exist any light sources */
469 boolean
470 any_light_source()
471 {
472     return (boolean) (light_base != (light_source *) 0);
473 }
474
475 /*
476  * Snuff an object light source if at (x,y).  This currently works
477  * only for burning light sources.
478  */
479 void
480 snuff_light_source(x, y)
481 int x, y;
482 {
483     light_source *ls;
484     struct obj *obj;
485
486     for (ls = light_base; ls; ls = ls->next)
487         /*
488          * Is this position check valid??? Can I assume that the positions
489          * will always be correct because the objects would have been
490          * updated with the last vision update?  [Is that recent enough???]
491          */
492         if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) {
493             obj = ls->id.a_obj;
494             if (obj_is_burning(obj)) {
495                 /* The only way to snuff Sunsword is to unwield it.  Darkness
496                  * scrolls won't affect it.  (If we got here because it was
497                  * dropped or thrown inside a monster, this won't matter
498                  * anyway because it will go out when dropped.)
499                  */
500                 if (artifact_light(obj))
501                     continue;
502                 end_burn(obj, obj->otyp != MAGIC_LAMP);
503                 /*
504                  * The current ls element has just been removed (and
505                  * ls->next is now invalid).  Return assuming that there
506                  * is only one light source attached to each object.
507                  */
508                 return;
509             }
510         }
511 }
512
513 /* Return TRUE if object sheds any light at all. */
514 boolean
515 obj_sheds_light(obj)
516 struct obj *obj;
517 {
518     /* so far, only burning objects shed light */
519     return obj_is_burning(obj);
520 }
521
522 /* Return TRUE if sheds light AND will be snuffed by end_burn(). */
523 boolean
524 obj_is_burning(obj)
525 struct obj *obj;
526 {
527     return (boolean) (obj->lamplit && (obj->otyp == MAGIC_LAMP
528                                        || ignitable(obj)
529                                        || artifact_light(obj)));
530 }
531
532 /* copy the light source(s) attached to src, and attach it/them to dest */
533 void
534 obj_split_light_source(src, dest)
535 struct obj *src, *dest;
536 {
537     light_source *ls, *new_ls;
538
539     for (ls = light_base; ls; ls = ls->next)
540         if (ls->type == LS_OBJECT && ls->id.a_obj == src) {
541             /*
542              * Insert the new source at beginning of list.  This will
543              * never interfere us walking down the list - we are already
544              * past the insertion point.
545              */
546             new_ls = (light_source *) alloc(sizeof(light_source));
547             *new_ls = *ls;
548             if (Is_candle(src)) {
549                 /* split candles may emit less light than original group */
550                 ls->range = candle_light_range(src);
551                 new_ls->range = candle_light_range(dest);
552                 vision_full_recalc = 1; /* in case range changed */
553             }
554             new_ls->id.a_obj = dest;
555             new_ls->next = light_base;
556             light_base = new_ls;
557             dest->lamplit = 1; /* now an active light source */
558         }
559 }
560
561 /* light source `src' has been folded into light source `dest';
562    used for merging lit candles and adding candle(s) to lit candelabrum */
563 void
564 obj_merge_light_sources(src, dest)
565 struct obj *src, *dest;
566 {
567     light_source *ls;
568
569     /* src == dest implies adding to candelabrum */
570     if (src != dest)
571         end_burn(src, TRUE); /* extinguish candles */
572
573     for (ls = light_base; ls; ls = ls->next)
574         if (ls->type == LS_OBJECT && ls->id.a_obj == dest) {
575             ls->range = candle_light_range(dest);
576             vision_full_recalc = 1; /* in case range changed */
577             break;
578         }
579 }
580
581 /* light source `obj' is being made brighter or dimmer */
582 void
583 obj_adjust_light_radius(obj, new_radius)
584 struct obj *obj;
585 int new_radius;
586 {
587     light_source *ls;
588
589     for (ls = light_base; ls; ls = ls->next)
590         if (ls->type == LS_OBJECT && ls->id.a_obj == obj) {
591             if (new_radius != ls->range)
592                 vision_full_recalc = 1;
593             ls->range = new_radius;
594             return;
595         }
596     impossible("obj_adjust_light_radius: can't find %s", xname(obj));
597 }
598
599 /* Candlelight is proportional to the number of candles;
600    minimum range is 2 rather than 1 for playability. */
601 int
602 candle_light_range(obj)
603 struct obj *obj;
604 {
605     int radius;
606
607     if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
608         /*
609          *      The special candelabrum emits more light than the
610          *      corresponding number of candles would.
611          *       1..3 candles, range 2 (minimum range);
612          *       4..6 candles, range 3 (normal lamp range);
613          *          7 candles, range 4 (bright).
614          */
615         radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4;
616     } else if (Is_candle(obj)) {
617         /*
618          *      Range is incremented by powers of 7 so that it will take
619          *      wizard mode quantities of candles to get more light than
620          *      from a lamp, without imposing an arbitrary limit.
621          *       1..6   candles, range 2;
622          *       7..48  candles, range 3;
623          *      49..342 candles, range 4; &c.
624          */
625         long n = obj->quan;
626
627         radius = 1; /* always incremented at least once */
628         do {
629             radius++;
630             n /= 7L;
631         } while (n > 0L);
632     } else {
633         /* we're only called for lit candelabrum or candles */
634         /* impossible("candlelight for %d?", obj->otyp); */
635         radius = 3; /* lamp's value */
636     }
637     return radius;
638 }
639
640 /* light emitting artifact's range depends upon its curse/bless state */
641 int
642 arti_light_radius(obj)
643 struct obj *obj;
644 {
645     /*
646      * Used by begin_burn() when setting up a new light source
647      * (obj->lamplit will already be set by this point) and
648      * also by bless()/unbless()/uncurse()/curse() to decide
649      * whether to call obj_adjust_light_radius().
650      */
651
652     /* sanity check [simplifies usage by bless()/curse()/&c] */
653     if (!obj->lamplit || !artifact_light(obj))
654         return 0;
655
656     /* cursed radius of 1 is not noticeable for an item that's
657        carried by the hero but is if it's carried by a monster
658        or left lit on the floor (not applicable for Sunsword) */
659     return (obj->blessed ? 3 : !obj->cursed ? 2 : 1);
660 }
661
662 /* adverb describing lit artifact's light; depends on curse/bless state */
663 const char *
664 arti_light_description(obj)
665 struct obj *obj;
666 {
667     switch (arti_light_radius(obj)) {
668     case 3:
669 #if 0 /*JP*/
670         return "brilliantly"; /* blessed */
671 #else
672         return "\83L\83\89\83L\83\89\82Æ"; /* blessed */
673 #endif
674     case 2:
675 #if 0 /*JP*/
676         return "brightly"; /* uncursed */
677 #else
678         return "\96¾\82é\82­"; /* uncursed */
679 #endif
680     case 1:
681 #if 0 /*JP*/
682         return "dimly"; /* cursed */
683 #else
684         return "\94\96\88Ã\82­"; /* cursed */
685 #endif
686     default:
687         break;
688     }
689 /*JP
690     return "strangely";
691 */
692     return "\95s\8ev\8bc\82É";
693 }
694
695 int
696 wiz_light_sources()
697 {
698     winid win;
699     char buf[BUFSZ];
700     light_source *ls;
701
702     win = create_nhwindow(NHW_MENU); /* corner text window */
703     if (win == WIN_ERR)
704         return 0;
705
706     Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy);
707     putstr(win, 0, buf);
708     putstr(win, 0, "");
709
710     if (light_base) {
711         putstr(win, 0, "location range flags  type    id");
712         putstr(win, 0, "-------- ----- ------ ----  -------");
713         for (ls = light_base; ls; ls = ls->next) {
714             Sprintf(buf, "  %2d,%2d   %2d   0x%04x  %s  %s", ls->x, ls->y,
715                     ls->range, ls->flags,
716                     (ls->type == LS_OBJECT
717                        ? "obj"
718                        : ls->type == LS_MONSTER
719                           ? (mon_is_local(ls->id.a_monst)
720                              ? "mon"
721                              : (ls->id.a_monst == &youmonst)
722                                 ? "you"
723                                 /* migrating monster */
724                                 : "<m>")
725                           : "???"),
726                     fmt_ptr(ls->id.a_void));
727             putstr(win, 0, buf);
728         }
729     } else
730         putstr(win, 0, "<none>");
731
732     display_nhwindow(win, FALSE);
733     destroy_nhwindow(win);
734
735     return 0;
736 }
737
738 /*light.c*/