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