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
new_light_source(x,y,range,type,id)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
del_light_source(type,id)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
do_light_sources(cs_rows)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 *
find_mid(nid,fmflags)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
save_light_sources(fd,mode,range)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
restore_light_sources(fd)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
relink_light_sources(ghostly)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
maybe_write_ls(fd,range,write_it)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
write_ls(fd,ls)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
obj_move_light_source(src,dest)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
any_light_source()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
snuff_light_source(x,y)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
obj_sheds_light(obj)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
obj_is_burning(obj)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
obj_split_light_source(src,dest)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
obj_merge_light_sources(src,dest)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
candle_light_range(obj)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
wiz_light_sources()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*/
636