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