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