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*/