OSDN Git Service

update year to 2018
[jnethack/source.git] / src / timeout.c
1 /* NetHack 3.6  timeout.c       $NHDT-Date: 1505214876 2017/09/12 11:14:36 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.75 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2018. */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2018            */
9 /* JNetHack may be freely redistributed.  See license for details. */
10
11 #include "hack.h"
12 #include "lev.h" /* for checking save modes */
13
14 STATIC_DCL void NDECL(stoned_dialogue);
15 STATIC_DCL void NDECL(vomiting_dialogue);
16 STATIC_DCL void NDECL(choke_dialogue);
17 STATIC_DCL void NDECL(levitation_dialogue);
18 STATIC_DCL void NDECL(slime_dialogue);
19 STATIC_DCL void NDECL(slip_or_trip);
20 STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *));
21 STATIC_DCL void FDECL(lantern_message, (struct obj *));
22 STATIC_DCL void FDECL(cleanup_burn, (ANY_P *, long));
23
24 /* used by wizard mode #timeout and #wizintrinsic; order by 'interest'
25    for timeout countdown, where most won't occur in normal play */
26 /*JP \83E\83B\83U\81[\83h\83\82\81[\83h\82Å\82µ\82©\8eg\82í\82ê\82È\82¢\82æ\82¤\82È\82Ì\82Å\96|\96ó\82µ\82È\82¢ */
27 const struct propname {
28     int prop_num;
29     const char *prop_name;
30 } propertynames[] = {
31     { INVULNERABLE, "invulnerable" },
32     { STONED, "petrifying" },
33     { SLIMED, "becoming slime" },
34     { STRANGLED, "strangling" },
35     { SICK, "fatally sick" },
36     { STUNNED, "stunned" },
37     { CONFUSION, "confused" },
38     { HALLUC, "hallucinating" },
39     { BLINDED, "blinded" },
40     { DEAF, "deafness" },
41     { VOMITING, "vomiting" },
42     { GLIB, "slippery fingers" },
43     { WOUNDED_LEGS, "wounded legs" },
44     { SLEEPY, "sleepy" },
45     { TELEPORT, "teleporting" },
46     { POLYMORPH, "polymorphing" },
47     { LEVITATION, "levitating" },
48     { FAST, "very fast" }, /* timed 'FAST' is very fast */
49     { CLAIRVOYANT, "clairvoyant" },
50     { DETECT_MONSTERS, "monster detection" },
51     { SEE_INVIS, "see invisible" },
52     { INVIS, "invisible" },
53     /* properties beyond here don't have timed values during normal play,
54        so there's no much point in trying to order them sensibly;
55        they're either on or off based on equipment, role, actions, &c */
56     { FIRE_RES, "fire resistance" },
57     { COLD_RES, "cold resistance" },
58     { SLEEP_RES, "sleep resistance" },
59     { DISINT_RES, "disintegration resistance" },
60     { SHOCK_RES, "shock resistance" },
61     { POISON_RES, "poison resistance" },
62     { ACID_RES, "acid resistance" },
63     { STONE_RES, "stoning resistance" },
64     { DRAIN_RES, "drain resistance" },
65     { SICK_RES, "sickness resistance" },
66     { ANTIMAGIC, "magic resistance" },
67     { HALLUC_RES, "hallucination resistance" },
68     { FUMBLING, "fumbling" },
69     { HUNGER, "voracious hunger" },
70     { TELEPAT, "telepathic" },
71     { WARNING, "warning" },
72     { WARN_OF_MON, "warn: monster type or class" },
73     { WARN_UNDEAD, "warn: undead" },
74     { SEARCHING, "searching" },
75     { INFRAVISION, "infravision" },
76     { ADORNED, "adorned (+/- Cha)" },
77     { DISPLACED, "displaced" },
78     { STEALTH, "stealthy" },
79     { AGGRAVATE_MONSTER, "monster aggravation" },
80     { CONFLICT, "conflict" },
81     { JUMPING, "jumping" },
82     { TELEPORT_CONTROL, "teleport control" },
83     { FLYING, "flying" },
84     { WWALKING, "water walking" },
85     { SWIMMING, "swimming" },
86     { MAGICAL_BREATHING, "magical breathing" },
87     { PASSES_WALLS, "pass thru walls" },
88     { SLOW_DIGESTION, "slow digestion" },
89     { HALF_SPDAM, "half spell damage" },
90     { HALF_PHDAM, "half physical damage" },
91     { REGENERATION, "HP regeneration" },
92     { ENERGY_REGENERATION, "energy regeneration" },
93     { PROTECTION, "extra protection" },
94     { PROT_FROM_SHAPE_CHANGERS, "protection from shape changers" },
95     { POLYMORPH_CONTROL, "polymorph control" },
96     { UNCHANGING, "unchanging" },
97     { REFLECTING, "reflecting" },
98     { FREE_ACTION, "free action" },
99     { FIXED_ABIL, "fixed abilites" },
100     { LIFESAVED, "life will be saved" },
101     {  0, 0 },
102 };
103
104 /* He is being petrified - dialogue by inmet!tower */
105 static NEARDATA const char *const stoned_texts[] = {
106 #if 0 /*JP*/
107     "You are slowing down.",            /* 5 */
108     "Your limbs are stiffening.",       /* 4 */
109     "Your limbs have turned to stone.", /* 3 */
110     "You have turned to stone.",        /* 2 */
111     "You are a statue."                 /* 1 */
112 #else
113     "\82 \82È\82½\82Í\82Ì\82ë\82­\82È\82Á\82½\81D",           /* 5 */
114     "\82 \82È\82½\82Ì\8eè\91«\82Í\8dd\92¼\82µ\82½\81D",         /* 4 */
115     "\82 \82È\82½\82Ì\8eè\91«\82Í\90Î\89»\82µ\82½\81D",         /* 3 */
116     "\82 \82È\82½\82Í\90Î\82É\82È\82Á\82½\81D",             /* 2 */
117     "\82 \82È\82½\82Í\92¤\91\9c\82É\82È\82Á\82½\81D"            /* 1 */
118 #endif
119 };
120
121 STATIC_OVL void
122 stoned_dialogue()
123 {
124     register long i = (Stoned & TIMEOUT);
125
126     if (i > 0L && i <= SIZE(stoned_texts)) {
127         char buf[BUFSZ];
128
129         Strcpy(buf, stoned_texts[SIZE(stoned_texts) - i]);
130 #if 0 /*JP*/
131         if (nolimbs(youmonst.data) && strstri(buf, "limbs"))
132             (void) strsubst(buf, "limbs", "extremities");
133 #else
134         if (nolimbs(youmonst.data) && strstri(buf, "\8eè\91«"))
135             (void) strsubst(buf, "\8eè\91«", "\90æ\92[");
136 #endif
137         pline1(buf);
138     }
139     switch ((int) i) {
140     case 5: /* slowing down */
141         HFast = 0L;
142         if (multi > 0)
143             nomul(0);
144         break;
145     case 4: /* limbs stiffening */
146         /* just one move left to save oneself so quit fiddling around;
147            don't stop attempt to eat tin--might be lizard or acidic */
148         if (!Popeye(STONED))
149             stop_occupation();
150         if (multi > 0)
151             nomul(0);
152         break;
153     case 3: /* limbs turned to stone */
154         stop_occupation();
155         nomul(-3); /* can't move anymore */
156 /*JP
157         multi_reason = "getting stoned";
158 */
159         multi_reason = "\90Î\89»\82µ\82Â\82Â\82 \82é\8e\9e\82É";
160         nomovemsg = You_can_move_again; /* not unconscious */
161         break;
162     case 2:
163         if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
164             set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
165     default:
166         break;
167     }
168     exercise(A_DEX, FALSE);
169 }
170
171 /* hero is getting sicker and sicker prior to vomiting */
172 static NEARDATA const char *const vomiting_texts[] = {
173 #if 0 /*JP:T*/
174     "are feeling mildly nauseated.", /* 14 */
175     "feel slightly confused.",       /* 11 */
176     "can't seem to think straight.", /* 8 */
177     "feel incredibly sick.",         /* 5 */
178     "are about to vomit."            /* 2 */
179 #else
180     "\82¿\82å\82Á\82Æ\93f\82«\8bC\82ª\82µ\82½\81D",        /* 14 */
181     "\8f­\82µ\8d¬\97\90\82µ\82½\81D",                /* 11 */
182     "\82Ü\82Æ\82à\82É\8ev\8dl\82Å\82«\82È\82­\82È\82Á\82½\81D",  /* 8 */
183     "\82Æ\82Ä\82à\8bC\95ª\82ª\88«\82­\82È\82Á\82½\81D",      /* 5 */
184     "\93Ë\91R\9aq\93f\82µ\82½\81D"                 /* 2 */
185 #endif
186 };
187
188 STATIC_OVL void
189 vomiting_dialogue()
190 {
191     const char *txt = 0;
192     long v = (Vomiting & TIMEOUT);
193
194     /* note: nhtimeout() hasn't decremented timed properties for the
195        current turn yet, so we use Vomiting-1 here */
196     switch ((int) (v - 1L)) {
197     case 14:
198         txt = vomiting_texts[0];
199         break;
200     case 11:
201         txt = vomiting_texts[1];
202         break;
203     case 6:
204         make_stunned((HStun & TIMEOUT) + (long) d(2, 4), FALSE);
205         if (!Popeye(VOMITING))
206             stop_occupation();
207     /*FALLTHRU*/
208     case 9:
209         make_confused((HConfusion & TIMEOUT) + (long) d(2, 4), FALSE);
210         if (multi > 0)
211             nomul(0);
212         break;
213     case 8:
214         txt = vomiting_texts[2];
215         break;
216     case 5:
217         txt = vomiting_texts[3];
218         break;
219     case 2:
220         txt = vomiting_texts[4];
221         if (cantvomit(youmonst.data))
222 /*JP
223             txt = "gag uncontrollably.";
224 */
225             txt = "\8bC\95ª\82Ì\88«\82³\82ª\97}\82¦\82ç\82ê\82È\82­\82È\82Á\82½\81D";
226         else if (Hallucination)
227             /* "hurl" is short for "hurl chunks" which is slang for
228                relatively violent vomiting... */
229 /*JP
230             txt = "are about to hurl!";
231 */
232             txt = "\83\8a\83o\81[\83X\90¡\91O\82¾\81I";
233         break;
234     case 0:
235         stop_occupation();
236         if (!cantvomit(youmonst.data)) {
237             morehungry(20);
238             /* case 2 used to be "You suddenly vomit!" but it wasn't sudden
239                since you've just been through the earlier messages of the
240                countdown, and it was still possible to move around between
241                that message and "You can move again." (from vomit()'s
242                nomul(-2)) with no intervening message; give one here to
243                have more specific point at which hero became unable to move
244                [vomit() issues its own message for the cantvomit() case
245                and for the FAINTING-or-worse case where stomach is empty] */
246             if (u.uhs < FAINTING)
247 /*JP
248                 You("%s!", !Hallucination ? "vomit" : "hurl chunks");
249 */
250                 You("%s\81I", !Hallucination ? "\93f\82¢\82½" : "\83\8a\83o\81[\83X\82µ\82½");
251         }
252         vomit();
253         break;
254     default:
255         break;
256     }
257     if (txt)
258         You1(txt);
259     exercise(A_CON, FALSE);
260 }
261
262 static NEARDATA const char *const choke_texts[] = {
263 #if 0 /*JP:T*/
264     "You find it hard to breathe.",
265     "You're gasping for air.",
266     "You can no longer breathe.",
267     "You're turning %s.",
268     "You suffocate."
269 #else
270     "\82 \82È\82½\82Í\8cÄ\8bz\82ª\8d¢\93ï\82É\82È\82Á\82½\81D",
271     "\82 \82È\82½\82Í\8bê\82µ\82­\82Ä\82 \82¦\82¢\82¾\81D",
272     "\82 \82È\82½\82Í\82à\82¤\8cÄ\8bz\82ª\82Å\82«\82È\82¢\81D",
273     "\82 \82È\82½\82Í%s\82È\82Á\82½\81D",
274     "\82 \82È\82½\82Í\92\82\91§\82µ\82½\81D"
275 #endif
276 };
277
278 static NEARDATA const char *const choke_texts2[] = {
279 #if 0 /*JP*/
280     "Your %s is becoming constricted.",
281     "Your blood is having trouble reaching your brain.",
282     "The pressure on your %s increases.",
283     "Your consciousness is fading.",
284     "You suffocate."
285 #else
286     "\82 \82È\82½\82Ì%s\82Í\8di\82ß\82Â\82¯\82ç\82ê\82½\81D",
287     "\8c\8c\89t\82Ì\82ß\82®\82è\82ª\88«\82­\82È\82Á\82½\81D",
288     "%s\82Ì\88³\97Í\82ª\8d\82\82­\82È\82Á\82½\81D",
289     "\88Ó\8e¯\82ª\89\93\82­\82È\82Á\82Ä\82«\82½\81D",
290     "\82 \82È\82½\82Í\92\82\91§\82µ\82½\81D"
291 #endif
292 };
293
294 STATIC_OVL void
295 choke_dialogue()
296 {
297     register long i = (Strangled & TIMEOUT);
298
299     if (i > 0 && i <= SIZE(choke_texts)) {
300         if (Breathless || !rn2(50))
301             pline(choke_texts2[SIZE(choke_texts2) - i], body_part(NECK));
302         else {
303             const char *str = choke_texts[SIZE(choke_texts) - i];
304
305             if (index(str, '%'))
306 /*JP
307                 pline(str, hcolor(NH_BLUE));
308 */
309                 pline(str, jconj_adj(hcolor(NH_BLUE)));
310             else
311                 pline1(str);
312         }
313     }
314     exercise(A_STR, FALSE);
315 }
316
317 static NEARDATA const char *const levi_texts[] = {
318 #if 0 /*JP*//* %s\82Í\8eg\82í\82È\82¢ */
319     "You float slightly lower.",
320     "You wobble unsteadily %s the %s."
321 #else
322     "\82 \82È\82½\82Ì\95\82\82«\8bï\8d\87\82Í\8f­\82µ\92á\82­\82È\82Á\82½\81D",
323     "\82 \82È\82½\82Í\82®\82ç\82®\82ç\97h\82ê\82Í\82\82ß\82½\81D"
324 #endif
325 };
326
327 STATIC_OVL void
328 levitation_dialogue()
329 {
330     /* -1 because the last message comes via float_down() */
331     long i = (((HLevitation & TIMEOUT) - 1L) / 2L);
332
333     if (ELevitation)
334         return;
335
336     if (!ACCESSIBLE(levl[u.ux][u.uy].typ)
337         && !is_pool_or_lava(u.ux,u.uy))
338         return;
339
340     if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) {
341         const char *s = levi_texts[SIZE(levi_texts) - i];
342
343 #if 0 /*JP*//* %s \82Í\8eg\82Á\82Ä\82¢\82È\82¢\82Ì\82Å\92P\8f\83\89» */
344         if (index(s, '%')) {
345             boolean danger = (is_pool_or_lava(u.ux, u.uy)
346                               && !Is_waterlevel(&u.uz));
347
348             pline(s, danger ? "over" : "in",
349                   danger ? surface(u.ux, u.uy) : "air");
350         } else
351 #endif
352             pline1(s);
353     }
354 }
355
356 static NEARDATA const char *const slime_texts[] = {
357 #if 0 /*JP*/
358     "You are turning a little %s.",   /* 5 */
359     "Your limbs are getting oozy.",   /* 4 */
360     "Your skin begins to peel away.", /* 3 */
361     "You are turning into %s.",       /* 2 */
362     "You have become %s."             /* 1 */
363 #else
364     "\8f­\82µ%s\82È\82Á\82½\81D",                 /* 5 */
365     "\8eè\91«\82ª\97n\82¯\82Í\82\82ß\82½\81D",           /* 4 */
366     "\94§\82ª\82Ç\82ë\82Ç\82ë\82É\82È\82Á\82Ä\82«\82½\81D",     /* 3 */
367     "%s\82É\82È\82è\82Í\82\82ß\82½\81D",             /* 2 */
368     "%s\82É\82È\82Á\82Ä\82µ\82Ü\82Á\82½\81D",           /* 1 */
369 #endif
370 };
371
372 STATIC_OVL void
373 slime_dialogue()
374 {
375     register long i = (Slimed & TIMEOUT) / 2L;
376
377     if (((Slimed & TIMEOUT) % 2L) && i >= 0L && i < SIZE(slime_texts)) {
378         char buf[BUFSZ];
379
380         Strcpy(buf, slime_texts[SIZE(slime_texts) - i - 1L]);
381 #if 0 /*JP*/
382         if (nolimbs(youmonst.data) && strstri(buf, "limbs"))
383             (void) strsubst(buf, "limbs", "extremities");
384 #else
385         if (nolimbs(youmonst.data) && strstri(buf, "\8eè\91«"))
386             (void) strsubst(buf, "\8eè\91«", "\90æ\92[");
387 #endif
388
389         if (index(buf, '%')) {
390             if (i == 4L) {  /* "you are turning green" */
391                 if (!Blind) /* [what if you're already green?] */
392 /*JP
393                     pline(buf, hcolor(NH_GREEN));
394 */
395                     pline(buf, jconj_adj(hcolor(NH_GREEN)));
396             } else
397                 pline(buf,
398 /*JP
399                       an(Hallucination ? rndmonnam(NULL) : "green slime"));
400 */
401                       Hallucination ? rndmonnam(NULL) : "\97Î\83X\83\89\83C\83\80");
402         } else
403             pline1(buf);
404     }
405     if (i == 3L) {  /* limbs becoming oozy */
406         HFast = 0L; /* lose intrinsic speed */
407         if (!Popeye(SLIMED))
408             stop_occupation();
409         if (multi > 0)
410             nomul(0);
411     }
412     if (i == 2L && (HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
413         set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
414     exercise(A_DEX, FALSE);
415 }
416
417 void
418 burn_away_slime()
419 {
420     if (Slimed) {
421 /*JP
422         make_slimed(0L, "The slime that covers you is burned away!");
423 */
424         pline("\82 \82È\82½\82ð\95¢\82Á\82Ä\82¢\82½\83X\83\89\83C\83\80\82Í\8fÄ\82¯\97\8e\82¿\82½\81I");
425     }
426 }
427
428 /* Intrinsic Passes_walls is temporary when your god is trying to fix
429    all troubles and then TROUBLE_STUCK_IN_WALL calls safe_teleds() but
430    it can't find anywhere to place you.  If that happens you get a small
431    value for (HPasses_walls & TIMEOUT) to move somewhere yourself.
432    Message given is "you feel much slimmer" as a joke hint that you can
433    move between things which are closely packed--like the substance of
434    solid rock! */
435 static NEARDATA const char *const phaze_texts[] = {
436 #if 0 /*JP*/
437     "You start to feel bloated.",
438     "You are feeling rather flabby.",
439 #else
440     "\82 \82È\82½\82Í\96c\82ç\82Ý\82Í\82\82ß\82½\8bC\82ª\82·\82é\81D",
441     "\82 \82È\82½\82Í\8f­\82µ\82½\82é\82ñ\82¾\8bC\82ª\82·\82é\81D",
442 #endif
443 };
444
445 STATIC_OVL void
446 phaze_dialogue()
447 {
448     long i = ((HPasses_walls & TIMEOUT) / 2L);
449
450     if (EPasses_walls || (HPasses_walls & ~TIMEOUT))
451         return;
452
453     if (((HPasses_walls & TIMEOUT) % 2L) && i > 0L && i <= SIZE(phaze_texts))
454         pline1(phaze_texts[SIZE(phaze_texts) - i]);
455 }
456
457 void
458 nh_timeout()
459 {
460     register struct prop *upp;
461     struct kinfo *kptr;
462     int sleeptime;
463     int m_idx;
464     int baseluck = (flags.moonphase == FULL_MOON) ? 1 : 0;
465
466     if (flags.friday13)
467         baseluck -= 1;
468
469     if (u.uluck != baseluck
470         && moves % ((u.uhave.amulet || u.ugangr) ? 300 : 600) == 0) {
471         /* Cursed luckstones stop bad luck from timing out; blessed luckstones
472          * stop good luck from timing out; normal luckstones stop both;
473          * neither is stopped if you don't have a luckstone.
474          * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th
475          */
476         register int time_luck = stone_luck(FALSE);
477         boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE);
478
479         if (u.uluck > baseluck && (nostone || time_luck < 0))
480             u.uluck--;
481         else if (u.uluck < baseluck && (nostone || time_luck > 0))
482             u.uluck++;
483     }
484     if (u.uinvulnerable)
485         return; /* things past this point could kill you */
486     if (Stoned)
487         stoned_dialogue();
488     if (Slimed)
489         slime_dialogue();
490     if (Vomiting)
491         vomiting_dialogue();
492     if (Strangled)
493         choke_dialogue();
494     if (HLevitation & TIMEOUT)
495         levitation_dialogue();
496     if (HPasses_walls & TIMEOUT)
497         phaze_dialogue();
498     if (u.mtimedone && !--u.mtimedone) {
499         if (Unchanging)
500             u.mtimedone = rnd(100 * youmonst.data->mlevel + 1);
501         else if (is_were(youmonst.data))
502             you_unwere(FALSE); /* if polycontrl, asks whether to rehumanize */
503         else
504             rehumanize();
505     }
506     if (u.ucreamed)
507         u.ucreamed--;
508
509     /* Dissipate spell-based protection. */
510     if (u.usptime) {
511         if (--u.usptime == 0 && u.uspellprot) {
512             u.usptime = u.uspmtime;
513             u.uspellprot--;
514             find_ac();
515             if (!Blind)
516 #if 0 /*JP*/
517                 Norep("The %s haze around you %s.", hcolor(NH_GOLDEN),
518                       u.uspellprot ? "becomes less dense" : "disappears");
519 #else
520                 Norep("\82 \82È\82½\82Ì\89ñ\82è\82Ì%s\96\82Í%s\81D", hcolor(NH_GOLDEN),
521                       u.uspellprot ? "\8fÁ\82¦\82Í\82\82ß\82½" : "\8fÁ\82¦\82½");
522 #endif
523         }
524     }
525
526     if (u.ugallop) {
527         if (--u.ugallop == 0L && u.usteed)
528 /*JP
529             pline("%s stops galloping.", Monnam(u.usteed));
530 */
531             pline("%s\82Í\91¬\8bì\82¯\82ð\82â\82ß\82½\81D", Monnam(u.usteed));
532     }
533
534     for (upp = u.uprops; upp < u.uprops + SIZE(u.uprops); upp++)
535         if ((upp->intrinsic & TIMEOUT) && !(--upp->intrinsic & TIMEOUT)) {
536             kptr = find_delayed_killer((int) (upp - u.uprops));
537             switch (upp - u.uprops) {
538             case STONED:
539                 if (kptr && kptr->name[0]) {
540                     killer.format = kptr->format;
541 #if 0 /*JP*/
542                     Strcpy(killer.name, kptr->name);
543 #else
544                     Sprintf(killer.name, "%s\82Ì\8dU\8c\82\82Å", kptr->name);
545 #endif
546                 } else {
547 #if 0 /*JP*/
548                     killer.format = NO_KILLER_PREFIX;
549                     Strcpy(killer.name, "killed by petrification");
550 #else
551                     killer.format = KILLED_BY;
552                     Strcpy(killer.name, "\90Î\89»\8dU\8c\82\82Å");
553 #endif
554                 }
555                 dealloc_killer(kptr);
556                 /* (unlike sliming, you aren't changing form here) */
557                 done(STONING);
558                 break;
559             case SLIMED:
560                 if (kptr && kptr->name[0]) {
561                     killer.format = kptr->format;
562                     Strcpy(killer.name, kptr->name);
563                 } else {
564                     killer.format = NO_KILLER_PREFIX;
565 /*JP
566                     Strcpy(killer.name, "turned into green slime");
567 */
568                     Strcpy(killer.name, "\97Î\83X\83\89\83C\83\80\82É\82È\82Á\82½");
569                 }
570                 dealloc_killer(kptr);
571                 /* involuntarily break "never changed form" conduct */
572                 u.uconduct.polyselfs++;
573                 done(TURNED_SLIME);
574                 break;
575             case VOMITING:
576                 make_vomiting(0L, TRUE);
577                 break;
578             case SICK:
579 /*JP
580                 You("die from your illness.");
581 */
582                 You("\95a\8bC\82Å\8e\80\82ñ\82¾\81D");
583                 if (kptr && kptr->name[0]) {
584                     killer.format = kptr->format;
585                     Strcpy(killer.name, kptr->name);
586                 } else {
587                     killer.format = KILLED_BY_AN;
588                     killer.name[0] = 0; /* take the default */
589                 }
590                 dealloc_killer(kptr);
591
592                 if ((m_idx = name_to_mon(killer.name)) >= LOW_PM) {
593                     if (type_is_pname(&mons[m_idx])) {
594                         killer.format = KILLED_BY;
595                     } else if (mons[m_idx].geno & G_UNIQ) {
596                         Strcpy(killer.name, the(killer.name));
597                         killer.format = KILLED_BY;
598                     }
599                 }
600                 u.usick_type = 0;
601                 done(POISONING);
602                 break;
603             case FAST:
604                 if (!Very_fast)
605 #if 0 /*JP*/
606                     You_feel("yourself slowing down%s.",
607                              Fast ? " a bit" : "");
608 #else
609                     You("%s\92x\82­\82È\82Á\82½\82æ\82¤\82È\8bC\82ª\82µ\82½\81D",
610                         Fast ? "\82¿\82å\82Á\82Æ" : "");
611 #endif
612                 break;
613             case CONFUSION:
614                 /* So make_confused works properly */
615                 set_itimeout(&HConfusion, 1L);
616                 make_confused(0L, TRUE);
617                 if (!Confusion)
618                     stop_occupation();
619                 break;
620             case STUNNED:
621                 set_itimeout(&HStun, 1L);
622                 make_stunned(0L, TRUE);
623                 if (!Stunned)
624                     stop_occupation();
625                 break;
626             case BLINDED:
627                 set_itimeout(&Blinded, 1L);
628                 make_blinded(0L, TRUE);
629                 if (!Blind)
630                     stop_occupation();
631                 break;
632             case DEAF:
633                 set_itimeout(&HDeaf, 1L);
634                 make_deaf(0L, TRUE);
635                 context.botl = TRUE;
636                 if (!Deaf)
637                     stop_occupation();
638                 break;
639             case INVIS:
640                 newsym(u.ux, u.uy);
641                 if (!Invis && !BInvis && !Blind) {
642 #if 0 /*JP*/
643                     You(!See_invisible
644                             ? "are no longer invisible."
645                             : "can no longer see through yourself.");
646 #else
647                     You(!See_invisible
648                         ? "\82à\82¤\93§\96¾\82Å\82Í\82È\82¢\81D"
649                         : "\93§\82«\82Æ\82¨\82Á\82Ä\82¢\82È\82¢\81D");
650 #endif
651                     stop_occupation();
652                 }
653                 break;
654             case SEE_INVIS:
655                 set_mimic_blocking(); /* do special mimic handling */
656                 see_monsters();       /* make invis mons appear */
657                 newsym(u.ux, u.uy);   /* make self appear */
658                 stop_occupation();
659                 break;
660             case WOUNDED_LEGS:
661                 heal_legs();
662                 stop_occupation();
663                 break;
664 #ifdef JPEXTENSION
665                 case TOTTER:
666                         Totter = 0;
667                         make_totter(0L, TRUE);
668                         stop_occupation();
669                         break;
670 #endif
671             case HALLUC:
672                 set_itimeout(&HHallucination, 1L);
673                 (void) make_hallucinated(0L, TRUE, 0L);
674                 if (!Hallucination)
675                     stop_occupation();
676                 break;
677             case SLEEPY:
678                 if (unconscious() || Sleep_resistance) {
679                     incr_itimeout(&HSleepy, rnd(100));
680                 } else if (Sleepy) {
681 /*JP
682                     You("fall asleep.");
683 */
684                     You("\96°\82è\82É\97\8e\82¿\82½\81D");
685                     sleeptime = rnd(20);
686                     fall_asleep(-sleeptime, TRUE);
687                     incr_itimeout(&HSleepy, sleeptime + rnd(100));
688                 }
689                 break;
690             case LEVITATION:
691                 (void) float_down(I_SPECIAL | TIMEOUT, 0L);
692                 break;
693             case PASSES_WALLS:
694                 if (!Passes_walls) {
695                     if (stuck_in_wall())
696 /*JP
697                         You_feel("hemmed in again.");
698 */
699                         You_feel("\82Ü\82½\95Â\82\8d\9e\82ß\82ç\82ê\82½\82æ\82¤\82¾\81D");
700                     else
701 #if 0 /*JP*/
702                         pline("You're back to your %s self again.",
703                               !Upolyd ? "normal" : "unusual");
704 #else
705                         pline("\82 \82È\82½\82Í\8dÄ\82Ñ\92Ê\8fí%s\82Ì\8fó\91Ô\82É\82È\82Á\82½\81D",
706                               !Upolyd ? "" : "\82Å\82Í\82È\82¢");
707 #endif
708                 }
709                 break;
710             case STRANGLED:
711                 killer.format = KILLED_BY;
712 #if 0 /*JP*/
713                 Strcpy(killer.name,
714                        (u.uburied) ? "suffocation" : "strangulation");
715 #else
716                 Strcpy(killer.name,
717                        (u.uburied) ? "\92\82\91§\82µ\82Ä" : "\8eñ\82ð\8di\82ß\82ç\82ê\82Ä");
718 #endif
719                 done(DIED);
720                 /* must be declining to die in explore|wizard mode;
721                    treat like being cured of strangulation by prayer */
722                 if (uamul && uamul->otyp == AMULET_OF_STRANGULATION) {
723 /*JP
724                     Your("amulet vanishes!");
725 */
726                     Your("\96\82\8f\9c\82¯\82Í\8fÁ\82¦\82½\81I");
727                     useup(uamul);
728                 }
729                 break;
730             case FUMBLING:
731                 /* call this only when a move took place.  */
732                 /* otherwise handle fumbling msgs locally. */
733                 if (u.umoved && !Levitation) {
734                     slip_or_trip();
735                     nomul(-2);
736 /*JP
737                     multi_reason = "fumbling";
738 */
739                     multi_reason = "\82Ö\82Ü\82ð\82µ\82½\8c\84\82É";
740                     nomovemsg = "";
741                     /* The more you are carrying the more likely you
742                      * are to make noise when you fumble.  Adjustments
743                      * to this number must be thoroughly play tested.
744                      */
745                     if ((inv_weight() > -500)) {
746 /*JP
747                         You("make a lot of noise!");
748 */
749                         You("\91å\82«\82È\89¹\82ð\82½\82Ä\82½\81I");
750                         wake_nearby();
751                     }
752                 }
753                 /* from outside means slippery ice; don't reset
754                    counter if that's the only fumble reason */
755                 HFumbling &= ~FROMOUTSIDE;
756                 if (Fumbling)
757                     incr_itimeout(&HFumbling, rnd(20));
758                 break;
759             case DETECT_MONSTERS:
760                 see_monsters();
761                 break;
762             }
763         }
764
765     run_timers();
766 }
767
768 void
769 fall_asleep(how_long, wakeup_msg)
770 int how_long;
771 boolean wakeup_msg;
772 {
773     stop_occupation();
774     nomul(how_long);
775 /*JP
776     multi_reason = "sleeping";
777 */
778     multi_reason = "\96°\82è\82Ì\82³\82È\82©\82É";
779     /* generally don't notice sounds while sleeping */
780     if (wakeup_msg && multi == how_long) {
781         /* caller can follow with a direct call to Hear_again() if
782            there's a need to override this when wakeup_msg is true */
783         incr_itimeout(&HDeaf, how_long);
784         context.botl = TRUE;
785         afternmv = Hear_again; /* this won't give any messages */
786     }
787     /* early wakeup from combat won't be possible until next monster turn */
788     u.usleep = monstermoves;
789 /*JP
790     nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again;
791 */
792     nomovemsg = wakeup_msg ? "\96Ú\82ð\8ao\82Ü\82µ\82½\81D" : You_can_move_again;
793 }
794
795 /* Attach an egg hatch timeout to the given egg.
796  *      when = Time to hatch, usually only passed if re-creating an
797  *             existing hatch timer. Pass 0L for random hatch time.
798  */
799 void
800 attach_egg_hatch_timeout(egg, when)
801 struct obj *egg;
802 long when;
803 {
804     int i;
805
806     /* stop previous timer, if any */
807     (void) stop_timer(HATCH_EGG, obj_to_any(egg));
808
809     /*
810      * Decide if and when to hatch the egg.  The old hatch_it() code tried
811      * once a turn from age 151 to 200 (inclusive), hatching if it rolled
812      * a number x, 1<=x<=age, where x>150.  This yields a chance of
813      * hatching > 99.9993%.  Mimic that here.
814      */
815     if (!when) {
816         for (i = (MAX_EGG_HATCH_TIME - 50) + 1; i <= MAX_EGG_HATCH_TIME; i++)
817             if (rnd(i) > 150) {
818                 /* egg will hatch */
819                 when = (long) i;
820                 break;
821             }
822     }
823     if (when) {
824         (void) start_timer(when, TIMER_OBJECT, HATCH_EGG, obj_to_any(egg));
825     }
826 }
827
828 /* prevent an egg from ever hatching */
829 void
830 kill_egg(egg)
831 struct obj *egg;
832 {
833     /* stop previous timer, if any */
834     (void) stop_timer(HATCH_EGG, obj_to_any(egg));
835 }
836
837 /* timer callback routine: hatch the given egg */
838 void
839 hatch_egg(arg, timeout)
840 anything *arg;
841 long timeout;
842 {
843     struct obj *egg;
844     struct monst *mon, *mon2;
845     coord cc;
846     xchar x, y;
847     boolean yours, silent, knows_egg = FALSE;
848     boolean cansee_hatchspot = FALSE;
849     int i, mnum, hatchcount = 0;
850
851     egg = arg->a_obj;
852     /* sterilized while waiting */
853     if (egg->corpsenm == NON_PM)
854         return;
855
856     mon = mon2 = (struct monst *) 0;
857     mnum = big_to_little(egg->corpsenm);
858     /* The identity of one's father is learned, not innate */
859     yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2)));
860     silent = (timeout != monstermoves); /* hatched while away */
861
862     /* only can hatch when in INVENT, FLOOR, MINVENT */
863     if (get_obj_location(egg, &x, &y, 0)) {
864         hatchcount = rnd((int) egg->quan);
865         cansee_hatchspot = cansee(x, y) && !silent;
866         if (!(mons[mnum].geno & G_UNIQ)
867             && !(mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) {
868             for (i = hatchcount; i > 0; i--) {
869                 if (!enexto(&cc, x, y, &mons[mnum])
870                     || !(mon = makemon(&mons[mnum], cc.x, cc.y, NO_MINVENT)))
871                     break;
872                 /* tame if your own egg hatches while you're on the
873                    same dungeon level, or any dragon egg which hatches
874                    while it's in your inventory */
875                 if ((yours && !silent)
876                     || (carried(egg) && mon->data->mlet == S_DRAGON)) {
877                     if (tamedog(mon, (struct obj *) 0)) {
878                         if (carried(egg) && mon->data->mlet != S_DRAGON)
879                             mon->mtame = 20;
880                     }
881                 }
882                 if (mvitals[mnum].mvflags & G_EXTINCT)
883                     break;  /* just made last one */
884                 mon2 = mon; /* in case makemon() fails on 2nd egg */
885             }
886             if (!mon)
887                 mon = mon2;
888             hatchcount -= i;
889             egg->quan -= (long) hatchcount;
890         }
891 #if 0
892     /*
893      * We could possibly hatch while migrating, but the code isn't
894      * set up for it...
895      */
896     } else if (obj->where == OBJ_MIGRATING) {
897         /*
898          * We can do several things.  The first ones that come to
899          * mind are:
900          * + Create the hatched monster then place it on the migrating
901          *   mons list.  This is tough because all makemon() is made
902          *   to place the monster as well.  Makemon() also doesn't lend
903          *   itself well to splitting off a "not yet placed" subroutine.
904          * + Mark the egg as hatched, then place the monster when we
905          *   place the migrating objects.
906          * + Or just kill any egg which gets sent to another level.
907          *   Falling is the usual reason such transportation occurs.
908          */
909         cansee_hatchspot = FALSE;
910         mon = ???;
911 #endif
912     }
913
914     if (mon) {
915         char monnambuf[BUFSZ], carriedby[BUFSZ];
916         boolean siblings = (hatchcount > 1), redraw = FALSE;
917
918         if (cansee_hatchspot) {
919             /* [bug?  m_monnam() yields accurate monster type
920                regardless of hallucination] */
921 #if 0 /*JP*/
922             Sprintf(monnambuf, "%s%s", siblings ? "some " : "",
923                     siblings ? makeplural(m_monnam(mon)) : an(m_monnam(mon)));
924 #else
925             Sprintf(monnambuf, "%s%s", siblings ? "\82¢\82­\82Â\82©\82Ì" : "",
926                     a_monnam(mon));
927 #endif
928             /* we don't learn the egg type here because learning
929                an egg type requires either seeing the egg hatch
930                or being familiar with the egg already,
931                as well as being able to see the resulting
932                monster, checked below
933             */
934         }
935         switch (egg->where) {
936         case OBJ_INVENT:
937             knows_egg = TRUE; /* true even if you are blind */
938             if (!cansee_hatchspot)
939 #if 0 /*JP*/
940                 You_feel("%s %s from your pack!", something,
941                          locomotion(mon->data, "drop"));
942 #else
943                 pline("\89½\82©\82ª\82 \82È\82½\82Ì\94w\95\89\82¢\91Ü\82©\82ç%s\82æ\82¤\82È\8bC\82ª\82µ\82½\81D",
944                       jpast(locomotion(mon->data, "\97\8e\82¿\82é")));
945 #endif
946             else
947 #if 0 /*JP*/
948                 You_see("%s %s out of your pack!", monnambuf,
949                         locomotion(mon->data, "drop"));
950 #else
951                 You("%s\82ª\82 \82È\82½\82Ì\94w\95\89\82¢\91Ü\82©\82ç%s\82Ì\82ð\8c©\82½\81D", monnambuf,
952                     jpast(locomotion(mon->data, "\97\8e\82¿\82é")));
953 #endif
954             if (yours) {
955 #if 0 /*JP*/
956                 pline("%s cries sound like \"%s%s\"",
957                       siblings ? "Their" : "Its",
958                       flags.female ? "mommy" : "daddy", egg->spe ? "." : "?");
959 #else
960                 pline("\82»\82ê\82Í\81w%s%s\81x\82Æ\96Â\82¢\82Ä\82¢\82é\82æ\82¤\82¾\81D",
961                       flags.female ? "\83}\83}" : "\83p\83p", egg->spe ? "" : "\81H");
962 #endif
963             } else if (mon->data->mlet == S_DRAGON && !Deaf) {
964 #if 0 /*JP*/
965                 verbalize("Gleep!"); /* Mything eggs :-) */
966 #else
967                 verbalize("\83u\83H\81[\81I"); /* Mything eggs :-) */
968 #endif
969             }
970             break;
971
972         case OBJ_FLOOR:
973             if (cansee_hatchspot) {
974                 knows_egg = TRUE;
975 /*JP
976                 You_see("%s hatch.", monnambuf);
977 */
978                 You("%s\82ª\97\91\82©\82ç\82©\82¦\82é\82Ì\82ð\8c©\82½\81D", monnambuf);
979                 redraw = TRUE; /* update egg's map location */
980             }
981             break;
982
983         case OBJ_MINVENT:
984             if (cansee_hatchspot) {
985                 /* egg carrying monster might be invisible */
986                 mon2 = egg->ocarry;
987                 if (canseemon(mon2)
988                     && (!mon2->wormno || cansee(mon2->mx, mon2->my))) {
989 #if 0 /*JP:T*/
990                     Sprintf(carriedby, "%s pack",
991                             s_suffix(a_monnam(mon2)));
992 #else
993                     Sprintf(carriedby, "%s\82Ì\94w\95\89\82¢\91Ü\82©\82ç",
994                             a_monnam(mon2));
995 #endif
996                     knows_egg = TRUE;
997                 } else if (is_pool(mon->mx, mon->my)) {
998 /*JP
999                     Strcpy(carriedby, "empty water");
1000 */
1001                     Strcpy(carriedby, "\89½\82à\82È\82¢\90\85\92\86\82©\82ç");
1002                 } else {
1003 /*JP
1004                     Strcpy(carriedby, "thin air");
1005 */
1006                     Strcpy(carriedby, "\89½\82à\82È\82¢\8bó\8aÔ\82©\82ç");
1007                 }
1008 #if 0 /*JP:T*/
1009                 You_see("%s %s out of %s!", monnambuf,
1010                         locomotion(mon->data, "drop"), carriedby);
1011 #else
1012                 You("%s\82ª%s%s\82Ì\82ð\8c©\82½\81D", monnambuf, carriedby, 
1013                     locomotion(mon->data, "\97\8e\82¿\82é"));
1014 #endif
1015             }
1016             break;
1017 #if 0
1018         case OBJ_MIGRATING:
1019             break;
1020 #endif
1021         default:
1022             impossible("egg hatched where? (%d)", (int) egg->where);
1023             break;
1024         }
1025
1026         if (cansee_hatchspot && knows_egg)
1027             learn_egg_type(mnum);
1028
1029         if (egg->quan > 0) {
1030             /* still some eggs left */
1031             /* Instead of ordinary egg timeout use a short one */
1032             attach_egg_hatch_timeout(egg, (long) rnd(12));
1033         } else if (carried(egg)) {
1034             useup(egg);
1035         } else {
1036             /* free egg here because we use it above */
1037             obj_extract_self(egg);
1038             obfree(egg, (struct obj *) 0);
1039         }
1040         if (redraw)
1041             newsym(x, y);
1042     }
1043 }
1044
1045 /* Learn to recognize eggs of the given type. */
1046 void
1047 learn_egg_type(mnum)
1048 int mnum;
1049 {
1050     /* baby monsters hatch from grown-up eggs */
1051     mnum = little_to_big(mnum);
1052     mvitals[mnum].mvflags |= MV_KNOWS_EGG;
1053     /* we might have just learned about other eggs being carried */
1054     update_inventory();
1055 }
1056
1057 /* Attach a fig_transform timeout to the given figurine. */
1058 void
1059 attach_fig_transform_timeout(figurine)
1060 struct obj *figurine;
1061 {
1062     int i;
1063
1064     /* stop previous timer, if any */
1065     (void) stop_timer(FIG_TRANSFORM, obj_to_any(figurine));
1066
1067     /*
1068      * Decide when to transform the figurine.
1069      */
1070     i = rnd(9000) + 200;
1071     /* figurine will transform */
1072     (void) start_timer((long) i, TIMER_OBJECT, FIG_TRANSFORM,
1073                        obj_to_any(figurine));
1074 }
1075
1076 /* give a fumble message */
1077 STATIC_OVL void
1078 slip_or_trip()
1079 {
1080     struct obj *otmp = vobj_at(u.ux, u.uy), *otmp2;
1081     const char *what;
1082     char buf[BUFSZ];
1083     boolean on_foot = TRUE;
1084     if (u.usteed)
1085         on_foot = FALSE;
1086
1087     if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy))
1088         otmp = 0;
1089
1090     if (otmp && on_foot) { /* trip over something in particular */
1091         /*
1092           If there is only one item, it will have just been named
1093           during the move, so refer to by via pronoun; otherwise,
1094           if the top item has been or can be seen, refer to it by
1095           name; if not, look for rocks to trip over; trip over
1096           anonymous "something" if there aren't any rocks.
1097         */
1098 #if 0 /*JP*/
1099         what = (iflags.last_msg == PLNMSG_ONE_ITEM_HERE)
1100                 ? ((otmp->quan == 1L) ? "it"
1101                       : Hallucination ? "they" : "them")
1102                 : (otmp->dknown || !Blind)
1103                       ? doname(otmp)
1104                       : ((otmp2 = sobj_at(ROCK, u.ux, u.uy)) == 0
1105                              ? something
1106                              : (otmp2->quan == 1L ? "a rock" : "some rocks"));
1107 #else
1108         what = (iflags.last_msg == PLNMSG_ONE_ITEM_HERE)
1109                 ? "\82»\82ê"
1110                 : (otmp->dknown || !Blind)
1111                       ? doname(otmp)
1112                       : ((otmp2 = sobj_at(ROCK, u.ux, u.uy)) == 0
1113                              ? something
1114                              : "\90Î");
1115 #endif
1116         if (Hallucination) {
1117             what = strcpy(buf, what);
1118             buf[0] = highc(buf[0]);
1119 #if 0 /*JP*/
1120             pline("Egads!  %s bite%s your %s!", what,
1121                   (!otmp || otmp->quan == 1L) ? "s" : "", body_part(FOOT));
1122 #else
1123             pline("\82®\82í\82\9f\81I%s\82ª%s\82É\8a\9a\82Ý\82Â\82¢\82½\81I", what, body_part(FOOT));
1124 #endif
1125         } else {
1126 /*JP
1127             You("trip over %s.", what);
1128 */
1129             You("%s\82É\82Â\82Ü\82Ã\82¢\82½\81D", what);
1130         }
1131         if (!uarmf && otmp->otyp == CORPSE
1132             && touch_petrifies(&mons[otmp->corpsenm]) && !Stone_resistance) {
1133 #if 0 /*JP*/
1134             Sprintf(killer.name, "tripping over %s corpse",
1135                     an(mons[otmp->corpsenm].mname));
1136 #else
1137             Sprintf(killer.name, "%s\82Ì\8e\80\91Ì\82É\82Â\82Ü\82Ã\82¢\82Ä",
1138                     mons[otmp->corpsenm].mname);
1139 #endif
1140             instapetrify(killer.name);
1141         }
1142     } else if (rn2(3) && is_ice(u.ux, u.uy)) {
1143 #if 0 /*JP*/
1144         pline("%s %s%s on the ice.",
1145               u.usteed ? upstart(x_monnam(u.usteed,
1146                                           (has_mname(u.usteed)) ? ARTICLE_NONE
1147                                                                 : ARTICLE_THE,
1148                                           (char *) 0, SUPPRESS_SADDLE, FALSE))
1149                        : "You",
1150               rn2(2) ? "slip" : "slide", on_foot ? "" : "s");
1151 #else
1152         pline("%s\82Í\95X\82Ì\8fã\82Å\8a\8a\82Á\82½\81D",
1153               u.usteed ? upstart(x_monnam(u.usteed,
1154                                           (has_mname(u.usteed)) ? ARTICLE_NONE
1155                                                                 : ARTICLE_THE,
1156                                           (char *) 0, SUPPRESS_SADDLE, FALSE))
1157                        : "\82 \82È\82½");
1158 #endif
1159     } else {
1160         if (on_foot) {
1161             switch (rn2(4)) {
1162             case 1:
1163 #if 0 /*JP*/
1164                 You("trip over your own %s.",
1165                     Hallucination ? "elbow" : makeplural(body_part(FOOT)));
1166 #else
1167                 You("\8e©\95ª\82Ì%s\82ð\93¥\82ñ\82Å\82Â\82Ü\82Ã\82¢\82½\81D",
1168                     Hallucination ? "\95I" : body_part(FOOT));
1169 #endif
1170                 break;
1171             case 2:
1172 #if 0 /*JP*/
1173                 You("slip %s.",
1174                     Hallucination ? "on a banana peel" : "and nearly fall");
1175 #else
1176                 You("%s\81D", Hallucination ?
1177                     "\83o\83i\83i\82Ì\94ç\82Å\8a\8a\82Á\82½" : "\8a\8a\82Á\82Ä\93]\82Ñ\82»\82¤\82É\82È\82Á\82½");
1178 #endif
1179                 break;
1180             case 3:
1181 /*JP
1182                 You("flounder.");
1183 */
1184                 You("\82\82½\82Î\82½\82µ\82½\81D");
1185                 break;
1186             default:
1187 /*JP
1188                 You("stumble.");
1189 */
1190                 You("\82æ\82ë\82ß\82¢\82½\81D");
1191                 break;
1192             }
1193         } else {
1194             switch (rn2(4)) {
1195             case 1:
1196 #if 0 /*JP*/
1197                 Your("%s slip out of the stirrups.",
1198                      makeplural(body_part(FOOT)));
1199 #else
1200                 You("\82 \82Ô\82Ý\82ð\93¥\82Ý\8aO\82µ\82Ä\82µ\82Ü\82Á\82½\81D");
1201 #endif
1202                 break;
1203             case 2:
1204 /*JP
1205                 You("let go of the reins.");
1206 */
1207                 You("\8eè\8dj\82ð\95ú\82µ\82Ä\82µ\82Ü\82Á\82½\81D");
1208                 break;
1209             case 3:
1210 /*JP
1211                 You("bang into the saddle-horn.");
1212 */
1213                 You("\83T\83h\83\8b\83z\81[\83\93\82É\82Ô\82Â\82©\82Á\82Ä\82µ\82Ü\82Á\82½\81D");
1214                 break;
1215             default:
1216 /*JP
1217                 You("slide to one side of the saddle.");
1218 */
1219                 You("\82·\82×\82Á\82Ä\88Æ\82Ì\95Ð\91¤\82É\82¸\82ê\82Ä\82µ\82Ü\82Á\82½\81D");
1220                 break;
1221             }
1222             dismount_steed(DISMOUNT_FELL);
1223         }
1224     }
1225 }
1226
1227 /* Print a lamp flicker message with tailer. */
1228 STATIC_OVL void
1229 see_lamp_flicker(obj, tailer)
1230 struct obj *obj;
1231 const char *tailer;
1232 {
1233     switch (obj->where) {
1234     case OBJ_INVENT:
1235     case OBJ_MINVENT:
1236 /*JP
1237         pline("%s flickers%s.", Yname2(obj), tailer);
1238 */
1239         pline("%s\82Í%s\93_\96Å\82µ\82½\81D", Yname2(obj), tailer);
1240         break;
1241     case OBJ_FLOOR:
1242 /*JP
1243         You_see("%s flicker%s.", an(xname(obj)), tailer);
1244 */
1245         You("%s\82ª%s\93_\96Å\82·\82é\82Ì\82ð\8c©\82½\81D", an(xname(obj)), tailer);
1246         break;
1247     }
1248 }
1249
1250 /* Print a dimming message for brass lanterns. */
1251 STATIC_OVL void
1252 lantern_message(obj)
1253 struct obj *obj;
1254 {
1255     /* from adventure */
1256     switch (obj->where) {
1257     case OBJ_INVENT:
1258 /*JP
1259         Your("lantern is getting dim.");
1260 */
1261         Your("\83\89\83\93\83^\83\93\82Í\88Ã\82­\82È\82Á\82Ä\82«\82½\81D");
1262         if (Hallucination)
1263 /*JP
1264             pline("Batteries have not been invented yet.");
1265 */
1266             pline("\93d\92r\82Í\82Ü\82¾\94­\96¾\82³\82ê\82Ä\82È\82¢\82ñ\82¾\82Á\82¯\81D");
1267         break;
1268     case OBJ_FLOOR:
1269 /*JP
1270         You_see("a lantern getting dim.");
1271 */
1272         pline("\83\89\83\93\83^\83\93\82ª\88Ã\82­\82È\82Á\82Ä\82«\82½\82Ì\82ª\8c©\82¦\82½\81D");
1273         break;
1274     case OBJ_MINVENT:
1275 /*JP
1276         pline("%s lantern is getting dim.", s_suffix(Monnam(obj->ocarry)));
1277 */
1278         pline("%s\82Ì\83\89\83\93\83^\83\93\82Í\88Ã\82­\82È\82Á\82Ä\82«\82½\81D", Monnam(obj->ocarry));
1279         break;
1280     }
1281 }
1282
1283 /*
1284  * Timeout callback for for objects that are burning. E.g. lamps, candles.
1285  * See begin_burn() for meanings of obj->age and obj->spe.
1286  */
1287 void
1288 burn_object(arg, timeout)
1289 anything *arg;
1290 long timeout;
1291 {
1292     struct obj *obj = arg->a_obj;
1293     boolean canseeit, many, menorah, need_newsym, need_invupdate;
1294     xchar x, y;
1295     char whose[BUFSZ];
1296
1297     menorah = obj->otyp == CANDELABRUM_OF_INVOCATION;
1298     many = menorah ? obj->spe > 1 : obj->quan > 1L;
1299
1300     /* timeout while away */
1301     if (timeout != monstermoves) {
1302         long how_long = monstermoves - timeout;
1303
1304         if (how_long >= obj->age) {
1305             obj->age = 0;
1306             end_burn(obj, FALSE);
1307
1308             if (menorah) {
1309                 obj->spe = 0; /* no more candles */
1310                 obj->owt = weight(obj);
1311             } else if (Is_candle(obj) || obj->otyp == POT_OIL) {
1312                 /* get rid of candles and burning oil potions;
1313                    we know this object isn't carried by hero,
1314                    nor is it migrating */
1315                 obj_extract_self(obj);
1316                 obfree(obj, (struct obj *) 0);
1317                 obj = (struct obj *) 0;
1318             }
1319
1320         } else {
1321             obj->age -= how_long;
1322             begin_burn(obj, TRUE);
1323         }
1324         return;
1325     }
1326
1327     /* only interested in INVENT, FLOOR, and MINVENT */
1328     if (get_obj_location(obj, &x, &y, 0)) {
1329         canseeit = !Blind && cansee(x, y);
1330         /* set `whose[]' to be "Your " or "Fred's " or "The goblin's " */
1331         (void) Shk_Your(whose, obj);
1332     } else {
1333         canseeit = FALSE;
1334     }
1335     need_newsym = need_invupdate = FALSE;
1336
1337     /* obj->age is the age remaining at this point.  */
1338     switch (obj->otyp) {
1339     case POT_OIL:
1340         /* this should only be called when we run out */
1341         if (canseeit) {
1342             switch (obj->where) {
1343             case OBJ_INVENT:
1344                 need_invupdate = TRUE;
1345                 /*FALLTHRU*/
1346             case OBJ_MINVENT:
1347 /*JP
1348                 pline("%spotion of oil has burnt away.", whose);
1349 */
1350                 pline("%s\83I\83C\83\8b\82Í\94R\82¦\82Â\82«\82½\81D", whose);
1351                 break;
1352             case OBJ_FLOOR:
1353 /*JP
1354                 You_see("a burning potion of oil go out.");
1355 */
1356                 You("\83I\83C\83\8b\82Ì\89Î\82ª\8fÁ\82¦\82½\82Ì\82ð\8c©\82½\81D");
1357                 need_newsym = TRUE;
1358                 break;
1359             }
1360         }
1361         end_burn(obj, FALSE); /* turn off light source */
1362         if (carried(obj)) {
1363             useupall(obj);
1364         } else {
1365             /* clear migrating obj's destination code before obfree
1366                to avoid false complaint of deleting worn item */
1367             if (obj->where == OBJ_MIGRATING)
1368                 obj->owornmask = 0L;
1369             obj_extract_self(obj);
1370             obfree(obj, (struct obj *) 0);
1371         }
1372         obj = (struct obj *) 0;
1373         break;
1374
1375     case BRASS_LANTERN:
1376     case OIL_LAMP:
1377         switch ((int) obj->age) {
1378         case 150:
1379         case 100:
1380         case 50:
1381             if (canseeit) {
1382                 if (obj->otyp == BRASS_LANTERN)
1383                     lantern_message(obj);
1384                 else
1385                     see_lamp_flicker(obj,
1386 /*JP
1387                                      obj->age == 50L ? " considerably" : "");
1388 */
1389                                      obj->age == 50L ? "\8c\83\82µ\82­" : "");
1390             }
1391             break;
1392
1393         case 25:
1394             if (canseeit) {
1395                 if (obj->otyp == BRASS_LANTERN)
1396                     lantern_message(obj);
1397                 else {
1398                     switch (obj->where) {
1399                     case OBJ_INVENT:
1400                     case OBJ_MINVENT:
1401 /*JP
1402                         pline("%s seems about to go out.", Yname2(obj));
1403 */
1404                         pline("%s\82Í\8d¡\82É\82à\8fÁ\82¦\82»\82¤\82¾\81D", Yname2(obj));
1405                         break;
1406                     case OBJ_FLOOR:
1407 /*JP
1408                         You_see("%s about to go out.", an(xname(obj)));
1409 */
1410                         pline("%s\82ª\8fÁ\82¦\82©\82¯\82Ä\82¢\82é\82Ì\82ª\8c©\82¦\82½\81D", an(xname(obj)));
1411                         break;
1412                     }
1413                 }
1414             }
1415             break;
1416
1417         case 0:
1418             /* even if blind you'll know if holding it */
1419             if (canseeit || obj->where == OBJ_INVENT) {
1420                 switch (obj->where) {
1421                 case OBJ_INVENT:
1422                     need_invupdate = TRUE;
1423                     /*FALLTHRU*/
1424                 case OBJ_MINVENT:
1425                     if (obj->otyp == BRASS_LANTERN)
1426 /*JP
1427                         pline("%slantern has run out of power.", whose);
1428 */
1429                         pline("%s\83\89\83\93\83^\83\93\82Ì\97Í\82ð\8eg\82¢\90Ø\82Á\82½\81D", whose);
1430                     else
1431 /*JP
1432                         pline("%s has gone out.", Yname2(obj));
1433 */
1434                         pline("%s\82Í\8fÁ\82¦\82½\81D", Yname2(obj));
1435                     break;
1436                 case OBJ_FLOOR:
1437                     if (obj->otyp == BRASS_LANTERN)
1438 /*JP
1439                         You_see("a lantern run out of power.");
1440 */
1441                         You("\83\89\83\93\83^\83\93\82ª\8fÁ\82¦\82é\82Ì\82ð\8c©\82½\81D");
1442                     else
1443 /*JP
1444                         You_see("%s go out.", an(xname(obj)));
1445 */
1446                         You("%s\82ª\8fÁ\82¦\82é\82Ì\82ð\8c©\82½\81D", an(xname(obj)));
1447                     break;
1448                 }
1449             }
1450             end_burn(obj, FALSE);
1451             break;
1452
1453         default:
1454             /*
1455              * Someone added fuel to the lamp while it was
1456              * lit.  Just fall through and let begin burn
1457              * handle the new age.
1458              */
1459             break;
1460         }
1461
1462         if (obj->age)
1463             begin_burn(obj, TRUE);
1464
1465         break;
1466
1467     case CANDELABRUM_OF_INVOCATION:
1468     case TALLOW_CANDLE:
1469     case WAX_CANDLE:
1470         switch (obj->age) {
1471         case 75:
1472             if (canseeit)
1473                 switch (obj->where) {
1474                 case OBJ_INVENT:
1475                 case OBJ_MINVENT:
1476 #if 0 /*JP*/
1477                     pline("%s%scandle%s getting short.", whose,
1478                           menorah ? "candelabrum's " : "",
1479                           many ? "s are" : " is");
1480 #else
1481                     pline("%s%s\82ë\82¤\82»\82­\82Í\92Z\82­\82È\82Á\82½\81D", whose,
1482                           menorah ? "\90C\91ä\82Ì" : "");
1483 #endif
1484                     break;
1485                 case OBJ_FLOOR:
1486 #if 0 /*JP*/
1487                     You_see("%scandle%s getting short.",
1488                             menorah ? "a candelabrum's " : many ? "some "
1489                                                                 : "a ",
1490                             many ? "s" : "");
1491 #else
1492                     You("%s\82ë\82¤\82»\82­\82ª\92Z\82­\82È\82é\82Ì\82ð\8c©\82½\81D",
1493                         menorah ? "\90C\91ä\82Ì" : "");
1494 #endif
1495                     break;
1496                 }
1497             break;
1498
1499         case 15:
1500             if (canseeit)
1501                 switch (obj->where) {
1502                 case OBJ_INVENT:
1503                 case OBJ_MINVENT:
1504 #if 0 /*JP*/
1505                     pline("%s%scandle%s flame%s flicker%s low!", whose,
1506                           menorah ? "candelabrum's " : "", many ? "s'" : "'s",
1507                           many ? "s" : "", many ? "" : "s");
1508 #else
1509                     pline("%s%s\82ë\82¤\82»\82­\82Ì\89\8a\82Í\93_\96Å\82µ\81C\88Ã\82­\82È\82Á\82½\81I", whose,
1510                           menorah ? "\90C\91ä\82Ì" : "");
1511 #endif
1512                     break;
1513                 case OBJ_FLOOR:
1514 #if 0 /*JP*/
1515                     You_see("%scandle%s flame%s flicker low!",
1516                             menorah ? "a candelabrum's " : many ? "some "
1517                                                                 : "a ",
1518                             many ? "s'" : "'s", many ? "s" : "");
1519 #else
1520                     You("%s\82ë\82¤\82»\82­\82Ì\89\8a\82ª\93_\96Å\82µ\81C\88Ã\82­\82È\82é\82Ì\82ð\8c©\82½\81I",
1521                         menorah ? "\90C\91ä\82Ì" : "");
1522 #endif
1523                     break;
1524                 }
1525             break;
1526
1527         case 0:
1528             /* we know even if blind and in our inventory */
1529             if (canseeit || obj->where == OBJ_INVENT) {
1530                 if (menorah) {
1531                     switch (obj->where) {
1532                     case OBJ_INVENT:
1533                         need_invupdate = TRUE;
1534                         /*FALLTHRU*/
1535                     case OBJ_MINVENT:
1536 #if 0 /*JP:T*/
1537                         pline("%scandelabrum's flame%s.", whose,
1538                               many ? "s die" : " dies");
1539 #else
1540                         pline("%s\90C\91ä\82Ì\89\8a\82Í\8fÁ\82¦\82½\81D", whose);
1541 #endif
1542                         break;
1543                     case OBJ_FLOOR:
1544 #if 0 /*JP*/
1545                         You_see("a candelabrum's flame%s die.",
1546                                 many ? "s" : "");
1547 #else
1548                         You("\90C\91ä\82Ì\89\8a\82ª\8fÁ\82¦\82é\82Ì\82ð\8c©\82½\81D");
1549 #endif
1550                         break;
1551                     }
1552                 } else {
1553                     switch (obj->where) {
1554                     case OBJ_INVENT:
1555                         /* no need_invupdate for update_inventory() necessary;
1556                            useupall() -> freeinv() handles it */
1557                         /*FALLTHRU*/
1558                     case OBJ_MINVENT:
1559 #if 0 /*JP*/
1560                         pline("%s %s consumed!", Yname2(obj),
1561                               many ? "are" : "is");
1562 #else
1563                         pline("%s\82Í\94R\82¦\82Â\82«\82½\81I", Yname2(obj));
1564 #endif
1565                         break;
1566                     case OBJ_FLOOR:
1567                         /*
1568                           You see some wax candles consumed!
1569                           You see a wax candle consumed!
1570                          */
1571 #if 0 /*JP*/
1572                         You_see("%s%s consumed!", many ? "some " : "",
1573                                 many ? xname(obj) : an(xname(obj)));
1574 #else
1575                         You("%s\82ª\94R\82¦\82Â\82«\82é\82Ì\82ð\8c©\82½\81I", xname(obj));
1576 #endif
1577                         need_newsym = TRUE;
1578                         break;
1579                     }
1580
1581                     /* post message */
1582 #if 0 /*JP*/
1583                     pline(Hallucination
1584                               ? (many ? "They shriek!" : "It shrieks!")
1585                               : Blind ? "" : (many ? "Their flames die."
1586                                                    : "Its flame dies."));
1587 #else
1588                     pline(Hallucination ? "\82»\82ê\82Í\8bà\90Ø\82è\90º\82ð\82 \82°\82½\81I"
1589                               : Blind ? "" 
1590                               : "\89\8a\82Í\8fÁ\82¦\82½\81D");
1591 #endif
1592                 }
1593             }
1594             end_burn(obj, FALSE);
1595
1596             if (menorah) {
1597                 obj->spe = 0;
1598                 obj->owt = weight(obj);
1599             } else {
1600                 if (carried(obj)) {
1601                     useupall(obj);
1602                 } else {
1603                     /* clear migrating obj's destination code
1604                        so obfree won't think this item is worn */
1605                     if (obj->where == OBJ_MIGRATING)
1606                         obj->owornmask = 0L;
1607                     obj_extract_self(obj);
1608                     obfree(obj, (struct obj *) 0);
1609                 }
1610                 obj = (struct obj *) 0;
1611             }
1612             break; /* case [age ==] 0 */
1613
1614         default:
1615             /*
1616              * Someone added fuel (candles) to the menorah while
1617              * it was lit.  Just fall through and let begin burn
1618              * handle the new age.
1619              */
1620             break;
1621         }
1622
1623         if (obj && obj->age)
1624             begin_burn(obj, TRUE);
1625         break; /* case [otyp ==] candelabrum|tallow_candle|wax_candle */
1626
1627     default:
1628         impossible("burn_object: unexpeced obj %s", xname(obj));
1629         break;
1630     }
1631     if (need_newsym)
1632         newsym(x, y);
1633     if (need_invupdate)
1634         update_inventory();
1635 }
1636
1637 /*
1638  * Start a burn timeout on the given object. If not "already lit" then
1639  * create a light source for the vision system.  There had better not
1640  * be a burn already running on the object.
1641  *
1642  * Magic lamps stay lit as long as there's a genie inside, so don't start
1643  * a timer.
1644  *
1645  * Burn rules:
1646  *      potions of oil, lamps & candles:
1647  *              age = # of turns of fuel left
1648  *              spe = <unused>
1649  *      magic lamps:
1650  *              age = <unused>
1651  *              spe = 0 not lightable, 1 lightable forever
1652  *      candelabrum:
1653  *              age = # of turns of fuel left
1654  *              spe = # of candles
1655  *
1656  * Once the burn begins, the age will be set to the amount of fuel
1657  * remaining _once_the_burn_finishes_.  If the burn is terminated
1658  * early then fuel is added back.
1659  *
1660  * This use of age differs from the use of age for corpses and eggs.
1661  * For the latter items, age is when the object was created, so we
1662  * know when it becomes "bad".
1663  *
1664  * This is a "silent" routine - it should not print anything out.
1665  */
1666 void
1667 begin_burn(obj, already_lit)
1668 struct obj *obj;
1669 boolean already_lit;
1670 {
1671     int radius = 3;
1672     long turns = 0;
1673     boolean do_timer = TRUE;
1674
1675     if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj))
1676         return;
1677
1678     switch (obj->otyp) {
1679     case MAGIC_LAMP:
1680         obj->lamplit = 1;
1681         do_timer = FALSE;
1682         break;
1683
1684     case POT_OIL:
1685         turns = obj->age;
1686         radius = 1; /* very dim light */
1687         break;
1688
1689     case BRASS_LANTERN:
1690     case OIL_LAMP:
1691         /* magic times are 150, 100, 50, 25, and 0 */
1692         if (obj->age > 150L)
1693             turns = obj->age - 150L;
1694         else if (obj->age > 100L)
1695             turns = obj->age - 100L;
1696         else if (obj->age > 50L)
1697             turns = obj->age - 50L;
1698         else if (obj->age > 25L)
1699             turns = obj->age - 25L;
1700         else
1701             turns = obj->age;
1702         break;
1703
1704     case CANDELABRUM_OF_INVOCATION:
1705     case TALLOW_CANDLE:
1706     case WAX_CANDLE:
1707         /* magic times are 75, 15, and 0 */
1708         if (obj->age > 75L)
1709             turns = obj->age - 75L;
1710         else if (obj->age > 15L)
1711             turns = obj->age - 15L;
1712         else
1713             turns = obj->age;
1714         radius = candle_light_range(obj);
1715         break;
1716
1717     default:
1718         /* [ALI] Support artifact light sources */
1719         if (artifact_light(obj)) {
1720             obj->lamplit = 1;
1721             do_timer = FALSE;
1722             radius = arti_light_radius(obj);
1723         } else {
1724             impossible("begin burn: unexpected %s", xname(obj));
1725             turns = obj->age;
1726         }
1727         break;
1728     }
1729
1730     if (do_timer) {
1731         if (start_timer(turns, TIMER_OBJECT, BURN_OBJECT, obj_to_any(obj))) {
1732             obj->lamplit = 1;
1733             obj->age -= turns;
1734             if (carried(obj) && !already_lit)
1735                 update_inventory();
1736         } else {
1737             obj->lamplit = 0;
1738         }
1739     } else {
1740         if (carried(obj) && !already_lit)
1741             update_inventory();
1742     }
1743
1744     if (obj->lamplit && !already_lit) {
1745         xchar x, y;
1746
1747         if (get_obj_location(obj, &x, &y, CONTAINED_TOO | BURIED_TOO))
1748             new_light_source(x, y, radius, LS_OBJECT, obj_to_any(obj));
1749         else
1750             impossible("begin_burn: can't get obj position");
1751     }
1752 }
1753
1754 /*
1755  * Stop a burn timeout on the given object if timer attached.  Darken
1756  * light source.
1757  */
1758 void
1759 end_burn(obj, timer_attached)
1760 struct obj *obj;
1761 boolean timer_attached;
1762 {
1763     if (!obj->lamplit) {
1764         impossible("end_burn: obj %s not lit", xname(obj));
1765         return;
1766     }
1767
1768     if (obj->otyp == MAGIC_LAMP || artifact_light(obj))
1769         timer_attached = FALSE;
1770
1771     if (!timer_attached) {
1772         /* [DS] Cleanup explicitly, since timer cleanup won't happen */
1773         del_light_source(LS_OBJECT, obj_to_any(obj));
1774         obj->lamplit = 0;
1775         if (obj->where == OBJ_INVENT)
1776             update_inventory();
1777     } else if (!stop_timer(BURN_OBJECT, obj_to_any(obj)))
1778         impossible("end_burn: obj %s not timed!", xname(obj));
1779 }
1780
1781 /*
1782  * Cleanup a burning object if timer stopped.
1783  */
1784 static void
1785 cleanup_burn(arg, expire_time)
1786 anything *arg;
1787 long expire_time;
1788 {
1789     struct obj *obj = arg->a_obj;
1790     if (!obj->lamplit) {
1791         impossible("cleanup_burn: obj %s not lit", xname(obj));
1792         return;
1793     }
1794
1795     del_light_source(LS_OBJECT, obj_to_any(obj));
1796
1797     /* restore unused time */
1798     obj->age += expire_time - monstermoves;
1799
1800     obj->lamplit = 0;
1801
1802     if (obj->where == OBJ_INVENT)
1803         update_inventory();
1804 }
1805
1806 void
1807 do_storms()
1808 {
1809     int nstrike;
1810     register int x, y;
1811     int dirx, diry;
1812     int count;
1813
1814     /* no lightning if not the air level or too often, even then */
1815     if (!Is_airlevel(&u.uz) || rn2(8))
1816         return;
1817
1818     /* the number of strikes is 8-log2(nstrike) */
1819     for (nstrike = rnd(64); nstrike <= 64; nstrike *= 2) {
1820         count = 0;
1821         do {
1822             x = rnd(COLNO - 1);
1823             y = rn2(ROWNO);
1824         } while (++count < 100 && levl[x][y].typ != CLOUD);
1825
1826         if (count < 100) {
1827             dirx = rn2(3) - 1;
1828             diry = rn2(3) - 1;
1829             if (dirx != 0 || diry != 0)
1830                 buzz(-15, /* "monster" LIGHTNING spell */
1831                      8, x, y, dirx, diry);
1832         }
1833     }
1834
1835     if (levl[u.ux][u.uy].typ == CLOUD) {
1836         /* Inside a cloud during a thunder storm is deafening. */
1837         /* Even if already deaf, we sense the thunder's vibrations. */
1838 /*JP
1839         pline("Kaboom!!!  Boom!!  Boom!!");
1840 */
1841         pline("\83s\83J\83b\81I\81I\83S\83\8d\83S\83\8d\83S\83\8d\83S\83\8d\81I\81I\83h\81[\83\93\81I");
1842         incr_itimeout(&HDeaf, rn1(20, 30));
1843         context.botl = TRUE;
1844         if (!u.uinvulnerable) {
1845             stop_occupation();
1846             nomul(-3);
1847 /*JP
1848             multi_reason = "hiding from thunderstorm";
1849 */
1850             multi_reason = "\97\8b\82Ì\97\92\82ð\94ð\82¯\82Ä\82¢\82é\8e\9e\82É";
1851             nomovemsg = 0;
1852         }
1853     } else
1854 /*JP
1855         You_hear("a rumbling noise.");
1856 */
1857         You_hear("\97\8b\82Ì\89¹\82ð\95·\82¢\82½\81D");
1858 }
1859
1860 /* -------------------------------------------------------------------------
1861  */
1862 /*
1863  * Generic Timeout Functions.
1864  *
1865  * Interface:
1866  *
1867  * General:
1868  *  boolean start_timer(long timeout,short kind,short func_index,
1869  *                      anything *arg)
1870  *      Start a timer of kind 'kind' that will expire at time
1871  *      monstermoves+'timeout'.  Call the function at 'func_index'
1872  *      in the timeout table using argument 'arg'.  Return TRUE if
1873  *      a timer was started.  This places the timer on a list ordered
1874  *      "sooner" to "later".  If an object, increment the object's
1875  *      timer count.
1876  *
1877  *  long stop_timer(short func_index, anything *arg)
1878  *      Stop a timer specified by the (func_index, arg) pair.  This
1879  *      assumes that such a pair is unique.  Return the time the
1880  *      timer would have gone off.  If no timer is found, return 0.
1881  *      If an object, decrement the object's timer count.
1882  *
1883  *  long peek_timer(short func_index, anything *arg)
1884  *      Return time specified timer will go off (0 if no such timer).
1885  *
1886  *  void run_timers(void)
1887  *      Call timers that have timed out.
1888  *
1889  * Save/Restore:
1890  *  void save_timers(int fd, int mode, int range)
1891  *      Save all timers of range 'range'.  Range is either global
1892  *      or local.  Global timers follow game play, local timers
1893  *      are saved with a level.  Object and monster timers are
1894  *      saved using their respective id's instead of pointers.
1895  *
1896  *  void restore_timers(int fd, int range, boolean ghostly, long adjust)
1897  *      Restore timers of range 'range'.  If from a ghost pile,
1898  *      adjust the timeout by 'adjust'.  The object and monster
1899  *      ids are not restored until later.
1900  *
1901  *  void relink_timers(boolean ghostly)
1902  *      Relink all object and monster timers that had been saved
1903  *      using their object's or monster's id number.
1904  *
1905  * Object Specific:
1906  *  void obj_move_timers(struct obj *src, struct obj *dest)
1907  *      Reassign all timers from src to dest.
1908  *
1909  *  void obj_split_timers(struct obj *src, struct obj *dest)
1910  *      Duplicate all timers assigned to src and attach them to dest.
1911  *
1912  *  void obj_stop_timers(struct obj *obj)
1913  *      Stop all timers attached to obj.
1914  *
1915  *  boolean obj_has_timer(struct obj *object, short timer_type)
1916  *      Check whether object has a timer of type timer_type.
1917  */
1918
1919 STATIC_DCL const char *FDECL(kind_name, (SHORT_P));
1920 STATIC_DCL void FDECL(print_queue, (winid, timer_element *));
1921 STATIC_DCL void FDECL(insert_timer, (timer_element *));
1922 STATIC_DCL timer_element *FDECL(remove_timer,
1923                                 (timer_element **, SHORT_P, ANY_P *));
1924 STATIC_DCL void FDECL(write_timer, (int, timer_element *));
1925 STATIC_DCL boolean FDECL(mon_is_local, (struct monst *));
1926 STATIC_DCL boolean FDECL(timer_is_local, (timer_element *));
1927 STATIC_DCL int FDECL(maybe_write_timer, (int, int, BOOLEAN_P));
1928
1929 /* ordered timer list */
1930 static timer_element *timer_base; /* "active" */
1931 static unsigned long timer_id = 1;
1932
1933 /* If defined, then include names when printing out the timer queue */
1934 #define VERBOSE_TIMER
1935
1936 typedef struct {
1937     timeout_proc f, cleanup;
1938 #ifdef VERBOSE_TIMER
1939     const char *name;
1940 #define TTAB(a, b, c) \
1941     {                 \
1942         a, b, c       \
1943     }
1944 #else
1945 #define TTAB(a, b, c) \
1946     {                 \
1947         a, b          \
1948     }
1949 #endif
1950 } ttable;
1951
1952 /* table of timeout functions */
1953 static const ttable timeout_funcs[NUM_TIME_FUNCS] = {
1954     TTAB(rot_organic, (timeout_proc) 0, "rot_organic"),
1955     TTAB(rot_corpse, (timeout_proc) 0, "rot_corpse"),
1956     TTAB(revive_mon, (timeout_proc) 0, "revive_mon"),
1957     TTAB(burn_object, cleanup_burn, "burn_object"),
1958     TTAB(hatch_egg, (timeout_proc) 0, "hatch_egg"),
1959     TTAB(fig_transform, (timeout_proc) 0, "fig_transform"),
1960     TTAB(melt_ice_away, (timeout_proc) 0, "melt_ice_away")
1961 };
1962 #undef TTAB
1963
1964 STATIC_OVL const char *
1965 kind_name(kind)
1966 short kind;
1967 {
1968     switch (kind) {
1969     case TIMER_LEVEL:
1970         return "level";
1971     case TIMER_GLOBAL:
1972         return "global";
1973     case TIMER_OBJECT:
1974         return "object";
1975     case TIMER_MONSTER:
1976         return "monster";
1977     }
1978     return "unknown";
1979 }
1980
1981 STATIC_OVL void
1982 print_queue(win, base)
1983 winid win;
1984 timer_element *base;
1985 {
1986     timer_element *curr;
1987     char buf[BUFSZ];
1988
1989     if (!base) {
1990         putstr(win, 0, " <empty>");
1991     } else {
1992         putstr(win, 0, "timeout  id   kind   call");
1993         for (curr = base; curr; curr = curr->next) {
1994 #ifdef VERBOSE_TIMER
1995             Sprintf(buf, " %4ld   %4ld  %-6s %s(%s)", curr->timeout,
1996                     curr->tid, kind_name(curr->kind),
1997                     timeout_funcs[curr->func_index].name,
1998                     fmt_ptr((genericptr_t) curr->arg.a_void));
1999 #else
2000             Sprintf(buf, " %4ld   %4ld  %-6s #%d(%s)", curr->timeout,
2001                     curr->tid, kind_name(curr->kind), curr->func_index,
2002                     fmt_ptr((genericptr_t) curr->arg.a_void));
2003 #endif
2004             putstr(win, 0, buf);
2005         }
2006     }
2007 }
2008
2009 int
2010 wiz_timeout_queue()
2011 {
2012     winid win;
2013     char buf[BUFSZ];
2014     const char *propname;
2015     long intrinsic;
2016     int i, p, count, longestlen, ln, specindx = 0;
2017
2018     win = create_nhwindow(NHW_MENU); /* corner text window */
2019     if (win == WIN_ERR)
2020         return 0;
2021
2022     Sprintf(buf, "Current time = %ld.", monstermoves);
2023     putstr(win, 0, buf);
2024     putstr(win, 0, "");
2025     putstr(win, 0, "Active timeout queue:");
2026     putstr(win, 0, "");
2027     print_queue(win, timer_base);
2028
2029     /* Timed properies:
2030      * check every one; the majority can't obtain temporary timeouts in
2031      * normal play but those can be forced via the #wizintrinsic command.
2032      */
2033     count = longestlen = 0;
2034     for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) {
2035         p = propertynames[i].prop_num;
2036         intrinsic = u.uprops[p].intrinsic;
2037         if (intrinsic & TIMEOUT) {
2038             ++count;
2039             if ((ln = (int) strlen(propname)) > longestlen)
2040                 longestlen = ln;
2041         }
2042         if (specindx == 0 && p == FIRE_RES)
2043             specindx = i;
2044     }
2045     putstr(win, 0, "");
2046     if (!count) {
2047         putstr(win, 0, "No timed properties.");
2048     } else {
2049         putstr(win, 0, "Timed properties:");
2050         putstr(win, 0, "");
2051         for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) {
2052             p = propertynames[i].prop_num;
2053             intrinsic = u.uprops[p].intrinsic;
2054             if (intrinsic & TIMEOUT) {
2055                 if (specindx > 0 && i >= specindx) {
2056                     putstr(win, 0, " -- settable via #wizinstrinc only --");
2057                     specindx = 0;
2058                 }
2059                 /* timeout value can be up to 16777215 (0x00ffffff) but
2060                    width of 4 digits should result in values lining up
2061                    almost all the time (if/when they don't, it won't
2062                    look nice but the information will still be accurate) */
2063                 Sprintf(buf, " %*s %4ld", -longestlen, propname,
2064                         (intrinsic & TIMEOUT));
2065                 putstr(win, 0, buf);
2066             }
2067         }
2068     }
2069     display_nhwindow(win, FALSE);
2070     destroy_nhwindow(win);
2071
2072     return 0;
2073 }
2074
2075 void
2076 timer_sanity_check()
2077 {
2078     timer_element *curr;
2079
2080     /* this should be much more complete */
2081     for (curr = timer_base; curr; curr = curr->next)
2082         if (curr->kind == TIMER_OBJECT) {
2083             struct obj *obj = curr->arg.a_obj;
2084
2085             if (obj->timed == 0) {
2086                 pline("timer sanity: untimed obj %s, timer %ld",
2087                       fmt_ptr((genericptr_t) obj), curr->tid);
2088             }
2089         }
2090 }
2091
2092 /*
2093  * Pick off timeout elements from the global queue and call their functions.
2094  * Do this until their time is less than or equal to the move count.
2095  */
2096 void
2097 run_timers()
2098 {
2099     timer_element *curr;
2100
2101     /*
2102      * Always use the first element.  Elements may be added or deleted at
2103      * any time.  The list is ordered, we are done when the first element
2104      * is in the future.
2105      */
2106     while (timer_base && timer_base->timeout <= monstermoves) {
2107         curr = timer_base;
2108         timer_base = curr->next;
2109
2110         if (curr->kind == TIMER_OBJECT)
2111             (curr->arg.a_obj)->timed--;
2112         (*timeout_funcs[curr->func_index].f)(&curr->arg, curr->timeout);
2113         free((genericptr_t) curr);
2114     }
2115 }
2116
2117 /*
2118  * Start a timer.  Return TRUE if successful.
2119  */
2120 boolean
2121 start_timer(when, kind, func_index, arg)
2122 long when;
2123 short kind;
2124 short func_index;
2125 anything *arg;
2126 {
2127     timer_element *gnu;
2128
2129     if (func_index < 0 || func_index >= NUM_TIME_FUNCS)
2130         panic("start_timer");
2131
2132     gnu = (timer_element *) alloc(sizeof(timer_element));
2133     (void) memset((genericptr_t)gnu, 0, sizeof(timer_element));
2134     gnu->next = 0;
2135     gnu->tid = timer_id++;
2136     gnu->timeout = monstermoves + when;
2137     gnu->kind = kind;
2138     gnu->needs_fixup = 0;
2139     gnu->func_index = func_index;
2140     gnu->arg = *arg;
2141     insert_timer(gnu);
2142
2143     if (kind == TIMER_OBJECT) /* increment object's timed count */
2144         (arg->a_obj)->timed++;
2145
2146     /* should check for duplicates and fail if any */
2147     return TRUE;
2148 }
2149
2150 /*
2151  * Remove the timer from the current list and free it up.  Return the time
2152  * remaining until it would have gone off, 0 if not found.
2153  */
2154 long
2155 stop_timer(func_index, arg)
2156 short func_index;
2157 anything *arg;
2158 {
2159     timer_element *doomed;
2160     long timeout;
2161
2162     doomed = remove_timer(&timer_base, func_index, arg);
2163
2164     if (doomed) {
2165         timeout = doomed->timeout;
2166         if (doomed->kind == TIMER_OBJECT)
2167             (arg->a_obj)->timed--;
2168         if (timeout_funcs[doomed->func_index].cleanup)
2169             (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout);
2170         free((genericptr_t) doomed);
2171         return (timeout - monstermoves);
2172     }
2173     return 0L;
2174 }
2175
2176 /*
2177  * Find the timeout of specified timer; return 0 if none.
2178  */
2179 long
2180 peek_timer(type, arg)
2181 short type;
2182 anything *arg;
2183 {
2184     timer_element *curr;
2185
2186     for (curr = timer_base; curr; curr = curr->next) {
2187         if (curr->func_index == type && curr->arg.a_void == arg->a_void)
2188             return curr->timeout;
2189     }
2190     return 0L;
2191 }
2192
2193 /*
2194  * Move all object timers from src to dest, leaving src untimed.
2195  */
2196 void
2197 obj_move_timers(src, dest)
2198 struct obj *src, *dest;
2199 {
2200     int count;
2201     timer_element *curr;
2202
2203     for (count = 0, curr = timer_base; curr; curr = curr->next)
2204         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
2205             curr->arg.a_obj = dest;
2206             dest->timed++;
2207             count++;
2208         }
2209     if (count != src->timed)
2210         panic("obj_move_timers");
2211     src->timed = 0;
2212 }
2213
2214 /*
2215  * Find all object timers and duplicate them for the new object "dest".
2216  */
2217 void
2218 obj_split_timers(src, dest)
2219 struct obj *src, *dest;
2220 {
2221     timer_element *curr, *next_timer = 0;
2222
2223     for (curr = timer_base; curr; curr = next_timer) {
2224         next_timer = curr->next; /* things may be inserted */
2225         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) {
2226             (void) start_timer(curr->timeout - monstermoves, TIMER_OBJECT,
2227                                curr->func_index, obj_to_any(dest));
2228         }
2229     }
2230 }
2231
2232 /*
2233  * Stop all timers attached to this object.  We can get away with this because
2234  * all object pointers are unique.
2235  */
2236 void
2237 obj_stop_timers(obj)
2238 struct obj *obj;
2239 {
2240     timer_element *curr, *prev, *next_timer = 0;
2241
2242     for (prev = 0, curr = timer_base; curr; curr = next_timer) {
2243         next_timer = curr->next;
2244         if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == obj) {
2245             if (prev)
2246                 prev->next = curr->next;
2247             else
2248                 timer_base = curr->next;
2249             if (timeout_funcs[curr->func_index].cleanup)
2250                 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
2251                                                            curr->timeout);
2252             free((genericptr_t) curr);
2253         } else {
2254             prev = curr;
2255         }
2256     }
2257     obj->timed = 0;
2258 }
2259
2260 /*
2261  * Check whether object has a timer of type timer_type.
2262  */
2263 boolean
2264 obj_has_timer(object, timer_type)
2265 struct obj *object;
2266 short timer_type;
2267 {
2268     long timeout = peek_timer(timer_type, obj_to_any(object));
2269
2270     return (boolean) (timeout != 0L);
2271 }
2272
2273 /*
2274  * Stop all timers of index func_index at this spot.
2275  *
2276  */
2277 void
2278 spot_stop_timers(x, y, func_index)
2279 xchar x, y;
2280 short func_index;
2281 {
2282     timer_element *curr, *prev, *next_timer = 0;
2283     long where = (((long) x << 16) | ((long) y));
2284
2285     for (prev = 0, curr = timer_base; curr; curr = next_timer) {
2286         next_timer = curr->next;
2287         if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
2288             && curr->arg.a_long == where) {
2289             if (prev)
2290                 prev->next = curr->next;
2291             else
2292                 timer_base = curr->next;
2293             if (timeout_funcs[curr->func_index].cleanup)
2294                 (*timeout_funcs[curr->func_index].cleanup)(&curr->arg,
2295                                                            curr->timeout);
2296             free((genericptr_t) curr);
2297         } else {
2298             prev = curr;
2299         }
2300     }
2301 }
2302
2303 /*
2304  * When is the spot timer of type func_index going to expire?
2305  * Returns 0L if no such timer.
2306  */
2307 long
2308 spot_time_expires(x, y, func_index)
2309 xchar x, y;
2310 short func_index;
2311 {
2312     timer_element *curr;
2313     long where = (((long) x << 16) | ((long) y));
2314
2315     for (curr = timer_base; curr; curr = curr->next) {
2316         if (curr->kind == TIMER_LEVEL && curr->func_index == func_index
2317             && curr->arg.a_long == where)
2318             return curr->timeout;
2319     }
2320     return 0L;
2321 }
2322
2323 long
2324 spot_time_left(x, y, func_index)
2325 xchar x, y;
2326 short func_index;
2327 {
2328     long expires = spot_time_expires(x, y, func_index);
2329     return (expires > 0L) ? expires - monstermoves : 0L;
2330 }
2331
2332 /* Insert timer into the global queue */
2333 STATIC_OVL void
2334 insert_timer(gnu)
2335 timer_element *gnu;
2336 {
2337     timer_element *curr, *prev;
2338
2339     for (prev = 0, curr = timer_base; curr; prev = curr, curr = curr->next)
2340         if (curr->timeout >= gnu->timeout)
2341             break;
2342
2343     gnu->next = curr;
2344     if (prev)
2345         prev->next = gnu;
2346     else
2347         timer_base = gnu;
2348 }
2349
2350 STATIC_OVL timer_element *
2351 remove_timer(base, func_index, arg)
2352 timer_element **base;
2353 short func_index;
2354 anything *arg;
2355 {
2356     timer_element *prev, *curr;
2357
2358     for (prev = 0, curr = *base; curr; prev = curr, curr = curr->next)
2359         if (curr->func_index == func_index && curr->arg.a_void == arg->a_void)
2360             break;
2361
2362     if (curr) {
2363         if (prev)
2364             prev->next = curr->next;
2365         else
2366             *base = curr->next;
2367     }
2368
2369     return curr;
2370 }
2371
2372 STATIC_OVL void
2373 write_timer(fd, timer)
2374 int fd;
2375 timer_element *timer;
2376 {
2377     anything arg_save;
2378
2379     arg_save = zeroany;
2380     switch (timer->kind) {
2381     case TIMER_GLOBAL:
2382     case TIMER_LEVEL:
2383         /* assume no pointers in arg */
2384         bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
2385         break;
2386
2387     case TIMER_OBJECT:
2388         if (timer->needs_fixup)
2389             bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
2390         else {
2391             /* replace object pointer with id */
2392             arg_save.a_obj = timer->arg.a_obj;
2393             timer->arg = zeroany;
2394             timer->arg.a_uint = (arg_save.a_obj)->o_id;
2395             timer->needs_fixup = 1;
2396             bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
2397             timer->arg.a_obj = arg_save.a_obj;
2398             timer->needs_fixup = 0;
2399         }
2400         break;
2401
2402     case TIMER_MONSTER:
2403         if (timer->needs_fixup)
2404             bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
2405         else {
2406             /* replace monster pointer with id */
2407             arg_save.a_monst = timer->arg.a_monst;
2408             timer->arg = zeroany;
2409             timer->arg.a_uint = (arg_save.a_monst)->m_id;
2410             timer->needs_fixup = 1;
2411             bwrite(fd, (genericptr_t) timer, sizeof(timer_element));
2412             timer->arg.a_monst = arg_save.a_monst;
2413             timer->needs_fixup = 0;
2414         }
2415         break;
2416
2417     default:
2418         panic("write_timer");
2419         break;
2420     }
2421 }
2422
2423 /*
2424  * Return TRUE if the object will stay on the level when the level is
2425  * saved.
2426  */
2427 boolean
2428 obj_is_local(obj)
2429 struct obj *obj;
2430 {
2431     switch (obj->where) {
2432     case OBJ_INVENT:
2433     case OBJ_MIGRATING:
2434         return FALSE;
2435     case OBJ_FLOOR:
2436     case OBJ_BURIED:
2437         return TRUE;
2438     case OBJ_CONTAINED:
2439         return obj_is_local(obj->ocontainer);
2440     case OBJ_MINVENT:
2441         return mon_is_local(obj->ocarry);
2442     }
2443     panic("obj_is_local");
2444     return FALSE;
2445 }
2446
2447 /*
2448  * Return TRUE if the given monster will stay on the level when the
2449  * level is saved.
2450  */
2451 STATIC_OVL boolean
2452 mon_is_local(mon)
2453 struct monst *mon;
2454 {
2455     struct monst *curr;
2456
2457     for (curr = migrating_mons; curr; curr = curr->nmon)
2458         if (curr == mon)
2459             return FALSE;
2460     /* `mydogs' is used during level changes, never saved and restored */
2461     for (curr = mydogs; curr; curr = curr->nmon)
2462         if (curr == mon)
2463             return FALSE;
2464     return TRUE;
2465 }
2466
2467 /*
2468  * Return TRUE if the timer is attached to something that will stay on the
2469  * level when the level is saved.
2470  */
2471 STATIC_OVL boolean
2472 timer_is_local(timer)
2473 timer_element *timer;
2474 {
2475     switch (timer->kind) {
2476     case TIMER_LEVEL:
2477         return TRUE;
2478     case TIMER_GLOBAL:
2479         return FALSE;
2480     case TIMER_OBJECT:
2481         return obj_is_local(timer->arg.a_obj);
2482     case TIMER_MONSTER:
2483         return mon_is_local(timer->arg.a_monst);
2484     }
2485     panic("timer_is_local");
2486     return FALSE;
2487 }
2488
2489 /*
2490  * Part of the save routine.  Count up the number of timers that would
2491  * be written.  If write_it is true, actually write the timer.
2492  */
2493 STATIC_OVL int
2494 maybe_write_timer(fd, range, write_it)
2495 int fd, range;
2496 boolean write_it;
2497 {
2498     int count = 0;
2499     timer_element *curr;
2500
2501     for (curr = timer_base; curr; curr = curr->next) {
2502         if (range == RANGE_GLOBAL) {
2503             /* global timers */
2504
2505             if (!timer_is_local(curr)) {
2506                 count++;
2507                 if (write_it)
2508                     write_timer(fd, curr);
2509             }
2510
2511         } else {
2512             /* local timers */
2513
2514             if (timer_is_local(curr)) {
2515                 count++;
2516                 if (write_it)
2517                     write_timer(fd, curr);
2518             }
2519         }
2520     }
2521
2522     return count;
2523 }
2524
2525 /*
2526  * Save part of the timer list.  The parameter 'range' specifies either
2527  * global or level timers to save.  The timer ID is saved with the global
2528  * timers.
2529  *
2530  * Global range:
2531  *      + timeouts that follow the hero (global)
2532  *      + timeouts that follow obj & monst that are migrating
2533  *
2534  * Level range:
2535  *      + timeouts that are level specific (e.g. storms)
2536  *      + timeouts that stay with the level (obj & monst)
2537  */
2538 void
2539 save_timers(fd, mode, range)
2540 int fd, mode, range;
2541 {
2542     timer_element *curr, *prev, *next_timer = 0;
2543     int count;
2544
2545     if (perform_bwrite(mode)) {
2546         if (range == RANGE_GLOBAL)
2547             bwrite(fd, (genericptr_t) &timer_id, sizeof(timer_id));
2548
2549         count = maybe_write_timer(fd, range, FALSE);
2550         bwrite(fd, (genericptr_t) &count, sizeof count);
2551         (void) maybe_write_timer(fd, range, TRUE);
2552     }
2553
2554     if (release_data(mode)) {
2555         for (prev = 0, curr = timer_base; curr; curr = next_timer) {
2556             next_timer = curr->next; /* in case curr is removed */
2557
2558             if (!(!!(range == RANGE_LEVEL) ^ !!timer_is_local(curr))) {
2559                 if (prev)
2560                     prev->next = curr->next;
2561                 else
2562                     timer_base = curr->next;
2563                 free((genericptr_t) curr);
2564                 /* prev stays the same */
2565             } else {
2566                 prev = curr;
2567             }
2568         }
2569     }
2570 }
2571
2572 /*
2573  * Pull in the structures from disk, but don't recalculate the object and
2574  * monster pointers.
2575  */
2576 void
2577 restore_timers(fd, range, ghostly, adjust)
2578 int fd, range;
2579 boolean ghostly; /* restoring from a ghost level */
2580 long adjust;     /* how much to adjust timeout */
2581 {
2582     int count;
2583     timer_element *curr;
2584
2585     if (range == RANGE_GLOBAL)
2586         mread(fd, (genericptr_t) &timer_id, sizeof timer_id);
2587
2588     /* restore elements */
2589     mread(fd, (genericptr_t) &count, sizeof count);
2590     while (count-- > 0) {
2591         curr = (timer_element *) alloc(sizeof(timer_element));
2592         mread(fd, (genericptr_t) curr, sizeof(timer_element));
2593         if (ghostly)
2594             curr->timeout += adjust;
2595         insert_timer(curr);
2596     }
2597 }
2598
2599 /* to support '#stats' wizard-mode command */
2600 void
2601 timer_stats(hdrfmt, hdrbuf, count, size)
2602 const char *hdrfmt;
2603 char *hdrbuf;
2604 long *count, *size;
2605 {
2606     timer_element *te;
2607
2608     Sprintf(hdrbuf, hdrfmt, (long) sizeof (timer_element));
2609     *count = *size = 0L;
2610     for (te = timer_base; te; te = te->next) {
2611         ++*count;
2612         *size += (long) sizeof *te;
2613     }
2614 }
2615
2616 /* reset all timers that are marked for reseting */
2617 void
2618 relink_timers(ghostly)
2619 boolean ghostly;
2620 {
2621     timer_element *curr;
2622     unsigned nid;
2623
2624     for (curr = timer_base; curr; curr = curr->next) {
2625         if (curr->needs_fixup) {
2626             if (curr->kind == TIMER_OBJECT) {
2627                 if (ghostly) {
2628                     if (!lookup_id_mapping(curr->arg.a_uint, &nid))
2629                         panic("relink_timers 1");
2630                 } else
2631                     nid = curr->arg.a_uint;
2632                 curr->arg.a_obj = find_oid(nid);
2633                 if (!curr->arg.a_obj)
2634                     panic("cant find o_id %d", nid);
2635                 curr->needs_fixup = 0;
2636             } else if (curr->kind == TIMER_MONSTER) {
2637                 panic("relink_timers: no monster timer implemented");
2638             } else
2639                 panic("relink_timers 2");
2640         }
2641     }
2642 }
2643
2644 /*timeout.c*/