1 /* NetHack 3.7	timeout.c	$NHDT-Date: 1606243387 2020/11/24 18:43:07 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.122 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2018. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 static void stoned_dialogue(void);
9 static void vomiting_dialogue(void);
10 static void choke_dialogue(void);
11 static void levitation_dialogue(void);
12 static void slime_dialogue(void);
13 static void slimed_to_death(struct kinfo *);
14 static void phaze_dialogue(void);
15 static void done_timeout(int, int);
16 static void slip_or_trip(void);
17 static void see_lamp_flicker(struct obj *, const char *);
18 static void lantern_message(struct obj *);
19 static void cleanup_burn(ANY_P *, long);
20 
21 /* used by wizard mode #timeout and #wizintrinsic; order by 'interest'
22    for timeout countdown, where most won't occur in normal play */
23 const struct propname {
24     int prop_num;
25     const char *prop_name;
26 } propertynames[] = {
27     { INVULNERABLE, "invulnerable" },
28     { STONED, "petrifying" },
29     { SLIMED, "becoming slime" },
30     { STRANGLED, "strangling" },
31     { SICK, "fatally sick" },
32     { STUNNED, "stunned" },
33     { CONFUSION, "confused" },
34     { HALLUC, "hallucinating" },
35     { BLINDED, "blinded" },
36     { DEAF, "deafness" },
37     { VOMITING, "vomiting" },
38     { GLIB, "slippery fingers" },
39     { WOUNDED_LEGS, "wounded legs" },
40     { SLEEPY, "sleepy" },
41     { TELEPORT, "teleporting" },
42     { POLYMORPH, "polymorphing" },
43     { LEVITATION, "levitating" },
44     { FAST, "very fast" }, /* timed 'FAST' is very fast */
45     { CLAIRVOYANT, "clairvoyant" },
46     { DETECT_MONSTERS, "monster detection" },
47     { SEE_INVIS, "see invisible" },
48     { INVIS, "invisible" },
49     /* timed displacement is possible via eating a displacer beast corpse */
50     { DISPLACED, "displaced" },
51     /* timed pass-walls is a potential prayer result if surrounded by stone
52        with nowhere to be safely teleported to */
53     { PASSES_WALLS, "pass thru walls" },
54     /*
55      * Properties beyond here don't have timed values during normal play,
56      * so there's not much point in trying to order them sensibly.
57      * They're either on or off based on equipment, role, actions, &c.
58      */
59     { FIRE_RES, "fire resistance" },
60     { COLD_RES, "cold resistance" },
61     { SLEEP_RES, "sleep resistance" },
62     { DISINT_RES, "disintegration resistance" },
63     { SHOCK_RES, "shock resistance" },
64     { POISON_RES, "poison resistance" },
65     { ACID_RES, "acid resistance" },
66     { STONE_RES, "stoning resistance" },
67     { DRAIN_RES, "drain resistance" },
68     { SICK_RES, "sickness resistance" },
69     { ANTIMAGIC, "magic resistance" },
70     { HALLUC_RES, "hallucination resistance" },
71     { FUMBLING, "fumbling" },
72     { HUNGER, "voracious hunger" },
73     { TELEPAT, "telepathic" },
74     { WARNING, "warning" },
75     { WARN_OF_MON, "warn: monster type or class" },
76     { WARN_UNDEAD, "warn: undead" },
77     { SEARCHING, "searching" },
78     { INFRAVISION, "infravision" },
79     { ADORNED, "adorned (+/- Cha)" },
80     { STEALTH, "stealthy" },
81     { AGGRAVATE_MONSTER, "monster aggravation" },
82     { CONFLICT, "conflict" },
83     { JUMPING, "jumping" },
84     { TELEPORT_CONTROL, "teleport control" },
85     { FLYING, "flying" },
86     { WWALKING, "water walking" },
87     { SWIMMING, "swimming" },
88     { MAGICAL_BREATHING, "magical breathing" },
89     { SLOW_DIGESTION, "slow digestion" },
90     { HALF_SPDAM, "half spell damage" },
91     { HALF_PHDAM, "half physical damage" },
92     { REGENERATION, "HP regeneration" },
93     { ENERGY_REGENERATION, "energy regeneration" },
94     { PROTECTION, "extra protection" },
95     { PROT_FROM_SHAPE_CHANGERS, "protection from shape changers" },
96     { POLYMORPH_CONTROL, "polymorph control" },
97     { UNCHANGING, "unchanging" },
98     { REFLECTING, "reflecting" },
99     { FREE_ACTION, "free action" },
100     { FIXED_ABIL, "fixed abilities" },
101     { WITHERING, "withering away" },
102     { LIFESAVED, "life will be saved" },
103     {  0, 0 },
104 };
105 
106 /* Someone whose only source of unchanging is polyinit mode or corruption on
107  * death can still turn into slime.
108  * TODO: Add a tracker for permanent corruption and check specifically for that
109  * and Polyinit_mode here, to guard against future ways of getting HUnchanging
110  * intrinsically.
111  */
112 boolean
can_slime_with_unchanging(void)113 can_slime_with_unchanging(void)
114 {
115     /* caller should have checked this but just in case... */
116     if (!Unchanging) {
117         return TRUE;
118     }
119     /* extrinsic always blocks sliming */
120     if (EUnchanging) {
121         return FALSE;
122     }
123     /* any sources of unchanging besides FROMOUTSIDE? if so, sliming is blocked
124      */
125     if (HUnchanging & ~FROMOUTSIDE) {
126         return FALSE;
127     }
128     return TRUE;
129 }
130 
131 /* He is being petrified - dialogue by inmet!tower */
132 static NEARDATA const char *const stoned_texts[] = {
133     "You are slowing down.",            /* 5 */
134     "Your limbs are stiffening.",       /* 4 */
135     "Your limbs have turned to stone.", /* 3 */
136     "You have turned to stone.",        /* 2 */
137     "You are a statue."                 /* 1 */
138 };
139 
140 static void
stoned_dialogue(void)141 stoned_dialogue(void)
142 {
143     register long i = (Stoned & TIMEOUT);
144 
145     if (i > 0L && i <= SIZE(stoned_texts)) {
146         char buf[BUFSZ];
147 
148         Strcpy(buf, stoned_texts[SIZE(stoned_texts) - i]);
149         if (nolimbs(g.youmonst.data) && strstri(buf, "limbs"))
150             (void) strsubst(buf, "limbs", "extremities");
151         pline1(buf);
152     }
153     switch ((int) i) {
154     case 5: /* slowing down */
155         HFast = 0L;
156         if (g.multi > 0)
157             nomul(0);
158         break;
159     case 4: /* limbs stiffening */
160         /* just one move left to save oneself so quit fiddling around;
161            don't stop attempt to eat tin--might be lizard or acidic */
162         if (!Popeye(STONED))
163             stop_occupation();
164         if (g.multi > 0)
165             nomul(0);
166         break;
167     case 3: /* limbs turned to stone */
168         stop_occupation();
169         nomul(-3); /* can't move anymore */
170         g.multi_reason = "getting stoned";
171         g.nomovemsg = You_can_move_again; /* not unconscious */
172         /* "your limbs have turned to stone" so terminate wounded legs */
173         if (Wounded_legs && !u.usteed)
174             heal_legs(2);
175         break;
176     case 2: /* turned to stone */
177         if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
178             set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
179         /* if also vomiting or turning into slime, stop those (no messages) */
180         if (Vomiting)
181             make_vomiting(0L, FALSE);
182         if (Slimed)
183             make_slimed(0L, (char *) 0);
184         break;
185     default:
186         break;
187     }
188     exercise(A_DEX, FALSE);
189 }
190 
191 /* hero is getting sicker and sicker prior to vomiting */
192 static NEARDATA const char *const vomiting_texts[] = {
193     "are feeling mildly nauseated.", /* 14 */
194     "feel slightly confused.",       /* 11 */
195     "can't seem to think straight.", /* 8 */
196     "feel incredibly sick.",         /* 5 */
197     "are about to vomit."            /* 2 */
198 };
199 
200 static void
vomiting_dialogue(void)201 vomiting_dialogue(void)
202 {
203     const char *txt = 0;
204     char buf[BUFSZ];
205     long v = (Vomiting & TIMEOUT);
206 
207     /* note: nhtimeout() hasn't decremented timed properties for the
208        current turn yet, so we use Vomiting-1 here */
209     switch ((int) (v - 1L)) {
210     case 14:
211         txt = vomiting_texts[0];
212         break;
213     case 11:
214         txt = vomiting_texts[1];
215         if (strstri(txt, " confused") && Confusion)
216             txt = strsubst(strcpy(buf, txt), " confused", " more confused");
217         break;
218     case 6:
219         make_stunned((HStun & TIMEOUT) + (long) d(2, 4), FALSE);
220         if (!Popeye(VOMITING))
221             stop_occupation();
222     /*FALLTHRU*/
223     case 9:
224         make_confused((HConfusion & TIMEOUT) + (long) d(2, 4), FALSE);
225         if (g.multi > 0)
226             nomul(0);
227         break;
228     case 8:
229         txt = vomiting_texts[2];
230         if (strstri(txt, " think") && Stunned)
231             txt = strsubst(strcpy(buf, txt), "can't seem to ", "can't ");
232         break;
233     case 5:
234         txt = vomiting_texts[3];
235         break;
236     case 2:
237         txt = vomiting_texts[4];
238         if (cantvomit(g.youmonst.data))
239             txt = "gag uncontrollably.";
240         else if (Hallucination)
241             /* "hurl" is short for "hurl chunks" which is slang for
242                relatively violent vomiting... */
243             txt = "are about to hurl!";
244         break;
245     case 0:
246         stop_occupation();
247         if (!cantvomit(g.youmonst.data)) {
248             morehungry(20);
249             /* case 2 used to be "You suddenly vomit!" but it wasn't sudden
250                since you've just been through the earlier messages of the
251                countdown, and it was still possible to move around between
252                that message and "You can move again." (from vomit()'s
253                nomul(-2)) with no intervening message; give one here to
254                have more specific point at which hero became unable to move
255                [vomit() issues its own message for the cantvomit() case
256                and for the FAINTING-or-worse case where stomach is empty] */
257             if (u.uhs < FAINTING)
258                 You("%s!", !Hallucination ? "vomit" : "hurl chunks");
259         }
260         vomit();
261         break;
262     default:
263         break;
264     }
265     if (txt)
266         You1(txt);
267     exercise(A_CON, FALSE);
268 }
269 
270 static NEARDATA const char *const choke_texts[] = {
271     "You find it hard to breathe.",
272     "You're gasping for air.",
273     "You can no longer breathe.",
274     "You're turning %s.",
275     "You suffocate."
276 };
277 
278 static NEARDATA const char *const choke_texts2[] = {
279     "Your %s is becoming constricted.",
280     "Your blood is having trouble reaching your brain.",
281     "The pressure on your %s increases.",
282     "Your consciousness is fading.",
283     "You suffocate."
284 };
285 
286 static void
choke_dialogue(void)287 choke_dialogue(void)
288 {
289     register long i = (Strangled & TIMEOUT);
290 
291     if (i > 0 && i <= SIZE(choke_texts)) {
292         if (Breathless || !rn2(50))
293             pline(choke_texts2[SIZE(choke_texts2) - i], body_part(NECK));
294         else {
295             const char *str = choke_texts[SIZE(choke_texts) - i];
296 
297             if (index(str, '%'))
298                 pline(str, hcolor(NH_BLUE));
299             else
300                 pline1(str);
301         }
302     }
303     exercise(A_STR, FALSE);
304 }
305 
306 static NEARDATA const char *const levi_texts[] = {
307     "You float slightly lower.",
308     "You wobble unsteadily %s the %s."
309 };
310 
311 static void
levitation_dialogue(void)312 levitation_dialogue(void)
313 {
314     /* -1 because the last message comes via float_down() */
315     long i = (((HLevitation & TIMEOUT) - 1L) / 2L);
316 
317     if (ELevitation)
318         return;
319 
320     if (!ACCESSIBLE(levl[u.ux][u.uy].typ)
321         && !is_pool_or_lava(u.ux,u.uy))
322         return;
323 
324     if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) {
325         const char *s = levi_texts[SIZE(levi_texts) - i];
326 
327         if (index(s, '%')) {
328             boolean danger = (is_pool_or_lava(u.ux, u.uy)
329                               && !Is_waterlevel(&u.uz));
330 
331             pline(s, danger ? "over" : "in",
332                   danger ? surface(u.ux, u.uy) : "air");
333         } else
334             pline1(s);
335     }
336 }
337 
338 static NEARDATA const char *const slime_texts[] = {
339     "You are turning a little %s.",   /* 5 */
340     "Your limbs are getting oozy.",   /* 4 */
341     "Your skin begins to peel away.", /* 3 */
342     "You are turning into %s.",       /* 2 */
343     "You have become %s."             /* 1 */
344 };
345 
346 static void
slime_dialogue(void)347 slime_dialogue(void)
348 {
349     long t = (Slimed & TIMEOUT), i = t / 2L;
350 
351     if (Unchanging && !can_slime_with_unchanging()) {
352         /* prevent this message from showing up if sliming is suspended due to
353          * Unchanging */
354         return;
355     }
356 
357     if (t == 1L) {
358         /* display as green slime during "You have become green slime."
359            but don't worry about not being able to see self; if already
360            mimicking something else at the time, implicitly be revealed */
361         g.youmonst.m_ap_type = M_AP_MONSTER;
362         g.youmonst.mappearance = PM_GREEN_SLIME;
363         /* no message given when 't' is odd, so no automatic update of
364            self; force one */
365         newsym(u.ux, u.uy);
366     }
367 
368     if ((t % 2L) != 0L && i >= 0L && i < SIZE(slime_texts)) {
369         char buf[BUFSZ];
370 
371         Strcpy(buf, slime_texts[SIZE(slime_texts) - i - 1L]);
372         if (nolimbs(g.youmonst.data) && strstri(buf, "limbs"))
373             (void) strsubst(buf, "limbs", "extremities");
374 
375         if (index(buf, '%')) {
376             if (i == 4L) {  /* "you are turning green" */
377                 if (!Blind) /* [what if you're already green?] */
378                     pline(buf, hcolor(NH_GREEN));
379             } else
380                 pline(buf,
381                       an(Hallucination ? rndmonnam(NULL) : "green slime"));
382         } else
383             pline1(buf);
384     }
385 
386     switch (i) {
387     case 3L:  /* limbs becoming oozy */
388         HFast = 0L; /* lose intrinsic speed */
389         if (!Popeye(SLIMED))
390             stop_occupation();
391         if (g.multi > 0)
392             nomul(0);
393         break;
394     case 2L: /* skin begins to peel */
395         if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
396             set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
397         break;
398     case 1L: /* turning into slime */
399         /* if also turning to stone, stop doing that (no message) */
400         if (Stoned)
401             make_stoned(0L, (char *) 0, KILLED_BY_AN, (char *) 0);
402         break;
403     }
404     exercise(A_DEX, FALSE);
405 }
406 
407 void
burn_away_slime(void)408 burn_away_slime(void)
409 {
410     if (Slimed) {
411         make_slimed(0L, "The slime that covers you is burned away!");
412     }
413 }
414 
415 /* countdown timer for turning into green slime has run out; kill our hero */
416 static void
slimed_to_death(struct kinfo * kptr)417 slimed_to_death(struct kinfo* kptr)
418 {
419     uchar save_mvflags;
420 
421     /* redundant: polymon() cures sliming when polying into green slime */
422     if (Upolyd && g.youmonst.data == &mons[PM_GREEN_SLIME]) {
423         dealloc_killer(kptr);
424         return;
425     }
426     /* more sure killer reason is set up */
427     if (kptr && kptr->name[0]) {
428         g.killer.format = kptr->format;
429         Strcpy(g.killer.name, kptr->name);
430     } else {
431         g.killer.format = NO_KILLER_PREFIX;
432         Strcpy(g.killer.name, "turned into green slime");
433     }
434     dealloc_killer(kptr);
435 
436     /*
437      * Polymorph into a green slime, which might destroy some worn armor
438      * (potentially affecting bones) and dismount from steed.
439      * Can't be Unchanging; wouldn't have turned into slime if we were.
440      * Despite lack of Unchanging, neither done() nor savelife() calls
441      * rehumanize() if hero dies while polymorphed.
442      * polymon() undoes the slime countdown's mimick-green-slime hack
443      * but does not perform polyself()'s light source bookkeeping.
444      * No longer need to manually increment uconduct.polyselfs to reflect
445      * [formerly implicit] change of form; polymon() takes care of that.
446      * Temporarily ungenocide if necessary.
447      */
448     if (emits_light(g.youmonst.data))
449         del_light_source(LS_MONSTER, monst_to_any(&g.youmonst));
450     save_mvflags = g.mvitals[PM_GREEN_SLIME].mvflags;
451     g.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags & ~G_GENOD;
452     /* become a green slime; also resets youmonst.m_ap_type+.mappearance */
453     /* suppress the transformation message to avoid "You have become a green
454      * slime.  You turn into a green slime!" */
455     (void) polymon(PM_GREEN_SLIME, (POLYMON_ALL_MSGS & ~POLYMON_TRANSFORM_MSG));
456     g.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags;
457     done_timeout(TURNED_SLIME, SLIMED);
458     return;
459 }
460 
461 /* Intrinsic Passes_walls is temporary when your god is trying to fix
462    all troubles and then TROUBLE_STUCK_IN_WALL calls safe_teleds() but
463    it can't find anywhere to place you.  If that happens you get a small
464    value for (HPasses_walls & TIMEOUT) to move somewhere yourself.
465    Message given is "you feel much slimmer" as a joke hint that you can
466    move between things which are closely packed--like the substance of
467    solid rock! */
468 static NEARDATA const char *const phaze_texts[] = {
469     "You start to feel bloated.",
470     "You are feeling rather flabby.",
471 };
472 
473 static void
phaze_dialogue(void)474 phaze_dialogue(void)
475 {
476     long i = ((HPasses_walls & TIMEOUT) / 2L);
477 
478     if (EPasses_walls || (HPasses_walls & ~TIMEOUT))
479         return;
480 
481     if (((HPasses_walls & TIMEOUT) % 2L) && i > 0L && i <= SIZE(phaze_texts))
482         pline1(phaze_texts[SIZE(phaze_texts) - i]);
483 }
484 
485 /* when a status timeout is fatal, keep the status line indicator shown
486    during end of game rundown (and potential dumplog);
487    timeout has already counted down to 0 by the time we get here */
488 static void
done_timeout(int how,int which)489 done_timeout(int how, int which)
490 {
491     long *intrinsic_p = &u.uprops[which].intrinsic;
492 
493     *intrinsic_p |= I_SPECIAL; /* affects final disclosure */
494     done(how);
495 
496     /* life-saved */
497     *intrinsic_p &= ~I_SPECIAL;
498     g.context.botl = TRUE;
499 }
500 
501 void
nh_timeout(void)502 nh_timeout(void)
503 {
504     register struct prop *upp;
505     struct kinfo *kptr;
506     boolean was_flying;
507     int sleeptime;
508     int m_idx;
509     int baseluck = (flags.moonphase == FULL_MOON) ? 1 : 0;
510 
511     if (flags.friday13)
512         baseluck -= 1;
513 
514     /* letting your quest leader die brings bad luck */
515     if (g.quest_status.leader_is_dead) {
516         baseluck = -4;
517     }
518 
519     if (u.uluck != baseluck
520         && g.moves % ((u.uhave.amulet || u.ugangr) ? 300 : 600) == 0) {
521         /* Cursed luckstones stop bad luck from timing out; blessed luckstones
522          * stop good luck from timing out; normal luckstones stop both;
523          * neither is stopped if you don't have a luckstone.
524          * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th
525          */
526         register int time_luck = stone_luck(FALSE);
527         boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE);
528 
529         if (u.uluck > baseluck && (nostone || time_luck < 0))
530             u.uluck--;
531         else if (u.uluck < baseluck && (nostone || time_luck > 0))
532             u.uluck++;
533     }
534     if (u.uinvulnerable)
535         return; /* things past this point could kill you */
536     if (Stoned)
537         stoned_dialogue();
538     if (Slimed)
539         slime_dialogue();
540     if (Vomiting)
541         vomiting_dialogue();
542     if (Strangled)
543         choke_dialogue();
544     if (HLevitation & TIMEOUT)
545         levitation_dialogue();
546     if (HPasses_walls & TIMEOUT)
547         phaze_dialogue();
548     if (u.mtimedone && !--u.mtimedone) {
549         if (Unchanging)
550             u.mtimedone = rnd(100 * g.youmonst.data->mlevel + 1);
551         else if (is_were(g.youmonst.data))
552             you_unwere(FALSE); /* if polycontrl, asks whether to rehumanize */
553         else
554             rehumanize();
555     }
556     if (u.ucreamed)
557         u.ucreamed--;
558 
559     /* Dissipate spell-based protection. */
560     if (u.usptime) {
561         if (--u.usptime == 0 && u.uspellprot) {
562             u.usptime = u.uspmtime;
563             u.uspellprot--;
564             find_ac();
565             if (!Blind)
566                 Norep("The %s haze around you %s.", hcolor(NH_GOLDEN),
567                       u.uspellprot ? "becomes less dense" : "disappears");
568         }
569     }
570 
571     if (u.ugallop) {
572         if (--u.ugallop == 0L && u.usteed)
573             pline("%s stops galloping.", Monnam(u.usteed));
574     }
575 
576     was_flying = Flying;
577     for (upp = u.uprops; upp < u.uprops + SIZE(u.uprops); upp++) {
578         boolean timed_out = FALSE;
579         if ((upp->intrinsic & TIMEOUT) > 0) {
580             /* pause sliming only if hero has unchanging and the unchanging is
581              * not obtained through e.g. polyinit mode;
582              * currently no other timeouts can be paused */
583             if (!(upp - u.uprops == SLIMED && Unchanging
584                   && !can_slime_with_unchanging())) {
585                 /* the actual tick down */
586                 upp->intrinsic--;
587                 if ((upp->intrinsic & TIMEOUT) == 0) {
588                     timed_out = TRUE;
589                 }
590             }
591         }
592 
593         /* now, did it time out? */
594         if (timed_out) {
595             kptr = find_delayed_killer((int) (upp - u.uprops));
596             switch (upp - u.uprops) {
597             case STONED:
598                 if (kptr && kptr->name[0]) {
599                     g.killer.format = kptr->format;
600                     Strcpy(g.killer.name, kptr->name);
601                 } else {
602                     g.killer.format = NO_KILLER_PREFIX;
603                     Strcpy(g.killer.name, "killed by petrification");
604                 }
605                 dealloc_killer(kptr);
606                 /* (unlike sliming, you aren't changing form here) */
607                 done_timeout(STONING, STONED);
608                 break;
609             case SLIMED:
610                 slimed_to_death(kptr); /* done_timeout(TURNED_SLIME,SLIMED) */
611                 break;
612             case VOMITING:
613                 make_vomiting(0L, TRUE);
614                 break;
615             case SICK:
616                 /* You might be able to bounce back from food poisoning, but not
617                  * other forms of illness. */
618                 if ((u.usick_type & SICK_NONVOMITABLE) == 0
619                     && rn2(100) < ACURR(A_CON)) {
620                     You("have recovered from your illness.");
621                     make_sick(0, NULL, FALSE, SICK_ALL);
622                     exercise(A_CON, FALSE);
623                     adjattrib(A_CON, -1, AA_NOMSG);
624                     break;
625                 }
626                 You("die from your illness.");
627                 if (kptr && kptr->name[0]) {
628                     g.killer.format = kptr->format;
629                     Strcpy(g.killer.name, kptr->name);
630                 } else {
631                     g.killer.format = KILLED_BY_AN;
632                     g.killer.name[0] = 0; /* take the default */
633                 }
634                 dealloc_killer(kptr);
635 
636                 if ((m_idx = name_to_mon(g.killer.name,
637                                          (int *) 0)) >= LOW_PM) {
638                     if (type_is_pname(&mons[m_idx])) {
639                         g.killer.format = KILLED_BY;
640                     } else if (mons[m_idx].geno & G_UNIQ) {
641                         Strcpy(g.killer.name, the(g.killer.name));
642                         g.killer.format = KILLED_BY;
643                     }
644                 }
645                 done_timeout(POISONING, SICK);
646                 u.usick_type = 0;
647                 break;
648             case WITHERING:
649                 You("are no longer withering away.");
650                 g.context.botl = TRUE;
651                 break;
652             case FAST:
653                 if (!Very_fast)
654                     You_feel("yourself slow down%s.",
655                              Fast ? " a bit" : "");
656                 break;
657             case CONFUSION:
658                 /* So make_confused works properly */
659                 set_itimeout(&HConfusion, 1L);
660                 make_confused(0L, TRUE);
661                 if (!Confusion)
662                     stop_occupation();
663                 break;
664             case STUNNED:
665                 set_itimeout(&HStun, 1L);
666                 make_stunned(0L, TRUE);
667                 if (!Stunned)
668                     stop_occupation();
669                 break;
670             case BLINDED:
671                 set_itimeout(&Blinded, 1L);
672                 make_blinded(0L, TRUE);
673                 if (!Blind)
674                     stop_occupation();
675                 break;
676             case DEAF:
677                 set_itimeout(&HDeaf, 1L);
678                 make_deaf(0L, TRUE);
679                 g.context.botl = TRUE;
680                 if (!Deaf)
681                     stop_occupation();
682                 break;
683             case INVIS:
684                 newsym(u.ux, u.uy);
685                 if (!Invis && !BInvis && !Blind) {
686                     You(!See_invisible
687                             ? "are no longer invisible."
688                             : "can no longer see through yourself.");
689                     stop_occupation();
690                 }
691                 break;
692             case SEE_INVIS:
693                 set_mimic_blocking(); /* do special mimic handling */
694                 see_monsters();       /* make invis mons appear */
695                 newsym(u.ux, u.uy);   /* make self appear */
696                 stop_occupation();
697                 break;
698             case WOUNDED_LEGS:
699                 heal_legs(0);
700                 stop_occupation();
701                 break;
702             case HALLUC:
703                 set_itimeout(&HHallucination, 1L);
704                 (void) make_hallucinated(0L, TRUE, 0L);
705                 if (!Hallucination)
706                     stop_occupation();
707                 break;
708             case SLEEPY:
709                 if (unconscious() || Sleep_resistance) {
710                     incr_itimeout(&HSleepy, rnd(100));
711                 } else if (Sleepy) {
712                     You("fall asleep.");
713                     sleeptime = rnd(20);
714                     fall_asleep(-sleeptime, TRUE);
715                     incr_itimeout(&HSleepy, sleeptime + rnd(100));
716                 }
717                 break;
718             case LEVITATION:
719                 /* timed Flying is via #wizintrinsic only; still, we want to
720                    avoid float_down() reporting "you have stopped levitating
721                    and are now flying" if both are timing out together;
722                    relies on knowing that Lev timeout is handled before Fly */
723                 if ((HFlying & TIMEOUT) == 1L)
724                     --HFlying; /* bypass pending 'case FLYING' */
725                 (void) float_down(I_SPECIAL | TIMEOUT, 0L);
726                 break;
727             case FLYING:
728                 /* timed Flying is via #wizintrinsic only */
729                 if (was_flying && !Flying) {
730                     g.context.botl = 1;
731                     You("land.");
732                     spoteffects(TRUE);
733                 }
734                 break;
735             case DISPLACED:
736                 if (!Displaced) /* give a message */
737                     toggle_displacement((struct obj *) 0, 0L, FALSE);
738                 break;
739             case WARN_OF_MON:
740                 /* timed Warn_of_mon is via #wizintrinsic only */
741                 if (!Warn_of_mon) {
742                     g.context.warntype.speciesidx = NON_PM;
743                     if (g.context.warntype.species) {
744                         You("are no longer warned about %s.",
745                      makeplural(g.context.warntype.species->pmnames[NEUTRAL]));
746                         g.context.warntype.species = (struct permonst *) 0;
747                     }
748                 }
749                 break;
750             case PASSES_WALLS:
751                 if (!Passes_walls) {
752                     if (stuck_in_wall())
753                         You_feel("hemmed in again.");
754                     else
755                         pline("You're back to your %s self again.",
756                               !Upolyd ? "normal" : "unusual");
757                 }
758                 break;
759             case STRANGLED:
760                 g.killer.format = KILLED_BY;
761                 Strcpy(g.killer.name,
762                        (u.uburied) ? "suffocation" : "strangulation");
763                 done_timeout(DIED, STRANGLED);
764                 /* must be declining to die in explore|wizard mode;
765                    treat like being cured of strangulation by prayer */
766                 if (uamul && uamul->otyp == AMULET_OF_STRANGULATION) {
767                     Your("amulet vanishes!");
768                     useup(uamul);
769                 }
770                 break;
771             case FUMBLING:
772                 /* call this only when a move took place.  */
773                 /* otherwise handle fumbling msgs locally. */
774                 if (u.umoved && !(Levitation || Flying)) {
775                     slip_or_trip();
776                     nomul(-2);
777                     g.multi_reason = "fumbling";
778                     g.nomovemsg = "";
779                     /* The more you are carrying the more likely you
780                      * are to make noise when you fumble.  Adjustments
781                      * to this number must be thoroughly play tested.
782                      */
783                     if ((inv_weight() > -500)) {
784                         if (Hallucination)
785                             You("forget the rules to Charades!");
786                         else
787                             You("make a lot of noise!");
788                         wake_nearby();
789                     }
790                 }
791                 /* from outside means slippery ice; don't reset
792                    counter if that's the only fumble reason */
793                 HFumbling &= ~FROMOUTSIDE;
794                 if (Fumbling)
795                     incr_itimeout(&HFumbling, rnd(20));
796 
797                 if (iflags.defer_decor) {
798                     /* 'mention_decor' was deferred for message sequencing
799                        reasons; catch up now */
800                     deferred_decor(FALSE);
801                 }
802                 break;
803             case DETECT_MONSTERS:
804                 see_monsters();
805                 break;
806             case GLIB:
807                 make_glib(0); /* might update persistent inventory */
808                 break;
809             }
810         }
811     }
812 
813     run_timers();
814 }
815 
816 void
fall_asleep(int how_long,boolean wakeup_msg)817 fall_asleep(int how_long, boolean wakeup_msg)
818 {
819     stop_occupation();
820     nomul(how_long);
821     g.multi_reason = "sleeping";
822     /* generally don't notice sounds while sleeping */
823     if (wakeup_msg && g.multi == how_long) {
824         /* caller can follow with a direct call to Hear_again() if
825            there's a need to override this when wakeup_msg is true */
826         incr_itimeout(&HDeaf, how_long);
827         g.context.botl = TRUE;
828         g.afternmv = Hear_again; /* this won't give any messages */
829     }
830     /* early wakeup from combat won't be possible until next monster turn */
831     u.usleep = g.monstermoves;
832     g.nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again;
833 }
834 
835 /* Attach an egg hatch timeout to the given egg.
836  *      when = Time to hatch, usually only passed if re-creating an
837  *             existing hatch timer. Pass 0L for random hatch time.
838  */
839 void
attach_egg_hatch_timeout(struct obj * egg,long when)840 attach_egg_hatch_timeout(struct obj* egg, long when)
841 {
842     int i;
843 
844     /* stop previous timer, if any */
845     (void) stop_timer(HATCH_EGG, obj_to_any(egg));
846 
847     /*
848      * Decide if and when to hatch the egg.  The old hatch_it() code tried
849      * once a turn from age 151 to 200 (inclusive), hatching if it rolled
850      * a number x, 1<=x<=age, where x>150.  This yields a chance of
851      * hatching > 99.9993%.  Mimic that here.
852      */
853     if (!when) {
854         for (i = (MAX_EGG_HATCH_TIME - 50) + 1; i <= MAX_EGG_HATCH_TIME; i++)
855             if (rnd(i) > 150) {
856                 /* egg will hatch */
857                 when = (long) i;
858                 break;
859             }
860     }
861     if (when) {
862         (void) start_timer(when, TIMER_OBJECT, HATCH_EGG, obj_to_any(egg));
863     }
864 }
865 
866 /* prevent an egg from ever hatching */
867 void
kill_egg(struct obj * egg)868 kill_egg(struct obj* egg)
869 {
870     /* stop previous timer, if any */
871     (void) stop_timer(HATCH_EGG, obj_to_any(egg));
872 }
873 
874 /* timer callback routine: hatch the given egg */
875 void
hatch_egg(anything * arg,long timeout)876 hatch_egg(anything *arg, long timeout)
877 {
878     struct obj *egg;
879     struct monst *mon, *mon2;
880     coord cc;
881     xchar x, y;
882     boolean yours, silent, knows_egg = FALSE;
883     boolean cansee_hatchspot = FALSE;
884     int i, mnum, hatchcount = 0;
885 
886     egg = arg->a_obj;
887     /* sterilized while waiting */
888     if (egg->corpsenm == NON_PM)
889         return;
890 
891     mon = mon2 = (struct monst *) 0;
892     mnum = big_to_little(egg->corpsenm);
893     /* The identity of one's father is learned, not innate */
894     yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2)));
895     silent = (timeout != g.monstermoves); /* hatched while away */
896 
897     /* only can hatch when in INVENT, FLOOR, MINVENT */
898     if (get_obj_location(egg, &x, &y, 0)) {
899         hatchcount = rnd((int) egg->quan);
900         cansee_hatchspot = cansee(x, y) && !silent;
901         if (!(mons[mnum].geno & G_UNIQ)
902             && !(g.mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) {
903             for (i = hatchcount; i > 0; i--) {
904                 if (!enexto(&cc, x, y, &mons[mnum])
905                     || !(mon = makemon(&mons[mnum], cc.x, cc.y, NO_MINVENT)))
906                     break;
907                 /* tame if your own egg hatches while you're on the
908                    same dungeon level, or any dragon egg which hatches
909                    while it's in your inventory */
910                 if ((yours && !silent)
911                     || (carried(egg) && mon->data->mlet == S_DRAGON)) {
912                     if (tamedog(mon, (struct obj *) 0, FALSE)) {
913                         if (carried(egg) && mon->data->mlet != S_DRAGON) {
914                             mon->mtame = 20;
915                             u.uconduct.pets++;
916                         }
917                     }
918                 }
919                 if (g.mvitals[mnum].mvflags & G_EXTINCT)
920                     break;  /* just made last one */
921                 mon2 = mon; /* in case makemon() fails on 2nd egg */
922             }
923             if (!mon)
924                 mon = mon2;
925             hatchcount -= i;
926             egg->quan -= (long) hatchcount;
927         }
928 #if 0
929     /*
930      * We could possibly hatch while migrating, but the code isn't
931      * set up for it...
932      */
933     } else if (obj->where == OBJ_MIGRATING) {
934         /*
935          * We can do several things.  The first ones that come to
936          * mind are:
937          * + Create the hatched monster then place it on the migrating
938          *   mons list.  This is tough because all makemon() is made
939          *   to place the monster as well.  Makemon() also doesn't lend
940          *   itself well to splitting off a "not yet placed" subroutine.
941          * + Mark the egg as hatched, then place the monster when we
942          *   place the migrating objects.
943          * + Or just kill any egg which gets sent to another level.
944          *   Falling is the usual reason such transportation occurs.
945          */
946         cansee_hatchspot = FALSE;
947         mon = ???;
948 #endif
949     }
950 
951     if (mon) {
952         char monnambuf[BUFSZ], carriedby[BUFSZ];
953         boolean siblings = (hatchcount > 1), redraw = FALSE;
954 
955         if (cansee_hatchspot) {
956             /* [bug?  m_monnam() yields accurate monster type
957                regardless of hallucination] */
958             Sprintf(monnambuf, "%s%s", siblings ? "some " : "",
959                     siblings ? makeplural(m_monnam(mon)) : an(m_monnam(mon)));
960             /* we don't learn the egg type here because learning
961                an egg type requires either seeing the egg hatch
962                or being familiar with the egg already,
963                as well as being able to see the resulting
964                monster, checked below
965             */
966         }
967         switch (egg->where) {
968         case OBJ_INVENT:
969             knows_egg = TRUE; /* true even if you are blind */
970             if (!cansee_hatchspot)
971                 You_feel("%s %s from your pack!", something,
972                          locomotion(mon->data, "drop"));
973             else
974                 You_see("%s %s out of your pack!", monnambuf,
975                         locomotion(mon->data, "drop"));
976             if (yours) {
977                 pline("%s cries sound like \"%s%s\"",
978                       siblings ? "Their" : "Its",
979                       flags.female ? "mommy" : "daddy", egg->spe ? "." : "?");
980             } else if (mon->data->mlet == S_DRAGON && !Deaf) {
981                 verbalize("Gleep!"); /* Mything eggs :-) */
982             }
983             break;
984 
985         case OBJ_FLOOR:
986             if (cansee_hatchspot) {
987                 knows_egg = TRUE;
988                 You_see("%s hatch.", monnambuf);
989                 redraw = TRUE; /* update egg's map location */
990             }
991             break;
992 
993         case OBJ_MINVENT:
994             if (cansee_hatchspot) {
995                 /* egg carrying monster might be invisible */
996                 mon2 = egg->ocarry;
997                 if (canseemon(mon2)
998                     && (!mon2->wormno || cansee(mon2->mx, mon2->my))) {
999                     Sprintf(carriedby, "%s pack",
1000                             s_suffix(a_monnam(mon2)));
1001                     knows_egg = TRUE;
1002                 } else if (is_pool(mon->mx, mon->my)) {
1003                     Strcpy(carriedby, "empty water");
1004                 } else {
1005                     Strcpy(carriedby, "thin air");
1006                 }
1007                 You_see("%s %s out of %s!", monnambuf,
1008                         locomotion(mon->data, "drop"), carriedby);
1009             }
1010             break;
1011 #if 0
1012         case OBJ_MIGRATING:
1013             break;
1014 #endif
1015         default:
1016             impossible("egg hatched where? (%d)", (int) egg->where);
1017             break;
1018         }
1019 
1020         if (cansee_hatchspot && knows_egg)
1021             learn_egg_type(mnum);
1022 
1023         if (egg->quan > 0) {
1024             /* still some eggs left */
1025             /* Instead of ordinary egg timeout use a short one */
1026             attach_egg_hatch_timeout(egg, (long) rnd(12));
1027         } else if (carried(egg)) {
1028             useup(egg);
1029         } else {
1030             /* free egg here because we use it above */
1031             obj_extract_self(egg);
1032             obfree(egg, (struct obj *) 0);
1033             if ((mon = m_at(x,y)) && !hideunder(mon) && cansee(x, y))
1034                 redraw = TRUE;
1035         }
1036         if (redraw)
1037             newsym(x, y);
1038     }
1039 }
1040 
1041 /* Learn to recognize eggs of the given type. */
1042 void
learn_egg_type(int mnum)1043 learn_egg_type(int mnum)
1044 {
1045     /* baby monsters hatch from grown-up eggs */
1046     mnum = little_to_big(mnum);
1047     g.mvitals[mnum].mvflags |= MV_KNOWS_EGG;
1048     /* we might have just learned about other eggs being carried */
1049     update_inventory();
1050 }
1051 
1052 /* Attach a fig_transform timeout to the given figurine. */
1053 void
attach_fig_transform_timeout(struct obj * figurine)1054 attach_fig_transform_timeout(struct obj* figurine)
1055 {
1056     int i;
1057 
1058     /* stop previous timer, if any */
1059     (void) stop_timer(FIG_TRANSFORM, obj_to_any(figurine));
1060 
1061     /*
1062      * Decide when to transform the figurine.
1063      */
1064     i = rnd(9000) + 200;
1065     /* figurine will transform */
1066     (void) start_timer((long) i, TIMER_OBJECT, FIG_TRANSFORM,
1067                        obj_to_any(figurine));
1068 }
1069 
1070 /* give a fumble message */
1071 static void
slip_or_trip(void)1072 slip_or_trip(void)
1073 {
1074     struct obj *otmp = vobj_at(u.ux, u.uy), *otmp2;
1075     const char *what;
1076     char buf[BUFSZ];
1077     boolean on_foot = TRUE;
1078     if (u.usteed)
1079         on_foot = FALSE;
1080 
1081     if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy))
1082         otmp = 0;
1083 
1084     if (otmp && on_foot) { /* trip over something in particular */
1085         /*
1086           If there is only one item, it will have just been named
1087           during the move, so refer to by via pronoun; otherwise,
1088           if the top item has been or can be seen, refer to it by
1089           name; if not, look for rocks to trip over; trip over
1090           anonymous "something" if there aren't any rocks.
1091         */
1092         what = (iflags.last_msg == PLNMSG_ONE_ITEM_HERE)
1093                 ? ((otmp->quan == 1L) ? "it"
1094                       : Hallucination ? "they" : "them")
1095                 : (otmp->dknown || !Blind)
1096                       ? doname(otmp)
1097                       : ((otmp2 = sobj_at(ROCK, u.ux, u.uy)) == 0
1098                              ? something
1099                              : (otmp2->quan == 1L ? "a rock" : "some rocks"));
1100         if (Hallucination) {
1101             what = strcpy(buf, what);
1102             buf[0] = highc(buf[0]);
1103             pline("Egads!  %s bite%s your %s!", what,
1104                   (!otmp || otmp->quan == 1L) ? "s" : "", body_part(FOOT));
1105         } else {
1106             You("trip over %s.", what);
1107         }
1108         if (!uarmf && otmp->otyp == CORPSE
1109             && touch_petrifies(&mons[otmp->corpsenm]) && !Stone_resistance) {
1110             Sprintf(g.killer.name, "tripping over %s corpse",
1111                     an(mons[otmp->corpsenm].pmnames[NEUTRAL]));
1112             instapetrify(g.killer.name);
1113         }
1114     } else if (rn2(3) && is_ice(u.ux, u.uy)) {
1115         pline("%s %s%s on the ice.",
1116               u.usteed ? upstart(x_monnam(u.usteed,
1117                                       (has_mgivenname(u.usteed)) ? ARTICLE_NONE
1118                                                                 : ARTICLE_THE,
1119                                           (char *) 0, SUPPRESS_SADDLE, FALSE))
1120                        : "You",
1121               rn2(2) ? "slip" : "slide", on_foot ? "" : "s");
1122     } else {
1123         if (on_foot) {
1124             switch (rn2(4)) {
1125             case 1:
1126                 You("trip over your own %s.",
1127                     Hallucination ? "elbow" : makeplural(body_part(FOOT)));
1128                 break;
1129             case 2:
1130                 You("slip %s.",
1131                     Hallucination ? "on a banana peel" : "and nearly fall");
1132                 break;
1133             case 3:
1134                 You("flounder.");
1135                 break;
1136             default:
1137                 You("stumble.");
1138                 break;
1139             }
1140         } else {
1141             switch (rn2(4)) {
1142             case 1:
1143                 Your("%s slip out of the stirrups.",
1144                      makeplural(body_part(FOOT)));
1145                 break;
1146             case 2:
1147                 You("let go of the reins.");
1148                 break;
1149             case 3:
1150                 You("bang into the saddle-horn.");
1151                 break;
1152             default:
1153                 You("slide to one side of the saddle.");
1154                 break;
1155             }
1156             dismount_steed(DISMOUNT_FELL);
1157         }
1158     }
1159 }
1160 
1161 /* Print a lamp flicker message with tailer. */
1162 static void
see_lamp_flicker(struct obj * obj,const char * tailer)1163 see_lamp_flicker(struct obj* obj, const char* tailer)
1164 {
1165     switch (obj->where) {
1166     case OBJ_INVENT:
1167     case OBJ_MINVENT:
1168         pline("%s flickers%s.", Yname2(obj), tailer);
1169         break;
1170     case OBJ_FLOOR:
1171         You_see("%s flicker%s.", an(xname(obj)), tailer);
1172         break;
1173     }
1174 }
1175 
1176 /* Print a dimming message for brass lanterns. */
1177 static void
lantern_message(struct obj * obj)1178 lantern_message(struct obj* obj)
1179 {
1180     /* from adventure */
1181     switch (obj->where) {
1182     case OBJ_INVENT:
1183         Your("lantern is getting dim.");
1184         if (Hallucination)
1185             pline("Batteries have not been invented yet.");
1186         break;
1187     case OBJ_FLOOR:
1188         if (Hallucination)
1189             You("witness the force of entropy at work.");
1190         else
1191             You_see("a lantern getting dim.");
1192         break;
1193     case OBJ_MINVENT:
1194         pline("%s lantern is getting dim.", s_suffix(Monnam(obj->ocarry)));
1195         break;
1196     }
1197 }
1198 
1199 /*
1200  * Timeout callback for for objects that are burning. E.g. lamps, candles.
1201  * See begin_burn() for meanings of obj->age and obj->spe.
1202  */
1203 void
burn_object(anything * arg,long timeout)1204 burn_object(anything* arg, long timeout)
1205 {
1206     struct obj *obj = arg->a_obj;
1207     boolean canseeit, many, menorah, need_newsym, need_invupdate;
1208     xchar x, y;
1209     char whose[BUFSZ];
1210 
1211     menorah = obj->otyp == CANDELABRUM_OF_INVOCATION;
1212     many = menorah ? obj->spe > 1 : obj->quan > 1L;
1213 
1214     /* timeout while away */
1215     if (timeout != g.monstermoves) {
1216         long how_long = g.monstermoves - timeout;
1217 
1218         if (how_long >= obj->age) {
1219             obj->age = 0;
1220             end_burn(obj, FALSE);
1221 
1222             if (menorah) {
1223                 obj->spe = 0; /* no more candles */
1224                 obj->owt = weight(obj);
1225             } else if (Is_candle(obj) || obj->otyp == POT_OIL) {
1226                 /* get rid of candles and burning oil potions;
1227                    we know this object isn't carried by hero,
1228                    nor is it migrating */
1229                 obj_extract_self(obj);
1230                 obfree(obj, (struct obj *) 0);
1231                 obj = (struct obj *) 0;
1232             }
1233 
1234         } else {
1235             obj->age -= how_long;
1236             begin_burn(obj, TRUE);
1237         }
1238         return;
1239     }
1240 
1241     /* only interested in INVENT, FLOOR, and MINVENT */
1242     if (get_obj_location(obj, &x, &y, 0)) {
1243         canseeit = !Blind && cansee(x, y);
1244         /* set `whose[]' to be "Your " or "Fred's " or "The goblin's " */
1245         (void) Shk_Your(whose, obj);
1246     } else {
1247         canseeit = FALSE;
1248     }
1249     need_newsym = need_invupdate = FALSE;
1250 
1251     /* obj->age is the age remaining at this point.  */
1252     switch (obj->otyp) {
1253     case POT_OIL:
1254         /* this should only be called when we run out */
1255         if (canseeit) {
1256             switch (obj->where) {
1257             case OBJ_INVENT:
1258                 need_invupdate = TRUE;
1259                 /*FALLTHRU*/
1260             case OBJ_MINVENT:
1261                 pline("%spotion of oil has burnt away.", whose);
1262                 break;
1263             case OBJ_FLOOR:
1264                 You_see("a burning potion of oil go out.");
1265                 need_newsym = TRUE;
1266                 break;
1267             }
1268         }
1269         end_burn(obj, FALSE); /* turn off light source */
1270         if (carried(obj)) {
1271             useupall(obj);
1272         } else {
1273             /* clear migrating obj's destination code before obfree
1274                to avoid false complaint of deleting worn item */
1275             if (obj->where == OBJ_MIGRATING)
1276                 obj->owornmask = 0L;
1277             obj_extract_self(obj);
1278             obfree(obj, (struct obj *) 0);
1279         }
1280         obj = (struct obj *) 0;
1281         break;
1282 
1283     case LANTERN:
1284     case OIL_LAMP:
1285         switch ((int) obj->age) {
1286         case 150:
1287         case 100:
1288         case 50:
1289             if (canseeit) {
1290                 if (obj->otyp == LANTERN)
1291                     lantern_message(obj);
1292                 else
1293                     see_lamp_flicker(obj,
1294                                      obj->age == 50L ? " considerably" : "");
1295             }
1296             break;
1297 
1298         case 25:
1299             if (canseeit) {
1300                 if (obj->otyp == LANTERN)
1301                     lantern_message(obj);
1302                 else {
1303                     switch (obj->where) {
1304                     case OBJ_INVENT:
1305                     case OBJ_MINVENT:
1306                         pline("%s seems about to go out.", Yname2(obj));
1307                         break;
1308                     case OBJ_FLOOR:
1309                         You_see("%s about to go out.", an(xname(obj)));
1310                         break;
1311                     }
1312                 }
1313             }
1314             break;
1315 
1316         case 0:
1317             /* even if blind you'll know if holding it */
1318             if (canseeit || obj->where == OBJ_INVENT) {
1319                 switch (obj->where) {
1320                 case OBJ_INVENT:
1321                     need_invupdate = TRUE;
1322                     /*FALLTHRU*/
1323                 case OBJ_MINVENT:
1324                     if (obj->otyp == LANTERN)
1325                         pline("%slantern has run out of power.", whose);
1326                     else
1327                         pline("%s has gone out.", Yname2(obj));
1328                     break;
1329                 case OBJ_FLOOR:
1330                     if (obj->otyp == LANTERN)
1331                         You_see("a lantern run out of power.");
1332                     else
1333                         You_see("%s go out.", an(xname(obj)));
1334                     break;
1335                 }
1336             }
1337             end_burn(obj, FALSE);
1338             break;
1339 
1340         default:
1341             /*
1342              * Someone added fuel to the lamp while it was
1343              * lit.  Just fall through and let begin burn
1344              * handle the new age.
1345              */
1346             break;
1347         }
1348 
1349         if (obj->age)
1350             begin_burn(obj, TRUE);
1351 
1352         break;
1353 
1354     case CANDELABRUM_OF_INVOCATION:
1355     case TALLOW_CANDLE:
1356     case WAX_CANDLE:
1357         switch (obj->age) {
1358         case 75:
1359             if (canseeit)
1360                 switch (obj->where) {
1361                 case OBJ_INVENT:
1362                 case OBJ_MINVENT:
1363                     pline("%s%scandle%s getting short.", whose,
1364                           menorah ? "candelabrum's " : "",
1365                           many ? "s are" : " is");
1366                     break;
1367                 case OBJ_FLOOR:
1368                     You_see("%scandle%s getting short.",
1369                             menorah ? "a candelabrum's " : many ? "some "
1370                                                                 : "a ",
1371                             many ? "s" : "");
1372                     break;
1373                 }
1374             break;
1375 
1376         case 15:
1377             if (canseeit)
1378                 switch (obj->where) {
1379                 case OBJ_INVENT:
1380                 case OBJ_MINVENT:
1381                     pline("%s%scandle%s flame%s flicker%s low!", whose,
1382                           menorah ? "candelabrum's " : "", many ? "s'" : "'s",
1383                           many ? "s" : "", many ? "" : "s");
1384                     break;
1385                 case OBJ_FLOOR:
1386                     You_see("%scandle%s flame%s flicker low!",
1387                             menorah ? "a candelabrum's " : many ? "some "
1388                                                                 : "a ",
1389                             many ? "s'" : "'s", many ? "s" : "");
1390                     break;
1391                 }
1392             break;
1393 
1394         case 0:
1395             /* we know even if blind and in our inventory */
1396             if (canseeit || obj->where == OBJ_INVENT) {
1397                 if (menorah) {
1398                     switch (obj->where) {
1399                     case OBJ_INVENT:
1400                         need_invupdate = TRUE;
1401                         /*FALLTHRU*/
1402                     case OBJ_MINVENT:
1403                         pline("%scandelabrum's flame%s.", whose,
1404                               many ? "s die" : " dies");
1405                         break;
1406                     case OBJ_FLOOR:
1407                         You_see("a candelabrum's flame%s die.",
1408                                 many ? "s" : "");
1409                         break;
1410                     }
1411                 } else {
1412                     switch (obj->where) {
1413                     case OBJ_INVENT:
1414                         /* no need_invupdate for update_inventory() necessary;
1415                            useupall() -> freeinv() handles it */
1416                         /*FALLTHRU*/
1417                     case OBJ_MINVENT:
1418                         pline("%s %s consumed!", Yname2(obj),
1419                               many ? "are" : "is");
1420                         break;
1421                     case OBJ_FLOOR:
1422                         /*
1423                           You see some wax candles consumed!
1424                           You see a wax candle consumed!
1425                          */
1426                         You_see("%s%s consumed!", many ? "some " : "",
1427                                 many ? xname(obj) : an(xname(obj)));
1428                         need_newsym = TRUE;
1429                         break;
1430                     }
1431 
1432                     /* post message */
1433                     pline(Hallucination
1434                               ? (many ? "They shriek!" : "It shrieks!")
1435                               : Blind ? "" : (many ? "Their flames die."
1436                                                    : "Its flame dies."));
1437                 }
1438             }
1439             end_burn(obj, FALSE);
1440 
1441             if (menorah) {
1442                 obj->spe = 0;
1443                 obj->owt = weight(obj);
1444             } else {
1445                 if (carried(obj)) {
1446                     useupall(obj);
1447                 } else {
1448                     /* clear migrating obj's destination code
1449                        so obfree won't think this item is worn */
1450                     if (obj->where == OBJ_MIGRATING)
1451                         obj->owornmask = 0L;
1452                     obj_extract_self(obj);
1453                     obfree(obj, (struct obj *) 0);
1454                 }
1455                 obj = (struct obj *) 0;
1456             }
1457             break; /* case [age ==] 0 */
1458 
1459         default:
1460             /*
1461              * Someone added fuel (candles) to the menorah while
1462              * it was lit.  Just fall through and let begin burn
1463              * handle the new age.
1464              */
1465             break;
1466         }
1467 
1468         if (obj && obj->age)
1469             begin_burn(obj, TRUE);
1470         break; /* case [otyp ==] candelabrum|tallow_candle|wax_candle */
1471 
1472     default:
1473         impossible("burn_object: unexpected obj %s", xname(obj));
1474         break;
1475     }
1476     if (need_newsym)
1477         newsym(x, y);
1478     if (need_invupdate)
1479         update_inventory();
1480 }
1481 
1482 /*
1483  * Start a burn timeout on the given object. If not "already lit" then
1484  * create a light source for the vision system.  There had better not
1485  * be a burn already running on the object.
1486  *
1487  * Magic lamps stay lit as long as there's a genie inside, so don't start
1488  * a timer.
1489  *
1490  * Burn rules:
1491  *      potions of oil, lamps & candles:
1492  *              age = # of turns of fuel left
1493  *              spe = <unused>
1494  *      magic lamps:
1495  *              age = <unused>
1496  *              spe = 0 not lightable, 1 lightable forever
1497  *      candelabrum:
1498  *              age = # of turns of fuel left
1499  *              spe = # of candles
1500  *
1501  * Once the burn begins, the age will be set to the amount of fuel
1502  * remaining _once_the_burn_finishes_.  If the burn is terminated
1503  * early then fuel is added back.
1504  *
1505  * This use of age differs from the use of age for corpses and eggs.
1506  * For the latter items, age is when the object was created, so we
1507  * know when it becomes "bad".
1508  *
1509  * This is a "silent" routine - it should not print anything out.
1510  */
1511 void
begin_burn(struct obj * obj,boolean already_lit)1512 begin_burn(struct obj* obj, boolean already_lit)
1513 {
1514     int radius = 3;
1515     long turns = 0;
1516     boolean do_timer = TRUE;
1517 
1518     if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj))
1519         return;
1520 
1521     switch (obj->otyp) {
1522     case MAGIC_LAMP:
1523         obj->lamplit = 1;
1524         do_timer = FALSE;
1525         break;
1526 
1527     case POT_OIL:
1528         turns = obj->age;
1529         if (obj->odiluted)
1530             turns = (3L * turns + 2L) / 4L;
1531         radius = 1; /* very dim light */
1532         break;
1533 
1534     case LANTERN:
1535     case OIL_LAMP:
1536         /* magic times are 150, 100, 50, 25, and 0 */
1537         if (obj->age > 150L)
1538             turns = obj->age - 150L;
1539         else if (obj->age > 100L)
1540             turns = obj->age - 100L;
1541         else if (obj->age > 50L)
1542             turns = obj->age - 50L;
1543         else if (obj->age > 25L)
1544             turns = obj->age - 25L;
1545         else
1546             turns = obj->age;
1547         break;
1548 
1549     case CANDELABRUM_OF_INVOCATION:
1550     case TALLOW_CANDLE:
1551     case WAX_CANDLE:
1552         /* magic times are 75, 15, and 0 */
1553         if (obj->age > 75L)
1554             turns = obj->age - 75L;
1555         else if (obj->age > 15L)
1556             turns = obj->age - 15L;
1557         else
1558             turns = obj->age;
1559         radius = candle_light_range(obj);
1560         break;
1561 
1562     default:
1563         /* [ALI] Support artifact light sources */
1564         if (artifact_light(obj)) {
1565             obj->lamplit = 1;
1566             do_timer = FALSE;
1567             radius = arti_light_radius(obj);
1568         } else {
1569             impossible("begin burn: unexpected %s", xname(obj));
1570             turns = obj->age;
1571         }
1572         break;
1573     }
1574 
1575     if (do_timer) {
1576         if (start_timer(turns, TIMER_OBJECT, BURN_OBJECT, obj_to_any(obj))) {
1577             obj->lamplit = 1;
1578             obj->age -= turns;
1579             if (carried(obj) && !already_lit)
1580                 update_inventory();
1581         } else {
1582             obj->lamplit = 0;
1583         }
1584     } else {
1585         if (carried(obj) && !already_lit)
1586             update_inventory();
1587     }
1588 
1589     if (obj->lamplit && !already_lit) {
1590         xchar x, y;
1591 
1592         if (get_obj_location(obj, &x, &y, CONTAINED_TOO | BURIED_TOO))
1593             new_light_source(x, y, radius, LS_OBJECT, obj_to_any(obj));
1594         else
1595             impossible("begin_burn: can't get obj position");
1596     }
1597 }
1598 
1599 /*
1600  * Stop a burn timeout on the given object if timer attached.  Darken
1601  * light source.
1602  */
1603 void
end_burn(struct obj * obj,boolean timer_attached)1604 end_burn(struct obj* obj, boolean timer_attached)
1605 {
1606     if (!obj->lamplit) {
1607         impossible("end_burn: obj %s not lit", xname(obj));
1608         return;
1609     }
1610 
1611     if (obj->otyp == MAGIC_LAMP || artifact_light(obj))
1612         timer_attached = FALSE;
1613 
1614     if (!timer_attached) {
1615         /* [DS] Cleanup explicitly, since timer cleanup won't happen */
1616         del_light_source(LS_OBJECT, obj_to_any(obj));
1617         obj->lamplit = 0;
1618         if (obj->where == OBJ_INVENT)
1619             update_inventory();
1620     } else if (!stop_timer(BURN_OBJECT, obj_to_any(obj)))
1621         impossible("end_burn: obj %s not timed!", xname(obj));
1622 }
1623 
1624 /*
1625  * Cleanup a burning object if timer stopped.
1626  */
1627 static void
cleanup_burn(anything * arg,long expire_time)1628 cleanup_burn(anything* arg, long expire_time)
1629 {
1630     struct obj *obj = arg->a_obj;
1631     if (!obj->lamplit) {
1632         impossible("cleanup_burn: obj %s not lit", xname(obj));
1633         return;
1634     }
1635 
1636     del_light_source(LS_OBJECT, obj_to_any(obj));
1637 
1638     /* restore unused time */
1639     obj->age += expire_time - g.monstermoves;
1640 
1641     obj->lamplit = 0;
1642 
1643     if (obj->where == OBJ_INVENT)
1644         update_inventory();
1645 }
1646 
1647 void
do_storms(void)1648 do_storms(void)
1649 {
1650     int nstrike;
1651     register int x, y;
1652     int dirx, diry;
1653     int count;
1654 
1655     /* no lightning if not the air level or too often, even then */
1656     if (!Is_airlevel(&u.uz) || rn2(8))
1657         return;
1658 
1659     /* the number of strikes is 8-log2(nstrike) */
1660     for (nstrike = rnd(64); nstrike <= 64; nstrike *= 2) {
1661         count = 0;
1662         do {
1663             x = rnd(COLNO - 1);
1664             y = rn2(ROWNO);
1665         } while (++count < 100 && levl[x][y].typ != CLOUD);
1666 
1667         if (count < 100) {
1668             dirx = rn2(3) - 1;
1669             diry = rn2(3) - 1;
1670             if (dirx != 0 || diry != 0)
1671                 buzz(-15, /* "monster" LIGHTNING spell */
1672                      8, x, y, dirx, diry);
1673         }
1674     }
1675 
1676     if (levl[u.ux][u.uy].typ == CLOUD) {
1677         /* Inside a cloud during a thunder storm is deafening. */
1678         /* Even if already deaf, we sense the thunder's vibrations. */
1679         pline("Kaboom!!!  Boom!!  Boom!!");
1680         incr_itimeout(&HDeaf, rn1(20, 30));
1681         g.context.botl = TRUE;
1682         if (!u.uinvulnerable) {
1683             stop_occupation();
1684             nomul(-3);
1685             g.multi_reason = "hiding from thunderstorm";
1686             g.nomovemsg = 0;
1687         }
1688     } else
1689         You_hear("a rumbling noise.");
1690 }
1691 
1692 /* -------------------------------------------------------------------------
1693  */
1694 /*
1695  * Generic Timeout Functions.
1696  *
1697  * Interface:
1698  *
1699  * General:
1700  *  boolean start_timer(long timeout,short kind,short func_index,
1701  *                      anything *arg)
1702  *      Start a timer of kind 'kind' that will expire at time
1703  *      g.monstermoves+'timeout'.  Call the function at 'func_index'
1704  *      in the timeout table using argument 'arg'.  Return TRUE if
1705  *      a timer was started.  This places the timer on a list ordered
1706  *      "sooner" to "later".  If an object, increment the object's
1707  *      timer count.
1708  *
1709  *  long stop_timer(short func_index, anything *arg)
1710  *      Stop a timer specified by the (func_index, arg) pair.  This
1711  *      assumes that such a pair is unique.  Return the time the
1712  *      timer would have gone off.  If no timer is found, return 0.
1713  *      If an object, decrement the object's timer count.
1714  *
1715  *  long peek_timer(short func_index, anything *arg)
1716  *      Return time specified timer will go off (0 if no such timer).
1717  *
1718  *  void run_timers(void)
1719  *      Call timers that have timed out.
1720  *
1721  * Save/Restore:
1722  *  void save_timers(NHFILE *, int range)
1723  *      Save all timers of range 'range'.  Range is either global
1724  *      or local.  Global timers follow game play, local timers
1725  *      are saved with a level.  Object and monster timers are
1726  *      saved using their respective id's instead of pointers.
1727  *
1728  *  void restore_timers(NHFILE *, int range, long adjust)
1729  *      Restore timers of range 'range'.  If from a ghost pile,
1730  *      adjust the timeout by 'adjust'.  The object and monster
1731  *      ids are not restored until later.
1732  *
1733  *  void relink_timers(boolean ghostly)
1734  *      Relink all object and monster timers that had been saved
1735  *      using their object's or monster's id number.
1736  *
1737  * Object Specific:
1738  *  void obj_move_timers(struct obj *src, struct obj *dest)
1739  *      Reassign all timers from src to dest.
1740  *
1741  *  void obj_split_timers(struct obj *src, struct obj *dest)
1742  *      Duplicate all timers assigned to src and attach them to dest.
1743  *
1744  *  void obj_stop_timers(struct obj *obj)
1745  *      Stop all timers attached to obj.
1746  *
1747  *  boolean obj_has_timer(struct obj *object, short timer_type)
1748  *      Check whether object has a timer of type timer_type.
1749  */
1750 
1751 static const char *kind_name(short);
1752 static void print_queue(winid, timer_element *);
1753 static void insert_timer(timer_element *);
1754 static timer_element *remove_timer(timer_element **, short, ANY_P *);
1755 static void write_timer(NHFILE *, timer_element *);
1756 static boolean mon_is_local(struct monst *);
1757 static boolean timer_is_local(timer_element *);
1758 static int maybe_write_timer(NHFILE *, int, boolean);
1759 
1760 /* If defined, then include names when printing out the timer queue */
1761 #define VERBOSE_TIMER
1762 
1763 typedef struct {
1764     timeout_proc f, cleanup;
1765 #ifdef VERBOSE_TIMER
1766     const char *name;
1767 #define TTAB(a, b, c) \
1768     {                 \
1769         a, b, c       \
1770     }
1771 #else
1772 #define TTAB(a, b, c) \
1773     {                 \
1774         a, b          \
1775     }
1776 #endif
1777 } ttable;
1778 
1779 /* table of timeout functions */
1780 static const ttable timeout_funcs[NUM_TIME_FUNCS] = {
1781     TTAB(rot_organic, (timeout_proc) 0, "rot_organic"),
1782     TTAB(rot_corpse, (timeout_proc) 0, "rot_corpse"),
1783     TTAB(revive_mon, (timeout_proc) 0, "revive_mon"),
1784     TTAB(zombify_mon, (timeout_proc) 0, "zombify_mon"),
1785     TTAB(burn_object, cleanup_burn, "burn_object"),
1786     TTAB(hatch_egg, (timeout_proc) 0, "hatch_egg"),
1787     TTAB(fig_transform, (timeout_proc) 0, "fig_transform"),
1788     TTAB(melt_ice_away, (timeout_proc) 0, "melt_ice_away"),
1789     TTAB(moldy_corpse, (timeout_proc) 0, "moldy_corpse"),
1790     TTAB(ferment, (timeout_proc) 0, "ferment")
1791 };
1792 #undef TTAB
1793 
1794 static const char *
kind_name(short kind)1795 kind_name(short kind)
1796 {
1797     switch (kind) {
1798     case TIMER_LEVEL:
1799         return "level";
1800     case TIMER_GLOBAL:
1801         return "global";
1802     case TIMER_OBJECT:
1803         return "object";
1804     case TIMER_MONSTER:
1805         return "monster";
1806     }
1807     return "unknown";
1808 }
1809 
1810 static void
print_queue(winid win,timer_element * base)1811 print_queue(winid win, timer_element* base)
1812 {
1813     timer_element *curr;
1814     char buf[BUFSZ];
1815 
1816     if (!base) {
1817         putstr(win, 0, " <empty>");
1818     } else {
1819         putstr(win, 0, "timeout  id   kind   call");
1820         for (curr = base; curr; curr = curr->next) {
1821 #ifdef VERBOSE_TIMER
1822             Sprintf(buf, " %4ld   %4ld  %-6s %s(%s)", curr->timeout,
1823                     curr->tid, kind_name(curr->kind),
1824                     timeout_funcs[curr->func_index].name,
1825                     fmt_ptr((genericptr_t) curr->arg.a_void));
1826 #else
1827             Sprintf(buf, " %4ld   %4ld  %-6s #%d(%s)", curr->timeout,
1828                     curr->tid, kind_name(curr->kind), curr->func_index,
1829                     fmt_ptr((genericptr_t) curr->arg.a_void));
1830 #endif
1831             putstr(win, 0, buf);
1832         }
1833     }
1834 }
1835 
1836 int
wiz_timeout_queue(void)1837 wiz_timeout_queue(void)
1838 {
1839     winid win;
1840     char buf[BUFSZ];
1841     const char *propname;
1842     long intrinsic;
1843     int i, p, count, longestlen, ln, specindx = 0;
1844 
1845     win = create_nhwindow(NHW_MENU); /* corner text window */
1846     if (win == WIN_ERR)
1847         return 0;
1848 
1849     Sprintf(buf, "Current time = %ld.", g.monstermoves);
1850     putstr(win, 0, buf);
1851     putstr(win, 0, "");
1852     putstr(win, 0, "Active timeout queue:");
1853     putstr(win, 0, "");
1854     print_queue(win, g.timer_base);
1855 
1856     /* Timed properies:
1857      * check every one; the majority can't obtain temporary timeouts in
1858      * normal play but those can be forced via the #wizintrinsic command.
1859      */
1860     count = longestlen = 0;
1861     for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) {
1862         p = propertynames[i].prop_num;
1863         intrinsic = u.uprops[p].intrinsic;
1864         if (intrinsic & TIMEOUT) {
1865             ++count;
1866             if ((ln = (int) strlen(propname)) > longestlen)
1867                 longestlen = ln;
1868         }
1869         if (specindx == 0 && p == FIRE_RES)
1870             specindx = i;
1871     }
1872     putstr(win, 0, "");
1873     if (!count) {
1874         putstr(win, 0, "No timed properties.");
1875     } else {
1876         putstr(win, 0, "Timed properties:");
1877         putstr(win, 0, "");
1878         for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) {
1879             p = propertynames[i].prop_num;
1880             intrinsic = u.uprops[p].intrinsic;
1881             if (intrinsic & TIMEOUT) {
1882                 if (specindx > 0 && i >= specindx) {
1883                     putstr(win, 0, " -- settable via #wizinstrinc only --");
1884                     specindx = 0;
1885                 }
1886                 /* timeout value can be up to 16777215 (0x00ffffff) but
1887                    width of 4 digits should result in values lining up
1888                    almost all the time (if/when they don't, it won't
1889                    look nice but the information will still be accurate) */
1890                 Sprintf(buf, " %*s %4ld", -longestlen, propname,
1891                         (intrinsic & TIMEOUT));
1892                 putstr(win, 0, buf);
1893             }
1894         }
1895     }
1896     display_nhwindow(win, FALSE);
1897     destroy_nhwindow(win);
1898 
1899     return 0;
1900 }
1901 
1902 void
timer_sanity_check(void)1903 timer_sanity_check(void)
1904 {
1905     timer_element *curr;
1906 
1907     /* this should be much more complete */
1908     for (curr = g.timer_base; curr; curr = curr->next)
1909         if (curr->kind == TIMER_OBJECT) {
1910             struct obj *obj = curr->arg.a_obj;
1911 
1912             if (obj->timed == 0) {
1913                 impossible("timer sanity: untimed obj %s, timer %ld",
1914                       fmt_ptr((genericptr_t) obj), curr->tid);
1915             }
1916         }
1917 }
1918 
1919 /*
1920  * Pick off timeout elements from the global queue and call their functions.
1921  * Do this until their time is less than or equal to the move count.
1922  */
1923 void
run_timers(void)1924 run_timers(void)
1925 {
1926     timer_element *curr;
1927 
1928     /*
1929      * Always use the first element.  Elements may be added or deleted at
1930      * any time.  The list is ordered, we are done when the first element
1931      * is in the future.
1932      */
1933     while (g.timer_base && g.timer_base->timeout <= g.monstermoves) {
1934         curr = g.timer_base;
1935         g.timer_base = curr->next;
1936 
1937         if (curr->kind == TIMER_OBJECT)
1938             (curr->arg.a_obj)->timed--;
1939         (*timeout_funcs[curr->func_index].f)(&curr->arg, curr->timeout);
1940         free((genericptr_t) curr);
1941     }
1942 }
1943 
1944 /*
1945  * Start a timer.  Return TRUE if successful.
1946  */
1947 boolean
start_timer(long when,short kind,short func_index,anything * arg)1948 start_timer(
1949     long when,
1950     short kind,
1951     short func_index,
1952     anything *arg)
1953 {
1954     timer_element *gnu, *dup;
1955 
1956     if (kind < 0 || kind >= NUM_TIMER_KINDS
1957         || func_index < 0 || func_index >= NUM_TIME_FUNCS)
1958         panic("start_timer (%s: %d)", kind_name(kind), (int) func_index);
1959 
1960     /* fail if <arg> already has a <func_index> timer running */
1961     for (dup = g.timer_base; dup; dup = dup->next)
1962         if (dup->kind == kind
1963             && dup->func_index == func_index
1964             && dup->arg.a_void == arg->a_void)
1965             break;
1966     if (dup) {
1967         char idbuf[QBUFSZ];
1968 
1969 #ifdef VERBOSE_TIMER
1970         Sprintf(idbuf, "%s timer", timeout_funcs[func_index].name);
1971 #else
1972         Sprintf(idbuf, "%s timer (%d)", kind_name(kind), (int) func_index);
1973 #endif
1974         impossible("Attempted to start duplicate %s, aborted.", idbuf);
1975         return FALSE;
1976     }
1977 
1978     gnu = (timer_element *) alloc(sizeof *gnu);
1979     (void) memset((genericptr_t) gnu, 0, sizeof *gnu);
1980     gnu->next = 0;
1981     gnu->tid = g.timer_id++;
1982     gnu->timeout = g.monstermoves + when;
1983     gnu->kind = kind;
1984     gnu->needs_fixup = 0;
1985     gnu->func_index = func_index;
1986     gnu->arg = *arg;
1987     insert_timer(gnu);
1988 
1989     if (kind == TIMER_OBJECT) /* increment object's timed count */
1990         (arg->a_obj)->timed++;
1991 
1992     return TRUE;
1993 }
1994 
1995 /*
1996  * Remove the timer from the current list and free it up.  Return the time
1997  * remaining until it would have gone off, 0 if not found.
1998  */
1999 long
stop_timer(short func_index,anything * arg)2000 stop_timer(short func_index, anything *arg)
2001 {
2002     timer_element *doomed;
2003     long timeout;
2004 
2005     doomed = remove_timer(&g.timer_base, func_index, arg);
2006 
2007     if (doomed) {
2008         timeout = doomed->timeout;
2009         if (doomed->kind == TIMER_OBJECT)
2010             (arg->a_obj)->timed--;
2011         if (timeout_funcs[doomed->func_index].cleanup)
2012             (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout);
2013         free((genericptr_t) doomed);
2014         return (timeout - g.monstermoves);
2015     }
2016     return 0L;
2017 }
2018 
2019 /*
2020  * Find the timeout of specified timer; return 0 if none.
2021  */
2022 long
peek_timer(short type,anything * arg)2023 peek_timer(short type, anything *arg)
2024 {
2025     timer_element *curr;
2026 
2027     for (curr = g.timer_base; curr; curr = curr->next) {
2028         if (curr->func_index == type && curr->arg.a_void == arg->a_void)
2029             return curr->timeout;
2030     }
2031     return 0L;
2032 }
2033 
2034 /*
2035  * Move all object timers from src to dest, leaving src untimed.
2036  */
2037 void
obj_move_timers(struct obj * src,struct obj * dest)2038 obj_move_timers(struct obj* src, struct obj* dest)
2039 {
2040     int count;
2041     timer_element *curr;
2042 
2043     for (count = 0, curr = g.timer_base; curr; curr = curr->next)
2044         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
2045             curr->arg.a_obj = dest;
2046             dest->timed++;
2047             count++;
2048         }
2049     if (count != src->timed)
2050         panic("obj_move_timers");
2051     src->timed = 0;
2052 }
2053 
2054 /*
2055  * Find all object timers and duplicate them for the new object "dest".
2056  */
2057 void
obj_split_timers(struct obj * src,struct obj * dest)2058 obj_split_timers(struct obj* src, struct obj* dest)
2059 {
2060     timer_element *curr, *next_timer = 0;
2061 
2062     for (curr = g.timer_base; curr; curr = next_timer) {
2063         next_timer = curr->next; /* things may be inserted */
2064         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
2065             (void) start_timer(curr->timeout - g.monstermoves, TIMER_OBJECT,
2066                                curr->func_index, obj_to_any(dest));
2067         }
2068     }
2069 }
2070 
2071 /*
2072  * Stop all timers attached to this object.  We can get away with this because
2073  * all object pointers are unique.
2074  */
2075 void
obj_stop_timers(struct obj * obj)2076 obj_stop_timers(struct obj* obj)
2077 {
2078     timer_element *curr, *prev, *next_timer = 0;
2079 
2080     for (prev = 0, curr = g.timer_base; curr; curr = next_timer) {
2081         next_timer = curr->next;
2082         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == obj) {
2083             if (prev)
2084                 prev->next = curr->next;
2085             else
2086                 g.timer_base = curr->next;
2087             if (timeout_funcs[curr->func_index].cleanup)
2088                 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
2089                                                            curr->timeout);
2090             free((genericptr_t) curr);
2091         } else {
2092             prev = curr;
2093         }
2094     }
2095     obj->timed = 0;
2096 }
2097 
2098 /*
2099  * Check whether object has a timer of type timer_type.
2100  */
2101 boolean
obj_has_timer(struct obj * object,short timer_type)2102 obj_has_timer(struct obj* object, short timer_type)
2103 {
2104     long timeout = peek_timer(timer_type, obj_to_any(object));
2105 
2106     return (boolean) (timeout != 0L);
2107 }
2108 
2109 /*
2110  * Stop all timers of index func_index at this spot.
2111  *
2112  */
2113 void
spot_stop_timers(xchar x,xchar y,short func_index)2114 spot_stop_timers(xchar x, xchar y, short func_index)
2115 {
2116     timer_element *curr, *prev, *next_timer = 0;
2117     long where = (((long) x << 16) | ((long) y));
2118 
2119     for (prev = 0, curr = g.timer_base; curr; curr = next_timer) {
2120         next_timer = curr->next;
2121         if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
2122             && curr->arg.a_long == where) {
2123             if (prev)
2124                 prev->next = curr->next;
2125             else
2126                 g.timer_base = curr->next;
2127             if (timeout_funcs[curr->func_index].cleanup)
2128                 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
2129                                                            curr->timeout);
2130             free((genericptr_t) curr);
2131         } else {
2132             prev = curr;
2133         }
2134     }
2135 }
2136 
2137 /*
2138  * When is the spot timer of type func_index going to expire?
2139  * Returns 0L if no such timer.
2140  */
2141 long
spot_time_expires(xchar x,xchar y,short func_index)2142 spot_time_expires(xchar x, xchar y, short func_index)
2143 {
2144     timer_element *curr;
2145     long where = (((long) x << 16) | ((long) y));
2146 
2147     for (curr = g.timer_base; curr; curr = curr->next) {
2148         if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
2149             && curr->arg.a_long == where)
2150             return curr->timeout;
2151     }
2152     return 0L;
2153 }
2154 
2155 long
spot_time_left(xchar x,xchar y,short func_index)2156 spot_time_left(xchar x, xchar y, short func_index)
2157 {
2158     long expires = spot_time_expires(x, y, func_index);
2159     return (expires > 0L) ? expires - g.monstermoves : 0L;
2160 }
2161 
2162 /* Insert timer into the global queue */
2163 static void
insert_timer(timer_element * gnu)2164 insert_timer(timer_element* gnu)
2165 {
2166     timer_element *curr, *prev;
2167 
2168     for (prev = 0, curr = g.timer_base; curr; prev = curr, curr = curr->next)
2169         if (curr->timeout >= gnu->timeout)
2170             break;
2171 
2172     gnu->next = curr;
2173     if (prev)
2174         prev->next = gnu;
2175     else
2176         g.timer_base = gnu;
2177 }
2178 
2179 static timer_element *
remove_timer(timer_element ** base,short func_index,anything * arg)2180 remove_timer(
2181     timer_element **base,
2182     short func_index,
2183     anything *arg)
2184 {
2185     timer_element *prev, *curr;
2186 
2187     for (prev = 0, curr = *base; curr; prev = curr, curr = curr->next)
2188         if (curr->func_index == func_index && curr->arg.a_void == arg->a_void)
2189             break;
2190 
2191     if (curr) {
2192         if (prev)
2193             prev->next = curr->next;
2194         else
2195             *base = curr->next;
2196     }
2197 
2198     return curr;
2199 }
2200 
2201 static void
write_timer(NHFILE * nhfp,timer_element * timer)2202 write_timer(NHFILE* nhfp, timer_element* timer)
2203 {
2204     anything arg_save;
2205 
2206     arg_save = cg.zeroany;
2207     switch (timer->kind) {
2208     case TIMER_GLOBAL:
2209     case TIMER_LEVEL:
2210         /* assume no pointers in arg */
2211         if (nhfp->structlevel)
2212             bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element));
2213         break;
2214 
2215     case TIMER_OBJECT:
2216         if (timer->needs_fixup) {
2217             if (nhfp->structlevel)
2218                 bwrite(nhfp->fd, (genericptr_t)timer, sizeof(timer_element));
2219         } else {
2220             /* replace object pointer with id */
2221             arg_save.a_obj = timer->arg.a_obj;
2222             timer->arg = cg.zeroany;
2223             timer->arg.a_uint = (arg_save.a_obj)->o_id;
2224             timer->needs_fixup = 1;
2225             if (nhfp->structlevel)
2226                 bwrite(nhfp->fd, (genericptr_t)timer, sizeof(timer_element));
2227             timer->arg.a_obj = arg_save.a_obj;
2228             timer->needs_fixup = 0;
2229         }
2230         break;
2231 
2232     case TIMER_MONSTER:
2233         if (timer->needs_fixup) {
2234             if (nhfp->structlevel)
2235                 bwrite(nhfp->fd, (genericptr_t)timer, sizeof(timer_element));
2236         } else {
2237             /* replace monster pointer with id */
2238             arg_save.a_monst = timer->arg.a_monst;
2239             timer->arg = cg.zeroany;
2240             timer->arg.a_uint = (arg_save.a_monst)->m_id;
2241             timer->needs_fixup = 1;
2242             if (nhfp->structlevel)
2243                 bwrite(nhfp->fd, (genericptr_t)timer, sizeof(timer_element));
2244             timer->arg.a_monst = arg_save.a_monst;
2245             timer->needs_fixup = 0;
2246         }
2247         break;
2248 
2249     default:
2250         panic("write_timer");
2251         break;
2252     }
2253 }
2254 
2255 /*
2256  * Return TRUE if the object will stay on the level when the level is
2257  * saved.
2258  */
2259 boolean
obj_is_local(struct obj * obj)2260 obj_is_local(struct obj* obj)
2261 {
2262     switch (obj->where) {
2263     case OBJ_INVENT:
2264     case OBJ_MIGRATING:
2265         return FALSE;
2266     case OBJ_FLOOR:
2267     case OBJ_BURIED:
2268         return TRUE;
2269     case OBJ_CONTAINED:
2270         return obj_is_local(obj->ocontainer);
2271     case OBJ_MINVENT:
2272         return mon_is_local(obj->ocarry);
2273     }
2274     panic("obj_is_local");
2275     return FALSE;
2276 }
2277 
2278 /*
2279  * Return TRUE if the given monster will stay on the level when the
2280  * level is saved.
2281  */
2282 static boolean
mon_is_local(struct monst * mon)2283 mon_is_local(struct monst* mon)
2284 {
2285     struct monst *curr;
2286 
2287     for (curr = g.migrating_mons; curr; curr = curr->nmon)
2288         if (curr == mon)
2289             return FALSE;
2290     /* `g.mydogs' is used during level changes, never saved and restored */
2291     for (curr = g.mydogs; curr; curr = curr->nmon)
2292         if (curr == mon)
2293             return FALSE;
2294     return TRUE;
2295 }
2296 
2297 /*
2298  * Return TRUE if the timer is attached to something that will stay on the
2299  * level when the level is saved.
2300  */
2301 static boolean
timer_is_local(timer_element * timer)2302 timer_is_local(timer_element* timer)
2303 {
2304     switch (timer->kind) {
2305     case TIMER_LEVEL:
2306         return TRUE;
2307     case TIMER_GLOBAL:
2308         return FALSE;
2309     case TIMER_OBJECT:
2310         return obj_is_local(timer->arg.a_obj);
2311     case TIMER_MONSTER:
2312         return mon_is_local(timer->arg.a_monst);
2313     }
2314     panic("timer_is_local");
2315     return FALSE;
2316 }
2317 
2318 /*
2319  * Part of the save routine.  Count up the number of timers that would
2320  * be written.  If write_it is true, actually write the timer.
2321  */
2322 static int
maybe_write_timer(NHFILE * nhfp,int range,boolean write_it)2323 maybe_write_timer(NHFILE* nhfp, int range, boolean write_it)
2324 {
2325     int count = 0;
2326     timer_element *curr;
2327 
2328     for (curr = g.timer_base; curr; curr = curr->next) {
2329         if (range == RANGE_GLOBAL) {
2330             /* global timers */
2331 
2332             if (!timer_is_local(curr)) {
2333                 count++;
2334                 if (write_it) write_timer(nhfp, curr);
2335             }
2336         } else {
2337             /* local timers */
2338 
2339             if (timer_is_local(curr)) {
2340                 count++;
2341                 if (write_it) write_timer(nhfp, curr);
2342             }
2343         }
2344     }
2345 
2346     return count;
2347 }
2348 
2349 
2350 /*
2351  * Save part of the timer list.  The parameter 'range' specifies either
2352  * global or level timers to save.  The timer ID is saved with the global
2353  * timers.
2354  *
2355  * Global range:
2356  *      + timeouts that follow the hero (global)
2357  *      + timeouts that follow obj & monst that are migrating
2358  *
2359  * Level range:
2360  *      + timeouts that are level specific (e.g. storms)
2361  *      + timeouts that stay with the level (obj & monst)
2362  */
2363 void
save_timers(NHFILE * nhfp,int range)2364 save_timers(NHFILE* nhfp, int range)
2365 {
2366     timer_element *curr, *prev, *next_timer = 0;
2367     int count;
2368 
2369     if (perform_bwrite(nhfp)) {
2370         if (range == RANGE_GLOBAL) {
2371             if (nhfp->structlevel)
2372                 bwrite(nhfp->fd, (genericptr_t) &g.timer_id, sizeof(g.timer_id));
2373         }
2374         count = maybe_write_timer(nhfp, range, FALSE);
2375         if (nhfp->structlevel)
2376             bwrite(nhfp->fd, (genericptr_t) &count, sizeof count);
2377         (void) maybe_write_timer(nhfp, range, TRUE);
2378     }
2379 
2380     if (release_data(nhfp)) {
2381         for (prev = 0, curr = g.timer_base; curr; curr = next_timer) {
2382             next_timer = curr->next; /* in case curr is removed */
2383 
2384             if (!(!!(range == RANGE_LEVEL) ^ !!timer_is_local(curr))) {
2385                 if (prev)
2386                     prev->next = curr->next;
2387                 else
2388                     g.timer_base = curr->next;
2389                 free((genericptr_t) curr);
2390                 /* prev stays the same */
2391             } else {
2392                 prev = curr;
2393             }
2394         }
2395     }
2396 }
2397 
2398 /*
2399  * Pull in the structures from disk, but don't recalculate the object and
2400  * monster pointers.
2401  */
2402 void
restore_timers(NHFILE * nhfp,int range,long adjust)2403 restore_timers(NHFILE* nhfp, int range, long adjust)
2404 {
2405     int count = 0;
2406     timer_element *curr;
2407     boolean ghostly = (nhfp->ftype == NHF_BONESFILE); /* from a ghost level */
2408 
2409     if (range == RANGE_GLOBAL) {
2410         if (nhfp->structlevel)
2411             mread(nhfp->fd, (genericptr_t) &g.timer_id, sizeof g.timer_id);
2412     }
2413 
2414     /* restore elements */
2415     if (nhfp->structlevel)
2416         mread(nhfp->fd, (genericptr_t) &count, sizeof count);
2417 
2418     while (count-- > 0) {
2419         curr = (timer_element *) alloc(sizeof(timer_element));
2420         if (nhfp->structlevel)
2421             mread(nhfp->fd, (genericptr_t) curr, sizeof(timer_element));
2422         if (ghostly)
2423             curr->timeout += adjust;
2424         insert_timer(curr);
2425     }
2426 }
2427 
2428 DISABLE_WARNING_FORMAT_NONLITERAL
2429 
2430 /* to support '#stats' wizard-mode command */
2431 void
timer_stats(const char * hdrfmt,char * hdrbuf,long * count,long * size)2432 timer_stats(const char* hdrfmt, char *hdrbuf, long *count, long *size)
2433 {
2434     timer_element *te;
2435 
2436     Sprintf(hdrbuf, hdrfmt, (long) sizeof (timer_element));
2437     *count = *size = 0L;
2438     for (te = g.timer_base; te; te = te->next) {
2439         ++*count;
2440         *size += (long) sizeof *te;
2441     }
2442 }
2443 
2444 RESTORE_WARNING_FORMAT_NONLITERAL
2445 
2446 /* reset all timers that are marked for reseting */
2447 void
relink_timers(boolean ghostly)2448 relink_timers(boolean ghostly)
2449 {
2450     timer_element *curr;
2451     unsigned nid;
2452 
2453     for (curr = g.timer_base; curr; curr = curr->next) {
2454         if (curr->needs_fixup) {
2455             if (curr->kind == TIMER_OBJECT) {
2456                 if (ghostly) {
2457                     if (!lookup_id_mapping(curr->arg.a_uint, &nid))
2458                         panic("relink_timers 1");
2459                 } else
2460                     nid = curr->arg.a_uint;
2461                 curr->arg.a_obj = find_oid(nid);
2462                 if (!curr->arg.a_obj)
2463                     panic("cant find o_id %d", nid);
2464                 curr->needs_fixup = 0;
2465             } else if (curr->kind == TIMER_MONSTER) {
2466                 panic("relink_timers: no monster timer implemented");
2467             } else
2468                 panic("relink_timers 2");
2469         }
2470     }
2471 }
2472 
2473 /*timeout.c*/
2474