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