NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / timeout.c
blob1fd6bb2516db584d9b273a65fd3b1c8359cf1b7e
1 /* aNetHack 0.0.1 timeout.c $ANH-Date: 1456526165 2016/02/26 22:36:05 $ $ANH-Branch: master $:$ANH-Revision: 1.65 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack 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.",
127 "You're gasping for air.",
128 "You can no longer breathe.",
129 "You're turning %s.",
130 "You suffocate."
133 static NEARDATA const char *const choke_texts2[] = {
134 "Your %s is becoming constricted.",
135 "Your blood is having trouble reaching your brain.",
136 "The pressure on your %s increases.",
137 "Your consciousness is fading.",
138 "You suffocate."
141 STATIC_OVL void
142 choke_dialogue()
144 register long i = (Strangled & TIMEOUT);
146 if (i > 0 && i <= SIZE(choke_texts)) {
147 if (Breathless || !rn2(50))
148 pline(choke_texts2[SIZE(choke_texts2) - i], body_part(NECK));
149 else {
150 const char *str = choke_texts[SIZE(choke_texts) - i];
152 if (index(str, '%'))
153 pline(str, hcolor(NH_BLUE));
154 else
155 pline1(str);
158 exercise(A_STR, FALSE);
161 static NEARDATA const char *const levi_texts[] = {
162 "You float slightly lower.",
163 "You wobble unsteadily %s the %s."
166 STATIC_OVL void
167 levitation_dialogue()
169 /* -1 because the last message comes via float_down() */
170 long i = (((HLevitation & TIMEOUT) - 1L) / 2L);
172 if (ELevitation)
173 return;
175 if (!ACCESSIBLE(levl[u.ux][u.uy].typ)
176 && !is_pool_or_lava(u.ux,u.uy))
177 return;
179 if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) {
180 const char *s = levi_texts[SIZE(levi_texts) - i];
181 if (index(s, '%')) {
182 boolean danger = is_pool_or_lava(u.ux, u.uy)
183 && !Is_waterlevel(&u.uz);
184 pline(s, danger ? "over" : "in",
185 danger ? surface(u.ux, u.uy) : "air");
186 } else
187 pline1(s);
191 static NEARDATA const char *const slime_texts[] = {
192 "You are turning a little %s.", /* 5 */
193 "Your limbs are getting oozy.", /* 4 */
194 "Your skin begins to peel away.", /* 3 */
195 "You are turning into %s.", /* 2 */
196 "You have become %s." /* 1 */
199 STATIC_OVL void
200 slime_dialogue()
202 register long i = (Slimed & TIMEOUT) / 2L;
204 if (((Slimed & TIMEOUT) % 2L) && i >= 0L && i < SIZE(slime_texts)) {
205 char buf[BUFSZ];
207 Strcpy(buf, slime_texts[SIZE(slime_texts) - i - 1L]);
208 if (nolimbs(youmonst.data) && strstri(buf, "limbs"))
209 (void) strsubst(buf, "limbs", "extremities");
211 if (index(buf, '%')) {
212 if (i == 4L) { /* "you are turning green" */
213 if (!Blind) /* [what if you're already green?] */
214 pline(buf, hcolor(NH_GREEN));
215 } else
216 pline(buf,
217 an(Hallucination ? rndmonnam(NULL) : "green slime"));
218 } else
219 pline1(buf);
221 if (i == 3L) { /* limbs becoming oozy */
222 HFast = 0L; /* lose intrinsic speed */
223 if (!Popeye(SLIMED))
224 stop_occupation();
225 if (multi > 0)
226 nomul(0);
228 exercise(A_DEX, FALSE);
231 void
232 burn_away_slime()
234 if (Slimed) {
235 make_slimed(0L, "The slime that covers you is burned away!");
239 void
240 nh_timeout()
242 register struct prop *upp;
243 struct kinfo *kptr;
244 int sleeptime;
245 int m_idx;
246 int baseluck = (flags.moonphase == FULL_MOON) ? 1 : 0;
248 if (flags.friday13)
249 baseluck -= 1;
251 if (u.uluck != baseluck
252 && moves % (u.uhave.amulet || u.ugangr ? 300 : 600) == 0) {
253 /* Cursed luckstones stop bad luck from timing out; blessed luckstones
254 * stop good luck from timing out; normal luckstones stop both;
255 * neither is stopped if you don't have a luckstone.
256 * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th
258 register int time_luck = stone_luck(FALSE);
259 boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE);
261 if (u.uluck > baseluck && (nostone || time_luck < 0))
262 u.uluck--;
263 else if (u.uluck < baseluck && (nostone || time_luck > 0))
264 u.uluck++;
266 if (u.uinvulnerable)
267 return; /* things past this point could kill you */
268 if (Stoned)
269 stoned_dialogue();
270 if (Slimed)
271 slime_dialogue();
272 if (Vomiting)
273 vomiting_dialogue();
274 if (Strangled)
275 choke_dialogue();
276 if (Levitation)
277 levitation_dialogue();
278 if (u.mtimedone && !--u.mtimedone) {
279 if (Unchanging)
280 u.mtimedone = rnd(100 * youmonst.data->mlevel + 1);
281 else
282 rehumanize();
284 if (u.ucreamed)
285 u.ucreamed--;
287 /* Dissipate spell-based protection. */
288 if (u.usptime) {
289 if (--u.usptime == 0 && u.uspellprot) {
290 u.usptime = u.uspmtime;
291 u.uspellprot--;
292 find_ac();
293 if (!Blind)
294 Norep("The %s haze around you %s.", hcolor(NH_GOLDEN),
295 u.uspellprot ? "becomes less dense" : "disappears");
299 if (u.ugallop) {
300 if (--u.ugallop == 0L && u.usteed)
301 pline("%s stops galloping.", Monnam(u.usteed));
304 for (upp = u.uprops; upp < u.uprops + SIZE(u.uprops); upp++)
305 if ((upp->intrinsic & TIMEOUT) && !(--upp->intrinsic & TIMEOUT)) {
306 kptr = find_delayed_killer((int) (upp - u.uprops));
307 switch (upp - u.uprops) {
308 case STONED:
309 if (kptr && kptr->name[0]) {
310 killer.format = kptr->format;
311 Strcpy(killer.name, kptr->name);
312 } else {
313 killer.format = NO_KILLER_PREFIX;
314 Strcpy(killer.name, "killed by petrification");
316 dealloc_killer(kptr);
317 /* (unlike sliming, you aren't changing form here) */
318 done(STONING);
319 break;
320 case SLIMED:
321 if (kptr && kptr->name[0]) {
322 killer.format = kptr->format;
323 Strcpy(killer.name, kptr->name);
324 } else {
325 killer.format = NO_KILLER_PREFIX;
326 Strcpy(killer.name, "turned into green slime");
328 dealloc_killer(kptr);
329 /* involuntarily break "never changed form" conduct */
330 u.uconduct.polyselfs++;
331 done(TURNED_SLIME);
332 break;
333 case VOMITING:
334 make_vomiting(0L, TRUE);
335 break;
336 case SICK:
337 You("die from your illness.");
338 if (kptr && kptr->name[0]) {
339 killer.format = kptr->format;
340 Strcpy(killer.name, kptr->name);
341 } else {
342 killer.format = KILLED_BY_AN;
343 killer.name[0] = 0; /* take the default */
345 dealloc_killer(kptr);
347 if ((m_idx = name_to_mon(killer.name)) >= LOW_PM) {
348 if (type_is_pname(&mons[m_idx])) {
349 killer.format = KILLED_BY;
350 } else if (mons[m_idx].geno & G_UNIQ) {
351 Strcpy(killer.name, the(killer.name));
352 killer.format = KILLED_BY;
355 u.usick_type = 0;
356 done(POISONING);
357 break;
358 case FAST:
359 if (!Very_fast)
360 You_feel("yourself slowing down%s.",
361 Fast ? " a bit" : "");
362 break;
363 case CONFUSION:
364 /* So make_confused works properly */
365 set_itimeout(&HConfusion, 1L);
366 make_confused(0L, TRUE);
367 if (!Confusion)
368 stop_occupation();
369 break;
370 case STUNNED:
371 set_itimeout(&HStun, 1L);
372 make_stunned(0L, TRUE);
373 if (!Stunned)
374 stop_occupation();
375 break;
376 case BLINDED:
377 set_itimeout(&Blinded, 1L);
378 make_blinded(0L, TRUE);
379 if (!Blind)
380 stop_occupation();
381 break;
382 case DEAF:
383 set_itimeout(&HDeaf, 1L);
384 make_deaf(0L, TRUE);
385 context.botl = TRUE;
386 if (!Deaf)
387 stop_occupation();
388 break;
389 case INVIS:
390 newsym(u.ux, u.uy);
391 if (!Invis && !BInvis && !Blind) {
392 You(!See_invisible
393 ? "are no longer invisible."
394 : "can no longer see through yourself.");
395 stop_occupation();
397 break;
398 case SEE_INVIS:
399 set_mimic_blocking(); /* do special mimic handling */
400 see_monsters(); /* make invis mons appear */
401 newsym(u.ux, u.uy); /* make self appear */
402 stop_occupation();
403 break;
404 case WOUNDED_LEGS:
405 heal_legs();
406 stop_occupation();
407 break;
408 case HALLUC:
409 set_itimeout(&HHallucination, 1L);
410 (void) make_hallucinated(0L, TRUE, 0L);
411 if (!Hallucination)
412 stop_occupation();
413 break;
414 case SLEEPY:
415 if (unconscious() || Sleep_resistance) {
416 incr_itimeout(&HSleepy, rnd(100));
417 } else if (Sleepy) {
418 You("fall asleep.");
419 sleeptime = rnd(20);
420 fall_asleep(-sleeptime, TRUE);
421 incr_itimeout(&HSleepy, sleeptime + rnd(100));
423 break;
424 case LEVITATION:
425 (void) float_down(I_SPECIAL | TIMEOUT, 0L);
426 break;
427 case STRANGLED:
428 killer.format = KILLED_BY;
429 Strcpy(killer.name,
430 (u.uburied) ? "suffocation" : "strangulation");
431 done(DIED);
432 /* must be declining to die in explore|wizard mode;
433 treat like being cured of strangulation by prayer */
434 if (uamul && uamul->otyp == AMULET_OF_STRANGULATION) {
435 Your("amulet vanishes!");
436 useup(uamul);
438 break;
439 case FUMBLING:
440 /* call this only when a move took place. */
441 /* otherwise handle fumbling msgs locally. */
442 if (u.umoved && !Levitation) {
443 slip_or_trip();
444 nomul(-2);
445 multi_reason = "fumbling";
446 nomovemsg = "";
447 /* The more you are carrying the more likely you
448 * are to make noise when you fumble. Adjustments
449 * to this number must be thoroughly play tested.
451 if ((inv_weight() > -500)) {
452 You("make a lot of noise!");
453 wake_nearby();
456 /* from outside means slippery ice; don't reset
457 counter if that's the only fumble reason */
458 HFumbling &= ~FROMOUTSIDE;
459 if (Fumbling)
460 incr_itimeout(&HFumbling, rnd(20));
461 break;
462 case DETECT_MONSTERS:
463 see_monsters();
464 break;
468 run_timers();
471 void
472 fall_asleep(how_long, wakeup_msg)
473 int how_long;
474 boolean wakeup_msg;
476 stop_occupation();
477 nomul(how_long);
478 multi_reason = "sleeping";
479 /* generally don't notice sounds while sleeping */
480 if (wakeup_msg && multi == how_long) {
481 /* caller can follow with a direct call to Hear_again() if
482 there's a need to override this when wakeup_msg is true */
483 incr_itimeout(&HDeaf, how_long);
484 context.botl = TRUE;
485 afternmv = Hear_again; /* this won't give any messages */
487 /* early wakeup from combat won't be possible until next monster turn */
488 u.usleep = monstermoves;
489 nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again;
492 /* Attach an egg hatch timeout to the given egg.
493 * when = Time to hatch, usually only passed if re-creating an
494 * existing hatch timer. Pass 0L for random hatch time.
496 void
497 attach_egg_hatch_timeout(egg, when)
498 struct obj *egg;
499 long when;
501 int i;
503 /* stop previous timer, if any */
504 (void) stop_timer(HATCH_EGG, obj_to_any(egg));
507 * Decide if and when to hatch the egg. The old hatch_it() code tried
508 * once a turn from age 151 to 200 (inclusive), hatching if it rolled
509 * a number x, 1<=x<=age, where x>150. This yields a chance of
510 * hatching > 99.9993%. Mimic that here.
512 if (!when) {
513 for (i = (MAX_EGG_HATCH_TIME - 50) + 1; i <= MAX_EGG_HATCH_TIME; i++)
514 if (rnd(i) > 150) {
515 /* egg will hatch */
516 when = (long) i;
517 break;
520 if (when) {
521 (void) start_timer(when, TIMER_OBJECT, HATCH_EGG, obj_to_any(egg));
525 /* prevent an egg from ever hatching */
526 void
527 kill_egg(egg)
528 struct obj *egg;
530 /* stop previous timer, if any */
531 (void) stop_timer(HATCH_EGG, obj_to_any(egg));
534 /* timer callback routine: hatch the given egg */
535 void
536 hatch_egg(arg, timeout)
537 anything *arg;
538 long timeout;
540 struct obj *egg;
541 struct monst *mon, *mon2;
542 coord cc;
543 xchar x, y;
544 boolean yours, silent, knows_egg = FALSE;
545 boolean cansee_hatchspot = FALSE;
546 int i, mnum, hatchcount = 0;
548 egg = arg->a_obj;
549 /* sterilized while waiting */
550 if (egg->corpsenm == NON_PM)
551 return;
553 mon = mon2 = (struct monst *) 0;
554 mnum = big_to_little(egg->corpsenm);
555 /* The identity of one's father is learned, not innate */
556 yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2)));
557 silent = (timeout != monstermoves); /* hatched while away */
559 /* only can hatch when in INVENT, FLOOR, MINVENT */
560 if (get_obj_location(egg, &x, &y, 0)) {
561 hatchcount = rnd((int) egg->quan);
562 cansee_hatchspot = cansee(x, y) && !silent;
563 if (!(mons[mnum].geno & G_UNIQ)
564 && !(mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) {
565 for (i = hatchcount; i > 0; i--) {
566 if (!enexto(&cc, x, y, &mons[mnum])
567 || !(mon = makemon(&mons[mnum], cc.x, cc.y, NO_MINVENT)))
568 break;
569 /* tame if your own egg hatches while you're on the
570 same dungeon level, or any dragon egg which hatches
571 while it's in your inventory */
572 if ((yours && !silent)
573 || (carried(egg) && mon->data->mlet == S_DRAGON)) {
574 if (tamedog(mon, (struct obj *) 0)) {
575 if (carried(egg) && mon->data->mlet != S_DRAGON)
576 mon->mtame = 20;
579 if (mvitals[mnum].mvflags & G_EXTINCT)
580 break; /* just made last one */
581 mon2 = mon; /* in case makemon() fails on 2nd egg */
583 if (!mon)
584 mon = mon2;
585 hatchcount -= i;
586 egg->quan -= (long) hatchcount;
588 #if 0
590 * We could possibly hatch while migrating, but the code isn't
591 * set up for it...
593 } else if (obj->where == OBJ_MIGRATING) {
595 * We can do several things. The first ones that come to
596 * mind are:
597 * + Create the hatched monster then place it on the migrating
598 * mons list. This is tough because all makemon() is made
599 * to place the monster as well. Makemon() also doesn't lend
600 * itself well to splitting off a "not yet placed" subroutine.
601 * + Mark the egg as hatched, then place the monster when we
602 * place the migrating objects.
603 * + Or just kill any egg which gets sent to another level.
604 * Falling is the usual reason such transportation occurs.
606 cansee_hatchspot = FALSE;
607 mon = ???;
608 #endif
611 if (mon) {
612 char monnambuf[BUFSZ], carriedby[BUFSZ];
613 boolean siblings = (hatchcount > 1), redraw = FALSE;
615 if (cansee_hatchspot) {
616 Sprintf(monnambuf, "%s%s", siblings ? "some " : "",
617 siblings ? makeplural(m_monnam(mon)) : an(m_monnam(mon)));
618 /* we don't learn the egg type here because learning
619 an egg type requires either seeing the egg hatch
620 or being familiar with the egg already,
621 as well as being able to see the resulting
622 monster, checked below
625 switch (egg->where) {
626 case OBJ_INVENT:
627 knows_egg = TRUE; /* true even if you are blind */
628 if (!cansee_hatchspot)
629 You_feel("%s %s from your pack!", something,
630 locomotion(mon->data, "drop"));
631 else
632 You_see("%s %s out of your pack!", monnambuf,
633 locomotion(mon->data, "drop"));
634 if (yours) {
635 pline("%s cries sound like \"%s%s\"",
636 siblings ? "Their" : "Its",
637 flags.female ? "mommy" : "daddy", egg->spe ? "." : "?");
638 } else if (mon->data->mlet == S_DRAGON && !Deaf) {
639 verbalize("Gleep!"); /* Mything eggs :-) */
641 break;
643 case OBJ_FLOOR:
644 if (cansee_hatchspot) {
645 knows_egg = TRUE;
646 You_see("%s hatch.", monnambuf);
647 redraw = TRUE; /* update egg's map location */
649 break;
651 case OBJ_MINVENT:
652 if (cansee_hatchspot) {
653 /* egg carrying monster might be invisible */
654 mon2 = egg->ocarry;
655 if (canseemon(mon2)
656 && (!mon2->wormno || cansee(mon2->mx, mon2->my))) {
657 Sprintf(carriedby, "%s pack",
658 s_suffix(a_monnam(mon2)));
659 knows_egg = TRUE;
660 } else if (is_pool(mon->mx, mon->my)) {
661 Strcpy(carriedby, "empty water");
662 } else {
663 Strcpy(carriedby, "thin air");
665 You_see("%s %s out of %s!", monnambuf,
666 locomotion(mon->data, "drop"), carriedby);
668 break;
669 #if 0
670 case OBJ_MIGRATING:
671 break;
672 #endif
673 default:
674 impossible("egg hatched where? (%d)", (int) egg->where);
675 break;
678 if (cansee_hatchspot && knows_egg)
679 learn_egg_type(mnum);
681 if (egg->quan > 0) {
682 /* still some eggs left */
683 /* Instead of ordinary egg timeout use a short one */
684 attach_egg_hatch_timeout(egg, (long) rnd(12));
685 } else if (carried(egg)) {
686 useup(egg);
687 } else {
688 /* free egg here because we use it above */
689 obj_extract_self(egg);
690 obfree(egg, (struct obj *) 0);
692 if (redraw)
693 newsym(x, y);
697 /* Learn to recognize eggs of the given type. */
698 void
699 learn_egg_type(mnum)
700 int mnum;
702 /* baby monsters hatch from grown-up eggs */
703 mnum = little_to_big(mnum);
704 mvitals[mnum].mvflags |= MV_KNOWS_EGG;
705 /* we might have just learned about other eggs being carried */
706 update_inventory();
709 /* Attach a fig_transform timeout to the given figurine. */
710 void
711 attach_fig_transform_timeout(figurine)
712 struct obj *figurine;
714 int i;
716 /* stop previous timer, if any */
717 (void) stop_timer(FIG_TRANSFORM, obj_to_any(figurine));
720 * Decide when to transform the figurine.
722 i = rnd(9000) + 200;
723 /* figurine will transform */
724 (void) start_timer((long) i, TIMER_OBJECT, FIG_TRANSFORM,
725 obj_to_any(figurine));
728 /* give a fumble message */
729 STATIC_OVL void
730 slip_or_trip()
732 struct obj *otmp = vobj_at(u.ux, u.uy), *otmp2;
733 const char *what;
734 char buf[BUFSZ];
735 boolean on_foot = TRUE;
736 if (u.usteed)
737 on_foot = FALSE;
739 if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy))
740 otmp = 0;
742 if (otmp && on_foot) { /* trip over something in particular */
744 If there is only one item, it will have just been named
745 during the move, so refer to by via pronoun; otherwise,
746 if the top item has been or can be seen, refer to it by
747 name; if not, look for rocks to trip over; trip over
748 anonymous "something" if there aren't any rocks.
750 what = (iflags.last_msg == PLNMSG_ONE_ITEM_HERE)
751 ? ((otmp->quan == 1L) ? "it"
752 : Hallucination ? "they" : "them")
753 : (otmp->dknown || !Blind)
754 ? doname(otmp)
755 : ((otmp2 = sobj_at(ROCK, u.ux, u.uy)) == 0
756 ? something
757 : (otmp2->quan == 1L ? "a rock" : "some rocks"));
758 if (Hallucination) {
759 what = strcpy(buf, what);
760 buf[0] = highc(buf[0]);
761 pline("Egads! %s bite%s your %s!", what,
762 (!otmp || otmp->quan == 1L) ? "s" : "", body_part(FOOT));
763 } else {
764 You("trip over %s.", what);
766 if (!uarmf && otmp->otyp == CORPSE
767 && touch_petrifies(&mons[otmp->corpsenm]) && !Stone_resistance) {
768 Sprintf(killer.name, "tripping over %s corpse",
769 an(mons[otmp->corpsenm].mname));
770 instapetrify(killer.name);
772 } else if (rn2(3) && is_ice(u.ux, u.uy)) {
773 pline("%s %s%s on the ice.",
774 u.usteed ? upstart(x_monnam(u.usteed,
775 (has_mname(u.usteed)) ? ARTICLE_NONE
776 : ARTICLE_THE,
777 (char *) 0, SUPPRESS_SADDLE, FALSE))
778 : "You",
779 rn2(2) ? "slip" : "slide", on_foot ? "" : "s");
780 } else {
781 if (on_foot) {
782 switch (rn2(4)) {
783 case 1:
784 You("trip over your own %s.",
785 Hallucination ? "elbow" : makeplural(body_part(FOOT)));
786 break;
787 case 2:
788 You("slip %s.",
789 Hallucination ? "on a banana peel" : "and nearly fall");
790 break;
791 case 3:
792 You("flounder.");
793 break;
794 default:
795 You("stumble.");
796 break;
798 } else {
799 switch (rn2(4)) {
800 case 1:
801 Your("%s slip out of the stirrups.",
802 makeplural(body_part(FOOT)));
803 break;
804 case 2:
805 You("let go of the reins.");
806 break;
807 case 3:
808 You("bang into the saddle-horn.");
809 break;
810 default:
811 You("slide to one side of the saddle.");
812 break;
814 dismount_steed(DISMOUNT_FELL);
819 /* Print a lamp flicker message with tailer. */
820 STATIC_OVL void
821 see_lamp_flicker(obj, tailer)
822 struct obj *obj;
823 const char *tailer;
825 switch (obj->where) {
826 case OBJ_INVENT:
827 case OBJ_MINVENT:
828 pline("%s flickers%s.", Yname2(obj), tailer);
829 break;
830 case OBJ_FLOOR:
831 You_see("%s flicker%s.", an(xname(obj)), tailer);
832 break;
836 /* Print a dimming message for brass lanterns. */
837 STATIC_OVL void
838 lantern_message(obj)
839 struct obj *obj;
841 /* from adventure */
842 switch (obj->where) {
843 case OBJ_INVENT:
844 Your("lantern is getting dim.");
845 if (Hallucination)
846 pline("Batteries have not been invented yet.");
847 break;
848 case OBJ_FLOOR:
849 You_see("a lantern getting dim.");
850 break;
851 case OBJ_MINVENT:
852 pline("%s lantern is getting dim.", s_suffix(Monnam(obj->ocarry)));
853 break;
858 * Timeout callback for for objects that are burning. E.g. lamps, candles.
859 * See begin_burn() for meanings of obj->age and obj->spe.
861 void
862 burn_object(arg, timeout)
863 anything *arg;
864 long timeout;
866 struct obj *obj = arg->a_obj;
867 boolean canseeit, many, menorah, need_newsym;
868 xchar x, y;
869 char whose[BUFSZ];
871 menorah = obj->otyp == CANDELABRUM_OF_INVOCATION;
872 many = menorah ? obj->spe > 1 : obj->quan > 1L;
874 /* timeout while away */
875 if (timeout != monstermoves) {
876 long how_long = monstermoves - timeout;
878 if (how_long >= obj->age) {
879 obj->age = 0;
880 end_burn(obj, FALSE);
882 if (menorah) {
883 obj->spe = 0; /* no more candles */
884 } else if (Is_candle(obj) || obj->otyp == POT_OIL) {
885 /* get rid of candles and burning oil potions;
886 we know this object isn't carried by hero,
887 nor is it migrating */
888 obj_extract_self(obj);
889 obfree(obj, (struct obj *) 0);
890 obj = (struct obj *) 0;
893 } else {
894 obj->age -= how_long;
895 begin_burn(obj, TRUE);
897 return;
900 /* only interested in INVENT, FLOOR, and MINVENT */
901 if (get_obj_location(obj, &x, &y, 0)) {
902 canseeit = !Blind && cansee(x, y);
903 /* set `whose[]' to be "Your " or "Fred's " or "The goblin's " */
904 (void) Shk_Your(whose, obj);
905 } else {
906 canseeit = FALSE;
908 need_newsym = FALSE;
910 /* obj->age is the age remaining at this point. */
911 switch (obj->otyp) {
912 case POT_OIL:
913 /* this should only be called when we run out */
914 if (canseeit) {
915 switch (obj->where) {
916 case OBJ_INVENT:
917 case OBJ_MINVENT:
918 pline("%spotion of oil has burnt away.", whose);
919 break;
920 case OBJ_FLOOR:
921 You_see("a burning potion of oil go out.");
922 need_newsym = TRUE;
923 break;
926 end_burn(obj, FALSE); /* turn off light source */
927 if (carried(obj)) {
928 useupall(obj);
929 } else {
930 /* clear migrating obj's destination code before obfree
931 to avoid false complaint of deleting worn item */
932 if (obj->where == OBJ_MIGRATING)
933 obj->owornmask = 0L;
934 obj_extract_self(obj);
935 obfree(obj, (struct obj *) 0);
937 obj = (struct obj *) 0;
938 break;
940 case BRASS_LANTERN:
941 case OIL_LAMP:
942 switch ((int) obj->age) {
943 case 150:
944 case 100:
945 case 50:
946 if (canseeit) {
947 if (obj->otyp == BRASS_LANTERN)
948 lantern_message(obj);
949 else
950 see_lamp_flicker(obj,
951 obj->age == 50L ? " considerably" : "");
953 break;
955 case 25:
956 if (canseeit) {
957 if (obj->otyp == BRASS_LANTERN)
958 lantern_message(obj);
959 else {
960 switch (obj->where) {
961 case OBJ_INVENT:
962 case OBJ_MINVENT:
963 pline("%s seems about to go out.", Yname2(obj));
964 break;
965 case OBJ_FLOOR:
966 You_see("%s about to go out.", an(xname(obj)));
967 break;
971 break;
973 case 0:
974 /* even if blind you'll know if holding it */
975 if (canseeit || obj->where == OBJ_INVENT) {
976 switch (obj->where) {
977 case OBJ_INVENT:
978 case OBJ_MINVENT:
979 if (obj->otyp == BRASS_LANTERN)
980 pline("%slantern has run out of power.", whose);
981 else
982 pline("%s has gone out.", Yname2(obj));
983 break;
984 case OBJ_FLOOR:
985 if (obj->otyp == BRASS_LANTERN)
986 You_see("a lantern run out of power.");
987 else
988 You_see("%s go out.", an(xname(obj)));
989 break;
992 end_burn(obj, FALSE);
993 break;
995 default:
997 * Someone added fuel to the lamp while it was
998 * lit. Just fall through and let begin burn
999 * handle the new age.
1001 break;
1004 if (obj->age)
1005 begin_burn(obj, TRUE);
1007 break;
1009 case CANDELABRUM_OF_INVOCATION:
1010 case TALLOW_CANDLE:
1011 case WAX_CANDLE:
1012 switch (obj->age) {
1013 case 75:
1014 if (canseeit)
1015 switch (obj->where) {
1016 case OBJ_INVENT:
1017 case OBJ_MINVENT:
1018 pline("%s%scandle%s getting short.", whose,
1019 menorah ? "candelabrum's " : "",
1020 many ? "s are" : " is");
1021 break;
1022 case OBJ_FLOOR:
1023 You_see("%scandle%s getting short.",
1024 menorah ? "a candelabrum's " : many ? "some "
1025 : "a ",
1026 many ? "s" : "");
1027 break;
1029 break;
1031 case 15:
1032 if (canseeit)
1033 switch (obj->where) {
1034 case OBJ_INVENT:
1035 case OBJ_MINVENT:
1036 pline("%s%scandle%s flame%s flicker%s low!", whose,
1037 menorah ? "candelabrum's " : "", many ? "s'" : "'s",
1038 many ? "s" : "", many ? "" : "s");
1039 break;
1040 case OBJ_FLOOR:
1041 You_see("%scandle%s flame%s flicker low!",
1042 menorah ? "a candelabrum's " : many ? "some "
1043 : "a ",
1044 many ? "s'" : "'s", many ? "s" : "");
1045 break;
1047 break;
1049 case 0:
1050 /* we know even if blind and in our inventory */
1051 if (canseeit || obj->where == OBJ_INVENT) {
1052 if (menorah) {
1053 switch (obj->where) {
1054 case OBJ_INVENT:
1055 case OBJ_MINVENT:
1056 pline("%scandelabrum's flame%s.", whose,
1057 many ? "s die" : " dies");
1058 break;
1059 case OBJ_FLOOR:
1060 You_see("a candelabrum's flame%s die.",
1061 many ? "s" : "");
1062 break;
1064 } else {
1065 switch (obj->where) {
1066 case OBJ_INVENT:
1067 case OBJ_MINVENT:
1068 pline("%s %s consumed!", Yname2(obj),
1069 many ? "are" : "is");
1070 break;
1071 case OBJ_FLOOR:
1073 You see some wax candles consumed!
1074 You see a wax candle consumed!
1076 You_see("%s%s consumed!", many ? "some " : "",
1077 many ? xname(obj) : an(xname(obj)));
1078 need_newsym = TRUE;
1079 break;
1082 /* post message */
1083 pline(Hallucination
1084 ? (many ? "They shriek!" : "It shrieks!")
1085 : Blind ? "" : (many ? "Their flames die."
1086 : "Its flame dies."));
1089 end_burn(obj, FALSE);
1091 if (menorah) {
1092 obj->spe = 0;
1093 } else {
1094 if (carried(obj)) {
1095 useupall(obj);
1096 } else {
1097 /* clear migrating obj's destination code
1098 so obfree won't think this item is worn */
1099 if (obj->where == OBJ_MIGRATING)
1100 obj->owornmask = 0L;
1101 obj_extract_self(obj);
1102 obfree(obj, (struct obj *) 0);
1104 obj = (struct obj *) 0;
1106 break;
1108 default:
1110 * Someone added fuel (candles) to the menorah while
1111 * it was lit. Just fall through and let begin burn
1112 * handle the new age.
1114 break;
1117 if (obj && obj->age)
1118 begin_burn(obj, TRUE);
1120 break;
1122 default:
1123 impossible("burn_object: unexpeced obj %s", xname(obj));
1124 break;
1126 if (need_newsym)
1127 newsym(x, y);
1131 * Start a burn timeout on the given object. If not "already lit" then
1132 * create a light source for the vision system. There had better not
1133 * be a burn already running on the object.
1135 * Magic lamps stay lit as long as there's a genie inside, so don't start
1136 * a timer.
1138 * Burn rules:
1139 * potions of oil, lamps & candles:
1140 * age = # of turns of fuel left
1141 * spe = <unused>
1142 * magic lamps:
1143 * age = <unused>
1144 * spe = 0 not lightable, 1 lightable forever
1145 * candelabrum:
1146 * age = # of turns of fuel left
1147 * spe = # of candles
1149 * Once the burn begins, the age will be set to the amount of fuel
1150 * remaining _once_the_burn_finishes_. If the burn is terminated
1151 * early then fuel is added back.
1153 * This use of age differs from the use of age for corpses and eggs.
1154 * For the latter items, age is when the object was created, so we
1155 * know when it becomes "bad".
1157 * This is a "silent" routine - it should not print anything out.
1159 void
1160 begin_burn(obj, already_lit)
1161 struct obj *obj;
1162 boolean already_lit;
1164 int radius = 3;
1165 long turns = 0;
1166 boolean do_timer = TRUE;
1168 if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj))
1169 return;
1171 switch (obj->otyp) {
1172 case MAGIC_LAMP:
1173 obj->lamplit = 1;
1174 do_timer = FALSE;
1175 break;
1177 case POT_OIL:
1178 turns = obj->age;
1179 radius = 1; /* very dim light */
1180 break;
1182 case BRASS_LANTERN:
1183 case OIL_LAMP:
1184 /* magic times are 150, 100, 50, 25, and 0 */
1185 if (obj->age > 150L)
1186 turns = obj->age - 150L;
1187 else if (obj->age > 100L)
1188 turns = obj->age - 100L;
1189 else if (obj->age > 50L)
1190 turns = obj->age - 50L;
1191 else if (obj->age > 25L)
1192 turns = obj->age - 25L;
1193 else
1194 turns = obj->age;
1195 break;
1197 case CANDELABRUM_OF_INVOCATION:
1198 case TALLOW_CANDLE:
1199 case WAX_CANDLE:
1200 /* magic times are 75, 15, and 0 */
1201 if (obj->age > 75L)
1202 turns = obj->age - 75L;
1203 else if (obj->age > 15L)
1204 turns = obj->age - 15L;
1205 else
1206 turns = obj->age;
1207 radius = candle_light_range(obj);
1208 break;
1210 default:
1211 /* [ALI] Support artifact light sources */
1212 if (artifact_light(obj)) {
1213 obj->lamplit = 1;
1214 do_timer = FALSE;
1215 radius = arti_light_radius(obj);
1216 } else {
1217 impossible("begin burn: unexpected %s", xname(obj));
1218 turns = obj->age;
1220 break;
1223 if (do_timer) {
1224 if (start_timer(turns, TIMER_OBJECT, BURN_OBJECT, obj_to_any(obj))) {
1225 obj->lamplit = 1;
1226 obj->age -= turns;
1227 if (carried(obj) && !already_lit)
1228 update_inventory();
1229 } else {
1230 obj->lamplit = 0;
1232 } else {
1233 if (carried(obj) && !already_lit)
1234 update_inventory();
1237 if (obj->lamplit && !already_lit) {
1238 xchar x, y;
1240 if (get_obj_location(obj, &x, &y, CONTAINED_TOO | BURIED_TOO))
1241 new_light_source(x, y, radius, LS_OBJECT, obj_to_any(obj));
1242 else
1243 impossible("begin_burn: can't get obj position");
1248 * Stop a burn timeout on the given object if timer attached. Darken
1249 * light source.
1251 void
1252 end_burn(obj, timer_attached)
1253 struct obj *obj;
1254 boolean timer_attached;
1256 if (!obj->lamplit) {
1257 impossible("end_burn: obj %s not lit", xname(obj));
1258 return;
1261 if (obj->otyp == MAGIC_LAMP || artifact_light(obj))
1262 timer_attached = FALSE;
1264 if (!timer_attached) {
1265 /* [DS] Cleanup explicitly, since timer cleanup won't happen */
1266 del_light_source(LS_OBJECT, obj_to_any(obj));
1267 obj->lamplit = 0;
1268 if (obj->where == OBJ_INVENT)
1269 update_inventory();
1270 } else if (!stop_timer(BURN_OBJECT, obj_to_any(obj)))
1271 impossible("end_burn: obj %s not timed!", xname(obj));
1275 * Cleanup a burning object if timer stopped.
1277 static void
1278 cleanup_burn(arg, expire_time)
1279 anything *arg;
1280 long expire_time;
1282 struct obj *obj = arg->a_obj;
1283 if (!obj->lamplit) {
1284 impossible("cleanup_burn: obj %s not lit", xname(obj));
1285 return;
1288 del_light_source(LS_OBJECT, obj_to_any(obj));
1290 /* restore unused time */
1291 obj->age += expire_time - monstermoves;
1293 obj->lamplit = 0;
1295 if (obj->where == OBJ_INVENT)
1296 update_inventory();
1299 void
1300 do_storms()
1302 int nstrike;
1303 register int x, y;
1304 int dirx, diry;
1305 int count;
1307 /* no lightning if not the air level or too often, even then */
1308 if (!Is_airlevel(&u.uz) || rn2(8))
1309 return;
1311 /* the number of strikes is 8-log2(nstrike) */
1312 for (nstrike = rnd(64); nstrike <= 64; nstrike *= 2) {
1313 count = 0;
1314 do {
1315 x = rnd(COLNO - 1);
1316 y = rn2(ROWNO);
1317 } while (++count < 100 && levl[x][y].typ != CLOUD);
1319 if (count < 100) {
1320 dirx = rn2(3) - 1;
1321 diry = rn2(3) - 1;
1322 if (dirx != 0 || diry != 0)
1323 buzz(-15, /* "monster" LIGHTNING spell */
1324 8, x, y, dirx, diry);
1328 if (levl[u.ux][u.uy].typ == CLOUD) {
1329 /* Inside a cloud during a thunder storm is deafening. */
1330 /* Even if already deaf, we sense the thunder's vibrations. */
1331 pline("Kaboom!!! Boom!! Boom!!");
1332 incr_itimeout(&HDeaf, rn1(20, 30));
1333 context.botl = TRUE;
1334 if (!u.uinvulnerable) {
1335 stop_occupation();
1336 nomul(-3);
1337 multi_reason = "hiding from thunderstorm";
1338 nomovemsg = 0;
1340 } else
1341 You_hear("a rumbling noise.");
1344 /* -------------------------------------------------------------------------
1347 * Generic Timeout Functions.
1349 * Interface:
1351 * General:
1352 * boolean start_timer(long timeout,short kind,short func_index,
1353 * anything *arg)
1354 * Start a timer of kind 'kind' that will expire at time
1355 * monstermoves+'timeout'. Call the function at 'func_index'
1356 * in the timeout table using argument 'arg'. Return TRUE if
1357 * a timer was started. This places the timer on a list ordered
1358 * "sooner" to "later". If an object, increment the object's
1359 * timer count.
1361 * long stop_timer(short func_index, anything *arg)
1362 * Stop a timer specified by the (func_index, arg) pair. This
1363 * assumes that such a pair is unique. Return the time the
1364 * timer would have gone off. If no timer is found, return 0.
1365 * If an object, decrement the object's timer count.
1367 * long peek_timer(short func_index, anything *arg)
1368 * Return time specified timer will go off (0 if no such timer).
1370 * void run_timers(void)
1371 * Call timers that have timed out.
1373 * Save/Restore:
1374 * void save_timers(int fd, int mode, int range)
1375 * Save all timers of range 'range'. Range is either global
1376 * or local. Global timers follow game play, local timers
1377 * are saved with a level. Object and monster timers are
1378 * saved using their respective id's instead of pointers.
1380 * void restore_timers(int fd, int range, boolean ghostly, long adjust)
1381 * Restore timers of range 'range'. If from a ghost pile,
1382 * adjust the timeout by 'adjust'. The object and monster
1383 * ids are not restored until later.
1385 * void relink_timers(boolean ghostly)
1386 * Relink all object and monster timers that had been saved
1387 * using their object's or monster's id number.
1389 * Object Specific:
1390 * void obj_move_timers(struct obj *src, struct obj *dest)
1391 * Reassign all timers from src to dest.
1393 * void obj_split_timers(struct obj *src, struct obj *dest)
1394 * Duplicate all timers assigned to src and attach them to dest.
1396 * void obj_stop_timers(struct obj *obj)
1397 * Stop all timers attached to obj.
1399 * boolean obj_has_timer(struct obj *object, short timer_type)
1400 * Check whether object has a timer of type timer_type.
1403 STATIC_DCL const char *FDECL(kind_name, (SHORT_P));
1404 STATIC_DCL void FDECL(print_queue, (winid, timer_element *));
1405 STATIC_DCL void FDECL(insert_timer, (timer_element *));
1406 STATIC_DCL timer_element *FDECL(remove_timer,
1407 (timer_element **, SHORT_P, ANY_P *));
1408 STATIC_DCL void FDECL(write_timer, (int, timer_element *));
1409 STATIC_DCL boolean FDECL(mon_is_local, (struct monst *));
1410 STATIC_DCL boolean FDECL(timer_is_local, (timer_element *));
1411 STATIC_DCL int FDECL(maybe_write_timer, (int, int, BOOLEAN_P));
1413 /* ordered timer list */
1414 static timer_element *timer_base; /* "active" */
1415 static unsigned long timer_id = 1;
1417 /* If defined, then include names when printing out the timer queue */
1418 #define VERBOSE_TIMER
1420 typedef struct {
1421 timeout_proc f, cleanup;
1422 #ifdef VERBOSE_TIMER
1423 const char *name;
1424 #define TTAB(a, b, c) \
1426 a, b, c \
1428 #else
1429 #define TTAB(a, b, c) \
1431 a, b \
1433 #endif
1434 } ttable;
1436 /* table of timeout functions */
1437 static const ttable timeout_funcs[NUM_TIME_FUNCS] = {
1438 TTAB(rot_organic, (timeout_proc) 0, "rot_organic"),
1439 TTAB(rot_corpse, (timeout_proc) 0, "rot_corpse"),
1440 TTAB(revive_mon, (timeout_proc) 0, "revive_mon"),
1441 TTAB(burn_object, cleanup_burn, "burn_object"),
1442 TTAB(hatch_egg, (timeout_proc) 0, "hatch_egg"),
1443 TTAB(fig_transform, (timeout_proc) 0, "fig_transform"),
1444 TTAB(melt_ice_away, (timeout_proc) 0, "melt_ice_away")
1446 #undef TTAB
1448 STATIC_OVL const char *
1449 kind_name(kind)
1450 short kind;
1452 switch (kind) {
1453 case TIMER_LEVEL:
1454 return "level";
1455 case TIMER_GLOBAL:
1456 return "global";
1457 case TIMER_OBJECT:
1458 return "object";
1459 case TIMER_MONSTER:
1460 return "monster";
1462 return "unknown";
1465 STATIC_OVL void
1466 print_queue(win, base)
1467 winid win;
1468 timer_element *base;
1470 timer_element *curr;
1471 char buf[BUFSZ];
1473 if (!base) {
1474 putstr(win, 0, "<empty>");
1475 } else {
1476 putstr(win, 0, "timeout id kind call");
1477 for (curr = base; curr; curr = curr->next) {
1478 #ifdef VERBOSE_TIMER
1479 Sprintf(buf, " %4ld %4ld %-6s %s(%s)", curr->timeout,
1480 curr->tid, kind_name(curr->kind),
1481 timeout_funcs[curr->func_index].name,
1482 fmt_ptr((genericptr_t) curr->arg.a_void));
1483 #else
1484 Sprintf(buf, " %4ld %4ld %-6s #%d(%s)", curr->timeout,
1485 curr->tid, kind_name(curr->kind), curr->func_index,
1486 fmt_ptr((genericptr_t) curr->arg.a_void));
1487 #endif
1488 putstr(win, 0, buf);
1493 static boolean print_prop_header = TRUE;
1494 void
1495 print_prop(win, text, prop)
1496 winid win;
1497 const char *text;
1498 long prop;
1500 char buf[BUFSZ];
1501 if (prop & TIMEOUT) {
1502 if (print_prop_header) {
1503 putstr(win, 0, "");
1504 putstr(win, 0, "Properties:");
1505 putstr(win, 0, "");
1506 print_prop_header = FALSE;
1508 Sprintf(buf, " %10s: %ld", text, (prop & TIMEOUT));
1509 putstr(win, 0, buf);
1514 wiz_timeout_queue()
1516 winid win;
1517 char buf[BUFSZ];
1519 win = create_nhwindow(NHW_MENU); /* corner text window */
1520 if (win == WIN_ERR)
1521 return 0;
1523 Sprintf(buf, "Current time = %ld.", monstermoves);
1524 putstr(win, 0, buf);
1525 putstr(win, 0, "");
1526 putstr(win, 0, "Active timeout queue:");
1527 putstr(win, 0, "");
1528 print_queue(win, timer_base);
1530 print_prop_header = TRUE;
1531 print_prop(win, "Levitation", HLevitation);
1532 print_prop(win, "Stoned", Stoned);
1533 print_prop(win, "Vomiting", Vomiting);
1534 print_prop(win, "Strangled", Strangled);
1535 print_prop(win, "Slimed", Slimed);
1537 display_nhwindow(win, FALSE);
1538 destroy_nhwindow(win);
1540 return 0;
1543 void
1544 timer_sanity_check()
1546 timer_element *curr;
1548 /* this should be much more complete */
1549 for (curr = timer_base; curr; curr = curr->next)
1550 if (curr->kind == TIMER_OBJECT) {
1551 struct obj *obj = curr->arg.a_obj;
1552 if (obj->timed == 0) {
1553 pline("timer sanity: untimed obj %s, timer %ld",
1554 fmt_ptr((genericptr_t) obj), curr->tid);
1560 * Pick off timeout elements from the global queue and call their functions.
1561 * Do this until their time is less than or equal to the move count.
1563 void
1564 run_timers()
1566 timer_element *curr;
1569 * Always use the first element. Elements may be added or deleted at
1570 * any time. The list is ordered, we are done when the first element
1571 * is in the future.
1573 while (timer_base && timer_base->timeout <= monstermoves) {
1574 curr = timer_base;
1575 timer_base = curr->next;
1577 if (curr->kind == TIMER_OBJECT)
1578 (curr->arg.a_obj)->timed--;
1579 (*timeout_funcs[curr->func_index].f)(&curr->arg, curr->timeout);
1580 free((genericptr_t) curr);
1585 * Start a timer. Return TRUE if successful.
1587 boolean
1588 start_timer(when, kind, func_index, arg)
1589 long when;
1590 short kind;
1591 short func_index;
1592 anything *arg;
1594 timer_element *gnu;
1596 if (func_index < 0 || func_index >= NUM_TIME_FUNCS)
1597 panic("start_timer");
1599 gnu = (timer_element *) alloc(sizeof(timer_element));
1600 (void) memset((genericptr_t)gnu, 0, sizeof(timer_element));
1601 gnu->next = 0;
1602 gnu->tid = timer_id++;
1603 gnu->timeout = monstermoves + when;
1604 gnu->kind = kind;
1605 gnu->needs_fixup = 0;
1606 gnu->func_index = func_index;
1607 gnu->arg = *arg;
1608 insert_timer(gnu);
1610 if (kind == TIMER_OBJECT) /* increment object's timed count */
1611 (arg->a_obj)->timed++;
1613 /* should check for duplicates and fail if any */
1614 return TRUE;
1618 * Remove the timer from the current list and free it up. Return the time
1619 * remaining until it would have gone off, 0 if not found.
1621 long
1622 stop_timer(func_index, arg)
1623 short func_index;
1624 anything *arg;
1626 timer_element *doomed;
1627 long timeout;
1629 doomed = remove_timer(&timer_base, func_index, arg);
1631 if (doomed) {
1632 timeout = doomed->timeout;
1633 if (doomed->kind == TIMER_OBJECT)
1634 (arg->a_obj)->timed--;
1635 if (timeout_funcs[doomed->func_index].cleanup)
1636 (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout);
1637 free((genericptr_t) doomed);
1638 return (timeout - monstermoves);
1640 return 0L;
1644 * Find the timeout of specified timer; return 0 if none.
1646 long
1647 peek_timer(type, arg)
1648 short type;
1649 anything *arg;
1651 timer_element *curr;
1653 for (curr = timer_base; curr; curr = curr->next) {
1654 if (curr->func_index == type && curr->arg.a_void == arg->a_void)
1655 return curr->timeout;
1657 return 0L;
1661 * Move all object timers from src to dest, leaving src untimed.
1663 void
1664 obj_move_timers(src, dest)
1665 struct obj *src, *dest;
1667 int count;
1668 timer_element *curr;
1670 for (count = 0, curr = timer_base; curr; curr = curr->next)
1671 if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
1672 curr->arg.a_obj = dest;
1673 dest->timed++;
1674 count++;
1676 if (count != src->timed)
1677 panic("obj_move_timers");
1678 src->timed = 0;
1682 * Find all object timers and duplicate them for the new object "dest".
1684 void
1685 obj_split_timers(src, dest)
1686 struct obj *src, *dest;
1688 timer_element *curr, *next_timer = 0;
1690 for (curr = timer_base; curr; curr = next_timer) {
1691 next_timer = curr->next; /* things may be inserted */
1692 if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
1693 (void) start_timer(curr->timeout - monstermoves, TIMER_OBJECT,
1694 curr->func_index, obj_to_any(dest));
1700 * Stop all timers attached to this object. We can get away with this because
1701 * all object pointers are unique.
1703 void
1704 obj_stop_timers(obj)
1705 struct obj *obj;
1707 timer_element *curr, *prev, *next_timer = 0;
1709 for (prev = 0, curr = timer_base; curr; curr = next_timer) {
1710 next_timer = curr->next;
1711 if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == obj) {
1712 if (prev)
1713 prev->next = curr->next;
1714 else
1715 timer_base = curr->next;
1716 if (timeout_funcs[curr->func_index].cleanup)
1717 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
1718 curr->timeout);
1719 free((genericptr_t) curr);
1720 } else {
1721 prev = curr;
1724 obj->timed = 0;
1728 * Check whether object has a timer of type timer_type.
1730 boolean
1731 obj_has_timer(object, timer_type)
1732 struct obj *object;
1733 short timer_type;
1735 long timeout = peek_timer(timer_type, obj_to_any(object));
1737 return (boolean) (timeout != 0L);
1741 * Stop all timers of index func_index at this spot.
1744 void
1745 spot_stop_timers(x, y, func_index)
1746 xchar x, y;
1747 short func_index;
1749 timer_element *curr, *prev, *next_timer = 0;
1750 long where = (((long) x << 16) | ((long) y));
1752 for (prev = 0, curr = timer_base; curr; curr = next_timer) {
1753 next_timer = curr->next;
1754 if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
1755 && curr->arg.a_long == where) {
1756 if (prev)
1757 prev->next = curr->next;
1758 else
1759 timer_base = curr->next;
1760 if (timeout_funcs[curr->func_index].cleanup)
1761 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
1762 curr->timeout);
1763 free((genericptr_t) curr);
1764 } else {
1765 prev = curr;
1771 * When is the spot timer of type func_index going to expire?
1772 * Returns 0L if no such timer.
1774 long
1775 spot_time_expires(x, y, func_index)
1776 xchar x, y;
1777 short func_index;
1779 timer_element *curr;
1780 long where = (((long) x << 16) | ((long) y));
1782 for (curr = timer_base; curr; curr = curr->next) {
1783 if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
1784 && curr->arg.a_long == where)
1785 return curr->timeout;
1787 return 0L;
1790 long
1791 spot_time_left(x, y, func_index)
1792 xchar x, y;
1793 short func_index;
1795 long expires = spot_time_expires(x, y, func_index);
1796 return (expires > 0L) ? expires - monstermoves : 0L;
1799 /* Insert timer into the global queue */
1800 STATIC_OVL void
1801 insert_timer(gnu)
1802 timer_element *gnu;
1804 timer_element *curr, *prev;
1806 for (prev = 0, curr = timer_base; curr; prev = curr, curr = curr->next)
1807 if (curr->timeout >= gnu->timeout)
1808 break;
1810 gnu->next = curr;
1811 if (prev)
1812 prev->next = gnu;
1813 else
1814 timer_base = gnu;
1817 STATIC_OVL timer_element *
1818 remove_timer(base, func_index, arg)
1819 timer_element **base;
1820 short func_index;
1821 anything *arg;
1823 timer_element *prev, *curr;
1825 for (prev = 0, curr = *base; curr; prev = curr, curr = curr->next)
1826 if (curr->func_index == func_index && curr->arg.a_void == arg->a_void)
1827 break;
1829 if (curr) {
1830 if (prev)
1831 prev->next = curr->next;
1832 else
1833 *base = curr->next;
1836 return curr;
1839 STATIC_OVL void
1840 write_timer(fd, timer)
1841 int fd;
1842 timer_element *timer;
1844 anything arg_save;
1846 arg_save = zeroany;
1847 switch (timer->kind) {
1848 case TIMER_GLOBAL:
1849 case TIMER_LEVEL:
1850 /* assume no pointers in arg */
1851 bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1852 break;
1854 case TIMER_OBJECT:
1855 if (timer->needs_fixup)
1856 bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1857 else {
1858 /* replace object pointer with id */
1859 arg_save.a_obj = timer->arg.a_obj;
1860 timer->arg = zeroany;
1861 timer->arg.a_uint = (arg_save.a_obj)->o_id;
1862 timer->needs_fixup = 1;
1863 bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1864 timer->arg.a_obj = arg_save.a_obj;
1865 timer->needs_fixup = 0;
1867 break;
1869 case TIMER_MONSTER:
1870 if (timer->needs_fixup)
1871 bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1872 else {
1873 /* replace monster pointer with id */
1874 arg_save.a_monst = timer->arg.a_monst;
1875 timer->arg = zeroany;
1876 timer->arg.a_uint = (arg_save.a_monst)->m_id;
1877 timer->needs_fixup = 1;
1878 bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
1879 timer->arg.a_monst = arg_save.a_monst;
1880 timer->needs_fixup = 0;
1882 break;
1884 default:
1885 panic("write_timer");
1886 break;
1891 * Return TRUE if the object will stay on the level when the level is
1892 * saved.
1894 boolean
1895 obj_is_local(obj)
1896 struct obj *obj;
1898 switch (obj->where) {
1899 case OBJ_INVENT:
1900 case OBJ_MIGRATING:
1901 return FALSE;
1902 case OBJ_FLOOR:
1903 case OBJ_BURIED:
1904 return TRUE;
1905 case OBJ_CONTAINED:
1906 return obj_is_local(obj->ocontainer);
1907 case OBJ_MINVENT:
1908 return mon_is_local(obj->ocarry);
1910 panic("obj_is_local");
1911 return FALSE;
1915 * Return TRUE if the given monster will stay on the level when the
1916 * level is saved.
1918 STATIC_OVL boolean
1919 mon_is_local(mon)
1920 struct monst *mon;
1922 struct monst *curr;
1924 for (curr = migrating_mons; curr; curr = curr->nmon)
1925 if (curr == mon)
1926 return FALSE;
1927 /* `mydogs' is used during level changes, never saved and restored */
1928 for (curr = mydogs; curr; curr = curr->nmon)
1929 if (curr == mon)
1930 return FALSE;
1931 return TRUE;
1935 * Return TRUE if the timer is attached to something that will stay on the
1936 * level when the level is saved.
1938 STATIC_OVL boolean
1939 timer_is_local(timer)
1940 timer_element *timer;
1942 switch (timer->kind) {
1943 case TIMER_LEVEL:
1944 return TRUE;
1945 case TIMER_GLOBAL:
1946 return FALSE;
1947 case TIMER_OBJECT:
1948 return obj_is_local(timer->arg.a_obj);
1949 case TIMER_MONSTER:
1950 return mon_is_local(timer->arg.a_monst);
1952 panic("timer_is_local");
1953 return FALSE;
1957 * Part of the save routine. Count up the number of timers that would
1958 * be written. If write_it is true, actually write the timer.
1960 STATIC_OVL int
1961 maybe_write_timer(fd, range, write_it)
1962 int fd, range;
1963 boolean write_it;
1965 int count = 0;
1966 timer_element *curr;
1968 for (curr = timer_base; curr; curr = curr->next) {
1969 if (range == RANGE_GLOBAL) {
1970 /* global timers */
1972 if (!timer_is_local(curr)) {
1973 count++;
1974 if (write_it)
1975 write_timer(fd, curr);
1978 } else {
1979 /* local timers */
1981 if (timer_is_local(curr)) {
1982 count++;
1983 if (write_it)
1984 write_timer(fd, curr);
1989 return count;
1993 * Save part of the timer list. The parameter 'range' specifies either
1994 * global or level timers to save. The timer ID is saved with the global
1995 * timers.
1997 * Global range:
1998 * + timeouts that follow the hero (global)
1999 * + timeouts that follow obj & monst that are migrating
2001 * Level range:
2002 * + timeouts that are level specific (e.g. storms)
2003 * + timeouts that stay with the level (obj & monst)
2005 void
2006 save_timers(fd, mode, range)
2007 int fd, mode, range;
2009 timer_element *curr, *prev, *next_timer = 0;
2010 int count;
2012 if (perform_bwrite(mode)) {
2013 if (range == RANGE_GLOBAL)
2014 bwrite(fd, (genericptr_t) &timer_id, sizeof(timer_id));
2016 count = maybe_write_timer(fd, range, FALSE);
2017 bwrite(fd, (genericptr_t) &count, sizeof count);
2018 (void) maybe_write_timer(fd, range, TRUE);
2021 if (release_data(mode)) {
2022 for (prev = 0, curr = timer_base; curr; curr = next_timer) {
2023 next_timer = curr->next; /* in case curr is removed */
2025 if (!(!!(range == RANGE_LEVEL) ^ !!timer_is_local(curr))) {
2026 if (prev)
2027 prev->next = curr->next;
2028 else
2029 timer_base = curr->next;
2030 free((genericptr_t) curr);
2031 /* prev stays the same */
2032 } else {
2033 prev = curr;
2040 * Pull in the structures from disk, but don't recalculate the object and
2041 * monster pointers.
2043 void
2044 restore_timers(fd, range, ghostly, adjust)
2045 int fd, range;
2046 boolean ghostly; /* restoring from a ghost level */
2047 long adjust; /* how much to adjust timeout */
2049 int count;
2050 timer_element *curr;
2052 if (range == RANGE_GLOBAL)
2053 mread(fd, (genericptr_t) &timer_id, sizeof timer_id);
2055 /* restore elements */
2056 mread(fd, (genericptr_t) &count, sizeof count);
2057 while (count-- > 0) {
2058 curr = (timer_element *) alloc(sizeof(timer_element));
2059 mread(fd, (genericptr_t) curr, sizeof(timer_element));
2060 if (ghostly)
2061 curr->timeout += adjust;
2062 insert_timer(curr);
2066 /* to support '#stats' wizard-mode command */
2067 void
2068 timer_stats(hdrfmt, hdrbuf, count, size)
2069 const char *hdrfmt;
2070 char *hdrbuf;
2071 long *count, *size;
2073 timer_element *te;
2075 Sprintf(hdrbuf, hdrfmt, (long) sizeof (timer_element));
2076 *count = *size = 0L;
2077 for (te = timer_base; te; te = te->next) {
2078 ++*count;
2079 *size += (long) sizeof *te;
2083 /* reset all timers that are marked for reseting */
2084 void
2085 relink_timers(ghostly)
2086 boolean ghostly;
2088 timer_element *curr;
2089 unsigned nid;
2091 for (curr = timer_base; curr; curr = curr->next) {
2092 if (curr->needs_fixup) {
2093 if (curr->kind == TIMER_OBJECT) {
2094 if (ghostly) {
2095 if (!lookup_id_mapping(curr->arg.a_uint, &nid))
2096 panic("relink_timers 1");
2097 } else
2098 nid = curr->arg.a_uint;
2099 curr->arg.a_obj = find_oid(nid);
2100 if (!curr->arg.a_obj)
2101 panic("cant find o_id %d", nid);
2102 curr->needs_fixup = 0;
2103 } else if (curr->kind == TIMER_MONSTER) {
2104 panic("relink_timers: no monster timer implemented");
2105 } else
2106 panic("relink_timers 2");
2111 /*timeout.c*/