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
stoned_dialogue()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
vomiting_dialogue()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
choke_dialogue()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
slime_dialogue()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
burn_away_slime()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
nh_timeout()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
fall_asleep(how_long,wakeup_msg)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
attach_egg_hatch_timeout(egg)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
kill_egg(egg)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
hatch_egg(arg,timeout)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
learn_egg_type(mnum)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
attach_fig_transform_timeout(figurine)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
slip_or_trip()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
see_lamp_flicker(obj,tailer)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
lantern_message(obj)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
burn_object(arg,timeout)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
begin_burn(obj,already_lit)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
end_burn(obj,timer_attached)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
do_storms()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 *
kind_name(kind)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
print_queue(win,base)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
wiz_timeout_queue()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
timer_sanity_check()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
run_timers()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
start_timer(when,kind,func_index,arg)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
stop_timer(func_index,arg)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
obj_move_timers(src,dest)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
obj_split_timers(src,dest)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
obj_stop_timers(obj)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
insert_timer(gnu)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 *
remove_timer(base,func_index,arg)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
write_timer(fd,timer)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
obj_is_local(obj)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
mon_is_local(mon)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
timer_is_local(timer)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
maybe_write_timer(fd,range,write_it)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
save_timers(fd,mode,range)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
restore_timers(fd,range,ghostly,adjust)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
relink_timers(ghostly)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*/
1785