1 /* NetHack 3.6 timeout.c $NHDT-Date: 1456526165 2016/02/26 22:36:05 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.65 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
6 #include "lev.h" /* for checking save modes */
8 STATIC_DCL
void NDECL(stoned_dialogue
);
9 STATIC_DCL
void NDECL(vomiting_dialogue
);
10 STATIC_DCL
void NDECL(choke_dialogue
);
11 STATIC_DCL
void NDECL(levitation_dialogue
);
12 STATIC_DCL
void NDECL(slime_dialogue
);
13 STATIC_DCL
void NDECL(slip_or_trip
);
14 STATIC_DCL
void FDECL(see_lamp_flicker
, (struct obj
*, const char *));
15 STATIC_DCL
void FDECL(lantern_message
, (struct obj
*));
16 STATIC_DCL
void FDECL(cleanup_burn
, (ANY_P
*, long));
18 /* He is being petrified - dialogue by inmet!tower */
19 static NEARDATA
const char *const stoned_texts
[] = {
20 "You are slowing down.", /* 5 */
21 "Your limbs are stiffening.", /* 4 */
22 "Your limbs have turned to stone.", /* 3 */
23 "You have turned to stone.", /* 2 */
24 "You are a statue." /* 1 */
30 register long i
= (Stoned
& TIMEOUT
);
32 if (i
> 0L && i
<= SIZE(stoned_texts
)) {
35 Strcpy(buf
, stoned_texts
[SIZE(stoned_texts
) - i
]);
36 if (nolimbs(youmonst
.data
) && strstri(buf
, "limbs"))
37 (void) strsubst(buf
, "limbs", "extremities");
41 case 5: /* slowing down */
46 case 4: /* limbs stiffening */
47 /* just one move left to save oneself so quit fiddling around;
48 don't stop attempt to eat tin--might be lizard or acidic */
54 case 3: /* limbs turned to stone */
56 nomul(-3); /* can't move anymore */
57 multi_reason
= "getting stoned";
58 nomovemsg
= You_can_move_again
; /* not unconscious */
63 exercise(A_DEX
, FALSE
);
66 /* He is getting sicker and sicker prior to vomiting */
67 static NEARDATA
const char *const vomiting_texts
[] = {
68 "are feeling mildly nauseated.", /* 14 */
69 "feel slightly confused.", /* 11 */
70 "can't seem to think straight.", /* 8 */
71 "feel incredibly sick.", /* 5 */
72 "suddenly vomit!" /* 2 */
79 long v
= (Vomiting
& TIMEOUT
);
81 /* note: nhtimeout() hasn't decremented timed properties for the
82 current turn yet, so we use Vomiting-1 here */
83 switch ((int) (v
- 1L)) {
85 txt
= vomiting_texts
[0];
88 txt
= vomiting_texts
[1];
91 make_stunned((HStun
& TIMEOUT
) + (long) d(2, 4), FALSE
);
92 if (!Popeye(VOMITING
))
96 make_confused((HConfusion
& TIMEOUT
) + (long) d(2, 4), FALSE
);
101 txt
= vomiting_texts
[2];
104 txt
= vomiting_texts
[3];
107 txt
= vomiting_texts
[4];
108 if (cantvomit(youmonst
.data
))
109 txt
= "gag uncontrolably.";
113 if (!cantvomit(youmonst
.data
))
122 exercise(A_CON
, FALSE
);
125 static NEARDATA
const char *const choke_texts
[] = {
126 "You find it hard to breathe.",
127 "You're gasping for air.",
128 "You can no longer breathe.",
129 "You're turning %s.",
133 static NEARDATA
const char *const choke_texts2
[] = {
134 "Your %s is becoming constricted.",
135 "Your blood is having trouble reaching your brain.",
136 "The pressure on your %s increases.",
137 "Your consciousness is fading.",
144 register long i
= (Strangled
& TIMEOUT
);
146 if (i
> 0 && i
<= SIZE(choke_texts
)) {
147 if (Breathless
|| !rn2(50))
148 pline(choke_texts2
[SIZE(choke_texts2
) - i
], body_part(NECK
));
150 const char *str
= choke_texts
[SIZE(choke_texts
) - i
];
153 pline(str
, hcolor(NH_BLUE
));
158 exercise(A_STR
, FALSE
);
161 static NEARDATA
const char *const levi_texts
[] = {
162 "You float slightly lower.",
163 "You wobble unsteadily %s the %s."
167 levitation_dialogue()
169 /* -1 because the last message comes via float_down() */
170 long i
= (((HLevitation
& TIMEOUT
) - 1L) / 2L);
175 if (!ACCESSIBLE(levl
[u
.ux
][u
.uy
].typ
)
176 && !is_pool_or_lava(u
.ux
,u
.uy
))
179 if (((HLevitation
& TIMEOUT
) % 2L) && i
> 0L && i
<= SIZE(levi_texts
)) {
180 const char *s
= levi_texts
[SIZE(levi_texts
) - i
];
182 boolean danger
= is_pool_or_lava(u
.ux
, u
.uy
)
183 && !Is_waterlevel(&u
.uz
);
184 pline(s
, danger
? "over" : "in",
185 danger
? surface(u
.ux
, u
.uy
) : "air");
191 static NEARDATA
const char *const slime_texts
[] = {
192 "You are turning a little %s.", /* 5 */
193 "Your limbs are getting oozy.", /* 4 */
194 "Your skin begins to peel away.", /* 3 */
195 "You are turning into %s.", /* 2 */
196 "You have become %s." /* 1 */
202 register long i
= (Slimed
& TIMEOUT
) / 2L;
204 if (((Slimed
& TIMEOUT
) % 2L) && i
>= 0L && i
< SIZE(slime_texts
)) {
207 Strcpy(buf
, slime_texts
[SIZE(slime_texts
) - i
- 1L]);
208 if (nolimbs(youmonst
.data
) && strstri(buf
, "limbs"))
209 (void) strsubst(buf
, "limbs", "extremities");
211 if (index(buf
, '%')) {
212 if (i
== 4L) { /* "you are turning green" */
213 if (!Blind
) /* [what if you're already green?] */
214 pline(buf
, hcolor(NH_GREEN
));
217 an(Hallucination
? rndmonnam(NULL
) : "green slime"));
221 if (i
== 3L) { /* limbs becoming oozy */
222 HFast
= 0L; /* lose intrinsic speed */
228 exercise(A_DEX
, FALSE
);
235 make_slimed(0L, "The slime that covers you is burned away!");
242 register struct prop
*upp
;
246 int baseluck
= (flags
.moonphase
== FULL_MOON
) ? 1 : 0;
251 if (u
.uluck
!= baseluck
252 && moves
% (u
.uhave
.amulet
|| u
.ugangr
? 300 : 600) == 0) {
253 /* Cursed luckstones stop bad luck from timing out; blessed luckstones
254 * stop good luck from timing out; normal luckstones stop both;
255 * neither is stopped if you don't have a luckstone.
256 * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th
258 register int time_luck
= stone_luck(FALSE
);
259 boolean nostone
= !carrying(LUCKSTONE
) && !stone_luck(TRUE
);
261 if (u
.uluck
> baseluck
&& (nostone
|| time_luck
< 0))
263 else if (u
.uluck
< baseluck
&& (nostone
|| time_luck
> 0))
267 return; /* things past this point could kill you */
277 levitation_dialogue();
278 if (u
.mtimedone
&& !--u
.mtimedone
) {
280 u
.mtimedone
= rnd(100 * youmonst
.data
->mlevel
+ 1);
287 /* Dissipate spell-based protection. */
289 if (--u
.usptime
== 0 && u
.uspellprot
) {
290 u
.usptime
= u
.uspmtime
;
294 Norep("The %s haze around you %s.", hcolor(NH_GOLDEN
),
295 u
.uspellprot
? "becomes less dense" : "disappears");
300 if (--u
.ugallop
== 0L && u
.usteed
)
301 pline("%s stops galloping.", Monnam(u
.usteed
));
304 for (upp
= u
.uprops
; upp
< u
.uprops
+ SIZE(u
.uprops
); upp
++)
305 if ((upp
->intrinsic
& TIMEOUT
) && !(--upp
->intrinsic
& TIMEOUT
)) {
306 kptr
= find_delayed_killer((int) (upp
- u
.uprops
));
307 switch (upp
- u
.uprops
) {
309 if (kptr
&& kptr
->name
[0]) {
310 killer
.format
= kptr
->format
;
311 Strcpy(killer
.name
, kptr
->name
);
313 killer
.format
= NO_KILLER_PREFIX
;
314 Strcpy(killer
.name
, "killed by petrification");
316 dealloc_killer(kptr
);
317 /* (unlike sliming, you aren't changing form here) */
321 if (kptr
&& kptr
->name
[0]) {
322 killer
.format
= kptr
->format
;
323 Strcpy(killer
.name
, kptr
->name
);
325 killer
.format
= NO_KILLER_PREFIX
;
326 Strcpy(killer
.name
, "turned into green slime");
328 dealloc_killer(kptr
);
329 /* involuntarily break "never changed form" conduct */
330 u
.uconduct
.polyselfs
++;
334 make_vomiting(0L, TRUE
);
337 You("die from your illness.");
338 if (kptr
&& kptr
->name
[0]) {
339 killer
.format
= kptr
->format
;
340 Strcpy(killer
.name
, kptr
->name
);
342 killer
.format
= KILLED_BY_AN
;
343 killer
.name
[0] = 0; /* take the default */
345 dealloc_killer(kptr
);
347 if ((m_idx
= name_to_mon(killer
.name
)) >= LOW_PM
) {
348 if (type_is_pname(&mons
[m_idx
])) {
349 killer
.format
= KILLED_BY
;
350 } else if (mons
[m_idx
].geno
& G_UNIQ
) {
351 Strcpy(killer
.name
, the(killer
.name
));
352 killer
.format
= KILLED_BY
;
360 You_feel("yourself slowing down%s.",
361 Fast
? " a bit" : "");
364 /* So make_confused works properly */
365 set_itimeout(&HConfusion
, 1L);
366 make_confused(0L, TRUE
);
371 set_itimeout(&HStun
, 1L);
372 make_stunned(0L, TRUE
);
377 set_itimeout(&Blinded
, 1L);
378 make_blinded(0L, TRUE
);
383 set_itimeout(&HDeaf
, 1L);
391 if (!Invis
&& !BInvis
&& !Blind
) {
393 ? "are no longer invisible."
394 : "can no longer see through yourself.");
399 set_mimic_blocking(); /* do special mimic handling */
400 see_monsters(); /* make invis mons appear */
401 newsym(u
.ux
, u
.uy
); /* make self appear */
409 set_itimeout(&HHallucination
, 1L);
410 (void) make_hallucinated(0L, TRUE
, 0L);
415 if (unconscious() || Sleep_resistance
) {
416 incr_itimeout(&HSleepy
, rnd(100));
420 fall_asleep(-sleeptime
, TRUE
);
421 incr_itimeout(&HSleepy
, sleeptime
+ rnd(100));
425 (void) float_down(I_SPECIAL
| TIMEOUT
, 0L);
428 killer
.format
= KILLED_BY
;
430 (u
.uburied
) ? "suffocation" : "strangulation");
432 /* must be declining to die in explore|wizard mode;
433 treat like being cured of strangulation by prayer */
434 if (uamul
&& uamul
->otyp
== AMULET_OF_STRANGULATION
) {
435 Your("amulet vanishes!");
440 /* call this only when a move took place. */
441 /* otherwise handle fumbling msgs locally. */
442 if (u
.umoved
&& !Levitation
) {
445 multi_reason
= "fumbling";
447 /* The more you are carrying the more likely you
448 * are to make noise when you fumble. Adjustments
449 * to this number must be thoroughly play tested.
451 if ((inv_weight() > -500)) {
452 You("make a lot of noise!");
456 /* from outside means slippery ice; don't reset
457 counter if that's the only fumble reason */
458 HFumbling
&= ~FROMOUTSIDE
;
460 incr_itimeout(&HFumbling
, rnd(20));
462 case DETECT_MONSTERS
:
472 fall_asleep(how_long
, wakeup_msg
)
478 multi_reason
= "sleeping";
479 /* generally don't notice sounds while sleeping */
480 if (wakeup_msg
&& multi
== how_long
) {
481 /* caller can follow with a direct call to Hear_again() if
482 there's a need to override this when wakeup_msg is true */
483 incr_itimeout(&HDeaf
, how_long
);
485 afternmv
= Hear_again
; /* this won't give any messages */
487 /* early wakeup from combat won't be possible until next monster turn */
488 u
.usleep
= monstermoves
;
489 nomovemsg
= wakeup_msg
? "You wake up." : You_can_move_again
;
492 /* Attach an egg hatch timeout to the given egg.
493 * when = Time to hatch, usually only passed if re-creating an
494 * existing hatch timer. Pass 0L for random hatch time.
497 attach_egg_hatch_timeout(egg
, when
)
503 /* stop previous timer, if any */
504 (void) stop_timer(HATCH_EGG
, obj_to_any(egg
));
507 * Decide if and when to hatch the egg. The old hatch_it() code tried
508 * once a turn from age 151 to 200 (inclusive), hatching if it rolled
509 * a number x, 1<=x<=age, where x>150. This yields a chance of
510 * hatching > 99.9993%. Mimic that here.
513 for (i
= (MAX_EGG_HATCH_TIME
- 50) + 1; i
<= MAX_EGG_HATCH_TIME
; i
++)
521 (void) start_timer(when
, TIMER_OBJECT
, HATCH_EGG
, obj_to_any(egg
));
525 /* prevent an egg from ever hatching */
530 /* stop previous timer, if any */
531 (void) stop_timer(HATCH_EGG
, obj_to_any(egg
));
534 /* timer callback routine: hatch the given egg */
536 hatch_egg(arg
, timeout
)
541 struct monst
*mon
, *mon2
;
544 boolean yours
, silent
, knows_egg
= FALSE
;
545 boolean cansee_hatchspot
= FALSE
;
546 int i
, mnum
, hatchcount
= 0;
549 /* sterilized while waiting */
550 if (egg
->corpsenm
== NON_PM
)
553 mon
= mon2
= (struct monst
*) 0;
554 mnum
= big_to_little(egg
->corpsenm
);
555 /* The identity of one's father is learned, not innate */
556 yours
= (egg
->spe
|| (!flags
.female
&& carried(egg
) && !rn2(2)));
557 silent
= (timeout
!= monstermoves
); /* hatched while away */
559 /* only can hatch when in INVENT, FLOOR, MINVENT */
560 if (get_obj_location(egg
, &x
, &y
, 0)) {
561 hatchcount
= rnd((int) egg
->quan
);
562 cansee_hatchspot
= cansee(x
, y
) && !silent
;
563 if (!(mons
[mnum
].geno
& G_UNIQ
)
564 && !(mvitals
[mnum
].mvflags
& (G_GENOD
| G_EXTINCT
))) {
565 for (i
= hatchcount
; i
> 0; i
--) {
566 if (!enexto(&cc
, x
, y
, &mons
[mnum
])
567 || !(mon
= makemon(&mons
[mnum
], cc
.x
, cc
.y
, NO_MINVENT
)))
569 /* tame if your own egg hatches while you're on the
570 same dungeon level, or any dragon egg which hatches
571 while it's in your inventory */
572 if ((yours
&& !silent
)
573 || (carried(egg
) && mon
->data
->mlet
== S_DRAGON
)) {
574 if (tamedog(mon
, (struct obj
*) 0)) {
575 if (carried(egg
) && mon
->data
->mlet
!= S_DRAGON
)
579 if (mvitals
[mnum
].mvflags
& G_EXTINCT
)
580 break; /* just made last one */
581 mon2
= mon
; /* in case makemon() fails on 2nd egg */
586 egg
->quan
-= (long) hatchcount
;
590 * We could possibly hatch while migrating, but the code isn't
593 } else if (obj
->where
== OBJ_MIGRATING
) {
595 * We can do several things. The first ones that come to
597 * + Create the hatched monster then place it on the migrating
598 * mons list. This is tough because all makemon() is made
599 * to place the monster as well. Makemon() also doesn't lend
600 * itself well to splitting off a "not yet placed" subroutine.
601 * + Mark the egg as hatched, then place the monster when we
602 * place the migrating objects.
603 * + Or just kill any egg which gets sent to another level.
604 * Falling is the usual reason such transportation occurs.
606 cansee_hatchspot
= FALSE
;
612 char monnambuf
[BUFSZ
], carriedby
[BUFSZ
];
613 boolean siblings
= (hatchcount
> 1), redraw
= FALSE
;
615 if (cansee_hatchspot
) {
616 Sprintf(monnambuf
, "%s%s", siblings
? "some " : "",
617 siblings
? makeplural(m_monnam(mon
)) : an(m_monnam(mon
)));
618 /* we don't learn the egg type here because learning
619 an egg type requires either seeing the egg hatch
620 or being familiar with the egg already,
621 as well as being able to see the resulting
622 monster, checked below
625 switch (egg
->where
) {
627 knows_egg
= TRUE
; /* true even if you are blind */
628 if (!cansee_hatchspot
)
629 You_feel("%s %s from your pack!", something
,
630 locomotion(mon
->data
, "drop"));
632 You_see("%s %s out of your pack!", monnambuf
,
633 locomotion(mon
->data
, "drop"));
635 pline("%s cries sound like \"%s%s\"",
636 siblings
? "Their" : "Its",
637 flags
.female
? "mommy" : "daddy", egg
->spe
? "." : "?");
638 } else if (mon
->data
->mlet
== S_DRAGON
&& !Deaf
) {
639 verbalize("Gleep!"); /* Mything eggs :-) */
644 if (cansee_hatchspot
) {
646 You_see("%s hatch.", monnambuf
);
647 redraw
= TRUE
; /* update egg's map location */
652 if (cansee_hatchspot
) {
653 /* egg carrying monster might be invisible */
656 && (!mon2
->wormno
|| cansee(mon2
->mx
, mon2
->my
))) {
657 Sprintf(carriedby
, "%s pack",
658 s_suffix(a_monnam(mon2
)));
660 } else if (is_pool(mon
->mx
, mon
->my
)) {
661 Strcpy(carriedby
, "empty water");
663 Strcpy(carriedby
, "thin air");
665 You_see("%s %s out of %s!", monnambuf
,
666 locomotion(mon
->data
, "drop"), carriedby
);
674 impossible("egg hatched where? (%d)", (int) egg
->where
);
678 if (cansee_hatchspot
&& knows_egg
)
679 learn_egg_type(mnum
);
682 /* still some eggs left */
683 /* Instead of ordinary egg timeout use a short one */
684 attach_egg_hatch_timeout(egg
, (long) rnd(12));
685 } else if (carried(egg
)) {
688 /* free egg here because we use it above */
689 obj_extract_self(egg
);
690 obfree(egg
, (struct obj
*) 0);
697 /* Learn to recognize eggs of the given type. */
702 /* baby monsters hatch from grown-up eggs */
703 mnum
= little_to_big(mnum
);
704 mvitals
[mnum
].mvflags
|= MV_KNOWS_EGG
;
705 /* we might have just learned about other eggs being carried */
709 /* Attach a fig_transform timeout to the given figurine. */
711 attach_fig_transform_timeout(figurine
)
712 struct obj
*figurine
;
716 /* stop previous timer, if any */
717 (void) stop_timer(FIG_TRANSFORM
, obj_to_any(figurine
));
720 * Decide when to transform the figurine.
723 /* figurine will transform */
724 (void) start_timer((long) i
, TIMER_OBJECT
, FIG_TRANSFORM
,
725 obj_to_any(figurine
));
728 /* give a fumble message */
732 struct obj
*otmp
= vobj_at(u
.ux
, u
.uy
), *otmp2
;
735 boolean on_foot
= TRUE
;
739 if (otmp
&& on_foot
&& !u
.uinwater
&& is_pool(u
.ux
, u
.uy
))
742 if (otmp
&& on_foot
) { /* trip over something in particular */
744 If there is only one item, it will have just been named
745 during the move, so refer to by via pronoun; otherwise,
746 if the top item has been or can be seen, refer to it by
747 name; if not, look for rocks to trip over; trip over
748 anonymous "something" if there aren't any rocks.
750 what
= (iflags
.last_msg
== PLNMSG_ONE_ITEM_HERE
)
751 ? ((otmp
->quan
== 1L) ? "it"
752 : Hallucination
? "they" : "them")
753 : (otmp
->dknown
|| !Blind
)
755 : ((otmp2
= sobj_at(ROCK
, u
.ux
, u
.uy
)) == 0
757 : (otmp2
->quan
== 1L ? "a rock" : "some rocks"));
759 what
= strcpy(buf
, what
);
760 buf
[0] = highc(buf
[0]);
761 pline("Egads! %s bite%s your %s!", what
,
762 (!otmp
|| otmp
->quan
== 1L) ? "s" : "", body_part(FOOT
));
764 You("trip over %s.", what
);
766 if (!uarmf
&& otmp
->otyp
== CORPSE
767 && touch_petrifies(&mons
[otmp
->corpsenm
]) && !Stone_resistance
) {
768 Sprintf(killer
.name
, "tripping over %s corpse",
769 an(mons
[otmp
->corpsenm
].mname
));
770 instapetrify(killer
.name
);
772 } else if (rn2(3) && is_ice(u
.ux
, u
.uy
)) {
773 pline("%s %s%s on the ice.",
774 u
.usteed
? upstart(x_monnam(u
.usteed
,
775 (has_mname(u
.usteed
)) ? ARTICLE_NONE
777 (char *) 0, SUPPRESS_SADDLE
, FALSE
))
779 rn2(2) ? "slip" : "slide", on_foot
? "" : "s");
784 You("trip over your own %s.",
785 Hallucination
? "elbow" : makeplural(body_part(FOOT
)));
789 Hallucination
? "on a banana peel" : "and nearly fall");
801 Your("%s slip out of the stirrups.",
802 makeplural(body_part(FOOT
)));
805 You("let go of the reins.");
808 You("bang into the saddle-horn.");
811 You("slide to one side of the saddle.");
814 dismount_steed(DISMOUNT_FELL
);
819 /* Print a lamp flicker message with tailer. */
821 see_lamp_flicker(obj
, tailer
)
825 switch (obj
->where
) {
828 pline("%s flickers%s.", Yname2(obj
), tailer
);
831 You_see("%s flicker%s.", an(xname(obj
)), tailer
);
836 /* Print a dimming message for brass lanterns. */
842 switch (obj
->where
) {
844 Your("lantern is getting dim.");
846 pline("Batteries have not been invented yet.");
849 You_see("a lantern getting dim.");
852 pline("%s lantern is getting dim.", s_suffix(Monnam(obj
->ocarry
)));
858 * Timeout callback for for objects that are burning. E.g. lamps, candles.
859 * See begin_burn() for meanings of obj->age and obj->spe.
862 burn_object(arg
, timeout
)
866 struct obj
*obj
= arg
->a_obj
;
867 boolean canseeit
, many
, menorah
, need_newsym
;
871 menorah
= obj
->otyp
== CANDELABRUM_OF_INVOCATION
;
872 many
= menorah
? obj
->spe
> 1 : obj
->quan
> 1L;
874 /* timeout while away */
875 if (timeout
!= monstermoves
) {
876 long how_long
= monstermoves
- timeout
;
878 if (how_long
>= obj
->age
) {
880 end_burn(obj
, FALSE
);
883 obj
->spe
= 0; /* no more candles */
884 } else if (Is_candle(obj
) || obj
->otyp
== POT_OIL
) {
885 /* get rid of candles and burning oil potions;
886 we know this object isn't carried by hero,
887 nor is it migrating */
888 obj_extract_self(obj
);
889 obfree(obj
, (struct obj
*) 0);
890 obj
= (struct obj
*) 0;
894 obj
->age
-= how_long
;
895 begin_burn(obj
, TRUE
);
900 /* only interested in INVENT, FLOOR, and MINVENT */
901 if (get_obj_location(obj
, &x
, &y
, 0)) {
902 canseeit
= !Blind
&& cansee(x
, y
);
903 /* set `whose[]' to be "Your " or "Fred's " or "The goblin's " */
904 (void) Shk_Your(whose
, obj
);
910 /* obj->age is the age remaining at this point. */
913 /* this should only be called when we run out */
915 switch (obj
->where
) {
918 pline("%spotion of oil has burnt away.", whose
);
921 You_see("a burning potion of oil go out.");
926 end_burn(obj
, FALSE
); /* turn off light source */
930 /* clear migrating obj's destination code before obfree
931 to avoid false complaint of deleting worn item */
932 if (obj
->where
== OBJ_MIGRATING
)
934 obj_extract_self(obj
);
935 obfree(obj
, (struct obj
*) 0);
937 obj
= (struct obj
*) 0;
942 switch ((int) obj
->age
) {
947 if (obj
->otyp
== BRASS_LANTERN
)
948 lantern_message(obj
);
950 see_lamp_flicker(obj
,
951 obj
->age
== 50L ? " considerably" : "");
957 if (obj
->otyp
== BRASS_LANTERN
)
958 lantern_message(obj
);
960 switch (obj
->where
) {
963 pline("%s seems about to go out.", Yname2(obj
));
966 You_see("%s about to go out.", an(xname(obj
)));
974 /* even if blind you'll know if holding it */
975 if (canseeit
|| obj
->where
== OBJ_INVENT
) {
976 switch (obj
->where
) {
979 if (obj
->otyp
== BRASS_LANTERN
)
980 pline("%slantern has run out of power.", whose
);
982 pline("%s has gone out.", Yname2(obj
));
985 if (obj
->otyp
== BRASS_LANTERN
)
986 You_see("a lantern run out of power.");
988 You_see("%s go out.", an(xname(obj
)));
992 end_burn(obj
, FALSE
);
997 * Someone added fuel to the lamp while it was
998 * lit. Just fall through and let begin burn
999 * handle the new age.
1005 begin_burn(obj
, TRUE
);
1009 case CANDELABRUM_OF_INVOCATION
:
1015 switch (obj
->where
) {
1018 pline("%s%scandle%s getting short.", whose
,
1019 menorah
? "candelabrum's " : "",
1020 many
? "s are" : " is");
1023 You_see("%scandle%s getting short.",
1024 menorah
? "a candelabrum's " : many
? "some "
1033 switch (obj
->where
) {
1036 pline("%s%scandle%s flame%s flicker%s low!", whose
,
1037 menorah
? "candelabrum's " : "", many
? "s'" : "'s",
1038 many
? "s" : "", many
? "" : "s");
1041 You_see("%scandle%s flame%s flicker low!",
1042 menorah
? "a candelabrum's " : many
? "some "
1044 many
? "s'" : "'s", many
? "s" : "");
1050 /* we know even if blind and in our inventory */
1051 if (canseeit
|| obj
->where
== OBJ_INVENT
) {
1053 switch (obj
->where
) {
1056 pline("%scandelabrum's flame%s.", whose
,
1057 many
? "s die" : " dies");
1060 You_see("a candelabrum's flame%s die.",
1065 switch (obj
->where
) {
1068 pline("%s %s consumed!", Yname2(obj
),
1069 many
? "are" : "is");
1073 You see some wax candles consumed!
1074 You see a wax candle consumed!
1076 You_see("%s%s consumed!", many
? "some " : "",
1077 many
? xname(obj
) : an(xname(obj
)));
1084 ? (many
? "They shriek!" : "It shrieks!")
1085 : Blind
? "" : (many
? "Their flames die."
1086 : "Its flame dies."));
1089 end_burn(obj
, FALSE
);
1097 /* clear migrating obj's destination code
1098 so obfree won't think this item is worn */
1099 if (obj
->where
== OBJ_MIGRATING
)
1100 obj
->owornmask
= 0L;
1101 obj_extract_self(obj
);
1102 obfree(obj
, (struct obj
*) 0);
1104 obj
= (struct obj
*) 0;
1110 * Someone added fuel (candles) to the menorah while
1111 * it was lit. Just fall through and let begin burn
1112 * handle the new age.
1117 if (obj
&& obj
->age
)
1118 begin_burn(obj
, TRUE
);
1123 impossible("burn_object: unexpeced obj %s", xname(obj
));
1131 * Start a burn timeout on the given object. If not "already lit" then
1132 * create a light source for the vision system. There had better not
1133 * be a burn already running on the object.
1135 * Magic lamps stay lit as long as there's a genie inside, so don't start
1139 * potions of oil, lamps & candles:
1140 * age = # of turns of fuel left
1144 * spe = 0 not lightable, 1 lightable forever
1146 * age = # of turns of fuel left
1147 * spe = # of candles
1149 * Once the burn begins, the age will be set to the amount of fuel
1150 * remaining _once_the_burn_finishes_. If the burn is terminated
1151 * early then fuel is added back.
1153 * This use of age differs from the use of age for corpses and eggs.
1154 * For the latter items, age is when the object was created, so we
1155 * know when it becomes "bad".
1157 * This is a "silent" routine - it should not print anything out.
1160 begin_burn(obj
, already_lit
)
1162 boolean already_lit
;
1166 boolean do_timer
= TRUE
;
1168 if (obj
->age
== 0 && obj
->otyp
!= MAGIC_LAMP
&& !artifact_light(obj
))
1171 switch (obj
->otyp
) {
1179 radius
= 1; /* very dim light */
1184 /* magic times are 150, 100, 50, 25, and 0 */
1185 if (obj
->age
> 150L)
1186 turns
= obj
->age
- 150L;
1187 else if (obj
->age
> 100L)
1188 turns
= obj
->age
- 100L;
1189 else if (obj
->age
> 50L)
1190 turns
= obj
->age
- 50L;
1191 else if (obj
->age
> 25L)
1192 turns
= obj
->age
- 25L;
1197 case CANDELABRUM_OF_INVOCATION
:
1200 /* magic times are 75, 15, and 0 */
1202 turns
= obj
->age
- 75L;
1203 else if (obj
->age
> 15L)
1204 turns
= obj
->age
- 15L;
1207 radius
= candle_light_range(obj
);
1211 /* [ALI] Support artifact light sources */
1212 if (artifact_light(obj
)) {
1215 radius
= arti_light_radius(obj
);
1217 impossible("begin burn: unexpected %s", xname(obj
));
1224 if (start_timer(turns
, TIMER_OBJECT
, BURN_OBJECT
, obj_to_any(obj
))) {
1227 if (carried(obj
) && !already_lit
)
1233 if (carried(obj
) && !already_lit
)
1237 if (obj
->lamplit
&& !already_lit
) {
1240 if (get_obj_location(obj
, &x
, &y
, CONTAINED_TOO
| BURIED_TOO
))
1241 new_light_source(x
, y
, radius
, LS_OBJECT
, obj_to_any(obj
));
1243 impossible("begin_burn: can't get obj position");
1248 * Stop a burn timeout on the given object if timer attached. Darken
1252 end_burn(obj
, timer_attached
)
1254 boolean timer_attached
;
1256 if (!obj
->lamplit
) {
1257 impossible("end_burn: obj %s not lit", xname(obj
));
1261 if (obj
->otyp
== MAGIC_LAMP
|| artifact_light(obj
))
1262 timer_attached
= FALSE
;
1264 if (!timer_attached
) {
1265 /* [DS] Cleanup explicitly, since timer cleanup won't happen */
1266 del_light_source(LS_OBJECT
, obj_to_any(obj
));
1268 if (obj
->where
== OBJ_INVENT
)
1270 } else if (!stop_timer(BURN_OBJECT
, obj_to_any(obj
)))
1271 impossible("end_burn: obj %s not timed!", xname(obj
));
1275 * Cleanup a burning object if timer stopped.
1278 cleanup_burn(arg
, expire_time
)
1282 struct obj
*obj
= arg
->a_obj
;
1283 if (!obj
->lamplit
) {
1284 impossible("cleanup_burn: obj %s not lit", xname(obj
));
1288 del_light_source(LS_OBJECT
, obj_to_any(obj
));
1290 /* restore unused time */
1291 obj
->age
+= expire_time
- monstermoves
;
1295 if (obj
->where
== OBJ_INVENT
)
1307 /* no lightning if not the air level or too often, even then */
1308 if (!Is_airlevel(&u
.uz
) || rn2(8))
1311 /* the number of strikes is 8-log2(nstrike) */
1312 for (nstrike
= rnd(64); nstrike
<= 64; nstrike
*= 2) {
1317 } while (++count
< 100 && levl
[x
][y
].typ
!= CLOUD
);
1322 if (dirx
!= 0 || diry
!= 0)
1323 buzz(-15, /* "monster" LIGHTNING spell */
1324 8, x
, y
, dirx
, diry
);
1328 if (levl
[u
.ux
][u
.uy
].typ
== CLOUD
) {
1329 /* Inside a cloud during a thunder storm is deafening. */
1330 /* Even if already deaf, we sense the thunder's vibrations. */
1331 pline("Kaboom!!! Boom!! Boom!!");
1332 incr_itimeout(&HDeaf
, rn1(20, 30));
1333 context
.botl
= TRUE
;
1334 if (!u
.uinvulnerable
) {
1337 multi_reason
= "hiding from thunderstorm";
1341 You_hear("a rumbling noise.");
1344 /* -------------------------------------------------------------------------
1347 * Generic Timeout Functions.
1352 * boolean start_timer(long timeout,short kind,short func_index,
1354 * Start a timer of kind 'kind' that will expire at time
1355 * monstermoves+'timeout'. Call the function at 'func_index'
1356 * in the timeout table using argument 'arg'. Return TRUE if
1357 * a timer was started. This places the timer on a list ordered
1358 * "sooner" to "later". If an object, increment the object's
1361 * long stop_timer(short func_index, anything *arg)
1362 * Stop a timer specified by the (func_index, arg) pair. This
1363 * assumes that such a pair is unique. Return the time the
1364 * timer would have gone off. If no timer is found, return 0.
1365 * If an object, decrement the object's timer count.
1367 * long peek_timer(short func_index, anything *arg)
1368 * Return time specified timer will go off (0 if no such timer).
1370 * void run_timers(void)
1371 * Call timers that have timed out.
1374 * void save_timers(int fd, int mode, int range)
1375 * Save all timers of range 'range'. Range is either global
1376 * or local. Global timers follow game play, local timers
1377 * are saved with a level. Object and monster timers are
1378 * saved using their respective id's instead of pointers.
1380 * void restore_timers(int fd, int range, boolean ghostly, long adjust)
1381 * Restore timers of range 'range'. If from a ghost pile,
1382 * adjust the timeout by 'adjust'. The object and monster
1383 * ids are not restored until later.
1385 * void relink_timers(boolean ghostly)
1386 * Relink all object and monster timers that had been saved
1387 * using their object's or monster's id number.
1390 * void obj_move_timers(struct obj *src, struct obj *dest)
1391 * Reassign all timers from src to dest.
1393 * void obj_split_timers(struct obj *src, struct obj *dest)
1394 * Duplicate all timers assigned to src and attach them to dest.
1396 * void obj_stop_timers(struct obj *obj)
1397 * Stop all timers attached to obj.
1399 * boolean obj_has_timer(struct obj *object, short timer_type)
1400 * Check whether object has a timer of type timer_type.
1403 STATIC_DCL
const char *FDECL(kind_name
, (SHORT_P
));
1404 STATIC_DCL
void FDECL(print_queue
, (winid
, timer_element
*));
1405 STATIC_DCL
void FDECL(insert_timer
, (timer_element
*));
1406 STATIC_DCL timer_element
*FDECL(remove_timer
,
1407 (timer_element
**, SHORT_P
, ANY_P
*));
1408 STATIC_DCL
void FDECL(write_timer
, (int, timer_element
*));
1409 STATIC_DCL boolean
FDECL(mon_is_local
, (struct monst
*));
1410 STATIC_DCL boolean
FDECL(timer_is_local
, (timer_element
*));
1411 STATIC_DCL
int FDECL(maybe_write_timer
, (int, int, BOOLEAN_P
));
1413 /* ordered timer list */
1414 static timer_element
*timer_base
; /* "active" */
1415 static unsigned long timer_id
= 1;
1417 /* If defined, then include names when printing out the timer queue */
1418 #define VERBOSE_TIMER
1421 timeout_proc f
, cleanup
;
1422 #ifdef VERBOSE_TIMER
1424 #define TTAB(a, b, c) \
1429 #define TTAB(a, b, c) \
1436 /* table of timeout functions */
1437 static const ttable timeout_funcs
[NUM_TIME_FUNCS
] = {
1438 TTAB(rot_organic
, (timeout_proc
) 0, "rot_organic"),
1439 TTAB(rot_corpse
, (timeout_proc
) 0, "rot_corpse"),
1440 TTAB(revive_mon
, (timeout_proc
) 0, "revive_mon"),
1441 TTAB(burn_object
, cleanup_burn
, "burn_object"),
1442 TTAB(hatch_egg
, (timeout_proc
) 0, "hatch_egg"),
1443 TTAB(fig_transform
, (timeout_proc
) 0, "fig_transform"),
1444 TTAB(melt_ice_away
, (timeout_proc
) 0, "melt_ice_away")
1448 STATIC_OVL
const char *
1466 print_queue(win
, base
)
1468 timer_element
*base
;
1470 timer_element
*curr
;
1474 putstr(win
, 0, "<empty>");
1476 putstr(win
, 0, "timeout id kind call");
1477 for (curr
= base
; curr
; curr
= curr
->next
) {
1478 #ifdef VERBOSE_TIMER
1479 Sprintf(buf
, " %4ld %4ld %-6s %s(%s)", curr
->timeout
,
1480 curr
->tid
, kind_name(curr
->kind
),
1481 timeout_funcs
[curr
->func_index
].name
,
1482 fmt_ptr((genericptr_t
) curr
->arg
.a_void
));
1484 Sprintf(buf
, " %4ld %4ld %-6s #%d(%s)", curr
->timeout
,
1485 curr
->tid
, kind_name(curr
->kind
), curr
->func_index
,
1486 fmt_ptr((genericptr_t
) curr
->arg
.a_void
));
1488 putstr(win
, 0, buf
);
1493 static boolean print_prop_header
= TRUE
;
1495 print_prop(win
, text
, prop
)
1501 if (prop
& TIMEOUT
) {
1502 if (print_prop_header
) {
1504 putstr(win
, 0, "Properties:");
1506 print_prop_header
= FALSE
;
1508 Sprintf(buf
, " %10s: %ld", text
, (prop
& TIMEOUT
));
1509 putstr(win
, 0, buf
);
1519 win
= create_nhwindow(NHW_MENU
); /* corner text window */
1523 Sprintf(buf
, "Current time = %ld.", monstermoves
);
1524 putstr(win
, 0, buf
);
1526 putstr(win
, 0, "Active timeout queue:");
1528 print_queue(win
, timer_base
);
1530 print_prop_header
= TRUE
;
1531 print_prop(win
, "Levitation", HLevitation
);
1532 print_prop(win
, "Stoned", Stoned
);
1533 print_prop(win
, "Vomiting", Vomiting
);
1534 print_prop(win
, "Strangled", Strangled
);
1535 print_prop(win
, "Slimed", Slimed
);
1537 display_nhwindow(win
, FALSE
);
1538 destroy_nhwindow(win
);
1544 timer_sanity_check()
1546 timer_element
*curr
;
1548 /* this should be much more complete */
1549 for (curr
= timer_base
; curr
; curr
= curr
->next
)
1550 if (curr
->kind
== TIMER_OBJECT
) {
1551 struct obj
*obj
= curr
->arg
.a_obj
;
1552 if (obj
->timed
== 0) {
1553 pline("timer sanity: untimed obj %s, timer %ld",
1554 fmt_ptr((genericptr_t
) obj
), curr
->tid
);
1560 * Pick off timeout elements from the global queue and call their functions.
1561 * Do this until their time is less than or equal to the move count.
1566 timer_element
*curr
;
1569 * Always use the first element. Elements may be added or deleted at
1570 * any time. The list is ordered, we are done when the first element
1573 while (timer_base
&& timer_base
->timeout
<= monstermoves
) {
1575 timer_base
= curr
->next
;
1577 if (curr
->kind
== TIMER_OBJECT
)
1578 (curr
->arg
.a_obj
)->timed
--;
1579 (*timeout_funcs
[curr
->func_index
].f
)(&curr
->arg
, curr
->timeout
);
1580 free((genericptr_t
) curr
);
1585 * Start a timer. Return TRUE if successful.
1588 start_timer(when
, kind
, func_index
, arg
)
1596 if (func_index
< 0 || func_index
>= NUM_TIME_FUNCS
)
1597 panic("start_timer");
1599 gnu
= (timer_element
*) alloc(sizeof(timer_element
));
1600 (void) memset((genericptr_t
)gnu
, 0, sizeof(timer_element
));
1602 gnu
->tid
= timer_id
++;
1603 gnu
->timeout
= monstermoves
+ when
;
1605 gnu
->needs_fixup
= 0;
1606 gnu
->func_index
= func_index
;
1610 if (kind
== TIMER_OBJECT
) /* increment object's timed count */
1611 (arg
->a_obj
)->timed
++;
1613 /* should check for duplicates and fail if any */
1618 * Remove the timer from the current list and free it up. Return the time
1619 * remaining until it would have gone off, 0 if not found.
1622 stop_timer(func_index
, arg
)
1626 timer_element
*doomed
;
1629 doomed
= remove_timer(&timer_base
, func_index
, arg
);
1632 timeout
= doomed
->timeout
;
1633 if (doomed
->kind
== TIMER_OBJECT
)
1634 (arg
->a_obj
)->timed
--;
1635 if (timeout_funcs
[doomed
->func_index
].cleanup
)
1636 (*timeout_funcs
[doomed
->func_index
].cleanup
)(arg
, timeout
);
1637 free((genericptr_t
) doomed
);
1638 return (timeout
- monstermoves
);
1644 * Find the timeout of specified timer; return 0 if none.
1647 peek_timer(type
, arg
)
1651 timer_element
*curr
;
1653 for (curr
= timer_base
; curr
; curr
= curr
->next
) {
1654 if (curr
->func_index
== type
&& curr
->arg
.a_void
== arg
->a_void
)
1655 return curr
->timeout
;
1661 * Move all object timers from src to dest, leaving src untimed.
1664 obj_move_timers(src
, dest
)
1665 struct obj
*src
, *dest
;
1668 timer_element
*curr
;
1670 for (count
= 0, curr
= timer_base
; curr
; curr
= curr
->next
)
1671 if (curr
->kind
== TIMER_OBJECT
&& curr
->arg
.a_obj
== src
) {
1672 curr
->arg
.a_obj
= dest
;
1676 if (count
!= src
->timed
)
1677 panic("obj_move_timers");
1682 * Find all object timers and duplicate them for the new object "dest".
1685 obj_split_timers(src
, dest
)
1686 struct obj
*src
, *dest
;
1688 timer_element
*curr
, *next_timer
= 0;
1690 for (curr
= timer_base
; curr
; curr
= next_timer
) {
1691 next_timer
= curr
->next
; /* things may be inserted */
1692 if (curr
->kind
== TIMER_OBJECT
&& curr
->arg
.a_obj
== src
) {
1693 (void) start_timer(curr
->timeout
- monstermoves
, TIMER_OBJECT
,
1694 curr
->func_index
, obj_to_any(dest
));
1700 * Stop all timers attached to this object. We can get away with this because
1701 * all object pointers are unique.
1704 obj_stop_timers(obj
)
1707 timer_element
*curr
, *prev
, *next_timer
= 0;
1709 for (prev
= 0, curr
= timer_base
; curr
; curr
= next_timer
) {
1710 next_timer
= curr
->next
;
1711 if (curr
->kind
== TIMER_OBJECT
&& curr
->arg
.a_obj
== obj
) {
1713 prev
->next
= curr
->next
;
1715 timer_base
= curr
->next
;
1716 if (timeout_funcs
[curr
->func_index
].cleanup
)
1717 (*timeout_funcs
[curr
->func_index
].cleanup
)(&curr
->arg
,
1719 free((genericptr_t
) curr
);
1728 * Check whether object has a timer of type timer_type.
1731 obj_has_timer(object
, timer_type
)
1735 long timeout
= peek_timer(timer_type
, obj_to_any(object
));
1737 return (boolean
) (timeout
!= 0L);
1741 * Stop all timers of index func_index at this spot.
1745 spot_stop_timers(x
, y
, func_index
)
1749 timer_element
*curr
, *prev
, *next_timer
= 0;
1750 long where
= (((long) x
<< 16) | ((long) y
));
1752 for (prev
= 0, curr
= timer_base
; curr
; curr
= next_timer
) {
1753 next_timer
= curr
->next
;
1754 if (curr
->kind
== TIMER_LEVEL
&& curr
->func_index
== func_index
1755 && curr
->arg
.a_long
== where
) {
1757 prev
->next
= curr
->next
;
1759 timer_base
= curr
->next
;
1760 if (timeout_funcs
[curr
->func_index
].cleanup
)
1761 (*timeout_funcs
[curr
->func_index
].cleanup
)(&curr
->arg
,
1763 free((genericptr_t
) curr
);
1771 * When is the spot timer of type func_index going to expire?
1772 * Returns 0L if no such timer.
1775 spot_time_expires(x
, y
, func_index
)
1779 timer_element
*curr
;
1780 long where
= (((long) x
<< 16) | ((long) y
));
1782 for (curr
= timer_base
; curr
; curr
= curr
->next
) {
1783 if (curr
->kind
== TIMER_LEVEL
&& curr
->func_index
== func_index
1784 && curr
->arg
.a_long
== where
)
1785 return curr
->timeout
;
1791 spot_time_left(x
, y
, func_index
)
1795 long expires
= spot_time_expires(x
, y
, func_index
);
1796 return (expires
> 0L) ? expires
- monstermoves
: 0L;
1799 /* Insert timer into the global queue */
1804 timer_element
*curr
, *prev
;
1806 for (prev
= 0, curr
= timer_base
; curr
; prev
= curr
, curr
= curr
->next
)
1807 if (curr
->timeout
>= gnu
->timeout
)
1817 STATIC_OVL timer_element
*
1818 remove_timer(base
, func_index
, arg
)
1819 timer_element
**base
;
1823 timer_element
*prev
, *curr
;
1825 for (prev
= 0, curr
= *base
; curr
; prev
= curr
, curr
= curr
->next
)
1826 if (curr
->func_index
== func_index
&& curr
->arg
.a_void
== arg
->a_void
)
1831 prev
->next
= curr
->next
;
1840 write_timer(fd
, timer
)
1842 timer_element
*timer
;
1847 switch (timer
->kind
) {
1850 /* assume no pointers in arg */
1851 bwrite(fd
, (genericptr_t
) timer
, sizeof(timer_element
));
1855 if (timer
->needs_fixup
)
1856 bwrite(fd
, (genericptr_t
) timer
, sizeof(timer_element
));
1858 /* replace object pointer with id */
1859 arg_save
.a_obj
= timer
->arg
.a_obj
;
1860 timer
->arg
= zeroany
;
1861 timer
->arg
.a_uint
= (arg_save
.a_obj
)->o_id
;
1862 timer
->needs_fixup
= 1;
1863 bwrite(fd
, (genericptr_t
) timer
, sizeof(timer_element
));
1864 timer
->arg
.a_obj
= arg_save
.a_obj
;
1865 timer
->needs_fixup
= 0;
1870 if (timer
->needs_fixup
)
1871 bwrite(fd
, (genericptr_t
) timer
, sizeof(timer_element
));
1873 /* replace monster pointer with id */
1874 arg_save
.a_monst
= timer
->arg
.a_monst
;
1875 timer
->arg
= zeroany
;
1876 timer
->arg
.a_uint
= (arg_save
.a_monst
)->m_id
;
1877 timer
->needs_fixup
= 1;
1878 bwrite(fd
, (genericptr_t
) timer
, sizeof(timer_element
));
1879 timer
->arg
.a_monst
= arg_save
.a_monst
;
1880 timer
->needs_fixup
= 0;
1885 panic("write_timer");
1891 * Return TRUE if the object will stay on the level when the level is
1898 switch (obj
->where
) {
1906 return obj_is_local(obj
->ocontainer
);
1908 return mon_is_local(obj
->ocarry
);
1910 panic("obj_is_local");
1915 * Return TRUE if the given monster will stay on the level when the
1924 for (curr
= migrating_mons
; curr
; curr
= curr
->nmon
)
1927 /* `mydogs' is used during level changes, never saved and restored */
1928 for (curr
= mydogs
; curr
; curr
= curr
->nmon
)
1935 * Return TRUE if the timer is attached to something that will stay on the
1936 * level when the level is saved.
1939 timer_is_local(timer
)
1940 timer_element
*timer
;
1942 switch (timer
->kind
) {
1948 return obj_is_local(timer
->arg
.a_obj
);
1950 return mon_is_local(timer
->arg
.a_monst
);
1952 panic("timer_is_local");
1957 * Part of the save routine. Count up the number of timers that would
1958 * be written. If write_it is true, actually write the timer.
1961 maybe_write_timer(fd
, range
, write_it
)
1966 timer_element
*curr
;
1968 for (curr
= timer_base
; curr
; curr
= curr
->next
) {
1969 if (range
== RANGE_GLOBAL
) {
1972 if (!timer_is_local(curr
)) {
1975 write_timer(fd
, curr
);
1981 if (timer_is_local(curr
)) {
1984 write_timer(fd
, curr
);
1993 * Save part of the timer list. The parameter 'range' specifies either
1994 * global or level timers to save. The timer ID is saved with the global
1998 * + timeouts that follow the hero (global)
1999 * + timeouts that follow obj & monst that are migrating
2002 * + timeouts that are level specific (e.g. storms)
2003 * + timeouts that stay with the level (obj & monst)
2006 save_timers(fd
, mode
, range
)
2007 int fd
, mode
, range
;
2009 timer_element
*curr
, *prev
, *next_timer
= 0;
2012 if (perform_bwrite(mode
)) {
2013 if (range
== RANGE_GLOBAL
)
2014 bwrite(fd
, (genericptr_t
) &timer_id
, sizeof(timer_id
));
2016 count
= maybe_write_timer(fd
, range
, FALSE
);
2017 bwrite(fd
, (genericptr_t
) &count
, sizeof count
);
2018 (void) maybe_write_timer(fd
, range
, TRUE
);
2021 if (release_data(mode
)) {
2022 for (prev
= 0, curr
= timer_base
; curr
; curr
= next_timer
) {
2023 next_timer
= curr
->next
; /* in case curr is removed */
2025 if (!(!!(range
== RANGE_LEVEL
) ^ !!timer_is_local(curr
))) {
2027 prev
->next
= curr
->next
;
2029 timer_base
= curr
->next
;
2030 free((genericptr_t
) curr
);
2031 /* prev stays the same */
2040 * Pull in the structures from disk, but don't recalculate the object and
2044 restore_timers(fd
, range
, ghostly
, adjust
)
2046 boolean ghostly
; /* restoring from a ghost level */
2047 long adjust
; /* how much to adjust timeout */
2050 timer_element
*curr
;
2052 if (range
== RANGE_GLOBAL
)
2053 mread(fd
, (genericptr_t
) &timer_id
, sizeof timer_id
);
2055 /* restore elements */
2056 mread(fd
, (genericptr_t
) &count
, sizeof count
);
2057 while (count
-- > 0) {
2058 curr
= (timer_element
*) alloc(sizeof(timer_element
));
2059 mread(fd
, (genericptr_t
) curr
, sizeof(timer_element
));
2061 curr
->timeout
+= adjust
;
2066 /* to support '#stats' wizard-mode command */
2068 timer_stats(hdrfmt
, hdrbuf
, count
, size
)
2075 Sprintf(hdrbuf
, hdrfmt
, (long) sizeof (timer_element
));
2076 *count
= *size
= 0L;
2077 for (te
= timer_base
; te
; te
= te
->next
) {
2079 *size
+= (long) sizeof *te
;
2083 /* reset all timers that are marked for reseting */
2085 relink_timers(ghostly
)
2088 timer_element
*curr
;
2091 for (curr
= timer_base
; curr
; curr
= curr
->next
) {
2092 if (curr
->needs_fixup
) {
2093 if (curr
->kind
== TIMER_OBJECT
) {
2095 if (!lookup_id_mapping(curr
->arg
.a_uint
, &nid
))
2096 panic("relink_timers 1");
2098 nid
= curr
->arg
.a_uint
;
2099 curr
->arg
.a_obj
= find_oid(nid
);
2100 if (!curr
->arg
.a_obj
)
2101 panic("cant find o_id %d", nid
);
2102 curr
->needs_fixup
= 0;
2103 } else if (curr
->kind
== TIMER_MONSTER
) {
2104 panic("relink_timers: no monster timer implemented");
2106 panic("relink_timers 2");