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