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