OSDN Git Service

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