1/*	SCCS Id: @(#)timeout.c	3.4	2002/12/17	*/
2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6#include "lev.h"	/* for checking save modes */
7
8STATIC_DCL void NDECL(stoned_dialogue);
9STATIC_DCL void NDECL(vomiting_dialogue);
10STATIC_DCL void NDECL(choke_dialogue);
11STATIC_DCL void NDECL(slime_dialogue);
12STATIC_DCL void NDECL(slip_or_trip);
13STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *));
14STATIC_DCL void FDECL(lantern_message, (struct obj *));
15STATIC_DCL void FDECL(cleanup_burn, (genericptr_t,long));
16
17#ifdef OVLB
18
19/* He is being petrified - dialogue by inmet!tower */
20static NEARDATA const char * const stoned_texts[] = {
21	"You are slowing down.",		/* 5 */
22	"Your limbs are stiffening.",		/* 4 */
23	"Your limbs have turned to stone.",	/* 3 */
24	"You have turned to stone.",		/* 2 */
25	"You are a statue."			/* 1 */
26};
27
28STATIC_OVL void
29stoned_dialogue()
30{
31	register long i = (Stoned & TIMEOUT);
32
33	if (i > 0L && i <= SIZE(stoned_texts))
34		pline(stoned_texts[SIZE(stoned_texts) - i]);
35	if (i == 5L)
36		HFast = 0L;
37	if (i == 3L)
38		nomul(-3);
39	exercise(A_DEX, FALSE);
40}
41
42/* He is getting sicker and sicker prior to vomiting */
43static NEARDATA const char * const vomiting_texts[] = {
44	"are feeling mildly nauseated.",	/* 14 */
45	"feel slightly confused.",		/* 11 */
46	"can't seem to think straight.",	/* 8 */
47	"feel incredibly sick.",		/* 5 */
48	"suddenly vomit!"			/* 2 */
49};
50
51STATIC_OVL void
52vomiting_dialogue()
53{
54	register long i = (Vomiting & TIMEOUT) / 3L;
55
56	if ((((Vomiting & TIMEOUT) % 3L) == 2) && (i >= 0)
57	    && (i < SIZE(vomiting_texts)))
58		You(vomiting_texts[SIZE(vomiting_texts) - i - 1]);
59
60	switch ((int) i) {
61	case 0:
62		vomit();
63		morehungry(20);
64		break;
65	case 2:
66		make_stunned(HStun + d(2,4), FALSE);
67		/* fall through */
68	case 3:
69		make_confused(HConfusion + d(2,4), FALSE);
70		break;
71	}
72	exercise(A_CON, FALSE);
73}
74
75static NEARDATA const char * const choke_texts[] = {
76	"You find it hard to breathe.",
77	"You're gasping for air.",
78	"You can no longer breathe.",
79	"You're turning %s.",
80	"You suffocate."
81};
82
83static NEARDATA const char * const choke_texts2[] = {
84	"Your %s is becoming constricted.",
85	"Your blood is having trouble reaching your brain.",
86	"The pressure on your %s increases.",
87	"Your consciousness is fading.",
88	"You suffocate."
89};
90
91STATIC_OVL void
92choke_dialogue()
93{
94	register long i = (Strangled & TIMEOUT);
95
96	if(i > 0 && i <= SIZE(choke_texts)) {
97	    if (Breathless || !rn2(50))
98		pline(choke_texts2[SIZE(choke_texts2) - i], body_part(NECK));
99	    else {
100		const char *str = choke_texts[SIZE(choke_texts)-i];
101
102		if (index(str, '%'))
103		    pline(str, hcolor(NH_BLUE));
104		else
105		    pline(str);
106	    }
107	}
108	exercise(A_STR, FALSE);
109}
110
111static NEARDATA const char * const slime_texts[] = {
112	"You are turning a little %s.",           /* 5 */
113	"Your limbs are getting oozy.",              /* 4 */
114	"Your skin begins to peel away.",            /* 3 */
115	"You are turning into %s.",       /* 2 */
116	"You have become %s."             /* 1 */
117};
118
119STATIC_OVL void
120slime_dialogue()
121{
122	register long i = (Slimed & TIMEOUT) / 2L;
123
124	if (((Slimed & TIMEOUT) % 2L) && i >= 0L
125		&& i < SIZE(slime_texts)) {
126	    const char *str = slime_texts[SIZE(slime_texts) - i - 1L];
127
128	    if (index(str, '%')) {
129		if (i == 4L) {	/* "you are turning green" */
130		    if (!Blind)	/* [what if you're already green?] */
131			pline(str, hcolor(NH_GREEN));
132		} else
133		    pline(str, an(Hallucination ? rndmonnam() : "green slime"));
134	    } else
135		pline(str);
136	}
137	if (i == 3L) {	/* limbs becoming oozy */
138	    HFast = 0L;	/* lose intrinsic speed */
139	    stop_occupation();
140	    if (multi > 0) nomul(0);
141	}
142	exercise(A_DEX, FALSE);
143}
144
145void
146burn_away_slime()
147{
148	if (Slimed) {
149	    pline_The("slime that covers you is burned away!");
150	    Slimed = 0L;
151	    flags.botl = 1;
152	}
153	return;
154}
155
156
157#endif /* OVLB */
158#ifdef OVL0
159
160void
161nh_timeout()
162{
163	register struct prop *upp;
164	int sleeptime;
165	int m_idx;
166	int baseluck = (flags.moonphase == FULL_MOON) ? 1 : 0;
167
168	if (flags.friday13) baseluck -= 1;
169
170	if (u.uluck != baseluck &&
171		moves % (u.uhave.amulet || u.ugangr ? 300 : 600) == 0) {
172	/* Cursed luckstones stop bad luck from timing out; blessed luckstones
173	 * stop good luck from timing out; normal luckstones stop both;
174	 * neither is stopped if you don't have a luckstone.
175	 * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th
176	 */
177	    register int time_luck = stone_luck(FALSE);
178	    boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE);
179
180	    if(u.uluck > baseluck && (nostone || time_luck < 0))
181		u.uluck--;
182	    else if(u.uluck < baseluck && (nostone || time_luck > 0))
183		u.uluck++;
184	}
185	if(u.uinvulnerable) return; /* things past this point could kill you */
186	if(Stoned) stoned_dialogue();
187	if(Slimed) slime_dialogue();
188	if(Vomiting) vomiting_dialogue();
189	if(Strangled) choke_dialogue();
190	if(u.mtimedone && !--u.mtimedone) {
191		if (Unchanging)
192			u.mtimedone = rnd(100*youmonst.data->mlevel + 1);
193		else
194			rehumanize();
195	}
196	if(u.ucreamed) u.ucreamed--;
197
198	/* Dissipate spell-based protection. */
199	if (u.usptime) {
200	    if (--u.usptime == 0 && u.uspellprot) {
201		u.usptime = u.uspmtime;
202		u.uspellprot--;
203		find_ac();
204		if (!Blind)
205		    Norep("The %s haze around you %s.", hcolor(NH_GOLDEN),
206			  u.uspellprot ? "becomes less dense" : "disappears");
207	    }
208	}
209
210#ifdef STEED
211	if (u.ugallop) {
212	    if (--u.ugallop == 0L && u.usteed)
213	    	pline("%s stops galloping.", Monnam(u.usteed));
214	}
215#endif
216
217	for(upp = u.uprops; upp < u.uprops+SIZE(u.uprops); upp++)
218	    if((upp->intrinsic & TIMEOUT) && !(--upp->intrinsic & TIMEOUT)) {
219		switch(upp - u.uprops){
220		case STONED:
221			if (delayed_killer && !killer) {
222				killer = delayed_killer;
223				delayed_killer = 0;
224			}
225			if (!killer) {
226				/* leaving killer_format would make it
227				   "petrified by petrification" */
228				killer_format = NO_KILLER_PREFIX;
229				killer = "killed by petrification";
230			}
231			done(STONING);
232			break;
233		case SLIMED:
234			if (delayed_killer && !killer) {
235				killer = delayed_killer;
236				delayed_killer = 0;
237			}
238			if (!killer) {
239				killer_format = NO_KILLER_PREFIX;
240				killer = "turned into green slime";
241			}
242			done(TURNED_SLIME);
243			break;
244		case VOMITING:
245			make_vomiting(0L, TRUE);
246			break;
247		case SICK:
248			You("die from your illness.");
249			killer_format = KILLED_BY_AN;
250			killer = u.usick_cause;
251			if ((m_idx = name_to_mon(killer)) >= LOW_PM) {
252			    if (type_is_pname(&mons[m_idx])) {
253				killer_format = KILLED_BY;
254			    } else if (mons[m_idx].geno & G_UNIQ) {
255				killer = the(killer);
256				Strcpy(u.usick_cause, killer);
257				killer_format = KILLED_BY;
258			    }
259			}
260			u.usick_type = 0;
261			done(POISONING);
262			break;
263		case FAST:
264			if (!Very_fast)
265				You_feel("yourself slowing down%s.",
266							Fast ? " a bit" : "");
267			break;
268		case CONFUSION:
269			HConfusion = 1; /* So make_confused works properly */
270			make_confused(0L, TRUE);
271			stop_occupation();
272			break;
273		case STUNNED:
274			HStun = 1;
275			make_stunned(0L, TRUE);
276			stop_occupation();
277			break;
278		case BLINDED:
279			Blinded = 1;
280			make_blinded(0L, TRUE);
281			stop_occupation();
282			break;
283		case INVIS:
284			newsym(u.ux,u.uy);
285			if (!Invis && !BInvis && !Blind) {
286			    You(!See_invisible ?
287				    "are no longer invisible." :
288				    "can no longer see through yourself.");
289			    stop_occupation();
290			}
291			break;
292		case SEE_INVIS:
293			set_mimic_blocking(); /* do special mimic handling */
294			see_monsters();		/* make invis mons appear */
295			newsym(u.ux,u.uy);	/* make self appear */
296			stop_occupation();
297			break;
298		case WOUNDED_LEGS:
299			heal_legs();
300			stop_occupation();
301			break;
302		case HALLUC:
303			HHallucination = 1;
304			(void) make_hallucinated(0L, TRUE, 0L);
305			stop_occupation();
306			break;
307		case SLEEPING:
308			if (unconscious() || Sleep_resistance)
309				HSleeping += rnd(100);
310			else if (Sleeping) {
311				You("fall asleep.");
312				sleeptime = rnd(20);
313				fall_asleep(-sleeptime, TRUE);
314				HSleeping += sleeptime + rnd(100);
315			}
316			break;
317		case LEVITATION:
318			(void) float_down(I_SPECIAL|TIMEOUT, 0L);
319			break;
320		case STRANGLED:
321			killer_format = KILLED_BY;
322			killer = (u.uburied) ? "suffocation" : "strangulation";
323			done(DIED);
324			break;
325		case FUMBLING:
326			/* call this only when a move took place.  */
327			/* otherwise handle fumbling msgs locally. */
328			if (u.umoved && !Levitation) {
329			    slip_or_trip();
330			    nomul(-2);
331			    nomovemsg = "";
332			    /* The more you are carrying the more likely you
333			     * are to make noise when you fumble.  Adjustments
334			     * to this number must be thoroughly play tested.
335			     */
336			    if ((inv_weight() > -500)) {
337				You("make a lot of noise!");
338				wake_nearby();
339			    }
340			}
341			/* from outside means slippery ice; don't reset
342			   counter if that's the only fumble reason */
343			HFumbling &= ~FROMOUTSIDE;
344			if (Fumbling)
345			    HFumbling += rnd(20);
346			break;
347		case DETECT_MONSTERS:
348			see_monsters();
349			break;
350		}
351	}
352
353	run_timers();
354}
355
356#endif /* OVL0 */
357#ifdef OVL1
358
359void
360fall_asleep(how_long, wakeup_msg)
361int how_long;
362boolean wakeup_msg;
363{
364	stop_occupation();
365	nomul(how_long);
366	/* generally don't notice sounds while sleeping */
367	if (wakeup_msg && multi == how_long) {
368	    /* caller can follow with a direct call to Hear_again() if
369	       there's a need to override this when wakeup_msg is true */
370	    flags.soundok = 0;
371	    afternmv = Hear_again;	/* this won't give any messages */
372	}
373	/* early wakeup from combat won't be possible until next monster turn */
374	u.usleep = monstermoves;
375	nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again;
376}
377
378/* Attach an egg hatch timeout to the given egg. */
379void
380attach_egg_hatch_timeout(egg)
381struct obj *egg;
382{
383	int i;
384
385	/* stop previous timer, if any */
386	(void) stop_timer(HATCH_EGG, (genericptr_t) egg);
387
388	/*
389	 * Decide if and when to hatch the egg.  The old hatch_it() code tried
390	 * once a turn from age 151 to 200 (inclusive), hatching if it rolled
391	 * a number x, 1<=x<=age, where x>150.  This yields a chance of
392	 * hatching > 99.9993%.  Mimic that here.
393	 */
394	for (i = (MAX_EGG_HATCH_TIME-50)+1; i <= MAX_EGG_HATCH_TIME; i++)
395	    if (rnd(i) > 150) {
396		/* egg will hatch */
397		(void) start_timer((long)i, TIMER_OBJECT,
398						HATCH_EGG, (genericptr_t)egg);
399		break;
400	    }
401}
402
403/* prevent an egg from ever hatching */
404void
405kill_egg(egg)
406struct obj *egg;
407{
408	/* stop previous timer, if any */
409	(void) stop_timer(HATCH_EGG, (genericptr_t) egg);
410}
411
412/* timer callback routine: hatch the given egg */
413void
414hatch_egg(arg, timeout)
415genericptr_t arg;
416long timeout;
417{
418	struct obj *egg;
419	struct monst *mon, *mon2;
420	coord cc;
421	xchar x, y;
422	boolean yours, silent, knows_egg = FALSE;
423	boolean cansee_hatchspot = FALSE;
424	int i, mnum, hatchcount = 0;
425
426	egg = (struct obj *) arg;
427	/* sterilized while waiting */
428	if (egg->corpsenm == NON_PM) return;
429
430	mon = mon2 = (struct monst *)0;
431	mnum = big_to_little(egg->corpsenm);
432	/* The identity of one's father is learned, not innate */
433	yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2)));
434	silent = (timeout != monstermoves);	/* hatched while away */
435
436	/* only can hatch when in INVENT, FLOOR, MINVENT */
437	if (get_obj_location(egg, &x, &y, 0)) {
438	    hatchcount = rnd((int)egg->quan);
439	    cansee_hatchspot = cansee(x, y) && !silent;
440	    if (!(mons[mnum].geno & G_UNIQ) &&
441		   !(mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) {
442		for (i = hatchcount; i > 0; i--) {
443		    if (!enexto(&cc, x, y, &mons[mnum]) ||
444			 !(mon = makemon(&mons[mnum], cc.x, cc.y, NO_MINVENT)))
445			break;
446		    /* tame if your own egg hatches while you're on the
447		       same dungeon level, or any dragon egg which hatches
448		       while it's in your inventory */
449		    if ((yours && !silent) ||
450			(carried(egg) && mon->data->mlet == S_DRAGON)) {
451			if ((mon2 = tamedog(mon, (struct obj *)0)) != 0) {
452			    mon = mon2;
453			    if (carried(egg) && mon->data->mlet != S_DRAGON)
454				mon->mtame = 20;
455			}
456		    }
457		    if (mvitals[mnum].mvflags & G_EXTINCT)
458			break;	/* just made last one */
459		    mon2 = mon;	/* in case makemon() fails on 2nd egg */
460		}
461		if (!mon) mon = mon2;
462		hatchcount -= i;
463		egg->quan -= (long)hatchcount;
464	    }
465	}
466#if 0
467	/*
468	 * We could possibly hatch while migrating, but the code isn't
469	 * set up for it...
470	 */
471	else if (obj->where == OBJ_MIGRATING) {
472	    /*
473	    We can do several things.  The first ones that come to
474	    mind are:
475
476	    + Create the hatched monster then place it on the migrating
477	      mons list.  This is tough because all makemon() is made
478	      to place the monster as well.    Makemon() also doesn't
479	      lend itself well to splitting off a "not yet placed"
480	      subroutine.
481
482	    + Mark the egg as hatched, then place the monster when we
483	      place the migrating objects.
484
485	    + Or just kill any egg which gets sent to another level.
486	      Falling is the usual reason such transportation occurs.
487	    */
488	    cansee_hatchspot = FALSE;
489	    mon = ???
490	    }
491#endif
492
493	if (mon) {
494	    char monnambuf[BUFSZ], carriedby[BUFSZ];
495	    boolean siblings = (hatchcount > 1), redraw = FALSE;
496
497	    if (cansee_hatchspot) {
498		Sprintf(monnambuf, "%s%s",
499			siblings ? "some " : "",
500			siblings ?
501			makeplural(m_monnam(mon)) : an(m_monnam(mon)));
502		/* we don't learn the egg type here because learning
503		   an egg type requires either seeing the egg hatch
504		   or being familiar with the egg already,
505		   as well as being able to see the resulting
506		   monster, checked below
507		*/
508	    }
509	    switch (egg->where) {
510		case OBJ_INVENT:
511		    knows_egg = TRUE; /* true even if you are blind */
512		    if (!cansee_hatchspot)
513			You_feel("%s %s from your pack!", something,
514			    locomotion(mon->data, "drop"));
515		    else
516			You("see %s %s out of your pack!",
517			    monnambuf, locomotion(mon->data, "drop"));
518		    if (yours) {
519			pline("%s cries sound like \"%s%s\"",
520			    siblings ? "Their" : "Its",
521			    flags.female ? "mommy" : "daddy",
522			    egg->spe ? "." : "?");
523		    } else if (mon->data->mlet == S_DRAGON) {
524			verbalize("Gleep!");		/* Mything eggs :-) */
525		    }
526		    break;
527
528		case OBJ_FLOOR:
529		    if (cansee_hatchspot) {
530			knows_egg = TRUE;
531			You("see %s hatch.", monnambuf);
532			redraw = TRUE;	/* update egg's map location */
533		    }
534		    break;
535
536		case OBJ_MINVENT:
537		    if (cansee_hatchspot) {
538			/* egg carring monster might be invisible */
539			if (canseemon(egg->ocarry)) {
540			    Sprintf(carriedby, "%s pack",
541				     s_suffix(a_monnam(egg->ocarry)));
542			    knows_egg = TRUE;
543			}
544			else if (is_pool(mon->mx, mon->my))
545			    Strcpy(carriedby, "empty water");
546			else
547			    Strcpy(carriedby, "thin air");
548			You("see %s %s out of %s!", monnambuf,
549			    locomotion(mon->data, "drop"), carriedby);
550		    }
551		    break;
552#if 0
553		case OBJ_MIGRATING:
554		    break;
555#endif
556		default:
557		    impossible("egg hatched where? (%d)", (int)egg->where);
558		    break;
559	    }
560
561	    if (cansee_hatchspot && knows_egg)
562		learn_egg_type(mnum);
563
564	    if (egg->quan > 0) {
565		/* still some eggs left */
566		attach_egg_hatch_timeout(egg);
567		if (egg->timed) {
568		    /* replace ordinary egg timeout with a short one */
569		    (void) stop_timer(HATCH_EGG, (genericptr_t)egg);
570		    (void) start_timer((long)rnd(12), TIMER_OBJECT,
571					HATCH_EGG, (genericptr_t)egg);
572		}
573	    } else if (carried(egg)) {
574		useup(egg);
575	    } else {
576		/* free egg here because we use it above */
577		obj_extract_self(egg);
578		obfree(egg, (struct obj *)0);
579	    }
580	    if (redraw) newsym(x, y);
581	}
582}
583
584/* Learn to recognize eggs of the given type. */
585void
586learn_egg_type(mnum)
587int mnum;
588{
589	/* baby monsters hatch from grown-up eggs */
590	mnum = little_to_big(mnum);
591	mvitals[mnum].mvflags |= MV_KNOWS_EGG;
592	/* we might have just learned about other eggs being carried */
593	update_inventory();
594}
595
596/* Attach a fig_transform timeout to the given figurine. */
597void
598attach_fig_transform_timeout(figurine)
599struct obj *figurine;
600{
601	int i;
602
603	/* stop previous timer, if any */
604	(void) stop_timer(FIG_TRANSFORM, (genericptr_t) figurine);
605
606	/*
607	 * Decide when to transform the figurine.
608	 */
609	i = rnd(9000) + 200;
610	/* figurine will transform */
611	(void) start_timer((long)i, TIMER_OBJECT,
612				FIG_TRANSFORM, (genericptr_t)figurine);
613}
614
615/* give a fumble message */
616STATIC_OVL void
617slip_or_trip()
618{
619	struct obj *otmp = vobj_at(u.ux, u.uy);
620	const char *what, *pronoun;
621	char buf[BUFSZ];
622	boolean on_foot = TRUE;
623#ifdef STEED
624	if (u.usteed) on_foot = FALSE;
625#endif
626
627	if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy)) otmp = 0;
628
629	if (otmp && on_foot) {		/* trip over something in particular */
630	    /*
631		If there is only one item, it will have just been named
632		during the move, so refer to by via pronoun; otherwise,
633		if the top item has been or can be seen, refer to it by
634		name; if not, look for rocks to trip over; trip over
635		anonymous "something" if there aren't any rocks.
636	     */
637	    pronoun = otmp->quan == 1L ? "it" : Hallucination ? "they" : "them";
638	    what = !otmp->nexthere ? pronoun :
639		  (otmp->dknown || !Blind) ? doname(otmp) :
640		  ((otmp = sobj_at(ROCK, u.ux, u.uy)) == 0 ? something :
641		  (otmp->quan == 1L ? "a rock" : "some rocks"));
642	    if (Hallucination) {
643		what = strcpy(buf, what);
644		buf[0] = highc(buf[0]);
645		pline("Egads!  %s bite%s your %s!",
646			what, (!otmp || otmp->quan == 1L) ? "s" : "",
647			body_part(FOOT));
648	    } else {
649		You("trip over %s.", what);
650	    }
651	} else if (rn2(3) && is_ice(u.ux, u.uy)) {
652	    pline("%s %s%s on the ice.",
653#ifdef STEED
654		u.usteed ? upstart(x_monnam(u.usteed,
655				u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
656				(char *)0, SUPPRESS_SADDLE, FALSE)) :
657#endif
658		"You", rn2(2) ? "slip" : "slide", on_foot ? "" : "s");
659	} else {
660	    if (on_foot) {
661		switch (rn2(4)) {
662		  case 1:
663			You("trip over your own %s.", Hallucination ?
664				"elbow" : makeplural(body_part(FOOT)));
665			break;
666		  case 2:
667			You("slip %s.", Hallucination ?
668				"on a banana peel" : "and nearly fall");
669			break;
670		  case 3:
671			You("flounder.");
672			break;
673		  default:
674			You("stumble.");
675			break;
676		}
677	    }
678#ifdef STEED
679	    else {
680		switch (rn2(4)) {
681		  case 1:
682			Your("%s slip out of the stirrups.", makeplural(body_part(FOOT)));
683			break;
684		  case 2:
685			You("let go of the reins.");
686			break;
687		  case 3:
688			You("bang into the saddle-horn.");
689			break;
690		  default:
691			You("slide to one side of the saddle.");
692			break;
693		}
694		dismount_steed(DISMOUNT_FELL);
695	    }
696#endif
697	}
698}
699
700/* Print a lamp flicker message with tailer. */
701STATIC_OVL void
702see_lamp_flicker(obj, tailer)
703struct obj *obj;
704const char *tailer;
705{
706	switch (obj->where) {
707	    case OBJ_INVENT:
708	    case OBJ_MINVENT:
709		pline("%s flickers%s.", Yname2(obj), tailer);
710		break;
711	    case OBJ_FLOOR:
712		You("see %s flicker%s.", an(xname(obj)), tailer);
713		break;
714	}
715}
716
717/* Print a dimming message for brass lanterns. */
718STATIC_OVL void
719lantern_message(obj)
720struct obj *obj;
721{
722	/* from adventure */
723	switch (obj->where) {
724	    case OBJ_INVENT:
725		Your("lantern is getting dim.");
726		if (Hallucination)
727		    pline("Batteries have not been invented yet.");
728		break;
729	    case OBJ_FLOOR:
730		You("see a lantern getting dim.");
731		break;
732	    case OBJ_MINVENT:
733		pline("%s lantern is getting dim.",
734		    s_suffix(Monnam(obj->ocarry)));
735		break;
736	}
737}
738
739/*
740 * Timeout callback for for objects that are burning. E.g. lamps, candles.
741 * See begin_burn() for meanings of obj->age and obj->spe.
742 */
743void
744burn_object(arg, timeout)
745genericptr_t arg;
746long timeout;
747{
748	struct obj *obj = (struct obj *) arg;
749	boolean canseeit, many, menorah, need_newsym;
750	xchar x, y;
751	char whose[BUFSZ];
752
753	menorah = obj->otyp == CANDELABRUM_OF_INVOCATION;
754	many = menorah ? obj->spe > 1 : obj->quan > 1L;
755
756	/* timeout while away */
757	if (timeout != monstermoves) {
758	    long how_long = monstermoves - timeout;
759
760	    if (how_long >= obj->age) {
761		obj->age = 0;
762		end_burn(obj, FALSE);
763
764		if (menorah) {
765		    obj->spe = 0;	/* no more candles */
766		} else if (Is_candle(obj) || obj->otyp == POT_OIL) {
767		    /* get rid of candles and burning oil potions */
768		    obj_extract_self(obj);
769		    obfree(obj, (struct obj *)0);
770		    obj = (struct obj *) 0;
771		}
772
773	    } else {
774		obj->age -= how_long;
775		begin_burn(obj, TRUE);
776	    }
777	    return;
778	}
779
780	/* only interested in INVENT, FLOOR, and MINVENT */
781	if (get_obj_location(obj, &x, &y, 0)) {
782	    canseeit = !Blind && cansee(x, y);
783	    /* set up `whose[]' to be "Your" or "Fred's" or "The goblin's" */
784	    (void) Shk_Your(whose, obj);
785	} else {
786	    canseeit = FALSE;
787	}
788	need_newsym = FALSE;
789
790	/* obj->age is the age remaining at this point.  */
791	switch (obj->otyp) {
792	    case POT_OIL:
793		    /* this should only be called when we run out */
794		    if (canseeit) {
795			switch (obj->where) {
796			    case OBJ_INVENT:
797			    case OBJ_MINVENT:
798				pline("%s potion of oil has burnt away.",
799				    whose);
800				break;
801			    case OBJ_FLOOR:
802				You("see a burning potion of oil go out.");
803				need_newsym = TRUE;
804				break;
805			}
806		    }
807		    end_burn(obj, FALSE);	/* turn off light source */
808		    obj_extract_self(obj);
809		    obfree(obj, (struct obj *)0);
810		    obj = (struct obj *) 0;
811		    break;
812
813	    case BRASS_LANTERN:
814	    case OIL_LAMP:
815		switch((int)obj->age) {
816		    case 150:
817		    case 100:
818		    case 50:
819			if (canseeit) {
820			    if (obj->otyp == BRASS_LANTERN)
821				lantern_message(obj);
822			    else
823				see_lamp_flicker(obj,
824				    obj->age == 50L ? " considerably" : "");
825			}
826			break;
827
828		    case 25:
829			if (canseeit) {
830			    if (obj->otyp == BRASS_LANTERN)
831				lantern_message(obj);
832			    else {
833				switch (obj->where) {
834				    case OBJ_INVENT:
835				    case OBJ_MINVENT:
836					pline("%s %s seems about to go out.",
837					    whose, xname(obj));
838					break;
839				    case OBJ_FLOOR:
840					You("see %s about to go out.",
841					    an(xname(obj)));
842					break;
843				}
844			    }
845			}
846			break;
847
848		    case 0:
849			/* even if blind you'll know if holding it */
850			if (canseeit || obj->where == OBJ_INVENT) {
851			    switch (obj->where) {
852				case OBJ_INVENT:
853				case OBJ_MINVENT:
854				    if (obj->otyp == BRASS_LANTERN)
855					pline("%s lantern has run out of power.",
856					    whose);
857				    else
858					pline("%s %s has gone out.",
859					    whose, xname(obj));
860				    break;
861				case OBJ_FLOOR:
862				    if (obj->otyp == BRASS_LANTERN)
863					You("see a lantern run out of power.");
864				    else
865					You("see %s go out.",
866					    an(xname(obj)));
867				    break;
868			    }
869			}
870			end_burn(obj, FALSE);
871			break;
872
873		    default:
874			/*
875			 * Someone added fuel to the lamp while it was
876			 * lit.  Just fall through and let begin burn
877			 * handle the new age.
878			 */
879			break;
880		}
881
882		if (obj->age)
883		    begin_burn(obj, TRUE);
884
885		break;
886
887	    case CANDELABRUM_OF_INVOCATION:
888	    case TALLOW_CANDLE:
889	    case WAX_CANDLE:
890		switch (obj->age) {
891		    case 75:
892			if (canseeit)
893			    switch (obj->where) {
894				case OBJ_INVENT:
895				case OBJ_MINVENT:
896				    pline("%s %scandle%s getting short.",
897					whose,
898					menorah ? "candelabrum's " : "",
899					many ? "s are" : " is");
900				    break;
901				case OBJ_FLOOR:
902				    You("see %scandle%s getting short.",
903					    menorah ? "a candelabrum's " :
904						many ? "some " : "a ",
905					    many ? "s" : "");
906				    break;
907			    }
908			break;
909
910		    case 15:
911			if (canseeit)
912			    switch (obj->where) {
913				case OBJ_INVENT:
914				case OBJ_MINVENT:
915				    pline(
916					"%s %scandle%s flame%s flicker%s low!",
917					    whose,
918					    menorah ? "candelabrum's " : "",
919					    many ? "s'" : "'s",
920					    many ? "s" : "",
921					    many ? "" : "s");
922				    break;
923				case OBJ_FLOOR:
924				    You("see %scandle%s flame%s flicker low!",
925					    menorah ? "a candelabrum's " :
926						many ? "some " : "a ",
927					    many ? "s'" : "'s",
928					    many ? "s" : "");
929				    break;
930			    }
931			break;
932
933		    case 0:
934			/* we know even if blind and in our inventory */
935			if (canseeit || obj->where == OBJ_INVENT) {
936			    if (menorah) {
937				switch (obj->where) {
938				    case OBJ_INVENT:
939				    case OBJ_MINVENT:
940					pline("%s candelabrum's flame%s.",
941					    whose,
942					    many ? "s die" : " dies");
943					break;
944				    case OBJ_FLOOR:
945					You("see a candelabrum's flame%s die.",
946						many ? "s" : "");
947					break;
948				}
949			    } else {
950				switch (obj->where) {
951				    case OBJ_INVENT:
952				    case OBJ_MINVENT:
953					pline("%s %s %s consumed!",
954					    whose,
955					    xname(obj),
956					    many ? "are" : "is");
957					break;
958				    case OBJ_FLOOR:
959					/*
960					You see some wax candles consumed!
961					You see a wax candle consumed!
962					*/
963					You("see %s%s consumed!",
964					    many ? "some " : "",
965					    many ? xname(obj):an(xname(obj)));
966					need_newsym = TRUE;
967					break;
968				}
969
970				/* post message */
971				pline(Hallucination ?
972					(many ? "They shriek!" :
973						"It shrieks!") :
974					Blind ? "" :
975					    (many ? "Their flames die." :
976						    "Its flame dies."));
977			    }
978			}
979			end_burn(obj, FALSE);
980
981			if (menorah) {
982			    obj->spe = 0;
983			} else {
984			    obj_extract_self(obj);
985			    obfree(obj, (struct obj *)0);
986			    obj = (struct obj *) 0;
987			}
988			break;
989
990		    default:
991			/*
992			 * Someone added fuel (candles) to the menorah while
993			 * it was lit.  Just fall through and let begin burn
994			 * handle the new age.
995			 */
996			break;
997		}
998
999		if (obj && obj->age)
1000		    begin_burn(obj, TRUE);
1001
1002		break;
1003
1004	    default:
1005		impossible("burn_object: unexpeced obj %s", xname(obj));
1006		break;
1007	}
1008	if (need_newsym) newsym(x, y);
1009}
1010
1011/*
1012 * Start a burn timeout on the given object. If not "already lit" then
1013 * create a light source for the vision system.  There had better not
1014 * be a burn already running on the object.
1015 *
1016 * Magic lamps stay lit as long as there's a genie inside, so don't start
1017 * a timer.
1018 *
1019 * Burn rules:
1020 *	potions of oil, lamps & candles:
1021 *		age = # of turns of fuel left
1022 *		spe = <unused>
1023 *
1024 *	magic lamps:
1025 *		age = <unused>
1026 *		spe = 0 not lightable, 1 lightable forever
1027 *
1028 *	candelabrum:
1029 *		age = # of turns of fuel left
1030 *		spe = # of candles
1031 *
1032 * Once the burn begins, the age will be set to the amount of fuel
1033 * remaining _once_the_burn_finishes_.  If the burn is terminated
1034 * early then fuel is added back.
1035 *
1036 * This use of age differs from the use of age for corpses and eggs.
1037 * For the latter items, age is when the object was created, so we
1038 * know when it becomes "bad".
1039 *
1040 * This is a "silent" routine - it should not print anything out.
1041 */
1042void
1043begin_burn(obj, already_lit)
1044	struct obj *obj;
1045	boolean already_lit;
1046{
1047	int radius = 3;
1048	long turns = 0;
1049	boolean do_timer = TRUE;
1050
1051	if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj))
1052	    return;
1053
1054	switch (obj->otyp) {
1055	    case MAGIC_LAMP:
1056		obj->lamplit = 1;
1057		do_timer = FALSE;
1058		break;
1059
1060	    case POT_OIL:
1061		turns = obj->age;
1062		radius = 1;	/* very dim light */
1063		break;
1064
1065	    case BRASS_LANTERN:
1066	    case OIL_LAMP:
1067		/* magic times are 150, 100, 50, 25, and 0 */
1068		if (obj->age > 150L)
1069		    turns = obj->age - 150L;
1070		else if (obj->age > 100L)
1071		    turns = obj->age - 100L;
1072		else if (obj->age > 50L)
1073		    turns = obj->age - 50L;
1074		else if (obj->age > 25L)
1075		    turns = obj->age - 25L;
1076		else
1077		    turns = obj->age;
1078		break;
1079
1080	    case CANDELABRUM_OF_INVOCATION:
1081	    case TALLOW_CANDLE:
1082	    case WAX_CANDLE:
1083		/* magic times are 75, 15, and 0 */
1084		if (obj->age > 75L)
1085		    turns = obj->age - 75L;
1086		else if (obj->age > 15L)
1087		    turns = obj->age - 15L;
1088		else
1089		    turns = obj->age;
1090		radius = candle_light_range(obj);
1091		break;
1092
1093	    default:
1094                /* [ALI] Support artifact light sources */
1095                if (artifact_light(obj)) {
1096		    obj->lamplit = 1;
1097		    do_timer = FALSE;
1098		    radius = 2;
1099		} else {
1100		    impossible("begin burn: unexpected %s", xname(obj));
1101		    turns = obj->age;
1102		}
1103		break;
1104	}
1105
1106	if (do_timer) {
1107	    if (start_timer(turns, TIMER_OBJECT,
1108					BURN_OBJECT, (genericptr_t)obj)) {
1109		obj->lamplit = 1;
1110		obj->age -= turns;
1111		if (carried(obj) && !already_lit)
1112		    update_inventory();
1113	    } else {
1114		obj->lamplit = 0;
1115	    }
1116	} else {
1117	    if (carried(obj) && !already_lit)
1118		update_inventory();
1119	}
1120
1121	if (obj->lamplit && !already_lit) {
1122	    xchar x, y;
1123
1124	    if (get_obj_location(obj, &x, &y, CONTAINED_TOO|BURIED_TOO))
1125		new_light_source(x, y, radius, LS_OBJECT, (genericptr_t) obj);
1126	    else
1127		impossible("begin_burn: can't get obj position");
1128	}
1129}
1130
1131/*
1132 * Stop a burn timeout on the given object if timer attached.  Darken
1133 * light source.
1134 */
1135void
1136end_burn(obj, timer_attached)
1137	struct obj *obj;
1138	boolean timer_attached;
1139{
1140	if (!obj->lamplit) {
1141	    impossible("end_burn: obj %s not lit", xname(obj));
1142	    return;
1143	}
1144
1145	if (obj->otyp == MAGIC_LAMP || artifact_light(obj))
1146	    timer_attached = FALSE;
1147
1148	if (!timer_attached) {
1149	    /* [DS] Cleanup explicitly, since timer cleanup won't happen */
1150	    del_light_source(LS_OBJECT, (genericptr_t)obj);
1151	    obj->lamplit = 0;
1152	    if (obj->where == OBJ_INVENT)
1153		update_inventory();
1154	} else if (!stop_timer(BURN_OBJECT, (genericptr_t) obj))
1155	    impossible("end_burn: obj %s not timed!", xname(obj));
1156}
1157
1158#endif /* OVL1 */
1159#ifdef OVL0
1160
1161/*
1162 * Cleanup a burning object if timer stopped.
1163 */
1164static void
1165cleanup_burn(arg, expire_time)
1166    genericptr_t arg;
1167    long expire_time;
1168{
1169    struct obj *obj = (struct obj *)arg;
1170    if (!obj->lamplit) {
1171	impossible("cleanup_burn: obj %s not lit", xname(obj));
1172	return;
1173    }
1174
1175    del_light_source(LS_OBJECT, arg);
1176
1177    /* restore unused time */
1178    obj->age += expire_time - monstermoves;
1179
1180    obj->lamplit = 0;
1181
1182    if (obj->where == OBJ_INVENT)
1183	update_inventory();
1184}
1185
1186#endif /* OVL0 */
1187#ifdef OVL1
1188
1189void
1190do_storms()
1191{
1192    int nstrike;
1193    register int x, y;
1194    int dirx, diry;
1195    int count;
1196
1197    /* no lightning if not the air level or too often, even then */
1198    if(!Is_airlevel(&u.uz) || rn2(8))
1199	return;
1200
1201    /* the number of strikes is 8-log2(nstrike) */
1202    for(nstrike = rnd(64); nstrike <= 64; nstrike *= 2) {
1203	count = 0;
1204	do {
1205	    x = rnd(COLNO-1);
1206	    y = rn2(ROWNO);
1207	} while (++count < 100 && levl[x][y].typ != CLOUD);
1208
1209	if(count < 100) {
1210	    dirx = rn2(3) - 1;
1211	    diry = rn2(3) - 1;
1212	    if(dirx != 0 || diry != 0)
1213		buzz(-15, /* "monster" LIGHTNING spell */
1214		     8, x, y, dirx, diry);
1215	}
1216    }
1217
1218    if(levl[u.ux][u.uy].typ == CLOUD) {
1219	/* inside a cloud during a thunder storm is deafening */
1220	pline("Kaboom!!!  Boom!!  Boom!!");
1221	if(!u.uinvulnerable) {
1222	    stop_occupation();
1223	    nomul(-3);
1224	}
1225    } else
1226	You_hear("a rumbling noise.");
1227}
1228#endif /* OVL1 */
1229
1230
1231#ifdef OVL0
1232/* ------------------------------------------------------------------------- */
1233/*
1234 * Generic Timeout Functions.
1235 *
1236 * Interface:
1237 *
1238 * General:
1239 *	boolean start_timer(long timeout,short kind,short func_index,
1240 *							genericptr_t arg)
1241 *		Start a timer of kind 'kind' that will expire at time
1242 *		monstermoves+'timeout'.  Call the function at 'func_index'
1243 *		in the timeout table using argument 'arg'.  Return TRUE if
1244 *		a timer was started.  This places the timer on a list ordered
1245 *		"sooner" to "later".  If an object, increment the object's
1246 *		timer count.
1247 *
1248 *	long stop_timer(short func_index, genericptr_t arg)
1249 *		Stop a timer specified by the (func_index, arg) pair.  This
1250 *		assumes that such a pair is unique.  Return the time the
1251 *		timer would have gone off.  If no timer is found, return 0.
1252 *		If an object, decrement the object's timer count.
1253 *
1254 *	void run_timers(void)
1255 *		Call timers that have timed out.
1256 *
1257 *
1258 * Save/Restore:
1259 *	void save_timers(int fd, int mode, int range)
1260 *		Save all timers of range 'range'.  Range is either global
1261 *		or local.  Global timers follow game play, local timers
1262 *		are saved with a level.  Object and monster timers are
1263 *		saved using their respective id's instead of pointers.
1264 *
1265 *	void restore_timers(int fd, int range, boolean ghostly, long adjust)
1266 *		Restore timers of range 'range'.  If from a ghost pile,
1267 *		adjust the timeout by 'adjust'.  The object and monster
1268 *		ids are not restored until later.
1269 *
1270 *	void relink_timers(boolean ghostly)
1271 *		Relink all object and monster timers that had been saved
1272 *		using their object's or monster's id number.
1273 *
1274 * Object Specific:
1275 *	void obj_move_timers(struct obj *src, struct obj *dest)
1276 *		Reassign all timers from src to dest.
1277 *
1278 *	void obj_split_timers(struct obj *src, struct obj *dest)
1279 *		Duplicate all timers assigned to src and attach them to dest.
1280 *
1281 *	void obj_stop_timers(struct obj *obj)
1282 *		Stop all timers attached to obj.
1283 */
1284
1285#ifdef WIZARD
1286STATIC_DCL const char *FDECL(kind_name, (SHORT_P));
1287STATIC_DCL void FDECL(print_queue, (winid, timer_element *));
1288#endif
1289STATIC_DCL void FDECL(insert_timer, (timer_element *));
1290STATIC_DCL timer_element *FDECL(remove_timer, (timer_element **, SHORT_P,
1291								genericptr_t));
1292STATIC_DCL void FDECL(write_timer, (int, timer_element *));
1293STATIC_DCL boolean FDECL(mon_is_local, (struct monst *));
1294STATIC_DCL boolean FDECL(timer_is_local, (timer_element *));
1295STATIC_DCL int FDECL(maybe_write_timer, (int, int, BOOLEAN_P));
1296
1297/* ordered timer list */
1298static timer_element *timer_base;		/* "active" */
1299static unsigned long timer_id = 1;
1300
1301/* If defined, then include names when printing out the timer queue */
1302#define VERBOSE_TIMER
1303
1304typedef struct {
1305    timeout_proc f, cleanup;
1306#ifdef VERBOSE_TIMER
1307    const char *name;
1308# define TTAB(a, b, c) {a,b,c}
1309#else
1310# define TTAB(a, b, c) {a,b}
1311#endif
1312} ttable;
1313
1314/* table of timeout functions */
1315static const ttable timeout_funcs[NUM_TIME_FUNCS] = {
1316    TTAB(rot_organic,	(timeout_proc)0,	"rot_organic"),
1317    TTAB(rot_corpse,	(timeout_proc)0,	"rot_corpse"),
1318    TTAB(revive_mon,	(timeout_proc)0,	"revive_mon"),
1319    TTAB(burn_object,	cleanup_burn,		"burn_object"),
1320    TTAB(hatch_egg,	(timeout_proc)0,	"hatch_egg"),
1321    TTAB(fig_transform,	(timeout_proc)0,	"fig_transform")
1322};
1323#undef TTAB
1324
1325
1326#if defined(WIZARD)
1327
1328STATIC_OVL const char *
1329kind_name(kind)
1330    short kind;
1331{
1332    switch (kind) {
1333	case TIMER_LEVEL: return "level";
1334	case TIMER_GLOBAL: return "global";
1335	case TIMER_OBJECT: return "object";
1336	case TIMER_MONSTER: return "monster";
1337    }
1338    return "unknown";
1339}
1340
1341STATIC_OVL void
1342print_queue(win, base)
1343    winid win;
1344    timer_element *base;
1345{
1346    timer_element *curr;
1347    char buf[BUFSZ], arg_address[20];
1348
1349    if (!base) {
1350	putstr(win, 0, "<empty>");
1351    } else {
1352	putstr(win, 0, "timeout  id   kind   call");
1353	for (curr = base; curr; curr = curr->next) {
1354#ifdef VERBOSE_TIMER
1355	    Sprintf(buf, " %4ld   %4ld  %-6s %s(%s)",
1356		curr->timeout, curr->tid, kind_name(curr->kind),
1357		timeout_funcs[curr->func_index].name,
1358		fmt_ptr((genericptr_t)curr->arg, arg_address));
1359#else
1360	    Sprintf(buf, " %4ld   %4ld  %-6s #%d(%s)",
1361		curr->timeout, curr->tid, kind_name(curr->kind),
1362		curr->func_index,
1363		fmt_ptr((genericptr_t)curr->arg, arg_address));
1364#endif
1365	    putstr(win, 0, buf);
1366	}
1367    }
1368}
1369
1370int
1371wiz_timeout_queue()
1372{
1373    winid win;
1374    char buf[BUFSZ];
1375
1376    win = create_nhwindow(NHW_MENU);	/* corner text window */
1377    if (win == WIN_ERR) return 0;
1378
1379    Sprintf(buf, "Current time = %ld.", monstermoves);
1380    putstr(win, 0, buf);
1381    putstr(win, 0, "");
1382    putstr(win, 0, "Active timeout queue:");
1383    putstr(win, 0, "");
1384    print_queue(win, timer_base);
1385
1386    display_nhwindow(win, FALSE);
1387    destroy_nhwindow(win);
1388
1389    return 0;
1390}
1391
1392void
1393timer_sanity_check()
1394{
1395    timer_element *curr;
1396    char obj_address[20];
1397
1398    /* this should be much more complete */
1399    for (curr = timer_base; curr; curr = curr->next)
1400	if (curr->kind == TIMER_OBJECT) {
1401	    struct obj *obj = (struct obj *) curr->arg;
1402	    if (obj->timed == 0) {
1403		pline("timer sanity: untimed obj %s, timer %ld",
1404		      fmt_ptr((genericptr_t)obj, obj_address), curr->tid);
1405	    }
1406	}
1407}
1408
1409#endif /* WIZARD */
1410
1411
1412/*
1413 * Pick off timeout elements from the global queue and call their functions.
1414 * Do this until their time is less than or equal to the move count.
1415 */
1416void
1417run_timers()
1418{
1419    timer_element *curr;
1420
1421    /*
1422     * Always use the first element.  Elements may be added or deleted at
1423     * any time.  The list is ordered, we are done when the first element
1424     * is in the future.
1425     */
1426    while (timer_base && timer_base->timeout <= monstermoves) {
1427	curr = timer_base;
1428	timer_base = curr->next;
1429
1430	if (curr->kind == TIMER_OBJECT) ((struct obj *)(curr->arg))->timed--;
1431	(*timeout_funcs[curr->func_index].f)(curr->arg, curr->timeout);
1432	free((genericptr_t) curr);
1433    }
1434}
1435
1436
1437/*
1438 * Start a timer.  Return TRUE if successful.
1439 */
1440boolean
1441start_timer(when, kind, func_index, arg)
1442long when;
1443short kind;
1444short func_index;
1445genericptr_t arg;
1446{
1447    timer_element *gnu;
1448
1449    if (func_index < 0 || func_index >= NUM_TIME_FUNCS)
1450	panic("start_timer");
1451
1452    gnu = (timer_element *) alloc(sizeof(timer_element));
1453    gnu->next = 0;
1454    gnu->tid = timer_id++;
1455    gnu->timeout = monstermoves + when;
1456    gnu->kind = kind;
1457    gnu->needs_fixup = 0;
1458    gnu->func_index = func_index;
1459    gnu->arg = arg;
1460    insert_timer(gnu);
1461
1462    if (kind == TIMER_OBJECT)	/* increment object's timed count */
1463	((struct obj *)arg)->timed++;
1464
1465    /* should check for duplicates and fail if any */
1466    return TRUE;
1467}
1468
1469
1470/*
1471 * Remove the timer from the current list and free it up.  Return the time
1472 * it would have gone off, 0 if not found.
1473 */
1474long
1475stop_timer(func_index, arg)
1476short func_index;
1477genericptr_t arg;
1478{
1479    timer_element *doomed;
1480    long timeout;
1481
1482    doomed = remove_timer(&timer_base, func_index, arg);
1483
1484    if (doomed) {
1485	timeout = doomed->timeout;
1486	if (doomed->kind == TIMER_OBJECT)
1487	    ((struct obj *)arg)->timed--;
1488	if (timeout_funcs[doomed->func_index].cleanup)
1489	    (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout);
1490	free((genericptr_t) doomed);
1491	return timeout;
1492    }
1493    return 0;
1494}
1495
1496
1497/*
1498 * Move all object timers from src to dest, leaving src untimed.
1499 */
1500void
1501obj_move_timers(src, dest)
1502    struct obj *src, *dest;
1503{
1504    int count;
1505    timer_element *curr;
1506
1507    for (count = 0, curr = timer_base; curr; curr = curr->next)
1508	if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)src) {
1509	    curr->arg = (genericptr_t) dest;
1510	    dest->timed++;
1511	    count++;
1512	}
1513    if (count != src->timed)
1514	panic("obj_move_timers");
1515    src->timed = 0;
1516}
1517
1518
1519/*
1520 * Find all object timers and duplicate them for the new object "dest".
1521 */
1522void
1523obj_split_timers(src, dest)
1524    struct obj *src, *dest;
1525{
1526    timer_element *curr, *next_timer=0;
1527
1528    for (curr = timer_base; curr; curr = next_timer) {
1529	next_timer = curr->next;	/* things may be inserted */
1530	if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)src) {
1531	    (void) start_timer(curr->timeout-monstermoves, TIMER_OBJECT,
1532					curr->func_index, (genericptr_t)dest);
1533	}
1534    }
1535}
1536
1537
1538/*
1539 * Stop all timers attached to this object.  We can get away with this because
1540 * all object pointers are unique.
1541 */
1542void
1543obj_stop_timers(obj)
1544    struct obj *obj;
1545{
1546    timer_element *curr, *prev, *next_timer=0;
1547
1548    for (prev = 0, curr = timer_base; curr; curr = next_timer) {
1549	next_timer = curr->next;
1550	if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)obj) {
1551	    if (prev)
1552		prev->next = curr->next;
1553	    else
1554		timer_base = curr->next;
1555	    if (timeout_funcs[curr->func_index].cleanup)
1556		(*timeout_funcs[curr->func_index].cleanup)(curr->arg,
1557			curr->timeout);
1558	    free((genericptr_t) curr);
1559	} else {
1560	    prev = curr;
1561	}
1562    }
1563    obj->timed = 0;
1564}
1565
1566
1567/* Insert timer into the global queue */
1568STATIC_OVL void
1569insert_timer(gnu)
1570    timer_element *gnu;
1571{
1572    timer_element *curr, *prev;
1573
1574    for (prev = 0, curr = timer_base; curr; prev = curr, curr = curr->next)
1575	if (curr->timeout >= gnu->timeout) break;
1576
1577    gnu->next = curr;
1578    if (prev)
1579	prev->next = gnu;
1580    else
1581	timer_base = gnu;
1582}
1583
1584
1585STATIC_OVL timer_element *
1586remove_timer(base, func_index, arg)
1587timer_element **base;
1588short func_index;
1589genericptr_t arg;
1590{
1591    timer_element *prev, *curr;
1592
1593    for (prev = 0, curr = *base; curr; prev = curr, curr = curr->next)
1594	if (curr->func_index == func_index && curr->arg == arg) break;
1595
1596    if (curr) {
1597	if (prev)
1598	    prev->next = curr->next;
1599	else
1600	    *base = curr->next;
1601    }
1602
1603    return curr;
1604}
1605
1606
1607STATIC_OVL void
1608write_timer(fd, timer)
1609    int fd;
1610    timer_element *timer;
1611{
1612    genericptr_t arg_save;
1613
1614    switch (timer->kind) {
1615	case TIMER_GLOBAL:
1616	case TIMER_LEVEL:
1617	    /* assume no pointers in arg */
1618	    bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1619	    break;
1620
1621	case TIMER_OBJECT:
1622	    if (timer->needs_fixup)
1623		bwrite(fd, (genericptr_t)timer, sizeof(timer_element));
1624	    else {
1625		/* replace object pointer with id */
1626		arg_save = timer->arg;
1627		timer->arg = (genericptr_t)((struct obj *)timer->arg)->o_id;
1628		timer->needs_fixup = 1;
1629		bwrite(fd, (genericptr_t)timer, sizeof(timer_element));
1630		timer->arg = arg_save;
1631		timer->needs_fixup = 0;
1632	    }
1633	    break;
1634
1635	case TIMER_MONSTER:
1636	    if (timer->needs_fixup)
1637		bwrite(fd, (genericptr_t)timer, sizeof(timer_element));
1638	    else {
1639		/* replace monster pointer with id */
1640		arg_save = timer->arg;
1641		timer->arg = (genericptr_t)((struct monst *)timer->arg)->m_id;
1642		timer->needs_fixup = 1;
1643		bwrite(fd, (genericptr_t)timer, sizeof(timer_element));
1644		timer->arg = arg_save;
1645		timer->needs_fixup = 0;
1646	    }
1647	    break;
1648
1649	default:
1650	    panic("write_timer");
1651	    break;
1652    }
1653}
1654
1655
1656/*
1657 * Return TRUE if the object will stay on the level when the level is
1658 * saved.
1659 */
1660boolean
1661obj_is_local(obj)
1662    struct obj *obj;
1663{
1664    switch (obj->where) {
1665	case OBJ_INVENT:
1666	case OBJ_MIGRATING:	return FALSE;
1667	case OBJ_FLOOR:
1668	case OBJ_BURIED:	return TRUE;
1669	case OBJ_CONTAINED:	return obj_is_local(obj->ocontainer);
1670	case OBJ_MINVENT:	return mon_is_local(obj->ocarry);
1671    }
1672    panic("obj_is_local");
1673    return FALSE;
1674}
1675
1676
1677/*
1678 * Return TRUE if the given monster will stay on the level when the
1679 * level is saved.
1680 */
1681STATIC_OVL boolean
1682mon_is_local(mon)
1683struct monst *mon;
1684{
1685    struct monst *curr;
1686
1687    for (curr = migrating_mons; curr; curr = curr->nmon)
1688	if (curr == mon) return FALSE;
1689    /* `mydogs' is used during level changes, never saved and restored */
1690    for (curr = mydogs; curr; curr = curr->nmon)
1691	if (curr == mon) return FALSE;
1692    return TRUE;
1693}
1694
1695
1696/*
1697 * Return TRUE if the timer is attached to something that will stay on the
1698 * level when the level is saved.
1699 */
1700STATIC_OVL boolean
1701timer_is_local(timer)
1702    timer_element *timer;
1703{
1704    switch (timer->kind) {
1705	case TIMER_LEVEL:	return TRUE;
1706	case TIMER_GLOBAL:	return FALSE;
1707	case TIMER_OBJECT:	return obj_is_local((struct obj *)timer->arg);
1708	case TIMER_MONSTER:	return mon_is_local((struct monst *)timer->arg);
1709    }
1710    panic("timer_is_local");
1711    return FALSE;
1712}
1713
1714
1715/*
1716 * Part of the save routine.  Count up the number of timers that would
1717 * be written.  If write_it is true, actually write the timer.
1718 */
1719STATIC_OVL int
1720maybe_write_timer(fd, range, write_it)
1721    int fd, range;
1722    boolean write_it;
1723{
1724    int count = 0;
1725    timer_element *curr;
1726
1727    for (curr = timer_base; curr; curr = curr->next) {
1728	if (range == RANGE_GLOBAL) {
1729	    /* global timers */
1730
1731	    if (!timer_is_local(curr)) {
1732		count++;
1733		if (write_it) write_timer(fd, curr);
1734	    }
1735
1736	} else {
1737	    /* local timers */
1738
1739	    if (timer_is_local(curr)) {
1740		count++;
1741		if (write_it) write_timer(fd, curr);
1742	    }
1743
1744	}
1745    }
1746
1747    return count;
1748}
1749
1750
1751/*
1752 * Save part of the timer list.  The parameter 'range' specifies either
1753 * global or level timers to save.  The timer ID is saved with the global
1754 * timers.
1755 *
1756 * Global range:
1757 *		+ timeouts that follow the hero (global)
1758 *		+ timeouts that follow obj & monst that are migrating
1759 *
1760 * Level range:
1761 *		+ timeouts that are level specific (e.g. storms)
1762 *		+ timeouts that stay with the level (obj & monst)
1763 */
1764void
1765save_timers(fd, mode, range)
1766    int fd, mode, range;
1767{
1768    timer_element *curr, *prev, *next_timer=0;
1769    int count;
1770
1771    if (perform_bwrite(mode)) {
1772	if (range == RANGE_GLOBAL)
1773	    bwrite(fd, (genericptr_t) &timer_id, sizeof(timer_id));
1774
1775	count = maybe_write_timer(fd, range, FALSE);
1776	bwrite(fd, (genericptr_t) &count, sizeof count);
1777	(void) maybe_write_timer(fd, range, TRUE);
1778    }
1779
1780    if (release_data(mode)) {
1781	for (prev = 0, curr = timer_base; curr; curr = next_timer) {
1782	    next_timer = curr->next;	/* in case curr is removed */
1783
1784	    if ( !(!!(range == RANGE_LEVEL) ^ !!timer_is_local(curr)) ) {
1785		if (prev)
1786		    prev->next = curr->next;
1787		else
1788		    timer_base = curr->next;
1789		free((genericptr_t) curr);
1790		/* prev stays the same */
1791	    } else {
1792		prev = curr;
1793	    }
1794	}
1795    }
1796}
1797
1798
1799/*
1800 * Pull in the structures from disk, but don't recalculate the object and
1801 * monster pointers.
1802 */
1803void
1804restore_timers(fd, range, ghostly, adjust)
1805    int fd, range;
1806    boolean ghostly;	/* restoring from a ghost level */
1807    long adjust;	/* how much to adjust timeout */
1808{
1809    int count;
1810    timer_element *curr;
1811
1812    if (range == RANGE_GLOBAL)
1813	mread(fd, (genericptr_t) &timer_id, sizeof timer_id);
1814
1815    /* restore elements */
1816    mread(fd, (genericptr_t) &count, sizeof count);
1817    while (count-- > 0) {
1818	curr = (timer_element *) alloc(sizeof(timer_element));
1819	mread(fd, (genericptr_t) curr, sizeof(timer_element));
1820	if (ghostly)
1821	    curr->timeout += adjust;
1822	insert_timer(curr);
1823    }
1824}
1825
1826
1827/* reset all timers that are marked for reseting */
1828void
1829relink_timers(ghostly)
1830    boolean ghostly;
1831{
1832    timer_element *curr;
1833    unsigned nid;
1834
1835    for (curr = timer_base; curr; curr = curr->next) {
1836	if (curr->needs_fixup) {
1837	    if (curr->kind == TIMER_OBJECT) {
1838		if (ghostly) {
1839		    if (!lookup_id_mapping((unsigned)curr->arg, &nid))
1840			panic("relink_timers 1");
1841		} else
1842		    nid = (unsigned) curr->arg;
1843		curr->arg = (genericptr_t) find_oid(nid);
1844		if (!curr->arg) panic("cant find o_id %d", nid);
1845		curr->needs_fixup = 0;
1846	    } else if (curr->kind == TIMER_MONSTER) {
1847		panic("relink_timers: no monster timer implemented");
1848	    } else
1849		panic("relink_timers 2");
1850	}
1851    }
1852}
1853
1854#endif /* OVL0 */
1855
1856/*timeout.c*/
1857