OSDN Git Service

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