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