OSDN Git Service

no bone
[nethackexpress/trunk.git] / src / light.c
1 /*      SCCS Id: @(#)light.c    3.4     1997/04/10      */
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 #ifdef OVL3
42
43 /* flags */
44 #define LSF_SHOW        0x1             /* display the light source */
45 #define LSF_NEEDS_FIXUP 0x2             /* need oid fixup */
46
47 static light_source *light_base = 0;
48
49 STATIC_DCL void FDECL(write_ls, (int, light_source *));
50 STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P));
51
52 /* imported from vision.c, for small circles */
53 extern char circle_data[];
54 extern char circle_start[];
55
56
57 /* Create a new light source.  */
58 void
59 new_light_source(x, y, range, type, id)
60     xchar x, y;
61     int range, type;
62     genericptr_t id;
63 {
64     light_source *ls;
65
66     if (range > MAX_RADIUS || range < 1) {
67         impossible("new_light_source:  illegal range %d", range);
68         return;
69     }
70
71     ls = (light_source *) alloc(sizeof(light_source));
72
73     ls->next = light_base;
74     ls->x = x;
75     ls->y = y;
76     ls->range = range;
77     ls->type = type;
78     ls->id = id;
79     ls->flags = 0;
80     light_base = ls;
81
82     vision_full_recalc = 1;     /* make the source show up */
83 }
84
85 /*
86  * Delete a light source. This assumes only one light source is attached
87  * to an object at a time.
88  */
89 void
90 del_light_source(type, id)
91     int type;
92     genericptr_t id;
93 {
94     light_source *curr, *prev;
95     genericptr_t tmp_id;
96
97     /* need to be prepared for dealing a with light source which
98        has only been partially restored during a level change
99        (in particular: chameleon vs prot. from shape changers) */
100     switch (type) {
101     case LS_OBJECT:     tmp_id = (genericptr_t)(((struct obj *)id)->o_id);
102                         break;
103     case LS_MONSTER:    tmp_id = (genericptr_t)(((struct monst *)id)->m_id);
104                         break;
105     default:            tmp_id = 0;
106                         break;
107     }
108
109     for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) {
110         if (curr->type != type) continue;
111         if (curr->id == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id : id)) {
112             if (prev)
113                 prev->next = curr->next;
114             else
115                 light_base = curr->next;
116
117             free((genericptr_t)curr);
118             vision_full_recalc = 1;
119             return;
120         }
121     }
122     impossible("del_light_source: not found type=%d, id=0x%lx", type, (long)id);
123 }
124
125 /* Mark locations that are temporarily lit via mobile light sources. */
126 void
127 do_light_sources(cs_rows)
128     char **cs_rows;
129 {
130     int x, y, min_x, max_x, max_y, offset;
131     char *limits;
132     short at_hero_range = 0;
133     light_source *ls;
134     char *row;
135
136     for (ls = light_base; ls; ls = ls->next) {
137         ls->flags &= ~LSF_SHOW;
138
139         /*
140          * Check for moved light sources.  It may be possible to
141          * save some effort if an object has not moved, but not in
142          * the current setup -- we need to recalculate for every
143          * vision recalc.
144          */
145         if (ls->type == LS_OBJECT) {
146             if (get_obj_location((struct obj *) ls->id, &ls->x, &ls->y, 0))
147                 ls->flags |= LSF_SHOW;
148         } else if (ls->type == LS_MONSTER) {
149             if (get_mon_location((struct monst *) ls->id, &ls->x, &ls->y, 0))
150                 ls->flags |= LSF_SHOW;
151         }
152
153         /* minor optimization: don't bother with duplicate light sources */
154         /* at hero */
155         if (ls->x == u.ux && ls->y == u.uy) {
156             if (at_hero_range >= ls->range)
157                 ls->flags &= ~LSF_SHOW;
158             else
159                 at_hero_range = ls->range;
160         }
161
162         if (ls->flags & LSF_SHOW) {
163             /*
164              * Walk the points in the circle and see if they are
165              * visible from the center.  If so, mark'em.
166              *
167              * Kevin's tests indicated that doing this brute-force
168              * method is faster for radius <= 3 (or so).
169              */
170             limits = circle_ptr(ls->range);
171             if ((max_y = (ls->y + ls->range)) >= ROWNO) max_y = ROWNO-1;
172             if ((y = (ls->y - ls->range)) < 0) y = 0;
173             for (; y <= max_y; y++) {
174                 row = cs_rows[y];
175                 offset = limits[abs(y - ls->y)];
176                 if ((min_x = (ls->x - offset)) < 0) min_x = 0;
177                 if ((max_x = (ls->x + offset)) >= COLNO) max_x = COLNO-1;
178
179                 if (ls->x == u.ux && ls->y == u.uy) {
180                     /*
181                      * If the light source is located at the hero, then
182                      * we can use the COULD_SEE bits already calcualted
183                      * by the vision system.  More importantly than
184                      * this optimization, is that it allows the vision
185                      * system to correct problems with clear_path().
186                      * The function clear_path() is a simple LOS
187                      * path checker that doesn't go out of its way
188                      * make things look "correct".  The vision system
189                      * does this.
190                      */
191                     for (x = min_x; x <= max_x; x++)
192                         if (row[x] & COULD_SEE)
193                             row[x] |= TEMP_LIT;
194                 } else {
195                     for (x = min_x; x <= max_x; x++)
196                         if ((ls->x == x && ls->y == y)
197                                 || clear_path((int)ls->x, (int) ls->y, x, y))
198                             row[x] |= TEMP_LIT;
199                 }
200             }
201         }
202     }
203 }
204
205 /* (mon->mx == 0) implies migrating */
206 #define mon_is_local(mon)       ((mon)->mx > 0)
207
208 struct monst *
209 find_mid(nid, fmflags)
210 unsigned nid;
211 unsigned fmflags;
212 {
213         struct monst *mtmp;
214
215         if (!nid)
216             return &youmonst;
217         if (fmflags & FM_FMON)
218                 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
219                     if (!DEADMONSTER(mtmp) && mtmp->m_id == nid) return mtmp;
220         if (fmflags & FM_MIGRATE)
221                 for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
222                     if (mtmp->m_id == nid) return mtmp;
223         if (fmflags & FM_MYDOGS)
224                 for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon)
225                     if (mtmp->m_id == nid) return mtmp;
226         return (struct monst *) 0;
227 }
228
229 /* Save all light sources of the given range. */
230 void
231 save_light_sources(fd, mode, range)
232     int fd, mode, range;
233 {
234     int count, actual, is_global;
235     light_source **prev, *curr;
236
237     if (perform_bwrite(mode)) {
238         count = maybe_write_ls(fd, range, FALSE);
239         bwrite(fd, (genericptr_t) &count, sizeof count);
240         actual = maybe_write_ls(fd, range, TRUE);
241         if (actual != count)
242             panic("counted %d light sources, wrote %d! [range=%d]",
243                   count, actual, range);
244     }
245
246     if (release_data(mode)) {
247         for (prev = &light_base; (curr = *prev) != 0; ) {
248             if (!curr->id) {
249                 impossible("save_light_sources: no id! [range=%d]", range);
250                 is_global = 0;
251             } else
252             switch (curr->type) {
253             case LS_OBJECT:
254                 is_global = !obj_is_local((struct obj *)curr->id);
255                 break;
256             case LS_MONSTER:
257                 is_global = !mon_is_local((struct monst *)curr->id);
258                 break;
259             default:
260                 is_global = 0;
261                 impossible("save_light_sources: bad type (%d) [range=%d]",
262                            curr->type, range);
263                 break;
264             }
265             /* if global and not doing local, or vice versa, remove it */
266             if (is_global ^ (range == RANGE_LEVEL)) {
267                 *prev = curr->next;
268                 free((genericptr_t)curr);
269             } else {
270                 prev = &(*prev)->next;
271             }
272         }
273     }
274 }
275
276 /*
277  * Pull in the structures from disk, but don't recalculate the object
278  * pointers.
279  */
280 void
281 restore_light_sources(fd)
282     int fd;
283 {
284     int count;
285     light_source *ls;
286
287     /* restore elements */
288     mread(fd, (genericptr_t) &count, sizeof count);
289
290     while (count-- > 0) {
291         ls = (light_source *) alloc(sizeof(light_source));
292         mread(fd, (genericptr_t) ls, sizeof(light_source));
293         ls->next = light_base;
294         light_base = ls;
295     }
296 }
297
298 /* Relink all lights that are so marked. */
299 void
300 relink_light_sources(ghostly)
301     boolean ghostly;
302 {
303     char which;
304     unsigned nid;
305     light_source *ls;
306
307     for (ls = light_base; ls; ls = ls->next) {
308         if (ls->flags & LSF_NEEDS_FIXUP) {
309             if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
310                 if (ghostly) {
311                     if (!lookup_id_mapping((unsigned)ls->id, &nid))
312                         impossible("relink_light_sources: no id mapping");
313                 } else
314                     nid = (unsigned) ls->id;
315                 if (ls->type == LS_OBJECT) {
316                     which = 'o';
317                     ls->id = (genericptr_t) find_oid(nid);
318                 } else {
319                     which = 'm';
320                     ls->id = (genericptr_t) find_mid(nid, FM_EVERYWHERE);
321                 }
322                 if (!ls->id)
323                     impossible("relink_light_sources: cant find %c_id %d",
324                                which, nid);
325             } else
326                 impossible("relink_light_sources: bad type (%d)", ls->type);
327
328             ls->flags &= ~LSF_NEEDS_FIXUP;
329         }
330     }
331 }
332
333 /*
334  * Part of the light source save routine.  Count up the number of light
335  * sources that would be written.  If write_it is true, actually write
336  * the light source out.
337  */
338 STATIC_OVL int
339 maybe_write_ls(fd, range, write_it)
340     int fd, range;
341     boolean write_it;
342 {
343     int count = 0, is_global;
344     light_source *ls;
345
346     for (ls = light_base; ls; ls = ls->next) {
347         if (!ls->id) {
348             impossible("maybe_write_ls: no id! [range=%d]", range);
349             continue;
350         }
351         switch (ls->type) {
352         case LS_OBJECT:
353             is_global = !obj_is_local((struct obj *)ls->id);
354             break;
355         case LS_MONSTER:
356             is_global = !mon_is_local((struct monst *)ls->id);
357             break;
358         default:
359             is_global = 0;
360             impossible("maybe_write_ls: bad type (%d) [range=%d]",
361                        ls->type, range);
362             break;
363         }
364         /* if global and not doing local, or vice versa, count it */
365         if (is_global ^ (range == RANGE_LEVEL)) {
366             count++;
367             if (write_it) write_ls(fd, ls);
368         }
369     }
370
371     return count;
372 }
373
374 /* Write a light source structure to disk. */
375 STATIC_OVL void
376 write_ls(fd, ls)
377     int fd;
378     light_source *ls;
379 {
380     genericptr_t arg_save;
381     struct obj *otmp;
382     struct monst *mtmp;
383
384     if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) {
385         if (ls->flags & LSF_NEEDS_FIXUP)
386             bwrite(fd, (genericptr_t)ls, sizeof(light_source));
387         else {
388             /* replace object pointer with id for write, then put back */
389             arg_save = ls->id;
390             if (ls->type == LS_OBJECT) {
391                 otmp = (struct obj *)ls->id;
392                 ls->id = (genericptr_t)otmp->o_id;
393 #ifdef DEBUG
394                 if (find_oid((unsigned)ls->id) != otmp)
395                     panic("write_ls: can't find obj #%u!", (unsigned)ls->id);
396 #endif
397             } else { /* ls->type == LS_MONSTER */
398                 mtmp = (struct monst *)ls->id;
399                 ls->id = (genericptr_t)mtmp->m_id;
400 #ifdef DEBUG
401                 if (find_mid((unsigned)ls->id, FM_EVERYWHERE) != mtmp)
402                     panic("write_ls: can't find mon #%u!", (unsigned)ls->id);
403 #endif
404             }
405             ls->flags |= LSF_NEEDS_FIXUP;
406             bwrite(fd, (genericptr_t)ls, sizeof(light_source));
407             ls->id = arg_save;
408             ls->flags &= ~LSF_NEEDS_FIXUP;
409         }
410     } else {
411         impossible("write_ls: bad type (%d)", ls->type);
412     }
413 }
414
415 /* Change light source's ID from src to dest. */
416 void
417 obj_move_light_source(src, dest)
418     struct obj *src, *dest;
419 {
420     light_source *ls;
421
422     for (ls = light_base; ls; ls = ls->next)
423         if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src)
424             ls->id = (genericptr_t) dest;
425     src->lamplit = 0;
426     dest->lamplit = 1;
427 }
428
429 /* return true if there exist any light sources */
430 boolean
431 any_light_source()
432 {
433     return light_base != (light_source *) 0;
434 }
435
436 /*
437  * Snuff an object light source if at (x,y).  This currently works
438  * only for burning light sources.
439  */
440 void
441 snuff_light_source(x, y)
442     int x, y;
443 {
444     light_source *ls;
445     struct obj *obj;
446
447     for (ls = light_base; ls; ls = ls->next)
448         /*
449         Is this position check valid??? Can I assume that the positions
450         will always be correct because the objects would have been
451         updated with the last vision update?  [Is that recent enough???]
452         */
453         if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) {
454             obj = (struct obj *) ls->id;
455             if (obj_is_burning(obj)) {
456                 /* The only way to snuff Sunsword is to unwield it.  Darkness
457                  * scrolls won't affect it.  (If we got here because it was
458                  * dropped or thrown inside a monster, this won't matter anyway
459                  * because it will go out when dropped.)
460                  */
461                 if (artifact_light(obj)) continue;
462                 end_burn(obj, obj->otyp != MAGIC_LAMP);
463                 /*
464                  * The current ls element has just been removed (and
465                  * ls->next is now invalid).  Return assuming that there
466                  * is only one light source attached to each object.
467                  */
468                 return;
469             }
470         }
471 }
472
473 /* Return TRUE if object sheds any light at all. */
474 boolean
475 obj_sheds_light(obj)
476     struct obj *obj;
477 {
478     /* so far, only burning objects shed light */
479     return obj_is_burning(obj);
480 }
481
482 /* Return TRUE if sheds light AND will be snuffed by end_burn(). */
483 boolean
484 obj_is_burning(obj)
485     struct obj *obj;
486 {
487     return (obj->lamplit &&
488                 (obj->otyp == MAGIC_LAMP || ignitable(obj) || artifact_light(obj)));
489 }
490
491 /* copy the light source(s) attachted to src, and attach it/them to dest */
492 void
493 obj_split_light_source(src, dest)
494     struct obj *src, *dest;
495 {
496     light_source *ls, *new_ls;
497
498     for (ls = light_base; ls; ls = ls->next)
499         if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src) {
500             /*
501              * Insert the new source at beginning of list.  This will
502              * never interfere us walking down the list - we are already
503              * past the insertion point.
504              */
505             new_ls = (light_source *) alloc(sizeof(light_source));
506             *new_ls = *ls;
507             if (Is_candle(src)) {
508                 /* split candles may emit less light than original group */
509                 ls->range = candle_light_range(src);
510                 new_ls->range = candle_light_range(dest);
511                 vision_full_recalc = 1; /* in case range changed */
512             }
513             new_ls->id = (genericptr_t) dest;
514             new_ls->next = light_base;
515             light_base = new_ls;
516             dest->lamplit = 1;          /* now an active light source */
517         }
518 }
519
520 /* light source `src' has been folded into light source `dest';
521    used for merging lit candles and adding candle(s) to lit candelabrum */
522 void
523 obj_merge_light_sources(src, dest)
524 struct obj *src, *dest;
525 {
526     light_source *ls;
527
528     /* src == dest implies adding to candelabrum */
529     if (src != dest) end_burn(src, TRUE);               /* extinguish candles */
530
531     for (ls = light_base; ls; ls = ls->next)
532         if (ls->type == LS_OBJECT && ls->id == (genericptr_t) dest) {
533             ls->range = candle_light_range(dest);
534             vision_full_recalc = 1;     /* in case range changed */
535             break;
536         }
537 }
538
539 /* Candlelight is proportional to the number of candles;
540    minimum range is 2 rather than 1 for playability. */
541 int
542 candle_light_range(obj)
543 struct obj *obj;
544 {
545     int radius;
546
547     if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
548         /*
549          *      The special candelabrum emits more light than the
550          *      corresponding number of candles would.
551          *       1..3 candles, range 2 (minimum range);
552          *       4..6 candles, range 3 (normal lamp range);
553          *          7 candles, range 4 (bright).
554          */
555         radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4;
556     } else if (Is_candle(obj)) {
557         /*
558          *      Range is incremented by powers of 7 so that it will take
559          *      wizard mode quantities of candles to get more light than
560          *      from a lamp, without imposing an arbitrary limit.
561          *       1..6   candles, range 2;
562          *       7..48  candles, range 3;
563          *      49..342 candles, range 4; &c.
564          */
565         long n = obj->quan;
566
567         radius = 1;     /* always incremented at least once */
568         do {
569             radius++;
570             n /= 7L;
571         } while (n > 0L);
572     } else {
573         /* we're only called for lit candelabrum or candles */
574      /* impossible("candlelight for %d?", obj->otyp); */
575         radius = 3;             /* lamp's value */
576     }
577     return radius;
578 }
579
580 #ifdef WIZARD
581 extern char *FDECL(fmt_ptr, (const genericptr, char *));  /* from alloc.c */
582
583 int
584 wiz_light_sources()
585 {
586     winid win;
587     char buf[BUFSZ], arg_address[20];
588     light_source *ls;
589
590     win = create_nhwindow(NHW_MENU);    /* corner text window */
591     if (win == WIN_ERR) return 0;
592
593     Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy);
594     putstr(win, 0, buf);
595     putstr(win, 0, "");
596
597     if (light_base) {
598         putstr(win, 0, "location range flags  type    id");
599         putstr(win, 0, "-------- ----- ------ ----  -------");
600         for (ls = light_base; ls; ls = ls->next) {
601             Sprintf(buf, "  %2d,%2d   %2d   0x%04x  %s  %s",
602                 ls->x, ls->y, ls->range, ls->flags,
603                 (ls->type == LS_OBJECT ? "obj" :
604                  ls->type == LS_MONSTER ?
605                     (mon_is_local((struct monst *)ls->id) ? "mon" :
606                      ((struct monst *)ls->id == &youmonst) ? "you" :
607                      "<m>") :           /* migrating monster */
608                  "???"),
609                 fmt_ptr(ls->id, arg_address));
610             putstr(win, 0, buf);
611         }
612     } else
613         putstr(win, 0, "<none>");
614
615
616     display_nhwindow(win, FALSE);
617     destroy_nhwindow(win);
618
619     return 0;
620 }
621
622 #endif /* WIZARD */
623
624 #endif /* OVL3 */
625
626 /*light.c*/