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