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