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
47static light_source *light_base = 0;
48
49STATIC_DCL void FDECL(write_ls, (int, light_source *));
50STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P));
51
52/* imported from vision.c, for small circles */
53extern char circle_data[];
54extern char circle_start[];
55
56
57/* Create a new light source.  */
58void
59new_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 */
89void
90del_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. */
126void
127do_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
208struct monst *
209find_mid(nid, fmflags)
210unsigned nid;
211unsigned 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. */
230void
231save_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 */
280void
281restore_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. */
299void
300relink_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 */
338STATIC_OVL int
339maybe_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. */
375STATIC_OVL void
376write_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 NETHACK_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 NETHACK_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. */
416void
417obj_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 */
430boolean
431any_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 */
440void
441snuff_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. */
474boolean
475obj_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(). */
483boolean
484obj_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 */
492void
493obj_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 */
522void
523obj_merge_light_sources(src, dest)
524struct 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. */
541int
542candle_light_range(obj)
543struct 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
581extern char *FDECL(fmt_ptr, (const genericptr, char *));  /* from alloc.c */
582
583int
584wiz_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*/
627