1 | /* SCCS Id: @(#)do.c 3.3 1999/11/29 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | /* Contains code for 'd', 'D' (drop), '>', '<' (up, down) */ 6 | 7 | #include "hack.h" 8 | #include "lev.h" 9 | 10 | #include <errno.h> 11 | #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */ 12 | # if (_MSC_VER >= 600) 13 | # define SKIP_ERRNO 14 | # endif 15 | #endif 16 | #ifndef SKIP_ERRNO 17 | #ifdef _DCC 18 | const 19 | #endif 20 | extern int errno; 21 | #endif 22 | 23 | #ifdef SINKS 24 | # ifdef OVLB 25 | STATIC_DCL void FDECL(trycall, (struct obj *)); 26 | # endif /* OVLB */ 27 | STATIC_DCL void FDECL(dosinkring, (struct obj *)); 28 | #endif /* SINKS */ 29 | 30 | STATIC_PTR int FDECL(drop, (struct obj *)); 31 | STATIC_PTR int NDECL(wipeoff); 32 | 33 | #ifdef OVL0 34 | STATIC_DCL int FDECL(menu_drop, (int)); 35 | #endif 36 | #ifdef OVL2 37 | STATIC_DCL int NDECL(currentlevel_rewrite); 38 | STATIC_DCL void NDECL(final_level); 39 | /* static boolean FDECL(badspot, (XCHAR_P,XCHAR_P)); */ 40 | #endif 41 | 42 | #ifdef OVLB 43 | 44 | static NEARDATA const char drop_types[] = 45 | { ALLOW_COUNT, GOLD_CLASS, ALL_CLASSES, 0 }; 46 | 47 | /* 'd' command: drop one inventory item */ 48 | int 49 | dodrop() 50 | { 51 | int result, i = (invent || u.ugold) ? 0 : (SIZE(drop_types) - 1); 52 | 53 | if (*u.ushops) sellobj_state(TRUE); 54 | result = drop(getobj(&drop_types[i], "drop")); 55 | if (*u.ushops) sellobj_state(FALSE); 56 | reset_occupations(); 57 | 58 | return result; 59 | } 60 | 61 | #endif /* OVLB */ 62 | #ifdef OVL0 63 | 64 | /* Called when a boulder is dropped, thrown, or pushed. If it ends up 65 | * in a pool, it either fills the pool up or sinks away. In either case, 66 | * it's gone for good... If the destination is not a pool, returns FALSE. 67 | */ 68 | boolean 69 | boulder_hits_pool(otmp, rx, ry, pushing) 70 | struct obj *otmp; 71 | register int rx, ry; 72 | boolean pushing; 73 | { 74 | if (!otmp || otmp->otyp != BOULDER) 75 | impossible("Not a boulder?"); 76 | else if (!Is_waterlevel(&u.uz) && (is_pool(rx,ry) || is_lava(rx,ry))) { 77 | boolean lava = is_lava(rx,ry), fills_up; 78 | const char *what = lava ? "lava" : "water"; 79 | schar ltyp = levl[rx][ry].typ; 80 | int chance = rn2(10); /* water: 90%; lava: 10% */ 81 | fills_up = lava ? chance == 0 : chance != 0; 82 | 83 | if (fills_up) { 84 | if (ltyp == DRAWBRIDGE_UP) { 85 | levl[rx][ry].drawbridgemask &= ~DB_UNDER; /* clear lava */ 86 | levl[rx][ry].drawbridgemask |= DB_FLOOR; 87 | } else 88 | levl[rx][ry].typ = ROOM; 89 | 90 | bury_objs(rx, ry); 91 | newsym(rx,ry); 92 | if (pushing) { 93 | You("push %s into the %s.", the(xname(otmp)), what); 94 | if (flags.verbose && !Blind) 95 | pline("Now you can cross it!"); 96 | /* no splashing in this case */ 97 | } 98 | } 99 | if (!fills_up || !pushing) { /* splashing occurs */ 100 | if (!u.uinwater) { 101 | if (pushing ? !Blind : cansee(rx,ry)) { 102 | boolean moat = (ltyp != WATER) && 103 | !Is_medusa_level(&u.uz) && !Is_waterlevel(&u.uz); 104 | 105 | There("is a large splash as %s %s the %s.", 106 | the(xname(otmp)), fills_up? "fills":"falls into", 107 | lava ? "lava" : ltyp==POOL ? "pool" : 108 | moat ? "moat" : "water"); 109 | } else if (flags.soundok) 110 | You_hear("a%s splash.", lava ? " sizzling" : ""); 111 | wake_nearto(rx, ry, 40); 112 | } 113 | 114 | if (fills_up && u.uinwater && distu(rx,ry) == 0) { 115 | u.uinwater = 0; 116 | docrt(); 117 | vision_full_recalc = 1; 118 | You("find yourself on dry land again!"); 119 | } else if (lava && distu(rx,ry) <= 2) { 120 | You("are hit by molten lava%c", 121 | Fire_resistance ? '.' : '!'); 122 | burn_away_slime(); 123 | losehp(d((Fire_resistance ? 1 : 3), 6), 124 | "molten lava", KILLED_BY); 125 | } else if (!fills_up && flags.verbose && 126 | (pushing ? !Blind : cansee(rx,ry))) 127 | pline("It sinks without a trace!"); 128 | } 129 | 130 | /* boulder is now gone */ 131 | if (pushing) delobj(otmp); 132 | else obfree(otmp, (struct obj *)0); 133 | return TRUE; 134 | } 135 | return FALSE; 136 | } 137 | 138 | /* Used for objects which sometimes do special things when dropped; must be 139 | * called with the object not in any chain. Returns TRUE if the object goes 140 | * away. 141 | */ 142 | boolean 143 | flooreffects(obj,x,y,verb) 144 | struct obj *obj; 145 | int x,y; 146 | const char *verb; 147 | { 148 | struct trap *t; 149 | struct monst *mtmp; 150 | 151 | if (obj->where != OBJ_FREE) 152 | panic("flooreffects: obj not free"); 153 | 154 | /* make sure things like water_damage() have no pointers to follow */ 155 | obj->nobj = obj->nexthere = (struct obj *)0; 156 | 157 | if (obj->otyp == BOULDER && boulder_hits_pool(obj, x, y, FALSE)) 158 | return TRUE; 159 | else if (obj->otyp == BOULDER && (t = t_at(x,y)) != 0 && 160 | (t->ttyp==PIT || t->ttyp==SPIKED_PIT 161 | || t->ttyp==TRAPDOOR || t->ttyp==HOLE)) { 162 | if (((mtmp = m_at(x, y)) && mtmp->mtrapped) || 163 | (u.utrap && u.ux == x && u.uy == y)) { 164 | if (*verb) 165 | pline_The("boulder %ss into the pit%s.", verb, 166 | (mtmp) ? "" : " with you"); 167 | if (mtmp) { 168 | if (!passes_walls(mtmp->data) && 169 | !throws_rocks(mtmp->data)) { 170 | if (hmon(mtmp, obj, TRUE)) 171 | return FALSE; /* still alive */ 172 | } else mtmp->mtrapped = 0; 173 | } else { 174 | if (!Passes_walls && !throws_rocks(youmonst.data)) { 175 | losehp(rnd(15), "squished under a boulder", 176 | NO_KILLER_PREFIX); 177 | return FALSE; /* player remains trapped */ 178 | } else u.utrap = 0; 179 | } 180 | } 181 | if (*verb) { 182 | if (Blind) { 183 | if ((x == u.ux) && (y == u.uy)) 184 | You_hear("a CRASH! beneath you."); 185 | else 186 | You_hear("the boulder %s.", verb); 187 | } else if (cansee(x, y)) { 188 | pline_The("boulder %s%s.", 189 | t->tseen ? "" : "triggers and ", 190 | t->ttyp == TRAPDOOR ? "plugs a trap door" : 191 | t->ttyp == HOLE ? "plugs a hole" : 192 | "fills a pit"); 193 | } 194 | } 195 | deltrap(t); 196 | obfree(obj, (struct obj *)0); 197 | bury_objs(x, y); 198 | newsym(x,y); 199 | return TRUE; 200 | } else if (is_pool(x, y)) { 201 | water_damage(obj, FALSE, FALSE); 202 | } 203 | return FALSE; 204 | } 205 | 206 | #endif /* OVL0 */ 207 | #ifdef OVLB 208 | 209 | void 210 | doaltarobj(obj) /* obj is an object dropped on an altar */ 211 | register struct obj *obj; 212 | { 213 | if (Blind) return; 214 | 215 | /* KMH, conduct */ 216 | u.uconduct.gnostic++; 217 | 218 | if (obj->blessed || obj->cursed) { 219 | There("is %s flash as %s hit%s the altar.", 220 | an(hcolor(obj->blessed ? amber : Black)), 221 | doname(obj), 222 | (obj->quan == 1L) ? "s" : ""); 223 | if (!Hallucination) obj->bknown = 1; 224 | } else { 225 | pline("%s land%s on the altar.", Doname2(obj), 226 | (obj->quan == 1L) ? "s" : ""); 227 | obj->bknown = 1; 228 | } 229 | } 230 | 231 | #ifdef SINKS 232 | STATIC_OVL 233 | void 234 | trycall(obj) 235 | register struct obj *obj; 236 | { 237 | if(!objects[obj->otyp].oc_name_known && 238 | !objects[obj->otyp].oc_uname) 239 | docall(obj); 240 | } 241 | 242 | STATIC_OVL 243 | void 244 | dosinkring(obj) /* obj is a ring being dropped over a kitchen sink */ 245 | register struct obj *obj; 246 | { 247 | register struct obj *otmp,*otmp2; 248 | register boolean ideed = TRUE; 249 | 250 | You("drop %s down the drain.", doname(obj)); 251 | obj->in_use = TRUE; /* block free identification via interrupt */ 252 | switch(obj->otyp) { /* effects that can be noticed without eyes */ 253 | case RIN_SEARCHING: 254 | You("thought your %s got lost in the sink, but there it is!", 255 | xname(obj)); 256 | goto giveback; 257 | case RIN_SLOW_DIGESTION: 258 | pline_The("ring is regurgitated!"); 259 | giveback: 260 | obj->in_use = FALSE; 261 | dropx(obj); 262 | trycall(obj); 263 | return; 264 | case RIN_LEVITATION: 265 | pline_The("sink quivers upward for a moment."); 266 | break; 267 | case RIN_POISON_RESISTANCE: 268 | You("smell rotten %s.", makeplural(pl_fruit)); 269 | break; 270 | case RIN_AGGRAVATE_MONSTER: 271 | pline("Several flies buzz angrily around the sink."); 272 | break; 273 | case RIN_SHOCK_RESISTANCE: 274 | pline("Static electricity surrounds the sink."); 275 | break; 276 | case RIN_CONFLICT: 277 | You_hear("loud noises coming from the drain."); 278 | break; 279 | case RIN_SUSTAIN_ABILITY: /* KMH */ 280 | pline_The("water flow seems fixed."); 281 | break; 282 | case RIN_GAIN_STRENGTH: 283 | pline_The("water flow seems %ser now.", 284 | (obj->spe<0) ? "weak" : "strong"); 285 | break; 286 | case RIN_GAIN_CONSTITUTION: 287 | pline_The("water flow seems %ser now.", 288 | (obj->spe<0) ? "less" : "great"); 289 | break; 290 | case RIN_INCREASE_ACCURACY: /* KMH */ 291 | pline_The("water flow %s the drain.", 292 | (obj->spe<0) ? "misses" : "hits"); 293 | break; 294 | case RIN_INCREASE_DAMAGE: 295 | pline_The("water's force seems %ser now.", 296 | (obj->spe<0) ? "small" : "great"); 297 | break; 298 | case RIN_HUNGER: 299 | ideed = FALSE; 300 | for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp2) { 301 | otmp2 = otmp->nexthere; 302 | if (otmp != uball && otmp != uchain && 303 | !obj_resists(otmp, 1, 99)) { 304 | if (!Blind) { 305 | pline("Suddenly, %s vanishes from the sink!", 306 | doname(otmp)); 307 | ideed = TRUE; 308 | } 309 | delobj(otmp); 310 | } 311 | } 312 | break; 313 | case MEAT_RING: 314 | /* Not the same as aggravate monster; besides, it's obvious. */ 315 | pline("Several flies buzz around the sink."); 316 | break; 317 | default: 318 | ideed = FALSE; 319 | break; 320 | } 321 | if(!Blind && !ideed && obj->otyp != RIN_HUNGER) { 322 | ideed = TRUE; 323 | switch(obj->otyp) { /* effects that need eyes */ 324 | case RIN_ADORNMENT: 325 | pline_The("faucets flash brightly for a moment."); 326 | break; 327 | case RIN_REGENERATION: 328 | pline_The("sink looks as good as new."); 329 | break; 330 | case RIN_INVISIBILITY: 331 | You("don't see anything happen to the sink."); 332 | break; 333 | case RIN_FREE_ACTION: 334 | You("see the ring slide right down the drain!"); 335 | break; 336 | case RIN_SEE_INVISIBLE: 337 | You("see some air in the sink."); 338 | break; 339 | case RIN_STEALTH: 340 | pline_The("sink seems to blend into the floor for a moment."); 341 | break; 342 | case RIN_FIRE_RESISTANCE: 343 | pline_The("hot water faucet flashes brightly for a moment."); 344 | break; 345 | case RIN_COLD_RESISTANCE: 346 | pline_The("cold water faucet flashes brightly for a moment."); 347 | break; 348 | case RIN_PROTECTION_FROM_SHAPE_CHAN: 349 | pline_The("sink looks nothing like a fountain."); 350 | break; 351 | case RIN_PROTECTION: 352 | pline_The("sink glows %s for a moment.", 353 | hcolor((obj->spe<0) ? Black : silver)); 354 | break; 355 | case RIN_WARNING: 356 | pline_The("sink glows %s for a moment.", hcolor(White)); 357 | break; 358 | case RIN_TELEPORTATION: 359 | pline_The("sink momentarily vanishes."); 360 | break; 361 | case RIN_TELEPORT_CONTROL: 362 | pline_The("sink looks like it is being beamed aboard somewhere."); 363 | break; 364 | case RIN_POLYMORPH: 365 | pline_The("sink momentarily looks like a fountain."); 366 | break; 367 | case RIN_POLYMORPH_CONTROL: 368 | pline_The("sink momentarily looks like a regularly erupting geyser."); 369 | break; 370 | } 371 | } 372 | if(ideed) 373 | trycall(obj); 374 | else 375 | You_hear("the ring bouncing down the drainpipe."); 376 | if (!rn2(20)) { 377 | pline_The("sink backs up, leaving %s.", doname(obj)); 378 | obj->in_use = FALSE; 379 | dropx(obj); 380 | } else 381 | useup(obj); 382 | } 383 | #endif 384 | 385 | #endif /* OVLB */ 386 | #ifdef OVL0 387 | 388 | /* some common tests when trying to drop or throw items */ 389 | boolean 390 | canletgo(obj,word) 391 | register struct obj *obj; 392 | register const char *word; 393 | { 394 | if(obj->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)){ 395 | if (*word) 396 | Norep("You cannot %s %s you are wearing.",word, 397 | something); 398 | return(FALSE); 399 | } 400 | if (obj->otyp == LOADSTONE && obj->cursed) { 401 | if (*word) 402 | pline("For some reason, you cannot %s the stone%s!", 403 | word, plur(obj->quan)); 404 | /* Kludge -- see invent.c */ 405 | if (obj->corpsenm) { 406 | struct obj *otmp; 407 | 408 | otmp = obj; 409 | obj = obj->nobj; 410 | obj->quan += otmp->quan; 411 | obj->owt = weight(obj); 412 | freeinv(otmp); 413 | obfree(otmp, obj); 414 | } 415 | obj->bknown = 1; 416 | return(FALSE); 417 | } 418 | if (obj->otyp == LEASH && obj->leashmon != 0) { 419 | if (*word) 420 | pline_The("leash is tied around your %s.", 421 | body_part(HAND)); 422 | return(FALSE); 423 | } 424 | #ifdef STEED 425 | if (obj->owornmask & W_SADDLE) { 426 | if (*word) 427 | You("cannot %s %s you are sitting on.", word, 428 | something); 429 | return (FALSE); 430 | } 431 | #endif 432 | return(TRUE); 433 | } 434 | 435 | STATIC_PTR 436 | int 437 | drop(obj) 438 | register struct obj *obj; 439 | { 440 | if(!obj) return(0); 441 | if(!canletgo(obj,"drop")) 442 | return(0); 443 | if(obj == uwep) { 444 | if(welded(uwep)) { 445 | weldmsg(obj); 446 | return(0); 447 | } 448 | setuwep((struct obj *)0); 449 | if(uwep) return 0; /* lifesaved and rewielded */ 450 | } 451 | if(obj == uquiver) { 452 | setuqwep((struct obj *)0); 453 | } 454 | if (obj == uswapwep) { 455 | setuswapwep((struct obj *)0); 456 | } 457 | 458 | if (u.uswallow) { 459 | /* barrier between you and the floor */ 460 | if(flags.verbose) 461 | You("drop %s into %s %s.", doname(obj), 462 | s_suffix(mon_nam(u.ustuck)), 463 | is_animal(u.ustuck->data) ? 464 | "stomach" : "interior"); 465 | } else { 466 | #ifdef SINKS 467 | if((obj->oclass == RING_CLASS || obj->otyp == MEAT_RING) && 468 | IS_SINK(levl[u.ux][u.uy].typ)) { 469 | dosinkring(obj); 470 | return(1); 471 | } 472 | #endif 473 | if (!can_reach_floor()) { 474 | if(flags.verbose) You("drop %s.", doname(obj)); 475 | if (obj->oclass != GOLD_CLASS || obj == invent) freeinv(obj); 476 | hitfloor(obj); 477 | return(1); 478 | } 479 | if (IS_ALTAR(levl[u.ux][u.uy].typ)) { 480 | doaltarobj(obj); /* set bknown */ 481 | } else 482 | if(flags.verbose) You("drop %s.", doname(obj)); 483 | } 484 | dropx(obj); 485 | return(1); 486 | } 487 | 488 | /* Called in several places - should not produce texts */ 489 | /* ship_object() _can_ produce texts--is that comment still correct? */ 490 | void 491 | dropx(obj) 492 | register struct obj *obj; 493 | { 494 | /* Money is usually not in our inventory */ 495 | if (obj->oclass != GOLD_CLASS || obj == invent) freeinv(obj); 496 | if (!u.uswallow && ship_object(obj, u.ux, u.uy, FALSE)) return; 497 | dropy(obj); 498 | } 499 | 500 | void 501 | dropy(obj) 502 | register struct obj *obj; 503 | { 504 | if (!u.uswallow && flooreffects(obj,u.ux,u.uy,"drop")) return; 505 | /* KMH -- Fixed crysknives have only 10% chance of reverting */ 506 | if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) { 507 | obj->otyp = WORM_TOOTH; 508 | obj->oerodeproof = 0; 509 | } 510 | /* uswallow check done by GAN 01/29/87 */ 511 | if(u.uswallow) { 512 | if (obj != uball) { /* mon doesn't pick up ball */ 513 | (void) mpickobj(u.ustuck,obj); 514 | } 515 | } else { 516 | place_object(obj, u.ux, u.uy); 517 | if (obj == uball) 518 | drop_ball(u.ux,u.uy); 519 | else 520 | sellobj(obj, u.ux, u.uy); 521 | stackobj(obj); 522 | if(Blind && Levitation) 523 | map_object(obj, 0); 524 | newsym(u.ux,u.uy); /* remap location under self */ 525 | } 526 | } 527 | 528 | /* 'D' command: drop several things */ 529 | int 530 | doddrop() 531 | { 532 | int result = 0; 533 | 534 | add_valid_menu_class(0); /* clear any classes already there */ 535 | if (*u.ushops) sellobj_state(TRUE); 536 | if (flags.menu_style != MENU_TRADITIONAL || 537 | (result = ggetobj("drop", drop, 0, FALSE)) < -1) 538 | result = menu_drop(result); 539 | if (*u.ushops) sellobj_state(FALSE); 540 | reset_occupations(); 541 | 542 | return result; 543 | } 544 | 545 | /* Drop things from the hero's inventory, using a menu. */ 546 | STATIC_OVL int 547 | menu_drop(retry) 548 | int retry; 549 | { 550 | int n, i, n_dropped = 0; 551 | long cnt; 552 | struct obj *otmp, *otmp2, *u_gold = 0; 553 | menu_item *pick_list; 554 | boolean all_categories = TRUE; 555 | boolean drop_everything = FALSE; 556 | 557 | if (u.ugold) { 558 | /* Hack: gold is not in the inventory, so make a gold object 559 | and put it at the head of the inventory list. */ 560 | u_gold = mkgoldobj(u.ugold); /* removes from u.ugold */ 561 | u.ugold = u_gold->quan; /* put the gold back */ 562 | assigninvlet(u_gold); /* might end up as NOINVSYM */ 563 | u_gold->nobj = invent; 564 | invent = u_gold; 565 | } 566 | 567 | if (retry) { 568 | all_categories = (retry == -2); 569 | } else if (flags.menu_style == MENU_FULL) { 570 | all_categories = FALSE; 571 | n = query_category("Drop what type of items?", 572 | invent, 573 | UNPAID_TYPES | ALL_TYPES | CHOOSE_ALL, 574 | &pick_list, PICK_ANY); 575 | if (!n) goto drop_done; 576 | for (i = 0; i < n; i++) { 577 | if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) 578 | all_categories = TRUE; 579 | else if (pick_list[i].item.a_int == 'A') 580 | drop_everything = TRUE; 581 | else 582 | add_valid_menu_class(pick_list[i].item.a_int); 583 | } 584 | free((genericptr_t) pick_list); 585 | } else if (flags.menu_style == MENU_COMBINATION) { 586 | all_categories = FALSE; 587 | /* Gather valid classes via traditional NetHack method */ 588 | i = ggetobj("drop", drop, 0, TRUE); 589 | if (i == -2) all_categories = TRUE; 590 | } 591 | 592 | if (drop_everything) { 593 | for(otmp = invent; otmp; otmp = otmp2) { 594 | otmp2 = otmp->nobj; 595 | n_dropped += drop(otmp); 596 | } 597 | } else { 598 | /* should coordinate with perm invent, maybe not show worn items */ 599 | n = query_objlist("What would you like to drop?", invent, 600 | USE_INVLET|INVORDER_SORT, &pick_list, 601 | PICK_ANY, all_categories ? allow_all : allow_category); 602 | if (n > 0) { 603 | for (i = 0; i < n; i++) { 604 | otmp = pick_list[i].item.a_obj; 605 | cnt = pick_list[i].count; 606 | if (cnt < otmp->quan && !welded(otmp) && 607 | (!otmp->cursed || otmp->otyp != LOADSTONE)) { 608 | otmp2 = splitobj(otmp, cnt); 609 | /* assume other worn items aren't mergable */ 610 | if (otmp == uwep) setuwep(otmp2); 611 | if (otmp == uquiver) setuqwep(otmp2); 612 | if (otmp == uswapwep) setuswapwep(otmp2); 613 | } 614 | n_dropped += drop(otmp); 615 | } 616 | free((genericptr_t) pick_list); 617 | } 618 | } 619 | 620 | drop_done: 621 | if (u_gold && invent && invent->oclass == GOLD_CLASS) { 622 | /* didn't drop [all of] it */ 623 | u_gold = invent; 624 | invent = u_gold->nobj; 625 | dealloc_obj(u_gold); 626 | } 627 | return n_dropped; 628 | } 629 | 630 | #endif /* OVL0 */ 631 | #ifdef OVL2 632 | 633 | /* on a ladder, used in goto_level */ 634 | static NEARDATA boolean at_ladder = FALSE; 635 | 636 | int 637 | dodown() 638 | { 639 | struct trap *trap = 0; 640 | boolean stairs_down = ((u.ux == xdnstair && u.uy == ydnstair) || 641 | (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)), 642 | ladder_down = (u.ux == xdnladder && u.uy == ydnladder); 643 | 644 | if (Levitation) { 645 | if ((HLevitation & I_SPECIAL) || (ELevitation & W_ARTI)) { 646 | /* end controlled levitation */ 647 | if (float_down(I_SPECIAL|TIMEOUT, W_ARTI)) 648 | return (1); /* came down, so moved */ 649 | } 650 | floating_above(stairs_down ? "stairs" : ladder_down ? 651 | "ladder" : surface(u.ux, u.uy)); 652 | return (0); /* didn't move */ 653 | } 654 | if (!stairs_down && !ladder_down) { 655 | if (!(trap = t_at(u.ux,u.uy)) || 656 | (trap->ttyp != TRAPDOOR && trap->ttyp != HOLE) 657 | || !Can_fall_thru(&u.uz) || !trap->tseen) { 658 | You_cant("go down here."); 659 | return(0); 660 | } 661 | } 662 | if(u.ustuck) { 663 | You("are being held, and cannot go down."); 664 | return(1); 665 | } 666 | if (on_level(&valley_level, &u.uz) && !u.uevent.gehennom_entered) { 667 | You("are standing at the gate to Gehennom."); 668 | pline("Unspeakable cruelty and harm lurk down there."); 669 | if (yn("Are you sure you want to enter?") != 'y') 670 | return(0); 671 | else pline("So be it."); 672 | u.uevent.gehennom_entered = 1; /* don't ask again */ 673 | } 674 | 675 | if(!next_to_u()) { 676 | You("are held back by your pet!"); 677 | return(0); 678 | } 679 | 680 | if (trap) 681 | You("%s %s.", locomotion(youmonst.data, "jump"), 682 | trap->ttyp == HOLE ? "down the hole" : "through the trap door"); 683 | 684 | if (trap && Is_stronghold(&u.uz)) { 685 | goto_hell(TRUE, TRUE); 686 | } else { 687 | at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER); 688 | next_level(!trap); 689 | at_ladder = FALSE; 690 | } 691 | return(1); 692 | } 693 | 694 | int 695 | doup() 696 | { 697 | if( (u.ux != xupstair || u.uy != yupstair) 698 | && (!xupladder || u.ux != xupladder || u.uy != yupladder) 699 | && (!sstairs.sx || u.ux != sstairs.sx || u.uy != sstairs.sy 700 | || !sstairs.up) 701 | ) { 702 | You_cant("go up here."); 703 | return(0); 704 | } 705 | if(u.ustuck) { 706 | You("are being held, and cannot go up."); 707 | return(1); 708 | } 709 | if(near_capacity() > SLT_ENCUMBER) { 710 | /* No levitation check; inv_weight() already allows for it */ 711 | Your("load is too heavy to climb the %s.", 712 | levl[u.ux][u.uy].typ == STAIRS ? "stairs" : "ladder"); 713 | return(1); 714 | } 715 | if(ledger_no(&u.uz) == 1) { 716 | if (yn("Beware, there will be no return! Still climb?") != 'y') 717 | return(0); 718 | } 719 | if(!next_to_u()) { 720 | You("are held back by your pet!"); 721 | return(0); 722 | } 723 | at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER); 724 | prev_level(TRUE); 725 | at_ladder = FALSE; 726 | return(1); 727 | } 728 | 729 | d_level save_dlevel = {0, 0}; 730 | 731 | /* check that we can write out the current level */ 732 | STATIC_OVL int 733 | currentlevel_rewrite() 734 | { 735 | register int fd; 736 | 737 | /* since level change might be a bit slow, flush any buffered screen 738 | * output (like "you fall through a trap door") */ 739 | mark_synch(); 740 | 741 | fd = create_levelfile(ledger_no(&u.uz)); 742 | 743 | if(fd < 0) { 744 | /* 745 | * This is not quite impossible: e.g., we may have 746 | * exceeded our quota. If that is the case then we 747 | * cannot leave this level, and cannot save either. 748 | * Another possibility is that the directory was not 749 | * writable. 750 | */ 751 | pline("Cannot create level file for level %d.", 752 | ledger_no(&u.uz)); 753 | return -1; 754 | } 755 | 756 | #ifdef MFLOPPY 757 | if (!savelev(fd, ledger_no(&u.uz), COUNT_SAVE)) { 758 | (void) close(fd); 759 | delete_levelfile(ledger_no(&u.uz)); 760 | pline("NetHack is out of disk space for making levels!"); 761 | You("can save, quit, or continue playing."); 762 | return -1; 763 | } 764 | #endif 765 | return fd; 766 | } 767 | 768 | #ifdef INSURANCE 769 | void 770 | save_currentstate() 771 | { 772 | int fd; 773 | 774 | if (flags.ins_chkpt) { 775 | /* write out just-attained level, with pets and everything */ 776 | fd = currentlevel_rewrite(); 777 | if(fd < 0) return; 778 | bufon(fd); 779 | savelev(fd,ledger_no(&u.uz), WRITE_SAVE); 780 | bclose(fd); 781 | } 782 | 783 | /* write out non-level state */ 784 | savestateinlock(); 785 | } 786 | #endif 787 | 788 | /* 789 | static boolean 790 | badspot(x, y) 791 | register xchar x, y; 792 | { 793 | return((levl[x][y].typ != ROOM && levl[x][y].typ != AIR && 794 | levl[x][y].typ != CORR) || MON_AT(x, y)); 795 | } 796 | */ 797 | 798 | void 799 | goto_level(newlevel, at_stairs, falling, portal) 800 | d_level *newlevel; 801 | boolean at_stairs, falling, portal; 802 | { 803 | int fd, l_idx; 804 | xchar new_ledger; 805 | boolean cant_go_back, 806 | up = (depth(newlevel) < depth(&u.uz)), 807 | newdungeon = (u.uz.dnum != newlevel->dnum), 808 | was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz), 809 | familiar = FALSE; 810 | boolean new = FALSE; /* made a new level? */ 811 | struct monst *mtmp; 812 | 813 | if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel)) 814 | newlevel->dlevel = dunlevs_in_dungeon(newlevel); 815 | if (newdungeon && In_endgame(newlevel)) { /* 1st Endgame Level !!! */ 816 | if (u.uhave.amulet) 817 | assign_level(newlevel, &earth_level); 818 | else return; 819 | } 820 | new_ledger = ledger_no(newlevel); 821 | if (new_ledger <= 0) 822 | done(ESCAPED); /* in fact < 0 is impossible */ 823 | 824 | /* If you have the amulet and are trying to get out of Gehennom, going 825 | * up a set of stairs sometimes does some very strange things! 826 | * Biased against law and towards chaos, but not nearly as strongly 827 | * as it used to be (prior to 3.2.0). 828 | * Odds: old new 829 | * "up" L N C "up" L N C 830 | * +1 75.0 75.0 75.0 +1 75.0 75.0 75.0 831 | * 0 0.0 12.5 25.0 0 6.25 8.33 12.5 832 | * -1 8.33 4.17 0.0 -1 6.25 8.33 12.5 833 | * -2 8.33 4.17 0.0 -2 6.25 8.33 0.0 834 | * -3 8.33 4.17 0.0 -3 6.25 0.0 0.0 835 | */ 836 | if (Inhell && up && u.uhave.amulet && !newdungeon && !portal && 837 | (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)-3)) { 838 | if (!rn2(4)) { 839 | int odds = 3 + (int)u.ualign.type, /* 2..4 */ 840 | diff = odds <= 1 ? 0 : rn2(odds); /* paranoia */ 841 | 842 | if (diff != 0) { 843 | assign_rnd_level(newlevel, &u.uz, diff); 844 | /* if inside the tower, stay inside */ 845 | if (was_in_W_tower && 846 | !On_W_tower_level(newlevel)) diff = 0; 847 | } 848 | if (diff == 0) 849 | assign_level(newlevel, &u.uz); 850 | 851 | new_ledger = ledger_no(newlevel); 852 | 853 | pline("A mysterious force momentarily surrounds you..."); 854 | if (on_level(newlevel, &u.uz)) { 855 | (void) safe_teleds(); 856 | (void) next_to_u(); 857 | return; 858 | } else 859 | at_stairs = at_ladder = FALSE; 860 | } 861 | } 862 | 863 | /* Prevent the player from going past the first quest level unless 864 | * (s)he has been given the go-ahead by the leader. 865 | */ 866 | if (on_level(&u.uz, &qstart_level) && !newdungeon && !ok_to_quest()) { 867 | pline("A mysterious force prevents you from descending."); 868 | return; 869 | } 870 | 871 | if (on_level(newlevel, &u.uz)) return; /* this can happen */ 872 | 873 | fd = currentlevel_rewrite(); 874 | if (fd < 0) return; 875 | 876 | if (falling) /* assuming this is only trap door or hole */ 877 | impact_drop((struct obj *)0, u.ux, u.uy, newlevel->dlevel); 878 | 879 | check_special_room(TRUE); /* probably was a trap door */ 880 | if (Punished) unplacebc(); 881 | u.utrap = 0; /* needed in level_tele */ 882 | fill_pit(u.ux, u.uy); 883 | u.ustuck = 0; /* idem */ 884 | u.uinwater = 0; 885 | keepdogs(FALSE); 886 | if (u.uswallow) /* idem */ 887 | u.uswldtim = u.uswallow = 0; 888 | /* 889 | * We no longer see anything on the level. Make sure that this 890 | * follows u.uswallow set to null since uswallow overrides all 891 | * normal vision. 892 | */ 893 | vision_recalc(2); 894 | 895 | /* 896 | * Save the level we're leaving. If we're entering the endgame, 897 | * we can get rid of all existing levels because they cannot be 898 | * reached any more. We still need to use savelev()'s cleanup 899 | * for the level being left, to recover dynamic memory in use and 900 | * to avoid dangling timers and light sources. 901 | */ 902 | cant_go_back = (newdungeon && In_endgame(newlevel)); 903 | if (!cant_go_back) { 904 | update_mlstmv(); /* current monsters are becoming inactive */ 905 | bufon(fd); /* use buffered output */ 906 | } 907 | savelev(fd, ledger_no(&u.uz), 908 | cant_go_back ? FREE_SAVE : (WRITE_SAVE | FREE_SAVE)); 909 | bclose(fd); 910 | if (cant_go_back) { 911 | /* discard unreachable levels; keep #0 */ 912 | for (l_idx = maxledgerno(); l_idx > 0; --l_idx) 913 | delete_levelfile(l_idx); 914 | } 915 | 916 | #ifdef REINCARNATION 917 | if (Is_rogue_level(newlevel) || Is_rogue_level(&u.uz)) 918 | assign_rogue_graphics(Is_rogue_level(newlevel)); 919 | #endif 920 | #ifdef USE_TILES 921 | substitute_tiles(newlevel); 922 | #endif 923 | assign_level(&u.uz0, &u.uz); 924 | assign_level(&u.uz, newlevel); 925 | assign_level(&u.utolev, newlevel); 926 | u.utotype = 0; 927 | if (dunlev_reached(&u.uz) < dunlev(&u.uz)) 928 | dunlev_reached(&u.uz) = dunlev(&u.uz); 929 | reset_rndmonst(NON_PM); /* u.uz change affects monster generation */ 930 | 931 | /* set default level change destination areas */ 932 | /* the special level code may override these */ 933 | (void) memset((genericptr_t) &updest, 0, sizeof updest); 934 | (void) memset((genericptr_t) &dndest, 0, sizeof dndest); 935 | 936 | if (!(level_info[new_ledger].flags & LFILE_EXISTS)) { 937 | /* entering this level for first time; make it now */ 938 | if (level_info[new_ledger].flags & (FORGOTTEN|VISITED)) { 939 | impossible("goto_level: returning to discarded level?"); 940 | level_info[new_ledger].flags &= ~(FORGOTTEN|VISITED); 941 | } 942 | mklev(); 943 | new = TRUE; /* made the level */ 944 | } else { 945 | /* returning to previously visited level; reload it */ 946 | fd = open_levelfile(new_ledger); 947 | if (fd < 0) { 948 | pline("Cannot open file (#%d) for level %d (errno %d).", 949 | (int) new_ledger, depth(&u.uz), errno); 950 | pline("Probably someone removed it."); 951 | done(TRICKED); 952 | } 953 | minit(); /* ZEROCOMP */ 954 | getlev(fd, hackpid, new_ledger, FALSE); 955 | (void) close(fd); 956 | } 957 | /* do this prior to level-change pline messages */ 958 | vision_reset(); /* clear old level's line-of-sight */ 959 | vision_full_recalc = 0; /* don't let that reenable vision yet */ 960 | 961 | if (portal && !In_endgame(&u.uz)) { 962 | /* find the portal on the new level */ 963 | register struct trap *ttrap; 964 | 965 | for (ttrap = ftrap; ttrap; ttrap = ttrap->ntrap) 966 | if (ttrap->ttyp == MAGIC_PORTAL) break; 967 | 968 | if (!ttrap) panic("goto_level: no corresponding portal!"); 969 | seetrap(ttrap); 970 | u_on_newpos(ttrap->tx, ttrap->ty); 971 | } else if (at_stairs && !In_endgame(&u.uz)) { 972 | if (up) { 973 | if (at_ladder) { 974 | u_on_newpos(xdnladder, ydnladder); 975 | } else { 976 | if (newdungeon) { 977 | if (Is_stronghold(&u.uz)) { 978 | register xchar x, y; 979 | 980 | do { 981 | x = (COLNO - 2 - rnd(5)); 982 | y = rn1(ROWNO - 4, 3); 983 | } while(occupied(x, y) || 984 | IS_WALL(levl[x][y].typ)); 985 | u_on_newpos(x, y); 986 | } else u_on_sstairs(); 987 | } else u_on_dnstairs(); 988 | } 989 | /* Remove bug which crashes with levitation/punishment KAA */ 990 | if (Punished && !Levitation) { 991 | pline("With great effort you climb the %s.", 992 | at_ladder ? "ladder" : "stairs"); 993 | } else if (at_ladder) 994 | You("climb up the ladder."); 995 | } else { /* down */ 996 | if (at_ladder) { 997 | u_on_newpos(xupladder, yupladder); 998 | } else { 999 | if (newdungeon) u_on_sstairs(); 1000 | else u_on_upstairs(); 1001 | } 1002 | if (u.dz && Flying) 1003 | You("fly down along the %s.", 1004 | at_ladder ? "ladder" : "stairs"); 1005 | else if (u.dz && 1006 | (near_capacity() > UNENCUMBERED || Punished || Fumbling)) { 1007 | You("fall down the %s.", at_ladder ? "ladder" : "stairs"); 1008 | if (Punished) { 1009 | drag_down(); 1010 | if (carried(uball)) { 1011 | if (uwep == uball) 1012 | setuwep((struct obj *)0); 1013 | if (uswapwep == uball) 1014 | setuswapwep((struct obj *)0); 1015 | if (uquiver == uball) 1016 | setuqwep((struct obj *)0); 1017 | freeinv(uball); 1018 | } 1019 | } 1020 | losehp(rnd(3), "falling downstairs", KILLED_BY); 1021 | #ifdef STEED 1022 | if (u.usteed) dismount_steed(DISMOUNT_FELL); 1023 | #endif 1024 | selftouch("Falling, you"); 1025 | } else if (u.dz && at_ladder) 1026 | You("climb down the ladder."); 1027 | } 1028 | } else { /* trap door or level_tele or In_endgame */ 1029 | if (was_in_W_tower && On_W_tower_level(&u.uz)) 1030 | /* Stay inside the Wizard's tower when feasible. */ 1031 | /* Note: up vs down doesn't really matter in this case. */ 1032 | place_lregion(dndest.nlx, dndest.nly, 1033 | dndest.nhx, dndest.nhy, 1034 | 0,0, 0,0, LR_DOWNTELE, (d_level *) 0); 1035 | else if (up) 1036 | place_lregion(updest.lx, updest.ly, 1037 | updest.hx, updest.hy, 1038 | updest.nlx, updest.nly, 1039 | updest.nhx, updest.nhy, 1040 | LR_UPTELE, (d_level *) 0); 1041 | else 1042 | place_lregion(dndest.lx, dndest.ly, 1043 | dndest.hx, dndest.hy, 1044 | dndest.nlx, dndest.nly, 1045 | dndest.nhx, dndest.nhy, 1046 | LR_DOWNTELE, (d_level *) 0); 1047 | if (falling) { 1048 | if (Punished) ballfall(); 1049 | selftouch("Falling, you"); 1050 | } 1051 | } 1052 | 1053 | if (Punished) placebc(); 1054 | obj_delivery(); /* before killing geno'd monsters' eggs */ 1055 | losedogs(); 1056 | kill_genocided_monsters(); /* for those wiped out while in limbo */ 1057 | /* 1058 | * Expire all timers that have gone off while away. Must be 1059 | * after migrating monsters and objects are delivered 1060 | * (losedogs and obj_delivery). 1061 | */ 1062 | run_timers(); 1063 | 1064 | initrack(); 1065 | 1066 | if ((mtmp = m_at(u.ux, u.uy)) != 0 1067 | #ifdef STEED 1068 | && mtmp != u.usteed 1069 | #endif 1070 | ) { 1071 | /* There's a monster at your target destination; it might be one 1072 | which accompanied you--see mon_arrive(dogmove.c)--or perhaps 1073 | it was already here. Randomly move you to an adjacent spot 1074 | or else the monster to any nearby location. Prior to 3.3.0 1075 | the latter was done unconditionally. */ 1076 | coord cc; 1077 | 1078 | if (!rn2(2) && 1079 | enexto(&cc, u.ux, u.uy, youmonst.data) && 1080 | distu(cc.x, cc.y) <= 2) 1081 | u_on_newpos(cc.x, cc.y); /*[maybe give message here?]*/ 1082 | else 1083 | mnexto(mtmp); 1084 | 1085 | if ((mtmp = m_at(u.ux, u.uy)) != 0) { 1086 | impossible("mnexto failed (do.c)?"); 1087 | rloc(mtmp); 1088 | } 1089 | } 1090 | 1091 | /* initial movement of bubbles just before vision_recalc */ 1092 | if (Is_waterlevel(&u.uz)) 1093 | movebubbles(); 1094 | 1095 | if (level_info[new_ledger].flags & FORGOTTEN) { 1096 | forget_map(ALL_MAP); /* forget the map */ 1097 | forget_traps(); /* forget all traps too */ 1098 | familiar = TRUE; 1099 | level_info[new_ledger].flags &= ~FORGOTTEN; 1100 | } 1101 | 1102 | /* Reset the screen. */ 1103 | vision_reset(); /* reset the blockages */ 1104 | docrt(); /* does a full vision recalc */ 1105 | flush_screen(1); 1106 | 1107 | /* 1108 | * Move all plines beyond the screen reset. 1109 | */ 1110 | 1111 | /* give room entrance message, if any */ 1112 | check_special_room(FALSE); 1113 | 1114 | /* Check whether we just entered Gehennom. */ 1115 | if (!In_hell(&u.uz0) && Inhell) { 1116 | if (Is_valley(&u.uz)) { 1117 | You("arrive at the Valley of the Dead..."); 1118 | pline_The("odor of burnt flesh and decay pervades the air."); 1119 | #ifdef MICRO 1120 | display_nhwindow(WIN_MESSAGE, FALSE); 1121 | #endif 1122 | You_hear("groans and moans everywhere."); 1123 | } else pline("It is hot here. You smell smoke..."); 1124 | } 1125 | 1126 | if (familiar) { 1127 | static const char *fam_msgs[4] = { 1128 | "You have a sense of deja vu.", 1129 | "You feel like you've been here before.", 1130 | "This place looks familiar...", 1131 | 0 /* no message */ 1132 | }; 1133 | static const char *halu_fam_msgs[4] = { 1134 | "Whoa! Everything looks different.", 1135 | "You are surrounded by twisty little passages, all alike.", 1136 | "Gee, this looks like uncle Conan's place...", 1137 | 0 /* no message */ 1138 | }; 1139 | const char *mesg; 1140 | int which = rn2(4); 1141 | 1142 | if (Hallucination) 1143 | mesg = halu_fam_msgs[which]; 1144 | else 1145 | mesg = fam_msgs[which]; 1146 | if (mesg) pline(mesg); 1147 | } 1148 | 1149 | #ifdef REINCARNATION 1150 | if (new && Is_rogue_level(&u.uz)) 1151 | You("enter what seems to be an older, more primitive world."); 1152 | #endif 1153 | /* Final confrontation */ 1154 | if (In_endgame(&u.uz) && newdungeon && u.uhave.amulet) 1155 | resurrect(); 1156 | if (newdungeon && In_V_tower(&u.uz) && In_hell(&u.uz0)) 1157 | pline_The("heat and smoke are gone."); 1158 | 1159 | /* the message from your quest leader */ 1160 | if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") && 1161 | !(u.uevent.qexpelled || u.uevent.qcompleted || leaderless())) { 1162 | 1163 | if (u.uevent.qcalled) { 1164 | com_pager(Role_if(PM_ROGUE) ? 4 : 3); 1165 | } else { 1166 | com_pager(2); 1167 | u.uevent.qcalled = TRUE; 1168 | } 1169 | } 1170 | 1171 | /* once Croesus is dead, his alarm doesn't work any more */ 1172 | if (Is_knox(&u.uz) && (new || !mvitals[PM_CROESUS].died)) { 1173 | You("penetrated a high security area!"); 1174 | pline("An alarm sounds!"); 1175 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 1176 | if (!DEADMONSTER(mtmp) && mtmp->msleeping) mtmp->msleeping = 0; 1177 | } 1178 | 1179 | if (on_level(&u.uz, &astral_level)) 1180 | final_level(); 1181 | else 1182 | onquest(); 1183 | assign_level(&u.uz0, &u.uz); /* reset u.uz0 */ 1184 | 1185 | #ifdef INSURANCE 1186 | save_currentstate(); 1187 | #endif 1188 | 1189 | (void) pickup(1); 1190 | } 1191 | 1192 | STATIC_OVL void 1193 | final_level() 1194 | { 1195 | struct monst *mtmp; 1196 | struct obj *otmp; 1197 | coord mm; 1198 | int i; 1199 | 1200 | /* reset monster hostility relative to player */ 1201 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) 1202 | if (!DEADMONSTER(mtmp)) reset_hostility(mtmp); 1203 | 1204 | /* create some player-monsters */ 1205 | create_mplayers(rn1(4, 3), TRUE); 1206 | 1207 | /* create a guardian angel next to player, if worthy */ 1208 | if (Conflict) { 1209 | pline( 1210 | "A voice booms: \"Thy desire for conflict shall be fulfilled!\""); 1211 | for (i = rnd(4); i > 0; --i) { 1212 | mm.x = u.ux; 1213 | mm.y = u.uy; 1214 | if (enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) 1215 | (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type, 1216 | mm.x, mm.y, FALSE); 1217 | } 1218 | 1219 | } else if (u.ualign.record > 8) { /* fervent */ 1220 | pline("A voice whispers: \"Thou hast been worthy of me!\""); 1221 | mm.x = u.ux; 1222 | mm.y = u.uy; 1223 | if (enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) { 1224 | if ((mtmp = mk_roamer(&mons[PM_ANGEL], u.ualign.type, 1225 | mm.x, mm.y, TRUE)) != 0) { 1226 | if (!Blind) 1227 | pline("An angel appears near you."); 1228 | else 1229 | You_feel("the presence of a friendly angel near you."); 1230 | /* guardian angel -- the one case mtame doesn't 1231 | * imply an edog structure, so we don't want to 1232 | * call tamedog(). 1233 | */ 1234 | mtmp->mtame = 10; 1235 | /* make him strong enough vs. endgame foes */ 1236 | mtmp->m_lev = rn1(8,15); 1237 | mtmp->mhp = mtmp->mhpmax = 1238 | d((int)mtmp->m_lev,10) + 30 + rnd(30); 1239 | if ((otmp = select_hwep(mtmp)) == 0) { 1240 | otmp = mksobj(SILVER_SABER, FALSE, FALSE); 1241 | if (mpickobj(mtmp, otmp)) 1242 | panic("merged weapon?"); 1243 | } 1244 | bless(otmp); 1245 | if (otmp->spe < 4) otmp->spe += rnd(4); 1246 | if ((otmp = which_armor(mtmp, W_ARMS)) == 0 || 1247 | otmp->otyp != SHIELD_OF_REFLECTION) { 1248 | (void) mongets(mtmp, AMULET_OF_REFLECTION); 1249 | m_dowear(mtmp, TRUE); 1250 | } 1251 | } 1252 | } 1253 | } 1254 | } 1255 | 1256 | static char *dfr_pre_msg = 0, /* pline() before level change */ 1257 | *dfr_post_msg = 0; /* pline() after level change */ 1258 | 1259 | /* change levels at the end of this turn, after monsters finish moving */ 1260 | void 1261 | schedule_goto(tolev, at_stairs, falling, portal_flag, pre_msg, post_msg) 1262 | d_level *tolev; 1263 | boolean at_stairs, falling; 1264 | int portal_flag; 1265 | const char *pre_msg, *post_msg; 1266 | { 1267 | int typmask = 0100; /* non-zero triggers `deferred_goto' */ 1268 | 1269 | /* destination flags (`goto_level' args) */ 1270 | if (at_stairs) typmask |= 1; 1271 | if (falling) typmask |= 2; 1272 | if (portal_flag) typmask |= 4; 1273 | if (portal_flag < 0) typmask |= 0200; /* flag for portal removal */ 1274 | u.utotype = typmask; 1275 | /* destination level */ 1276 | assign_level(&u.utolev, tolev); 1277 | 1278 | if (pre_msg) 1279 | dfr_pre_msg = strcpy((char *)alloc(strlen(pre_msg) + 1), pre_msg); 1280 | if (post_msg) 1281 | dfr_post_msg = strcpy((char *)alloc(strlen(post_msg)+1), post_msg); 1282 | } 1283 | 1284 | /* handle something like portal ejection */ 1285 | void 1286 | deferred_goto() 1287 | { 1288 | if (!on_level(&u.uz, &u.utolev)) { 1289 | d_level dest; 1290 | int typmask = u.utotype; /* save it; goto_level zeroes u.utotype */ 1291 | 1292 | assign_level(&dest, &u.utolev); 1293 | if (dfr_pre_msg) pline(dfr_pre_msg); 1294 | goto_level(&dest, !!(typmask&1), !!(typmask&2), !!(typmask&4)); 1295 | if (typmask & 0200) { /* remove portal */ 1296 | struct trap *t = t_at(u.ux, u.uy); 1297 | 1298 | if (t) { 1299 | deltrap(t); 1300 | newsym(u.ux, u.uy); 1301 | } 1302 | } 1303 | if (dfr_post_msg) pline(dfr_post_msg); 1304 | } 1305 | u.utotype = 0; /* our caller keys off of this */ 1306 | if (dfr_pre_msg) 1307 | free((genericptr_t)dfr_pre_msg), dfr_pre_msg = 0; 1308 | if (dfr_post_msg) 1309 | free((genericptr_t)dfr_post_msg), dfr_post_msg = 0; 1310 | } 1311 | 1312 | #endif /* OVL2 */ 1313 | #ifdef OVL3 1314 | 1315 | /* 1316 | * Return TRUE if we created a monster for the corpse. If successful, the 1317 | * corpse is gone. 1318 | */ 1319 | boolean 1320 | revive_corpse(corpse) 1321 | struct obj *corpse; 1322 | { 1323 | struct monst *mtmp, *mcarry; 1324 | boolean is_uwep, chewed; 1325 | xchar where; 1326 | char *cname, cname_buf[BUFSZ]; 1327 | 1328 | where = corpse->where; 1329 | is_uwep = corpse == uwep; 1330 | cname = eos(strcpy(cname_buf, "bite-covered ")); 1331 | Strcpy(cname, corpse_xname(corpse, TRUE)); 1332 | mcarry = (where == OBJ_MINVENT) ? corpse->ocarry : 0; 1333 | mtmp = revive(corpse); /* corpse is gone if successful */ 1334 | 1335 | if (mtmp) { 1336 | chewed = (mtmp->mhp < mtmp->mhpmax); 1337 | if (chewed) cname = cname_buf; /* include "bite-covered" prefix */ 1338 | switch (where) { 1339 | case OBJ_INVENT: 1340 | if (is_uwep) 1341 | pline_The("%s writhes out of your grasp!", cname); 1342 | else 1343 | You_feel("squirming in your backpack!"); 1344 | break; 1345 | 1346 | case OBJ_FLOOR: 1347 | if (cansee(mtmp->mx, mtmp->my)) 1348 | pline("%s rises from the dead!", chewed ? 1349 | Adjmonnam(mtmp, "bite-covered") : Monnam(mtmp)); 1350 | break; 1351 | 1352 | case OBJ_MINVENT: /* probably a nymph's */ 1353 | if (cansee(mtmp->mx, mtmp->my)) { 1354 | if (canseemon(mcarry)) 1355 | pline("Startled, %s drops %s as it revives!", 1356 | mon_nam(mcarry), an(cname)); 1357 | else 1358 | pline("%s suddenly appears!", chewed ? 1359 | Adjmonnam(mtmp, "bite-covered") : Monnam(mtmp)); 1360 | } 1361 | break; 1362 | 1363 | default: 1364 | /* we should be able to handle the other cases... */ 1365 | impossible("revive_corpse: lost corpse @ %d", where); 1366 | break; 1367 | } 1368 | return TRUE; 1369 | } 1370 | return FALSE; 1371 | } 1372 | 1373 | /* Revive the corpse via a timeout. */ 1374 | /*ARGSUSED*/ 1375 | void 1376 | revive_mon(arg, timeout) 1377 | genericptr_t arg; 1378 | long timeout; 1379 | { 1380 | struct obj *body = (struct obj *) arg; 1381 | 1382 | /* if we succeed, the corpse is gone, otherwise, rot it away */ 1383 | if (!revive_corpse(body)) { 1384 | if (is_rider(&mons[body->corpsenm])) 1385 | You_feel("less hassled."); 1386 | (void) start_timer(250L - (monstermoves-body->age), 1387 | TIMER_OBJECT, ROT_CORPSE, arg); 1388 | } 1389 | } 1390 | 1391 | int 1392 | donull() 1393 | { 1394 | return(1); /* Do nothing, but let other things happen */ 1395 | } 1396 | 1397 | #endif /* OVL3 */ 1398 | #ifdef OVLB 1399 | 1400 | STATIC_PTR int 1401 | wipeoff() 1402 | { 1403 | if(u.ucreamed < 4) u.ucreamed = 0; 1404 | else u.ucreamed -= 4; 1405 | if (Blinded < 4) Blinded = 0; 1406 | else Blinded -= 4; 1407 | if (!Blinded) { 1408 | pline("You've got the glop off."); 1409 | u.ucreamed = 0; 1410 | Blinded = 1; 1411 | make_blinded(0L,TRUE); 1412 | return(0); 1413 | } else if (!u.ucreamed) { 1414 | Your("%s feels clean now.", body_part(FACE)); 1415 | return(0); 1416 | } 1417 | return(1); /* still busy */ 1418 | } 1419 | 1420 | int 1421 | dowipe() 1422 | { 1423 | if(u.ucreamed) { 1424 | static NEARDATA char buf[39]; 1425 | 1426 | Sprintf(buf, "wiping off your %s", body_part(FACE)); 1427 | set_occupation(wipeoff, buf, 0); 1428 | /* Not totally correct; what if they change back after now 1429 | * but before they're finished wiping? 1430 | */ 1431 | return(1); 1432 | } 1433 | Your("%s is already clean.", body_part(FACE)); 1434 | return(1); 1435 | } 1436 | 1437 | void 1438 | set_wounded_legs(side, timex) 1439 | register long side; 1440 | register int timex; 1441 | { 1442 | /* KMH -- STEED 1443 | * If you are riding, your steed gets the wounded legs instead. 1444 | * You still call this function, but don't lose hp. 1445 | * Caller is also responsible for adjusting messages. 1446 | */ 1447 | 1448 | if(!Wounded_legs) { 1449 | ATEMP(A_DEX)--; 1450 | flags.botl = 1; 1451 | } 1452 | 1453 | if(!Wounded_legs || (HWounded_legs & TIMEOUT)) 1454 | HWounded_legs = timex; 1455 | EWounded_legs = side; 1456 | (void)encumber_msg(); 1457 | } 1458 | 1459 | void 1460 | heal_legs() 1461 | { 1462 | if(Wounded_legs) { 1463 | if (ATEMP(A_DEX) < 0) { 1464 | ATEMP(A_DEX)++; 1465 | flags.botl = 1; 1466 | } 1467 | 1468 | #ifdef STEED 1469 | if (!u.usteed) 1470 | #endif 1471 | { 1472 | /* KMH, intrinsics patch */ 1473 | if((EWounded_legs & BOTH_SIDES) == BOTH_SIDES) { 1474 | Your("%s feel somewhat better.", 1475 | makeplural(body_part(LEG))); 1476 | } else { 1477 | Your("%s feels somewhat better.", 1478 | body_part(LEG)); 1479 | } 1480 | } 1481 | HWounded_legs = EWounded_legs = 0; 1482 | } 1483 | (void)encumber_msg(); 1484 | } 1485 | 1486 | #endif /* OVLB */ 1487 | 1488 | /*do.c*/