1 /* NetHack 3.6 trap.c $NHDT-Date: 1461568321 2016/04/25 07:12:01 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.268 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 extern const char *const destroy_strings
[][3]; /* from zap.c */
9 STATIC_DCL
void FDECL(dofiretrap
, (struct obj
*));
10 STATIC_DCL
void NDECL(domagictrap
);
11 STATIC_DCL boolean
FDECL(emergency_disrobe
, (boolean
*));
12 STATIC_DCL
int FDECL(untrap_prob
, (struct trap
*));
13 STATIC_DCL
void FDECL(move_into_trap
, (struct trap
*));
14 STATIC_DCL
int FDECL(try_disarm
, (struct trap
*, BOOLEAN_P
));
15 STATIC_DCL
void FDECL(reward_untrap
, (struct trap
*, struct monst
*));
16 STATIC_DCL
int FDECL(disarm_holdingtrap
, (struct trap
*));
17 STATIC_DCL
int FDECL(disarm_landmine
, (struct trap
*));
18 STATIC_DCL
int FDECL(disarm_squeaky_board
, (struct trap
*));
19 STATIC_DCL
int FDECL(disarm_shooting_trap
, (struct trap
*, int));
20 STATIC_DCL
int FDECL(try_lift
, (struct monst
*, struct trap
*, int,
22 STATIC_DCL
int FDECL(help_monster_out
, (struct monst
*, struct trap
*));
23 STATIC_DCL boolean
FDECL(thitm
, (int, struct monst
*, struct obj
*, int,
25 STATIC_DCL
void FDECL(launch_drop_spot
, (struct obj
*, XCHAR_P
, XCHAR_P
));
26 STATIC_DCL
int FDECL(mkroll_launch
, (struct trap
*, XCHAR_P
, XCHAR_P
,
28 STATIC_DCL boolean
FDECL(isclearpath
, (coord
*, int, SCHAR_P
, SCHAR_P
));
29 STATIC_DCL
char *FDECL(trapnote
, (struct trap
*, BOOLEAN_P
));
31 STATIC_DCL
void FDECL(join_adjacent_pits
, (struct trap
*));
33 STATIC_DCL
void FDECL(clear_conjoined_pits
, (struct trap
*));
34 STATIC_DCL
int FDECL(steedintrap
, (struct trap
*, struct obj
*));
35 STATIC_DCL boolean
FDECL(keep_saddle_with_steedcorpse
, (unsigned,
38 STATIC_DCL
void NDECL(maybe_finish_sokoban
);
40 /* mintrap() should take a flags argument, but for time being we use this */
41 STATIC_VAR
int force_mintrap
= 0;
43 STATIC_VAR
const char *const a_your
[2] = { "a", "your" };
44 STATIC_VAR
const char *const A_Your
[2] = { "A", "Your" };
45 STATIC_VAR
const char tower_of_flame
[] = "tower of flame";
46 STATIC_VAR
const char *const A_gush_of_water_hits
= "A gush of water hits";
47 STATIC_VAR
const char *const blindgas
[6] = { "humid", "odorless",
48 "pungent", "chilling",
51 /* called when you're hit by fire (dofiretrap,buzz,zapyourself,explode);
52 returns TRUE if hit on torso */
64 hitting_u
= (victim
== &youmonst
);
66 /* burning damage may dry wet towel */
67 item
= hitting_u
? carrying(TOWEL
) : m_carrying(victim
, TOWEL
);
69 if (is_wet_towel(item
)) {
71 dry_a_towel(item
, rn2(oldspe
+ 1), TRUE
);
72 if (item
->spe
!= oldspe
)
73 break; /* stop once one towel has been affected */
78 #define burn_dmg(obj, descr) erode_obj(obj, descr, ERODE_BURN, EF_GREASE)
82 item
= hitting_u
? uarmh
: which_armor(victim
, W_ARMH
);
84 mat_idx
= objects
[item
->otyp
].oc_material
;
85 Sprintf(buf
, "%s %s", materialnm
[mat_idx
],
86 helm_simple_name(item
));
88 if (!burn_dmg(item
, item
? buf
: "helmet"))
92 item
= hitting_u
? uarmc
: which_armor(victim
, W_ARMC
);
94 (void) burn_dmg(item
, cloak_simple_name(item
));
97 item
= hitting_u
? uarm
: which_armor(victim
, W_ARM
);
99 (void) burn_dmg(item
, xname(item
));
102 item
= hitting_u
? uarmu
: which_armor(victim
, W_ARMU
);
104 (void) burn_dmg(item
, "shirt");
107 item
= hitting_u
? uarms
: which_armor(victim
, W_ARMS
);
108 if (!burn_dmg(item
, "wooden shield"))
112 item
= hitting_u
? uarmg
: which_armor(victim
, W_ARMG
);
113 if (!burn_dmg(item
, "gloves"))
117 item
= hitting_u
? uarmf
: which_armor(victim
, W_ARMF
);
118 if (!burn_dmg(item
, "boots"))
122 break; /* Out of while loop */
129 /* Generic erode-item function.
130 * "ostr", if non-null, is an alternate string to print instead of the
132 * "type" is an ERODE_* value for the erosion type
133 * "flags" is an or-ed list of EF_* flags
135 * Returns an erosion return value (ER_*)
138 erode_obj(otmp
, ostr
, type
, ef_flags
)
139 register struct obj
*otmp
;
144 static NEARDATA
const char
145 *const action
[] = { "smoulder", "rust", "rot", "corrode" },
146 *const msg
[] = { "burnt", "rusted", "rotten", "corroded" },
147 *const bythe
[] = { "heat", "oxidation", "decay", "corrosion" };
148 boolean vulnerable
= FALSE
, is_primary
= TRUE
,
149 check_grease
= (ef_flags
& EF_GREASE
) ? TRUE
: FALSE
,
150 print
= (ef_flags
& EF_VERBOSE
) ? TRUE
: FALSE
,
151 uvictim
, vismon
, visobj
;
152 int erosion
, cost_type
;
153 struct monst
*victim
;
158 victim
= carried(otmp
) ? &youmonst
: mcarried(otmp
) ? otmp
->ocarry
: NULL
;
159 uvictim
= (victim
== &youmonst
);
160 vismon
= victim
&& (victim
!= &youmonst
) && canseemon(victim
);
161 /* Is bhitpos correct here? Ugh. */
162 visobj
= !victim
&& cansee(bhitpos
.x
, bhitpos
.y
);
166 vulnerable
= is_flammable(otmp
);
167 check_grease
= FALSE
;
168 cost_type
= COST_BURN
;
171 vulnerable
= is_rustprone(otmp
);
172 cost_type
= COST_RUST
;
175 vulnerable
= is_rottable(otmp
);
176 check_grease
= FALSE
;
178 cost_type
= COST_ROT
;
181 vulnerable
= is_corrodeable(otmp
);
183 cost_type
= COST_CORRODE
;
186 impossible("Invalid erosion type in erode_obj");
189 erosion
= is_primary
? otmp
->oeroded
: otmp
->oeroded2
;
193 /* 'visobj' messages insert "the"; probably ought to switch to the() */
194 if (visobj
&& !(uvictim
|| vismon
) && !strncmpi(ostr
, "the ", 4))
197 if (check_grease
&& otmp
->greased
) {
198 grease_protect(otmp
, ostr
, victim
);
200 } else if (!erosion_matters(otmp
)) {
202 } else if (!vulnerable
|| (otmp
->oerodeproof
&& otmp
->rknown
)) {
203 if (flags
.verbose
&& print
&& (uvictim
|| vismon
))
204 pline("%s %s %s not affected by %s.",
205 uvictim
? "Your" : s_suffix(Monnam(victim
)),
206 ostr
, vtense(ostr
, "are"), bythe
[type
]);
208 } else if (otmp
->oerodeproof
|| (otmp
->blessed
&& !rnl(4))) {
209 if (flags
.verbose
&& (print
|| otmp
->oerodeproof
)
210 && (uvictim
|| vismon
|| visobj
))
211 pline("Somehow, %s %s %s not affected by the %s.",
213 : !vismon
? "the" /* visobj */
214 : s_suffix(mon_nam(victim
)),
215 ostr
, vtense(ostr
, "are"), bythe
[type
]);
216 /* We assume here that if the object is protected because it
217 * is blessed, it still shows some minor signs of wear, and
218 * the hero can distinguish this from an object that is
219 * actually proof against damage.
221 if (otmp
->oerodeproof
) {
223 if (victim
== &youmonst
)
228 } else if (erosion
< MAX_ERODE
) {
229 const char *adverb
= (erosion
+ 1 == MAX_ERODE
)
231 : erosion
? " further" : "";
233 if (uvictim
|| vismon
|| visobj
)
236 : !vismon
? "The" /* visobj */
237 : s_suffix(Monnam(victim
)),
238 ostr
, vtense(ostr
, action
[type
]), adverb
);
240 if (ef_flags
& EF_PAY
)
241 costly_alteration(otmp
, cost_type
);
248 if (victim
== &youmonst
)
252 } else if (ef_flags
& EF_DESTROY
) {
253 if (uvictim
|| vismon
|| visobj
)
254 pline("%s %s %s away!",
256 : !vismon
? "The" /* visobj */
257 : s_suffix(Monnam(victim
)),
258 ostr
, vtense(ostr
, action
[type
]));
260 if (ef_flags
& EF_PAY
)
261 costly_alteration(otmp
, cost_type
);
267 if (flags
.verbose
&& print
) {
269 Your("%s %s completely %s.",
270 ostr
, vtense(ostr
, Blind
? "feel" : "look"), msg
[type
]);
271 else if (vismon
|| visobj
)
272 pline("%s %s %s completely %s.",
273 !vismon
? "The" : s_suffix(Monnam(victim
)),
274 ostr
, vtense(ostr
, "look"), msg
[type
]);
280 /* Protect an item from erosion with grease. Returns TRUE if the grease
284 grease_protect(otmp
, ostr
, victim
)
285 register struct obj
*otmp
;
287 struct monst
*victim
;
289 static const char txt
[] = "protected by the layer of grease!";
290 boolean vismon
= victim
&& (victim
!= &youmonst
) && canseemon(victim
);
293 if (victim
== &youmonst
)
294 Your("%s %s %s", ostr
, vtense(ostr
, "are"), txt
);
296 pline("%s's %s %s %s", Monnam(victim
),
297 ostr
, vtense(ostr
, "are"), txt
);
298 } else if (victim
== &youmonst
|| vismon
) {
299 pline("%s %s", Yobjnam2(otmp
, "are"), txt
);
304 pline_The("grease dissolves.");
316 static union vlaunchinfo zero_vl
;
319 struct rm
*lev
= &levl
[x
][y
];
321 if ((ttmp
= t_at(x
, y
)) != 0) {
322 if (ttmp
->ttyp
== MAGIC_PORTAL
|| ttmp
->ttyp
== VIBRATING_SQUARE
)
323 return (struct trap
*) 0;
325 if (u
.utrap
&& x
== u
.ux
&& y
== u
.uy
326 && ((u
.utraptype
== TT_BEARTRAP
&& typ
!= BEAR_TRAP
)
327 || (u
.utraptype
== TT_WEB
&& typ
!= WEB
)
328 || (u
.utraptype
== TT_PIT
&& typ
!= PIT
329 && typ
!= SPIKED_PIT
)))
331 /* old <tx,ty> remain valid */
332 } else if (IS_FURNITURE(lev
->typ
)
333 && (!IS_GRAVE(lev
->typ
) || (typ
!= PIT
&& typ
!= HOLE
))) {
334 /* no trap on top of furniture (caller usually screens the
335 location to inhibit this, but wizard mode wishing doesn't) */
336 return (struct trap
*) 0;
344 /* [re-]initialize all fields except ntrap (handled below) and <tx,ty> */
346 ttmp
->launch
.x
= ttmp
->launch
.y
= -1; /* force error if used before set */
347 ttmp
->dst
.dnum
= ttmp
->dst
.dlevel
= -1;
350 ttmp
->tseen
= (typ
== HOLE
); /* hide non-holes */
355 int tavail
[12], tpick
[12], tcnt
= 0, k
;
358 for (k
= 0; k
< 12; ++k
)
359 tavail
[k
] = tpick
[k
] = 0;
360 for (t
= ftrap
; t
; t
= t
->ntrap
)
361 if (t
->ttyp
== SQKY_BOARD
&& t
!= ttmp
)
362 tavail
[t
->tnote
] = 1;
363 /* now populate tpick[] with the available indices */
364 for (k
= 0; k
< 12; ++k
)
367 /* choose an unused note; if all are in use, pick a random one */
368 ttmp
->tnote
= (short) ((tcnt
> 0) ? tpick
[rn2(tcnt
)] : rn2(12));
371 case STATUE_TRAP
: { /* create a "living" statue */
373 struct obj
*otmp
, *statue
;
374 struct permonst
*mptr
;
377 do { /* avoid ultimately hostile co-aligned unicorn */
378 mptr
= &mons
[rndmonnum()];
379 } while (--trycount
> 0 && is_unicorn(mptr
)
380 && sgn(u
.ualign
.type
) == sgn(mptr
->maligntyp
));
381 statue
= mkcorpstat(STATUE
, (struct monst
*) 0, mptr
, x
, y
,
383 mtmp
= makemon(&mons
[statue
->corpsenm
], 0, 0, MM_NOCOUNTBIRTH
);
385 break; /* should never happen */
386 while (mtmp
->minvent
) {
387 otmp
= mtmp
->minvent
;
389 obj_extract_self(otmp
);
390 (void) add_to_container(statue
, otmp
);
392 statue
->owt
= weight(statue
);
396 case ROLLING_BOULDER_TRAP
: /* boulder will roll towards trigger */
397 (void) mkroll_launch(ttmp
, x
, y
, BOULDER
, 1L);
405 if (*in_rooms(x
, y
, SHOPBASE
)
406 && (typ
== HOLE
|| typ
== TRAPDOOR
407 || IS_DOOR(lev
->typ
) || IS_WALL(lev
->typ
)))
408 add_damage(x
, y
, /* schedule repair */
409 ((IS_DOOR(lev
->typ
) || IS_WALL(lev
->typ
))
410 && !context
.mon_moving
)
413 lev
->doormask
= 0; /* subsumes altarmask, icedpool... */
414 if (IS_ROOM(lev
->typ
)) /* && !IS_AIR(lev->typ) */
417 * some cases which can happen when digging
418 * down while phazing thru solid areas
420 else if (lev
->typ
== STONE
|| lev
->typ
== SCORR
)
422 else if (IS_WALL(lev
->typ
) || lev
->typ
== SDOOR
)
423 lev
->typ
= level
.flags
.is_maze_lev
425 : level
.flags
.is_cavernous_lev
? CORR
: DOOR
;
436 it shouldn't be possible to override a sokoban pit or hole
437 with some other trap, but we'll check just to be safe */
439 maybe_finish_sokoban();
446 boolean td
; /* td == TRUE : trap door or hole */
450 const char *dont_fall
= 0;
451 int newlevel
, bottom
;
453 /* we'll fall even while levitating in Sokoban; otherwise, if we
454 won't fall and won't be told that we aren't falling, give up now */
455 if (Blind
&& Levitation
&& !Sokoban
)
458 bottom
= dunlevs_in_dungeon(&u
.uz
);
459 /* when in the upper half of the quest, don't fall past the
460 middle "quest locate" level if hero hasn't been there yet */
461 if (In_quest(&u
.uz
)) {
462 int qlocate_depth
= qlocate_level
.dlevel
;
464 /* deepest reached < qlocate implies current < qlocate */
465 if (dunlev_reached(&u
.uz
) < qlocate_depth
)
466 bottom
= qlocate_depth
; /* early cut-off */
468 newlevel
= dunlev(&u
.uz
); /* current level */
471 } while (!rn2(4) && newlevel
< bottom
);
474 struct trap
*t
= t_at(u
.ux
, u
.uy
);
478 if (t
->ttyp
== TRAPDOOR
)
479 pline("A trap door opens up under you!");
481 pline("There's a gaping hole under you!");
484 pline_The("%s opens up under you!", surface(u
.ux
, u
.uy
));
486 if (Sokoban
&& Can_fall_thru(&u
.uz
))
487 ; /* KMH -- You can't escape the Sokoban level traps */
488 else if (Levitation
|| u
.ustuck
489 || (!Can_fall_thru(&u
.uz
) && !levl
[u
.ux
][u
.uy
].candig
) || Flying
490 || is_clinger(youmonst
.data
)
491 || (Inhell
&& !u
.uevent
.invoked
&& newlevel
== bottom
)) {
492 dont_fall
= "don't fall in.";
493 } else if (youmonst
.data
->msize
>= MZ_HUGE
) {
494 dont_fall
= "don't fit through.";
495 } else if (!next_to_u()) {
496 dont_fall
= "are jerked back by your pet!";
500 /* hero didn't fall through, but any objects here might */
501 impact_drop((struct obj
*) 0, u
.ux
, u
.uy
, 0);
503 display_nhwindow(WIN_MESSAGE
, FALSE
);
504 pline_The("opening under you closes up.");
511 if (Is_stronghold(&u
.uz
)) {
514 int dist
= newlevel
- dunlev(&u
.uz
);
515 dtmp
.dnum
= u
.uz
.dnum
;
516 dtmp
.dlevel
= newlevel
;
518 You("fall down a %s%sshaft!", dist
> 3 ? "very " : "",
519 dist
> 2 ? "deep " : "");
522 Sprintf(msgbuf
, "The hole in the %s above you closes up.",
523 ceiling(u
.ux
, u
.uy
));
525 schedule_goto(&dtmp
, FALSE
, TRUE
, 0, (char *) 0,
526 !td
? msgbuf
: (char *) 0);
530 * Animate the given statue. May have been via shatter attempt, trap,
531 * or stone to flesh spell. Return a monster if successfully animated.
532 * If the monster is animated, the object is deleted. If fail_reason
533 * is non-null, then fill in the reason for failure (or success).
535 * The cause of animation is:
537 * ANIMATE_NORMAL - hero "finds" the monster
538 * ANIMATE_SHATTER - hero tries to destroy the statue
539 * ANIMATE_SPELL - stone to flesh spell hits the statue
541 * Perhaps x, y is not needed if we can use get_obj_location() to find
542 * the statue's location... ???
544 * Sequencing matters:
545 * create monster; if it fails, give up with statue intact;
546 * give "statue comes to life" message;
547 * if statue belongs to shop, have shk give "you owe" message;
548 * transfer statue contents to monster (after stolen_value());
550 * [This ordering means that if the statue ends up wearing a cloak of
551 * invisibility or a mummy wrapping, the visibility checks might be
552 * wrong, but to avoid that we'd have to clone the statue contents
553 * first in order to give them to the monster before checking their
554 * shop status--it's not worth the hassle.]
557 animate_statue(statue
, x
, y
, cause
, fail_reason
)
563 int mnum
= statue
->corpsenm
;
564 struct permonst
*mptr
= &mons
[mnum
];
565 struct monst
*mon
= 0, *shkp
;
568 boolean historic
= (Role_if(PM_ARCHEOLOGIST
)
569 && (statue
->spe
& STATUE_HISTORIC
) != 0),
570 golem_xform
= FALSE
, use_saved_traits
;
571 const char *comes_to_life
;
572 char statuename
[BUFSZ
], tmpbuf
[BUFSZ
];
573 static const char historic_statue_is_gone
[] =
574 "that the historic statue is now gone";
576 if (cant_revive(&mnum
, TRUE
, statue
)) {
577 /* mnum has changed; we won't be animating this statue as itself */
578 if (mnum
!= PM_DOPPELGANGER
)
580 use_saved_traits
= FALSE
;
581 } else if (is_golem(mptr
) && cause
== ANIMATE_SPELL
) {
582 /* statue of any golem hit by stone-to-flesh becomes flesh golem */
583 golem_xform
= (mptr
!= &mons
[PM_FLESH_GOLEM
]);
584 mnum
= PM_FLESH_GOLEM
;
585 mptr
= &mons
[PM_FLESH_GOLEM
];
586 use_saved_traits
= (has_omonst(statue
) && !golem_xform
);
588 use_saved_traits
= has_omonst(statue
);
591 if (use_saved_traits
) {
592 /* restore a petrified monster */
594 mon
= montraits(statue
, &cc
);
595 if (mon
&& mon
->mtame
&& !mon
->isminion
)
598 /* statues of unique monsters from bones or wishing end
599 up here (cant_revive() sets mnum to be doppelganger;
600 mptr reflects the original form for use by newcham()) */
601 if ((mnum
== PM_DOPPELGANGER
&& mptr
!= &mons
[PM_DOPPELGANGER
])
602 /* block quest guards from other roles */
603 || (mptr
->msound
== MS_GUARDIAN
604 && quest_info(MS_GUARDIAN
) != mnum
)) {
605 mon
= makemon(&mons
[PM_DOPPELGANGER
], x
, y
,
606 NO_MINVENT
| MM_NOCOUNTBIRTH
| MM_ADJACENTOK
);
607 /* if hero has protection from shape changers, cham field will
608 be NON_PM; otherwise, set form to match the statue */
609 if (mon
&& mon
->cham
>= LOW_PM
)
610 (void) newcham(mon
, mptr
, FALSE
, FALSE
);
612 mon
= makemon(mptr
, x
, y
, (cause
== ANIMATE_SPELL
)
613 ? (NO_MINVENT
| MM_ADJACENTOK
)
619 *fail_reason
= unique_corpstat(&mons
[statue
->corpsenm
])
622 return (struct monst
*) 0;
625 /* a non-montraits() statue might specify gender */
626 if (statue
->spe
& STATUE_MALE
)
628 else if (statue
->spe
& STATUE_FEMALE
)
630 /* if statue has been named, give same name to the monster */
631 if (has_oname(statue
) && !unique_corpstat(mon
->data
))
632 mon
= christen_monst(mon
, ONAME(statue
));
633 /* mimic statue becomes seen mimic; other hiders won't be hidden */
637 mon
->mundetected
= FALSE
;
639 if (cause
== ANIMATE_NORMAL
|| cause
== ANIMATE_SHATTER
) {
640 /* trap always releases hostile monster */
641 mon
->mtame
= 0; /* (might be petrified pet tossed onto trap) */
646 comes_to_life
= !canspotmon(mon
)
650 : (nonliving(mon
->data
) || is_vampshifter(mon
))
653 if ((x
== u
.ux
&& y
== u
.uy
) || cause
== ANIMATE_SPELL
) {
654 /* "the|your|Manlobbi's statue [of a wombat]" */
655 shkp
= shop_keeper(*in_rooms(mon
->mx
, mon
->my
, SHOPBASE
));
656 Sprintf(statuename
, "%s%s", shk_your(tmpbuf
, statue
),
657 (cause
== ANIMATE_SPELL
658 /* avoid "of a shopkeeper" if it's Manlobbi himself
659 (if carried, it can't be unpaid--hence won't be
660 described as "Manlobbi's statue"--because there
661 wasn't any living shk when statue was picked up) */
662 && (mon
!= shkp
|| carried(statue
)))
665 pline("%s %s!", upstart(statuename
), comes_to_life
);
666 } else if (Hallucination
) { /* They don't know it's a statue */
667 pline_The("%s suddenly seems more animated.", rndmonnam((char *) 0));
668 } else if (cause
== ANIMATE_SHATTER
) {
670 Sprintf(statuename
, "%s%s", shk_your(tmpbuf
, statue
),
673 Strcpy(statuename
, "a statue");
674 pline("Instead of shattering, %s suddenly %s!", statuename
,
676 } else { /* cause == ANIMATE_NORMAL */
677 You("find %s posing as a statue.",
678 canspotmon(mon
) ? a_monnam(mon
) : something
);
679 if (!canspotmon(mon
) && Blind
)
684 /* if this isn't caused by a monster using a wand of striking,
685 there might be consequences for the hero */
686 if (!context
.mon_moving
) {
687 /* if statue is owned by a shop, hero will have to pay for it;
688 stolen_value gives a message (about debt or use of credit)
689 which refers to "it" so needs to follow a message describing
690 the object ("the statue comes to life" one above) */
691 if (cause
!= ANIMATE_NORMAL
&& costly_spot(x
, y
)
692 && (shkp
= shop_keeper(*in_rooms(x
, y
, SHOPBASE
))) != 0
693 /* avoid charging for Manlobbi's statue of Manlobbi
694 if stone-to-flesh is used on petrified shopkeep */
696 (void) stolen_value(statue
, x
, y
, (boolean
) shkp
->mpeaceful
,
700 You_feel("guilty %s.", historic_statue_is_gone
);
704 if (historic
&& cansee(x
, y
))
705 You_feel("regret %s.", historic_statue_is_gone
);
706 /* no alignment penalty */
709 /* transfer any statue contents to monster's inventory */
710 while ((item
= statue
->cobj
) != 0) {
711 obj_extract_self(item
);
712 (void) mpickobj(mon
, item
);
715 /* in case statue is wielded and hero zaps stone-to-flesh at self */
716 if (statue
->owornmask
)
717 remove_worn_item(statue
, TRUE
);
718 /* statue no longer exists */
721 /* avoid hiding under nothing */
722 if (x
== u
.ux
&& y
== u
.uy
&& Upolyd
&& hides_under(youmonst
.data
)
727 *fail_reason
= AS_OK
;
732 * You've either stepped onto a statue trap's location or you've triggered a
733 * statue trap by searching next to it or by trying to break it with a wand
737 activate_statue_trap(trap
, x
, y
, shatter
)
742 struct monst
*mtmp
= (struct monst
*) 0;
743 struct obj
*otmp
= sobj_at(STATUE
, x
, y
);
747 * Try to animate the first valid statue. Stop the loop when we
748 * actually create something or the failure cause is not because
749 * the mon was unique.
753 mtmp
= animate_statue(otmp
, x
, y
,
754 shatter
? ANIMATE_SHATTER
: ANIMATE_NORMAL
,
756 if (mtmp
|| fail_reason
!= AS_MON_IS_UNIQUE
)
759 otmp
= nxtobj(otmp
, STATUE
, TRUE
);
767 keep_saddle_with_steedcorpse(steed_mid
, objchn
, saddle
)
769 struct obj
*objchn
, *saddle
;
774 if (objchn
->otyp
== CORPSE
&& has_omonst(objchn
)) {
775 struct monst
*mtmp
= OMONST(objchn
);
777 if (mtmp
->m_id
== steed_mid
) {
780 if (get_obj_location(objchn
, &x
, &y
, 0)) {
781 obj_extract_self(saddle
);
782 place_object(saddle
, x
, y
);
788 if (Has_contents(objchn
)
789 && keep_saddle_with_steedcorpse(steed_mid
, objchn
->cobj
, saddle
))
791 objchn
= objchn
->nobj
;
796 /* monster or you go through and possibly destroy a web.
797 return TRUE if could go through. */
799 mu_maybe_destroy_web(mtmp
, domsg
, trap
)
804 boolean isyou
= (mtmp
== &youmonst
);
805 struct permonst
*mptr
= mtmp
->data
;
806 if (amorphous(mptr
) || is_whirly(mptr
) || flaming(mptr
)
807 || unsolid(mptr
) || mptr
== &mons
[PM_GELATINOUS_CUBE
]) {
810 if (flaming(mptr
) || acidic(mptr
)) {
813 You("%s %s spider web!",
814 (flaming(mptr
)) ? "burn" : "dissolve",
815 a_your
[trap
->madeby_u
]);
817 pline("%s %s %s spider web!", Monnam(mtmp
),
818 (flaming(mptr
)) ? "burns" : "dissolves",
819 a_your
[trap
->madeby_u
]);
827 You("flow through %s spider web.", a_your
[trap
->madeby_u
]);
829 pline("%s flows through %s spider web.", Monnam(mtmp
),
830 a_your
[trap
->madeby_u
]);
840 dotrap(trap
, trflags
)
841 register struct trap
*trap
;
844 register int ttype
= trap
->ttyp
;
845 register struct obj
*otmp
;
846 boolean already_seen
= trap
->tseen
,
847 forcetrap
= (trflags
& FORCETRAP
) != 0,
848 webmsgok
= (trflags
& NOWEBMSG
) == 0,
849 forcebungle
= (trflags
& FORCEBUNGLE
) != 0,
850 plunged
= (trflags
& TOOKPLUNGE
) != 0,
851 adj_pit
= conjoined_pits(trap
, t_at(u
.ux0
, u
.uy0
), TRUE
);
853 int steed_article
= ARTICLE_THE
;
857 /* KMH -- You can't escape the Sokoban level traps */
858 if (Sokoban
&& (ttype
== PIT
|| ttype
== SPIKED_PIT
859 || ttype
== HOLE
|| ttype
== TRAPDOOR
)) {
860 /* The "air currents" message is still appropriate -- even when
861 * the hero isn't flying or levitating -- because it conveys the
862 * reason why the player cannot escape the trap with a dexterity
863 * check, clinging to the ceiling, etc.
865 pline("Air currents pull you down into %s %s!",
866 a_your
[trap
->madeby_u
],
867 defsyms
[trap_to_defsym(ttype
)].explanation
);
868 /* then proceed to normal trap effect */
869 } else if (already_seen
&& !forcetrap
) {
870 if ((Levitation
|| (Flying
&& !plunged
))
871 && (ttype
== PIT
|| ttype
== SPIKED_PIT
|| ttype
== HOLE
872 || ttype
== BEAR_TRAP
)) {
873 You("%s over %s %s.", Levitation
? "float" : "fly",
874 a_your
[trap
->madeby_u
],
875 defsyms
[trap_to_defsym(ttype
)].explanation
);
878 if (!Fumbling
&& ttype
!= MAGIC_PORTAL
&& ttype
!= VIBRATING_SQUARE
879 && ttype
!= ANTI_MAGIC
&& !forcebungle
&& !plunged
&& !adj_pit
880 && (!rn2(5) || ((ttype
== PIT
|| ttype
== SPIKED_PIT
)
881 && is_clinger(youmonst
.data
)))) {
882 You("escape %s %s.", (ttype
== ARROW_TRAP
&& !trap
->madeby_u
)
884 : a_your
[trap
->madeby_u
],
885 defsyms
[trap_to_defsym(ttype
)].explanation
);
891 u
.usteed
->mtrapseen
|= (1 << (ttype
- 1));
892 /* suppress article in various steed messages when using its
893 name (which won't occur when hallucinating) */
894 if (has_mname(u
.usteed
) && !Hallucination
)
895 steed_article
= ARTICLE_NONE
;
900 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
901 You_hear("a loud click!");
908 pline("An arrow shoots out at you!");
909 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
911 otmp
->owt
= weight(otmp
);
913 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
915 } else if (thitu(8, dmgval(otmp
, &youmonst
), otmp
, "arrow")) {
916 obfree(otmp
, (struct obj
*) 0);
918 place_object(otmp
, u
.ux
, u
.uy
);
927 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
928 You_hear("a soft click.");
935 pline("A little dart shoots out at you!");
936 otmp
= mksobj(DART
, TRUE
, FALSE
);
938 otmp
->owt
= weight(otmp
);
941 oldumort
= u
.umortality
;
942 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
944 } else if (thitu(7, dmgval(otmp
, &youmonst
), otmp
, "little dart")) {
946 poisoned("dart", A_CON
, "little dart",
947 /* if damage triggered life-saving,
948 poison is limited to attrib loss */
949 (u
.umortality
> oldumort
) ? 0 : 10, TRUE
);
950 obfree(otmp
, (struct obj
*) 0);
952 place_object(otmp
, u
.ux
, u
.uy
);
961 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
962 pline("A trap door in %s opens, but nothing falls out!",
963 the(ceiling(u
.ux
, u
.uy
)));
967 int dmg
= d(2, 6); /* should be std ROCK dmg? */
971 otmp
= mksobj_at(ROCK
, u
.ux
, u
.uy
, TRUE
, FALSE
);
973 otmp
->owt
= weight(otmp
);
975 pline("A trap door in %s opens and %s falls on your %s!",
976 the(ceiling(u
.ux
, u
.uy
)), an(xname(otmp
)), body_part(HEAD
));
979 if (is_metallic(uarmh
)) {
980 pline("Fortunately, you are wearing a hard helmet.");
982 } else if (flags
.verbose
) {
983 pline("%s does not protect you.", Yname2(uarmh
));
990 newsym(u
.ux
, u
.uy
); /* map the rock */
992 losehp(Maybe_Half_Phys(dmg
), "falling rock", KILLED_BY_AN
);
993 exercise(A_STR
, FALSE
);
997 case SQKY_BOARD
: /* stepped on a squeaky board */
998 if ((Levitation
|| Flying
) && !forcetrap
) {
1002 You("notice a crease in the linoleum.");
1004 You("notice a loose board below you.");
1008 pline("A board beneath you %s%s%s.",
1009 Deaf
? "vibrates" : "squeaks ",
1010 Deaf
? "" : trapnote(trap
, 0), Deaf
? "" : " loudly");
1018 if ((Levitation
|| Flying
) && !forcetrap
)
1021 if (amorphous(youmonst
.data
) || is_whirly(youmonst
.data
)
1022 || unsolid(youmonst
.data
)) {
1023 pline("%s bear trap closes harmlessly through you.",
1024 A_Your
[trap
->madeby_u
]);
1027 if (!u
.usteed
&& youmonst
.data
->msize
<= MZ_SMALL
) {
1028 pline("%s bear trap closes harmlessly over you.",
1029 A_Your
[trap
->madeby_u
]);
1032 u
.utrap
= rn1(4, 4);
1033 u
.utraptype
= TT_BEARTRAP
;
1035 pline("%s bear trap closes on %s %s!", A_Your
[trap
->madeby_u
],
1036 s_suffix(mon_nam(u
.usteed
)), mbodypart(u
.usteed
, FOOT
));
1037 if (thitm(0, u
.usteed
, (struct obj
*) 0, dmg
, FALSE
))
1038 u
.utrap
= 0; /* steed died, hero not trapped */
1040 pline("%s bear trap closes on your %s!", A_Your
[trap
->madeby_u
],
1042 set_wounded_legs(rn2(2) ? RIGHT_SIDE
: LEFT_SIDE
, rn1(10, 10));
1043 if (u
.umonnum
== PM_OWLBEAR
|| u
.umonnum
== PM_BUGBEAR
)
1044 You("howl in anger!");
1045 losehp(Maybe_Half_Phys(dmg
), "bear trap", KILLED_BY_AN
);
1047 exercise(A_DEX
, FALSE
);
1053 if (Sleep_resistance
|| breathless(youmonst
.data
)) {
1054 You("are enveloped in a cloud of gas!");
1056 pline("A cloud of gas puts you to sleep!");
1057 fall_asleep(-rnd(25), TRUE
);
1059 (void) steedintrap(trap
, (struct obj
*) 0);
1065 /* Unlike monsters, traps cannot aim their rust attacks at
1066 * you, so instead of looping through and taking either the
1067 * first rustable one or the body, we take whatever we get,
1068 * even if it is not rustable.
1072 pline("%s you on the %s!", A_gush_of_water_hits
, body_part(HEAD
));
1073 (void) water_damage(uarmh
, helm_simple_name(uarmh
), TRUE
);
1076 pline("%s your left %s!", A_gush_of_water_hits
, body_part(ARM
));
1077 if (water_damage(uarms
, "shield", TRUE
) != ER_NOTHING
)
1079 if (u
.twoweap
|| (uwep
&& bimanual(uwep
)))
1080 (void) water_damage(u
.twoweap
? uswapwep
: uwep
, 0, TRUE
);
1082 (void) water_damage(uarmg
, "gauntlets", TRUE
);
1083 /* Not "metal gauntlets" since it gets called
1084 * even if it's leather for the message
1088 pline("%s your right %s!", A_gush_of_water_hits
, body_part(ARM
));
1089 (void) water_damage(uwep
, 0, TRUE
);
1092 pline("%s you!", A_gush_of_water_hits
);
1093 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1094 if (otmp
->lamplit
&& otmp
!= uwep
1095 && (otmp
!= uswapwep
|| !u
.twoweap
))
1096 (void) snuff_lit(otmp
);
1098 (void) water_damage(uarmc
, cloak_simple_name(uarmc
), TRUE
);
1100 (void) water_damage(uarm
, "armor", TRUE
);
1102 (void) water_damage(uarmu
, "shirt", TRUE
);
1106 if (u
.umonnum
== PM_IRON_GOLEM
) {
1109 pline("%s you!", A_gush_of_water_hits
);
1110 You("are covered with rust!");
1111 losehp(Maybe_Half_Phys(dam
), "rusting away", KILLED_BY
);
1112 } else if (u
.umonnum
== PM_GREMLIN
&& rn2(3)) {
1113 pline("%s you!", A_gush_of_water_hits
);
1114 (void) split_mon(&youmonst
, (struct monst
*) 0);
1121 dofiretrap((struct obj
*) 0);
1126 /* KMH -- You can't escape the Sokoban level traps */
1127 if (!Sokoban
&& (Levitation
|| (Flying
&& !plunged
)))
1130 if (!Sokoban
&& is_clinger(youmonst
.data
) && !plunged
) {
1132 You_see("%s %spit below you.", a_your
[trap
->madeby_u
],
1133 ttype
== SPIKED_PIT
? "spiked " : "");
1135 pline("%s pit %sopens up under you!", A_Your
[trap
->madeby_u
],
1136 ttype
== SPIKED_PIT
? "full of spikes " : "");
1137 You("don't fall in!");
1142 char verbbuf
[BUFSZ
];
1145 if ((trflags
& RECURSIVETRAP
) != 0)
1146 Sprintf(verbbuf
, "and %s fall",
1147 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1148 SUPPRESS_SADDLE
, FALSE
));
1150 Sprintf(verbbuf
, "lead %s",
1151 x_monnam(u
.usteed
, steed_article
, "poor",
1152 SUPPRESS_SADDLE
, FALSE
));
1153 } else if (adj_pit
) {
1154 You("move into an adjacent pit.");
1157 !plunged
? "fall" : (Flying
? "dive" : "plunge"));
1158 You("%s into %s pit!", verbbuf
, a_your
[trap
->madeby_u
]);
1161 /* wumpus reference */
1162 if (Role_if(PM_RANGER
) && !trap
->madeby_u
&& !trap
->once
1163 && In_quest(&u
.uz
) && Is_qlocate(&u
.uz
)) {
1164 pline("Fortunately it has a bottom after all...");
1166 } else if (u
.umonnum
== PM_PIT_VIPER
|| u
.umonnum
== PM_PIT_FIEND
) {
1167 pline("How pitiful. Isn't that the pits?");
1169 if (ttype
== SPIKED_PIT
) {
1170 const char *predicament
= "on a set of sharp iron spikes";
1174 upstart(x_monnam(u
.usteed
, steed_article
, "poor",
1175 SUPPRESS_SADDLE
, FALSE
)),
1176 adj_pit
? "steps" : "lands", predicament
);
1178 You("%s %s!", adj_pit
? "step" : "land", predicament
);
1180 u
.utrap
= rn1(6, 2);
1181 u
.utraptype
= TT_PIT
;
1182 if (!steedintrap(trap
, (struct obj
*) 0)) {
1183 if (ttype
== SPIKED_PIT
) {
1184 oldumort
= u
.umortality
;
1185 losehp(Maybe_Half_Phys(rnd(adj_pit
? 6 : 10)),
1187 ? "deliberately plunged into a pit of iron spikes"
1188 : adj_pit
? "stepped into a pit of iron spikes"
1189 : "fell into a pit of iron spikes",
1192 poisoned("spikes", A_STR
,
1193 adj_pit
? "stepping on poison spikes"
1194 : "fall onto poison spikes",
1195 /* if damage triggered life-saving,
1196 poison is limited to attrib loss */
1197 (u
.umortality
> oldumort
) ? 0 : 8, FALSE
);
1199 /* plunging flyers take spike damage but not pit damage */
1201 && !(plunged
&& (Flying
|| is_clinger(youmonst
.data
))))
1202 losehp(Maybe_Half_Phys(rnd(6)),
1203 plunged
? "deliberately plunged into a pit"
1204 : "fell into a pit",
1207 if (Punished
&& !carried(uball
)) {
1213 selftouch("Falling, you");
1214 vision_full_recalc
= 1; /* vision limits change */
1215 exercise(A_STR
, FALSE
);
1216 exercise(A_DEX
, FALSE
);
1222 if (!Can_fall_thru(&u
.uz
)) {
1223 seetrap(trap
); /* normally done in fall_through */
1224 impossible("dotrap: %ss cannot exist on this level.",
1225 defsyms
[trap_to_defsym(ttype
)].explanation
);
1226 break; /* don't activate it after all */
1238 level_tele_trap(trap
);
1241 case WEB
: /* Our luckless player has stumbled into a web. */
1243 if (mu_maybe_destroy_web(&youmonst
, webmsgok
, trap
))
1245 if (webmaker(youmonst
.data
)) {
1247 pline(trap
->madeby_u
? "You take a walk on your web."
1248 : "There is a spider web here.");
1252 char verbbuf
[BUFSZ
];
1255 Strcpy(verbbuf
, "are caught by");
1256 } else if (u
.usteed
) {
1257 Sprintf(verbbuf
, "lead %s into",
1258 x_monnam(u
.usteed
, steed_article
, "poor",
1259 SUPPRESS_SADDLE
, FALSE
));
1261 Sprintf(verbbuf
, "%s into",
1262 Levitation
? (const char *) "float"
1263 : locomotion(youmonst
.data
, "stumble"));
1265 You("%s %s spider web!", verbbuf
, a_your
[trap
->madeby_u
]);
1267 u
.utraptype
= TT_WEB
;
1269 /* Time stuck in the web depends on your/steed strength. */
1271 register int str
= ACURR(A_STR
);
1273 /* If mounted, the steed gets trapped. Use mintrap
1274 * to do all the work. If mtrapped is set as a result,
1275 * unset it and set utrap instead. In the case of a
1276 * strongmonst and mintrap said it's trapped, use a
1277 * short but non-zero trap time. Otherwise, monsters
1278 * have no specific strength, so use player strength.
1279 * This gets skipped for webmsgok, which implies that
1280 * the steed isn't a factor.
1282 if (u
.usteed
&& webmsgok
) {
1283 /* mtmp location might not be up to date */
1284 u
.usteed
->mx
= u
.ux
;
1285 u
.usteed
->my
= u
.uy
;
1287 /* mintrap currently does not return 2(died) for webs */
1288 if (mintrap(u
.usteed
)) {
1289 u
.usteed
->mtrapped
= 0;
1290 if (strongmonst(u
.usteed
->data
))
1296 webmsgok
= FALSE
; /* mintrap printed the messages */
1299 u
.utrap
= rn1(6, 6);
1301 u
.utrap
= rn1(6, 4);
1303 u
.utrap
= rn1(4, 4);
1305 u
.utrap
= rn1(4, 2);
1307 u
.utrap
= rn1(2, 2);
1315 You("tear through %s web!", a_your
[trap
->madeby_u
]);
1317 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1323 (void) activate_statue_trap(trap
, u
.ux
, u
.uy
, FALSE
);
1326 case MAGIC_TRAP
: /* A magic trap. */
1330 newsym(u
.ux
, u
.uy
); /* update position */
1331 You("are caught in a magical explosion!");
1332 losehp(rnd(10), "magical explosion", KILLED_BY_AN
);
1333 Your("body absorbs some of the magical energy!");
1334 u
.uen
= (u
.uenmax
+= 2);
1339 (void) steedintrap(trap
, (struct obj
*) 0);
1344 /* hero without magic resistance loses spell energy,
1345 hero with magic resistance takes damage instead;
1346 possibly non-intuitive but useful for play balance */
1348 drain_en(rnd(u
.ulevel
) + 1);
1350 int dmgval2
= rnd(4), hp
= Upolyd
? u
.mh
: u
.uhp
;
1352 /* Half_XXX_damage has opposite its usual effect (approx)
1353 but isn't cumulative if hero has more than one */
1354 if (Half_physical_damage
|| Half_spell_damage
)
1356 /* give Magicbane wielder dose of own medicine */
1357 if (uwep
&& uwep
->oartifact
== ART_MAGICBANE
)
1359 /* having an artifact--other than own quest one--which
1360 confers magic resistance simply by being carried
1361 also increases the effect */
1362 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1363 if (otmp
->oartifact
&& !is_quest_artifact(otmp
)
1364 && defends_when_carried(AD_MAGM
, otmp
))
1369 dmgval2
= (dmgval2
+ 3) / 4;
1371 You_feel((dmgval2
>= hp
) ? "unbearably torpid!"
1372 : (dmgval2
>= hp
/ 4) ? "very lethargic."
1374 /* opposite of magical explosion */
1375 losehp(dmgval2
, "anti-magic implosion", KILLED_BY_AN
);
1380 char verbbuf
[BUFSZ
];
1384 Sprintf(verbbuf
, "lead %s",
1385 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1386 SUPPRESS_SADDLE
, FALSE
));
1388 Sprintf(verbbuf
, "%s", Levitation
1389 ? (const char *) "float"
1390 : locomotion(youmonst
.data
, "step"));
1391 You("%s onto a polymorph trap!", verbbuf
);
1392 if (Antimagic
|| Unchanging
) {
1393 shieldeff(u
.ux
, u
.uy
);
1394 You_feel("momentarily different.");
1395 /* Trap did nothing; don't remove it --KAA */
1397 (void) steedintrap(trap
, (struct obj
*) 0);
1398 deltrap(trap
); /* delete trap before polymorph */
1399 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1400 You_feel("a change coming over you.");
1406 unsigned steed_mid
= 0;
1407 struct obj
*saddle
= 0;
1409 if ((Levitation
|| Flying
) && !forcetrap
) {
1410 if (!already_seen
&& rn2(3))
1413 pline("%s %s in a pile of soil below you.",
1414 already_seen
? "There is" : "You discover",
1415 trap
->madeby_u
? "the trigger of your mine" : "a trigger");
1416 if (already_seen
&& rn2(3))
1418 pline("KAABLAMM!!! %s %s%s off!",
1419 forcebungle
? "Your inept attempt sets"
1420 : "The air currents set",
1421 already_seen
? a_your
[trap
->madeby_u
] : "",
1422 already_seen
? " land mine" : "it");
1424 /* prevent landmine from killing steed, throwing you to
1425 * the ground, and you being affected again by the same
1426 * mine because it hasn't been deleted yet
1428 static boolean recursive_mine
= FALSE
;
1433 pline("KAABLAMM!!! You triggered %s land mine!",
1434 a_your
[trap
->madeby_u
]);
1436 steed_mid
= u
.usteed
->m_id
;
1437 recursive_mine
= TRUE
;
1438 (void) steedintrap(trap
, (struct obj
*) 0);
1439 recursive_mine
= FALSE
;
1440 saddle
= sobj_at(SADDLE
, u
.ux
, u
.uy
);
1441 set_wounded_legs(LEFT_SIDE
, rn1(35, 41));
1442 set_wounded_legs(RIGHT_SIDE
, rn1(35, 41));
1443 exercise(A_DEX
, FALSE
);
1445 blow_up_landmine(trap
);
1446 if (steed_mid
&& saddle
&& !u
.usteed
)
1447 (void) keep_saddle_with_steedcorpse(steed_mid
, fobj
, saddle
);
1448 newsym(u
.ux
, u
.uy
); /* update trap symbol */
1449 losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN
);
1450 /* fall recursively into the pit... */
1451 if ((trap
= t_at(u
.ux
, u
.uy
)) != 0)
1452 dotrap(trap
, RECURSIVETRAP
);
1453 fill_pit(u
.ux
, u
.uy
);
1457 case ROLLING_BOULDER_TRAP
: {
1458 int style
= ROLL
| (trap
->tseen
? LAUNCH_KNOWN
: 0);
1461 pline("Click! You trigger a rolling boulder trap!");
1462 if (!launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
1463 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
1465 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1466 pline("Fortunately for you, no boulder was released.");
1473 domagicportal(trap
);
1476 case VIBRATING_SQUARE
:
1478 /* messages handled elsewhere; the trap symbol is merely to mark the
1479 * square for future reference */
1484 impossible("You hit a trap of type %u", trap
->ttyp
);
1489 trapnote(trap
, noprefix
)
1493 static char tnbuf
[12];
1495 *tnnames
[12] = { "C note", "D flat", "D note", "E flat",
1496 "E note", "F note", "F sharp", "G note",
1497 "G sharp", "A note", "B flat", "B note" };
1500 tn
= tnnames
[trap
->tnote
];
1502 Sprintf(tnbuf
, "%s ",
1503 (*tn
== 'A' || *tn
== 'E' || *tn
== 'F') ? "an" : "a");
1504 Sprintf(eos(tnbuf
), "%s", tn
);
1509 steedintrap(trap
, otmp
)
1513 struct monst
*steed
= u
.usteed
;
1515 boolean trapkilled
, steedhit
;
1517 if (!steed
|| !trap
)
1522 trapkilled
= steedhit
= FALSE
;
1527 impossible("steed hit by non-existant arrow?");
1530 trapkilled
= thitm(8, steed
, otmp
, 0, FALSE
);
1535 impossible("steed hit by non-existant dart?");
1538 trapkilled
= thitm(7, steed
, otmp
, 0, FALSE
);
1542 if (!resists_sleep(steed
) && !breathless(steed
->data
)
1543 && !steed
->msleeping
&& steed
->mcanmove
) {
1544 if (sleep_monst(steed
, rnd(25), -1))
1545 /* no in_sight check here; you can feel it even if blind */
1546 pline("%s suddenly falls asleep!", Monnam(steed
));
1551 trapkilled
= thitm(0, steed
, (struct obj
*) 0, rnd(16), FALSE
);
1556 trapkilled
= (steed
->mhp
<= 0
1557 || thitm(0, steed
, (struct obj
*) 0,
1558 rnd((tt
== PIT
) ? 6 : 10), FALSE
));
1562 if (!resists_magm(steed
) && !resist(steed
, WAND_CLASS
, 0, NOTELL
)) {
1563 (void) newcham(steed
, (struct permonst
*) 0, FALSE
, FALSE
);
1564 if (!can_saddle(steed
) || !can_ride(steed
))
1565 dismount_steed(DISMOUNT_POLY
);
1567 You("have to adjust yourself in the saddle on %s.",
1568 x_monnam(steed
, ARTICLE_A
, (char *) 0, SUPPRESS_SADDLE
,
1578 dismount_steed(DISMOUNT_POLY
);
1581 return steedhit
? 1 : 0;
1584 /* some actions common to both player and monsters for triggered landmine */
1586 blow_up_landmine(trap
)
1589 int x
= trap
->tx
, y
= trap
->ty
, dbx
, dby
;
1590 struct rm
*lev
= &levl
[x
][y
];
1592 (void) scatter(x
, y
, 4,
1593 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
| VIS_EFFECTS
,
1596 wake_nearto(x
, y
, 400);
1597 if (IS_DOOR(lev
->typ
))
1598 lev
->doormask
= D_BROKEN
;
1599 /* destroy drawbridge if present */
1600 if (lev
->typ
== DRAWBRIDGE_DOWN
|| is_drawbridge_wall(x
, y
) >= 0) {
1602 /* if under the portcullis, the bridge is adjacent */
1603 if (find_drawbridge(&dbx
, &dby
))
1604 destroy_drawbridge(dbx
, dby
);
1605 trap
= t_at(x
, y
); /* expected to be null after destruction */
1607 /* convert landmine into pit */
1609 if (Is_waterlevel(&u
.uz
) || Is_airlevel(&u
.uz
)) {
1613 trap
->ttyp
= PIT
; /* explosion creates a pit */
1614 trap
->madeby_u
= FALSE
; /* resulting pit isn't yours */
1615 seetrap(trap
); /* and it isn't concealed */
1621 * The following are used to track launched objects to
1622 * prevent them from vanishing if you are killed. They
1623 * will reappear at the launchplace in bones files.
1631 launch_drop_spot(obj
, x
, y
)
1636 launchplace
.obj
= (struct obj
*) 0;
1640 launchplace
.obj
= obj
;
1647 launch_in_progress()
1649 if (launchplace
.obj
)
1655 force_launch_placement()
1657 if (launchplace
.obj
) {
1658 launchplace
.obj
->otrapped
= 0;
1659 place_object(launchplace
.obj
, launchplace
.x
, launchplace
.y
);
1664 * Move obj from (x1,y1) to (x2,y2)
1666 * Return 0 if no object was launched.
1667 * 1 if an object was launched and placed somewhere.
1668 * 2 if an object was launched, but used up.
1671 launch_obj(otyp
, x1
, y1
, x2
, y2
, style
)
1673 register int x1
, y1
, x2
, y2
;
1676 register struct monst
*mtmp
;
1677 register struct obj
*otmp
, *otmp2
;
1678 register int dx
, dy
;
1679 struct obj
*singleobj
;
1680 boolean used_up
= FALSE
;
1681 boolean otherside
= FALSE
;
1686 otmp
= sobj_at(otyp
, x1
, y1
);
1687 /* Try the other side too, for rolling boulder traps */
1688 if (!otmp
&& otyp
== BOULDER
) {
1690 otmp
= sobj_at(otyp
, x2
, y2
);
1694 if (otherside
) { /* swap 'em */
1705 if (otmp
->quan
== 1L) {
1706 obj_extract_self(otmp
);
1708 otmp
= (struct obj
*) 0;
1710 singleobj
= splitobj(otmp
, 1L);
1711 obj_extract_self(singleobj
);
1714 /* in case you're using a pick-axe to chop the boulder that's being
1715 launched (perhaps a monster triggered it), destroy context so that
1716 next dig attempt never thinks you're resuming previous effort */
1717 if ((otyp
== BOULDER
|| otyp
== STATUE
)
1718 && singleobj
->ox
== context
.digging
.pos
.x
1719 && singleobj
->oy
== context
.digging
.pos
.y
)
1720 (void) memset((genericptr_t
) &context
.digging
, 0,
1721 sizeof(struct dig_info
));
1723 dist
= distmin(x1
, y1
, x2
, y2
);
1729 case ROLL
| LAUNCH_UNSEEN
:
1730 if (otyp
== BOULDER
) {
1731 You_hear(Hallucination
? "someone bowling."
1732 : "rumbling in the distance.");
1734 style
&= ~LAUNCH_UNSEEN
;
1736 case ROLL
| LAUNCH_KNOWN
:
1737 /* use otrapped as a flag to ohitmon */
1738 singleobj
->otrapped
= 1;
1739 style
&= ~LAUNCH_KNOWN
;
1748 if (!cansee(bhitpos
.x
, bhitpos
.y
))
1750 tmp_at(DISP_FLASH
, obj_to_glyph(singleobj
));
1751 tmp_at(bhitpos
.x
, bhitpos
.y
);
1753 /* Mark a spot to place object in bones files to prevent
1754 * loss of object. Use the starting spot to ensure that
1755 * a rolling boulder will still launch, which it wouldn't
1756 * do if left midstream. Unfortunately we can't use the
1757 * target resting spot, because there are some things/situations
1758 * that would prevent it from ever getting there (bars), and we
1759 * can't tell that yet.
1761 launch_drop_spot(singleobj
, bhitpos
.x
, bhitpos
.y
);
1763 /* Set the object in motion */
1764 while (dist
-- > 0 && !used_up
) {
1766 tmp_at(bhitpos
.x
, bhitpos
.y
);
1769 /* dstage@u.washington.edu -- Delay only if hero sees it */
1770 if (cansee(bhitpos
.x
, bhitpos
.y
))
1776 t
= t_at(bhitpos
.x
, bhitpos
.y
);
1778 if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
1779 if (otyp
== BOULDER
&& throws_rocks(mtmp
->data
)) {
1781 if (cansee(bhitpos
.x
, bhitpos
.y
))
1782 pline("%s snatches the boulder.", Monnam(mtmp
));
1783 singleobj
->otrapped
= 0;
1784 (void) mpickobj(mtmp
, singleobj
);
1786 launch_drop_spot((struct obj
*) 0, 0, 0);
1790 if (ohitmon(mtmp
, singleobj
, (style
== ROLL
) ? -1 : dist
,
1793 launch_drop_spot((struct obj
*) 0, 0, 0);
1796 } else if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) {
1799 if (thitu(9 + singleobj
->spe
, dmgval(singleobj
, &youmonst
),
1800 singleobj
, (char *) 0))
1803 if (style
== ROLL
) {
1804 if (down_gate(bhitpos
.x
, bhitpos
.y
) != -1) {
1805 if (ship_object(singleobj
, bhitpos
.x
, bhitpos
.y
, FALSE
)) {
1807 launch_drop_spot((struct obj
*) 0, 0, 0);
1811 if (t
&& otyp
== BOULDER
) {
1817 cansee(bhitpos
.x
, bhitpos
.y
)
1818 ? " The rolling boulder triggers a land mine."
1821 del_engr_at(bhitpos
.x
, bhitpos
.y
);
1822 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1823 singleobj
->otrapped
= 0;
1824 fracture_rock(singleobj
);
1825 (void) scatter(bhitpos
.x
, bhitpos
.y
, 4,
1826 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
1829 if (cansee(bhitpos
.x
, bhitpos
.y
))
1830 newsym(bhitpos
.x
, bhitpos
.y
);
1832 launch_drop_spot((struct obj
*) 0, 0, 0);
1837 if (cansee(bhitpos
.x
, bhitpos
.y
))
1838 pline("Suddenly the rolling boulder disappears!");
1840 You_hear("a rumbling stop abruptly.");
1841 singleobj
->otrapped
= 0;
1842 if (t
->ttyp
== TELEP_TRAP
)
1843 (void) rloco(singleobj
);
1845 int newlev
= random_teleport_level();
1848 if (newlev
== depth(&u
.uz
) || In_endgame(&u
.uz
))
1850 add_to_migration(singleobj
);
1851 get_level(&dest
, newlev
);
1852 singleobj
->ox
= dest
.dnum
;
1853 singleobj
->oy
= dest
.dlevel
;
1854 singleobj
->owornmask
= (long) MIGR_RANDOM
;
1858 launch_drop_spot((struct obj
*) 0, 0, 0);
1864 /* the boulder won't be used up if there is a
1865 monster in the trap; stop rolling anyway */
1866 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* stops here */
1867 if (flooreffects(singleobj
, x2
, y2
, "fall")) {
1869 launch_drop_spot((struct obj
*) 0, 0, 0);
1871 dist
= -1; /* stop rolling immediately */
1874 if (used_up
|| dist
== -1)
1877 if (flooreffects(singleobj
, bhitpos
.x
, bhitpos
.y
, "fall")) {
1879 launch_drop_spot((struct obj
*) 0, 0, 0);
1883 && (otmp2
= sobj_at(BOULDER
, bhitpos
.x
, bhitpos
.y
)) != 0) {
1884 const char *bmsg
= " as one boulder sets another in motion";
1886 if (!isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
) || !dist
1887 || IS_ROCK(levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
))
1888 bmsg
= " as one boulder hits another";
1890 You_hear("a loud crash%s!",
1891 cansee(bhitpos
.x
, bhitpos
.y
) ? bmsg
: "");
1892 obj_extract_self(otmp2
);
1893 /* pass off the otrapped flag to the next boulder */
1894 otmp2
->otrapped
= singleobj
->otrapped
;
1895 singleobj
->otrapped
= 0;
1896 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1898 otmp2
= (struct obj
*) 0;
1899 wake_nearto(bhitpos
.x
, bhitpos
.y
, 10 * 10);
1902 if (otyp
== BOULDER
&& closed_door(bhitpos
.x
, bhitpos
.y
)) {
1903 if (cansee(bhitpos
.x
, bhitpos
.y
))
1904 pline_The("boulder crashes through a door.");
1905 levl
[bhitpos
.x
][bhitpos
.y
].doormask
= D_BROKEN
;
1907 unblock_point(bhitpos
.x
, bhitpos
.y
);
1910 /* if about to hit iron bars, do so now */
1911 if (dist
> 0 && isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
)
1912 && levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
== IRONBARS
) {
1913 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* object stops here */
1914 if (hits_bars(&singleobj
,
1915 x2
, y2
, x2
+dx
, y2
+dy
,
1919 launch_drop_spot((struct obj
*) 0, 0, 0);
1925 tmp_at(DISP_END
, 0);
1926 launch_drop_spot((struct obj
*) 0, 0, 0);
1928 singleobj
->otrapped
= 0;
1929 place_object(singleobj
, x2
, y2
);
1942 newsym(trap
->tx
, trap
->ty
);
1946 /* like seetrap() but overrides vision */
1953 /* in case it's beneath something, redisplay the something */
1954 newsym(trap
->tx
, trap
->ty
);
1958 mkroll_launch(ttmp
, x
, y
, otyp
, ocount
)
1971 boolean success
= FALSE
;
1974 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
)
1976 distance
= rn1(5, 4); /* 4..8 away */
1977 tmp
= rn2(8); /* randomly pick a direction to try first */
1978 while (distance
>= mindist
) {
1983 /* Prevent boulder from being placed on water */
1984 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
1985 && is_pool_or_lava(x
+ distance
* dx
, y
+ distance
* dy
))
1988 success
= isclearpath(&cc
, distance
, dx
, dy
);
1989 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
1990 boolean success_otherway
;
1994 success_otherway
= isclearpath(&bcc
, distance
, -(dx
), -(dy
));
1995 if (!success_otherway
)
2002 if ((++trycount
% 8) == 0)
2006 /* create the trap without any ammo, launch pt at trap location */
2010 otmp
= mksobj(otyp
, TRUE
, FALSE
);
2011 otmp
->quan
= ocount
;
2012 otmp
->owt
= weight(otmp
);
2013 place_object(otmp
, cc
.x
, cc
.y
);
2016 ttmp
->launch
.x
= cc
.x
;
2017 ttmp
->launch
.y
= cc
.y
;
2018 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
2019 ttmp
->launch2
.x
= bcc
.x
;
2020 ttmp
->launch2
.y
= bcc
.y
;
2022 ttmp
->launch_otyp
= otyp
;
2023 newsym(ttmp
->launch
.x
, ttmp
->launch
.y
);
2028 isclearpath(cc
, distance
, dx
, dy
)
2038 while (distance
-- > 0) {
2041 typ
= levl
[x
][y
].typ
;
2042 if (!isok(x
, y
) || !ZAP_POS(typ
) || closed_door(x
, y
))
2052 register struct monst
*mtmp
;
2054 register struct trap
*trap
= t_at(mtmp
->mx
, mtmp
->my
);
2055 boolean trapkilled
= FALSE
;
2056 struct permonst
*mptr
= mtmp
->data
;
2060 mtmp
->mtrapped
= 0; /* perhaps teleported? */
2061 } else if (mtmp
->mtrapped
) { /* is currently in the trap */
2062 if (!trap
->tseen
&& cansee(mtmp
->mx
, mtmp
->my
) && canseemon(mtmp
)
2063 && (trap
->ttyp
== SPIKED_PIT
|| trap
->ttyp
== BEAR_TRAP
2064 || trap
->ttyp
== HOLE
|| trap
->ttyp
== PIT
2065 || trap
->ttyp
== WEB
)) {
2066 /* If you come upon an obviously trapped monster, then
2067 * you must be able to see the trap it's in too.
2073 if (sobj_at(BOULDER
, mtmp
->mx
, mtmp
->my
)
2074 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
2077 if (canseemon(mtmp
))
2078 pline("%s pulls free...", Monnam(mtmp
));
2079 fill_pit(mtmp
->mx
, mtmp
->my
);
2084 } else if (metallivorous(mptr
)) {
2085 if (trap
->ttyp
== BEAR_TRAP
) {
2086 if (canseemon(mtmp
))
2087 pline("%s eats a bear trap!", Monnam(mtmp
));
2091 } else if (trap
->ttyp
== SPIKED_PIT
) {
2092 if (canseemon(mtmp
))
2093 pline("%s munches on some spikes!", Monnam(mtmp
));
2099 register int tt
= trap
->ttyp
;
2100 boolean in_sight
, tear_web
, see_it
,
2101 inescapable
= force_mintrap
|| ((tt
== HOLE
|| tt
== PIT
)
2102 && Sokoban
&& !trap
->madeby_u
);
2103 const char *fallverb
;
2105 /* true when called from dotrap, inescapable is not an option */
2106 if (mtmp
== u
.usteed
)
2108 if (!inescapable
&& ((mtmp
->mtrapseen
& (1 << (tt
- 1))) != 0
2109 || (tt
== HOLE
&& !mindless(mptr
)))) {
2110 /* it has been in such a trap - perhaps it escapes */
2114 mtmp
->mtrapseen
|= (1 << (tt
- 1));
2116 /* Monster is aggravated by being trapped by you.
2117 Recognizing who made the trap isn't completely
2118 unreasonable; everybody has their own style. */
2119 if (trap
->madeby_u
&& rnl(5))
2122 in_sight
= canseemon(mtmp
);
2123 see_it
= cansee(mtmp
->mx
, mtmp
->my
);
2124 /* assume hero can tell what's going on for the steed */
2125 if (mtmp
== u
.usteed
)
2129 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2130 if (in_sight
&& see_it
)
2131 pline("%s triggers a trap but nothing happens.",
2134 newsym(mtmp
->mx
, mtmp
->my
);
2138 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
2140 otmp
->owt
= weight(otmp
);
2141 otmp
->opoisoned
= 0;
2144 if (thitm(8, mtmp
, otmp
, 0, FALSE
))
2148 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2149 if (in_sight
&& see_it
)
2150 pline("%s triggers a trap but nothing happens.",
2153 newsym(mtmp
->mx
, mtmp
->my
);
2157 otmp
= mksobj(DART
, TRUE
, FALSE
);
2159 otmp
->owt
= weight(otmp
);
2161 otmp
->opoisoned
= 1;
2164 if (thitm(7, mtmp
, otmp
, 0, FALSE
))
2168 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2169 if (in_sight
&& see_it
)
2171 "A trap door above %s opens, but nothing falls out!",
2174 newsym(mtmp
->mx
, mtmp
->my
);
2178 otmp
= mksobj(ROCK
, TRUE
, FALSE
);
2180 otmp
->owt
= weight(otmp
);
2183 if (thitm(0, mtmp
, otmp
, d(2, 6), FALSE
))
2189 /* stepped on a squeaky board */
2192 pline("A board beneath %s squeaks %s loudly.",
2193 mon_nam(mtmp
), trapnote(trap
, 0));
2196 pline("%s stops momentarily and appears to cringe.",
2200 You_hear("a distant %s squeak.", trapnote(trap
, 1));
2201 /* wake up nearby monsters */
2202 wake_nearto(mtmp
->mx
, mtmp
->my
, 40);
2205 if (mptr
->msize
> MZ_SMALL
&& !amorphous(mptr
) && !is_flyer(mptr
)
2206 && !is_whirly(mptr
) && !unsolid(mptr
)) {
2209 pline("%s is caught in %s bear trap!", Monnam(mtmp
),
2210 a_your
[trap
->madeby_u
]);
2213 if (mptr
== &mons
[PM_OWLBEAR
]
2214 || mptr
== &mons
[PM_BUGBEAR
])
2215 You_hear("the roaring of an angry bear!");
2217 } else if (force_mintrap
) {
2219 pline("%s evades %s bear trap!", Monnam(mtmp
),
2220 a_your
[trap
->madeby_u
]);
2225 trapkilled
= thitm(0, mtmp
, (struct obj
*) 0, d(2, 4), FALSE
);
2228 if (!resists_sleep(mtmp
) && !breathless(mptr
) && !mtmp
->msleeping
2229 && mtmp
->mcanmove
) {
2230 if (sleep_monst(mtmp
, rnd(25), -1) && in_sight
) {
2231 pline("%s suddenly falls asleep!", Monnam(mtmp
));
2244 pline("%s %s on the %s!", A_gush_of_water_hits
,
2245 mon_nam(mtmp
), mbodypart(mtmp
, HEAD
));
2246 target
= which_armor(mtmp
, W_ARMH
);
2247 (void) water_damage(target
, helm_simple_name(target
), TRUE
);
2251 pline("%s %s's left %s!", A_gush_of_water_hits
,
2252 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2253 target
= which_armor(mtmp
, W_ARMS
);
2254 if (water_damage(target
, "shield", TRUE
) != ER_NOTHING
)
2256 target
= MON_WEP(mtmp
);
2257 if (target
&& bimanual(target
))
2258 (void) water_damage(target
, 0, TRUE
);
2260 target
= which_armor(mtmp
, W_ARMG
);
2261 (void) water_damage(target
, "gauntlets", TRUE
);
2265 pline("%s %s's right %s!", A_gush_of_water_hits
,
2266 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2267 (void) water_damage(MON_WEP(mtmp
), 0, TRUE
);
2271 pline("%s %s!", A_gush_of_water_hits
, mon_nam(mtmp
));
2272 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2274 && (otmp
->owornmask
& (W_WEP
| W_SWAPWEP
)) == 0)
2275 (void) snuff_lit(otmp
);
2276 if ((target
= which_armor(mtmp
, W_ARMC
)) != 0)
2277 (void) water_damage(target
, cloak_simple_name(target
),
2279 else if ((target
= which_armor(mtmp
, W_ARM
)) != 0)
2280 (void) water_damage(target
, "armor", TRUE
);
2281 else if ((target
= which_armor(mtmp
, W_ARMU
)) != 0)
2282 (void) water_damage(target
, "shirt", TRUE
);
2285 if (mptr
== &mons
[PM_IRON_GOLEM
]) {
2287 pline("%s falls to pieces!", Monnam(mtmp
));
2288 else if (mtmp
->mtame
)
2289 pline("May %s rust in peace.", mon_nam(mtmp
));
2293 } else if (mptr
== &mons
[PM_GREMLIN
] && rn2(3)) {
2294 (void) split_mon(mtmp
, (struct monst
*) 0);
2301 pline("A %s erupts from the %s under %s!", tower_of_flame
,
2302 surface(mtmp
->mx
, mtmp
->my
), mon_nam(mtmp
));
2303 else if (see_it
) /* evidently `mtmp' is invisible */
2304 You_see("a %s erupt from the %s!", tower_of_flame
,
2305 surface(mtmp
->mx
, mtmp
->my
));
2307 if (resists_fire(mtmp
)) {
2309 shieldeff(mtmp
->mx
, mtmp
->my
);
2310 pline("%s is uninjured.", Monnam(mtmp
));
2313 int num
= d(2, 4), alt
;
2314 boolean immolate
= FALSE
;
2316 /* paper burns very fast, assume straw is tightly
2317 * packed and burns a bit slower */
2318 switch (monsndx(mptr
)) {
2319 case PM_PAPER_GOLEM
:
2323 case PM_STRAW_GOLEM
:
2324 alt
= mtmp
->mhpmax
/ 2;
2327 alt
= mtmp
->mhpmax
/ 4;
2329 case PM_LEATHER_GOLEM
:
2330 alt
= mtmp
->mhpmax
/ 8;
2339 if (thitm(0, mtmp
, (struct obj
*) 0, num
, immolate
))
2342 /* we know mhp is at least `num' below mhpmax,
2343 so no (mhp > mhpmax) check is needed here */
2344 mtmp
->mhpmax
-= rn2(num
+ 1);
2346 if (burnarmor(mtmp
) || rn2(3)) {
2347 (void) destroy_mitem(mtmp
, SCROLL_CLASS
, AD_FIRE
);
2348 (void) destroy_mitem(mtmp
, SPBOOK_CLASS
, AD_FIRE
);
2349 (void) destroy_mitem(mtmp
, POTION_CLASS
, AD_FIRE
);
2351 if (burn_floor_objects(mtmp
->mx
, mtmp
->my
, see_it
, FALSE
)
2352 && !see_it
&& distu(mtmp
->mx
, mtmp
->my
) <= 3 * 3)
2353 You("smell smoke.");
2354 if (is_ice(mtmp
->mx
, mtmp
->my
))
2355 melt_ice(mtmp
->mx
, mtmp
->my
, (char *) 0);
2362 if (is_flyer(mptr
) || is_floater(mptr
)
2363 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2364 || is_clinger(mptr
)) {
2365 if (force_mintrap
&& !Sokoban
) {
2366 /* openfallingtrap; not inescapable here */
2369 pline("%s doesn't fall into the pit.", Monnam(mtmp
));
2371 break; /* inescapable = FALSE; */
2374 break; /* avoids trap */
2375 fallverb
= "is dragged"; /* sokoban pit */
2377 if (!passes_walls(mptr
))
2380 pline("%s %s into %s pit!", Monnam(mtmp
), fallverb
,
2381 a_your
[trap
->madeby_u
]);
2382 if (mptr
== &mons
[PM_PIT_VIPER
]
2383 || mptr
== &mons
[PM_PIT_FIEND
])
2384 pline("How pitiful. Isn't that the pits?");
2387 mselftouch(mtmp
, "Falling, ", FALSE
);
2388 if (mtmp
->mhp
<= 0 || thitm(0, mtmp
, (struct obj
*) 0,
2389 rnd((tt
== PIT
) ? 6 : 10), FALSE
))
2394 if (!Can_fall_thru(&u
.uz
)) {
2395 impossible("mintrap: %ss cannot exist on this level.",
2396 defsyms
[trap_to_defsym(tt
)].explanation
);
2397 break; /* don't activate it after all */
2399 if (is_flyer(mptr
) || is_floater(mptr
) || mptr
== &mons
[PM_WUMPUS
]
2400 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2401 || mptr
->msize
>= MZ_HUGE
) {
2402 if (force_mintrap
&& !Sokoban
) {
2403 /* openfallingtrap; not inescapable here */
2408 "A trap door opens, but %s doesn't fall through.",
2410 else /* (tt == HOLE) */
2411 pline("%s doesn't fall through the hole.",
2414 break; /* inescapable = FALSE; */
2416 if (inescapable
) { /* sokoban hole */
2418 pline("%s seems to be yanked down!", Monnam(mtmp
));
2419 /* suppress message in mlevel_tele_trap() */
2428 case MAGIC_PORTAL
: {
2431 mlev_res
= mlevel_tele_trap(mtmp
, trap
, inescapable
, in_sight
);
2437 mtele_trap(mtmp
, trap
, in_sight
);
2440 /* Monster in a web. */
2443 if (mu_maybe_destroy_web(mtmp
, in_sight
, trap
))
2446 switch (monsndx(mptr
)) {
2447 case PM_OWLBEAR
: /* Eric Backus */
2450 You_hear("the roaring of a confused bear!");
2456 if (mptr
->mlet
== S_GIANT
2457 /* exclude baby dragons and relatively short worms */
2458 || (mptr
->mlet
== S_DRAGON
&& extra_nasty(mptr
))
2459 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)) {
2461 } else if (in_sight
) {
2462 pline("%s is caught in %s spider web.", Monnam(mtmp
),
2463 a_your
[trap
->madeby_u
]);
2466 mtmp
->mtrapped
= tear_web
? 0 : 1;
2468 /* this list is fairly arbitrary; it deliberately
2469 excludes wumpus & giant/ettin zombies/mummies */
2470 case PM_TITANOTHERE
:
2471 case PM_BALUCHITHERIUM
:
2472 case PM_PURPLE_WORM
:
2481 case PM_LORD_SURTUR
:
2487 pline("%s tears through %s spider web!", Monnam(mtmp
),
2488 a_your
[trap
->madeby_u
]);
2490 newsym(mtmp
->mx
, mtmp
->my
);
2491 } else if (force_mintrap
&& !mtmp
->mtrapped
) {
2493 pline("%s avoids %s spider web!", Monnam(mtmp
),
2494 a_your
[trap
->madeby_u
]);
2502 /* A magic trap. Monsters usually immune. */
2507 /* similar to hero's case, more or less */
2508 if (!resists_magm(mtmp
)) { /* lose spell energy */
2509 if (!mtmp
->mcan
&& (attacktype(mptr
, AT_MAGC
)
2510 || attacktype(mptr
, AT_BREA
))) {
2511 mtmp
->mspec_used
+= d(2, 2);
2514 pline("%s seems lethargic.", Monnam(mtmp
));
2517 } else { /* take some damage */
2518 int dmgval2
= rnd(4);
2520 if ((otmp
= MON_WEP(mtmp
)) != 0
2521 && otmp
->oartifact
== ART_MAGICBANE
)
2523 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2525 && defends_when_carried(AD_MAGM
, otmp
))
2529 if (passes_walls(mptr
))
2530 dmgval2
= (dmgval2
+ 3) / 4;
2534 if ((mtmp
->mhp
-= dmgval2
) <= 0)
2537 ? "compression from an anti-magic field"
2543 newsym(trap
->tx
, trap
->ty
);
2548 break; /* monsters usually don't set it off */
2549 if (is_flyer(mptr
)) {
2550 boolean already_seen
= trap
->tseen
;
2552 if (in_sight
&& !already_seen
) {
2553 pline("A trigger appears in a pile of soil below %s.",
2560 newsym(mtmp
->mx
, mtmp
->my
);
2561 pline_The("air currents set %s off!",
2562 already_seen
? "a land mine" : "it");
2564 } else if (in_sight
) {
2565 newsym(mtmp
->mx
, mtmp
->my
);
2566 pline("%s%s triggers %s land mine!",
2567 !Deaf
? "KAABLAMM!!! " : "", Monnam(mtmp
),
2568 a_your
[trap
->madeby_u
]);
2570 if (!in_sight
&& !Deaf
)
2571 pline("Kaablamm! You hear an explosion in the distance!");
2572 blow_up_landmine(trap
);
2573 /* explosion might have destroyed a drawbridge; don't
2574 dish out more damage if monster is already dead */
2576 || thitm(0, mtmp
, (struct obj
*) 0, rnd(16), FALSE
)) {
2579 /* monsters recursively fall into new pit */
2580 if (mintrap(mtmp
) == 2)
2583 /* a boulder may fill the new pit, crushing monster */
2584 fill_pit(trap
->tx
, trap
->ty
);
2587 if (unconscious()) {
2589 nomovemsg
= "The explosion awakens you!";
2593 if (resists_magm(mtmp
)) {
2594 shieldeff(mtmp
->mx
, mtmp
->my
);
2595 } else if (!resist(mtmp
, WAND_CLASS
, 0, NOTELL
)) {
2596 if (newcham(mtmp
, (struct permonst
*) 0, FALSE
, FALSE
))
2597 /* we're done with mptr but keep it up to date */
2603 case ROLLING_BOULDER_TRAP
:
2604 if (!is_flyer(mptr
)) {
2605 int style
= ROLL
| (in_sight
? 0 : LAUNCH_UNSEEN
);
2607 newsym(mtmp
->mx
, mtmp
->my
);
2609 pline("Click! %s triggers %s.", Monnam(mtmp
),
2610 trap
->tseen
? "a rolling boulder trap" : something
);
2611 if (launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
2612 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
2619 newsym(mtmp
->mx
, mtmp
->my
);
2623 case VIBRATING_SQUARE
:
2624 if (see_it
&& !Blind
) {
2626 pline("You see a strange vibration beneath %s %s.",
2627 s_suffix(mon_nam(mtmp
)),
2628 makeplural(mbodypart(mtmp
, FOOT
)));
2630 pline("You see the ground vibrate in the distance.");
2635 impossible("Some monster encountered a strange trap of type %d.",
2641 return mtmp
->mtrapped
;
2644 /* Combine cockatrice checks into single functions to avoid repeating code. */
2649 if (Stone_resistance
)
2651 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
))
2653 You("turn to stone...");
2654 killer
.format
= KILLED_BY
;
2655 if (str
!= killer
.name
)
2656 Strcpy(killer
.name
, str
? str
: "");
2661 minstapetrify(mon
, byplayer
)
2665 if (resists_ston(mon
))
2667 if (poly_when_stoned(mon
->data
)) {
2671 if (!vamp_stone(mon
))
2674 /* give a "<mon> is slowing down" message and also remove
2675 intrinsic speed (comparable to similar effect on the hero) */
2676 mon_adjust_speed(mon
, -3, (struct obj
*) 0);
2678 if (cansee(mon
->mx
, mon
->my
))
2679 pline("%s turns to stone.", Monnam(mon
));
2682 xkilled(mon
, XKILL_NOMSG
);
2693 if (uwep
&& uwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[uwep
->corpsenm
])
2694 && !Stone_resistance
) {
2695 pline("%s touch the %s corpse.", arg
, mons
[uwep
->corpsenm
].mname
);
2696 Sprintf(kbuf
, "%s corpse", an(mons
[uwep
->corpsenm
].mname
));
2698 /* life-saved; unwield the corpse if we can't handle it */
2699 if (!uarmg
&& !Stone_resistance
)
2702 /* Or your secondary weapon, if wielded [hypothetical; we don't
2703 allow two-weapon combat when either weapon is a corpse] */
2704 if (u
.twoweap
&& uswapwep
&& uswapwep
->otyp
== CORPSE
2705 && touch_petrifies(&mons
[uswapwep
->corpsenm
]) && !Stone_resistance
) {
2706 pline("%s touch the %s corpse.", arg
, mons
[uswapwep
->corpsenm
].mname
);
2707 Sprintf(kbuf
, "%s corpse", an(mons
[uswapwep
->corpsenm
].mname
));
2709 /* life-saved; unwield the corpse */
2710 if (!uarmg
&& !Stone_resistance
)
2716 mselftouch(mon
, arg
, byplayer
)
2721 struct obj
*mwep
= MON_WEP(mon
);
2723 if (mwep
&& mwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[mwep
->corpsenm
])
2724 && !resists_ston(mon
)) {
2725 if (cansee(mon
->mx
, mon
->my
)) {
2726 pline("%s%s touches %s.", arg
? arg
: "",
2727 arg
? mon_nam(mon
) : Monnam(mon
),
2728 corpse_xname(mwep
, (const char *) 0, CXN_PFX_THE
));
2730 minstapetrify(mon
, byplayer
);
2731 /* if life-saved, might not be able to continue wielding */
2732 if (mon
->mhp
> 0 && !which_armor(mon
, W_ARMG
) && !resists_ston(mon
))
2737 /* start levitating */
2741 context
.botl
= TRUE
;
2743 if (u
.utraptype
== TT_PIT
) {
2745 You("float up, out of the pit!");
2746 vision_full_recalc
= 1; /* vision limits change */
2747 fill_pit(u
.ux
, u
.uy
);
2748 } else if (u
.utraptype
== TT_INFLOOR
) {
2749 Your("body pulls upward, but your %s are still stuck.",
2750 makeplural(body_part(LEG
)));
2752 You("float up, only your %s is still stuck.", body_part(LEG
));
2755 } else if (Is_waterlevel(&u
.uz
)) {
2756 pline("It feels as though you've lost some weight.");
2758 } else if (u
.uinwater
) {
2760 } else if (u
.uswallow
) {
2761 You(is_animal(u
.ustuck
->data
) ? "float away from the %s."
2762 : "spiral up into %s.",
2763 is_animal(u
.ustuck
->data
) ? surface(u
.ux
, u
.uy
)
2764 : mon_nam(u
.ustuck
));
2765 } else if (Hallucination
) {
2766 pline("Up, up, and awaaaay! You're walking on air!");
2767 } else if (Is_airlevel(&u
.uz
)) {
2768 You("gain control over your movements.");
2770 You("start to float in the air!");
2772 if (u
.usteed
&& !is_floater(u
.usteed
->data
)
2773 && !is_flyer(u
.usteed
->data
)) {
2775 pline("%s magically floats up!", Monnam(u
.usteed
));
2777 You("cannot stay on %s.", mon_nam(u
.usteed
));
2778 dismount_steed(DISMOUNT_GENERIC
);
2782 You("are no longer able to control your flight.");
2783 BFlying
|= I_SPECIAL
;
2794 if ((t
= t_at(x
, y
)) && ((t
->ttyp
== PIT
) || (t
->ttyp
== SPIKED_PIT
))
2795 && (otmp
= sobj_at(BOULDER
, x
, y
))) {
2796 obj_extract_self(otmp
);
2797 (void) flooreffects(otmp
, x
, y
, "settle");
2801 /* stop levitating */
2803 float_down(hmask
, emask
)
2804 long hmask
, emask
; /* might cancel timeout */
2806 register struct trap
*trap
= (struct trap
*) 0;
2807 d_level current_dungeon_level
;
2808 boolean no_msg
= FALSE
;
2810 HLevitation
&= ~hmask
;
2811 ELevitation
&= ~emask
;
2813 return 0; /* maybe another ring/potion/boots */
2815 /* Levitation is blocked, so hero is not actually floating
2816 hence shouldn't have float_down effects and feedback */
2817 float_vs_flight(); /* before nomul() rather than after */
2820 context
.botl
= TRUE
;
2821 nomul(0); /* stop running or resting */
2823 /* controlled flight no longer overridden by levitation */
2824 BFlying
&= ~I_SPECIAL
;
2826 You("have stopped levitating and are now flying.");
2831 You("float down, but you are still %s.",
2832 is_animal(u
.ustuck
->data
) ? "swallowed" : "engulfed");
2836 if (Punished
&& !carried(uball
)
2837 && (is_pool(uball
->ox
, uball
->oy
)
2838 || ((trap
= t_at(uball
->ox
, uball
->oy
))
2839 && ((trap
->ttyp
== PIT
) || (trap
->ttyp
== SPIKED_PIT
)
2840 || (trap
->ttyp
== TRAPDOOR
) || (trap
->ttyp
== HOLE
))))) {
2845 movobj(uchain
, uball
->ox
, uball
->oy
);
2846 newsym(u
.ux0
, u
.uy0
);
2847 vision_full_recalc
= 1; /* in case the hero moved. */
2849 /* check for falling into pool - added by GAN 10/20/86 */
2851 if (!u
.uswallow
&& u
.ustuck
) {
2852 if (sticks(youmonst
.data
))
2853 You("aren't able to maintain your hold on %s.",
2856 pline("Startled, %s can no longer hold you!",
2861 * drown() and lava_effects() print various messages almost
2862 * every time they're called which conflict with the "fall
2863 * into" message below. Thus, we want to avoid printing
2864 * confusing, duplicate or out-of-order messages.
2865 * Use knowledge of the two routines as a hack -- this
2866 * should really be handled differently -dlc
2868 if (is_pool(u
.ux
, u
.uy
) && !Wwalking
&& !Swimming
&& !u
.uinwater
)
2871 if (is_lava(u
.ux
, u
.uy
)) {
2872 (void) lava_effects();
2877 trap
= t_at(u
.ux
, u
.uy
);
2878 if (Is_airlevel(&u
.uz
)) {
2879 You("begin to tumble in place.");
2880 } else if (Is_waterlevel(&u
.uz
) && !no_msg
) {
2881 You_feel("heavier.");
2882 /* u.uinwater msgs already in spoteffects()/drown() */
2883 } else if (!u
.uinwater
&& !no_msg
) {
2884 if (!(emask
& W_SADDLE
)) {
2885 if (Sokoban
&& trap
) {
2886 /* Justification elsewhere for Sokoban traps is based
2887 * on air currents. This is consistent with that.
2888 * The unexpected additional force of the air currents
2889 * once levitation ceases knocks you off your feet.
2892 pline("Bummer! You've crashed.");
2895 losehp(rnd(2), "dangerous winds", KILLED_BY
);
2897 dismount_steed(DISMOUNT_FELL
);
2898 selftouch("As you fall, you");
2899 } else if (u
.usteed
&& (is_floater(u
.usteed
->data
)
2900 || is_flyer(u
.usteed
->data
))) {
2901 You("settle more firmly in the saddle.");
2902 } else if (Hallucination
) {
2903 pline("Bummer! You've %s.",
2906 : "hit the ground");
2908 You("float gently to the %s.", surface(u
.ux
, u
.uy
));
2914 /* can't rely on u.uz0 for detecting trap door-induced level change;
2915 it gets changed to reflect the new level before we can check it */
2916 assign_level(¤t_dungeon_level
, &u
.uz
);
2918 switch (trap
->ttyp
) {
2923 if (!Can_fall_thru(&u
.uz
) || u
.ustuck
)
2927 if (!u
.utrap
) /* not already in the trap */
2931 if (!Is_airlevel(&u
.uz
) && !Is_waterlevel(&u
.uz
) && !u
.uswallow
2932 /* falling through trap door calls goto_level,
2933 and goto_level does its own pickup() call */
2934 && on_level(&u
.uz
, ¤t_dungeon_level
))
2939 /* shared code for climbing out of a pit */
2943 if (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
2947 /* marked as trapped so they can pick things up */
2948 You("ascend from the pit.");
2950 fill_pit(u
.ux
, u
.uy
);
2951 vision_full_recalc
= 1; /* vision limits change */
2952 } else if (!rn2(2) && sobj_at(BOULDER
, u
.ux
, u
.uy
)) {
2953 Your("%s gets stuck in a crevice.", body_part(LEG
));
2954 display_nhwindow(WIN_MESSAGE
, FALSE
);
2955 clear_nhwindow(WIN_MESSAGE
);
2956 You("free your %s.", body_part(LEG
));
2957 } else if ((Flying
|| is_clinger(youmonst
.data
)) && !Sokoban
) {
2958 /* eg fell in pit, then poly'd to a flying monster;
2959 or used '>' to deliberately enter it */
2960 You("%s from the pit.", Flying
? "fly" : "climb");
2962 fill_pit(u
.ux
, u
.uy
);
2963 vision_full_recalc
= 1; /* vision limits change */
2964 } else if (!(--u
.utrap
)) {
2965 You("%s to the edge of the pit.",
2966 (Sokoban
&& Levitation
)
2967 ? "struggle against the air currents and float"
2968 : u
.usteed
? "ride" : "crawl");
2969 fill_pit(u
.ux
, u
.uy
);
2970 vision_full_recalc
= 1; /* vision limits change */
2971 } else if (u
.dz
|| flags
.verbose
) {
2973 Norep("%s is still in a pit.", upstart(y_monnam(u
.usteed
)));
2975 Norep((Hallucination
&& !rn2(5))
2976 ? "You've fallen, and you can't get up."
2977 : "You are still in a pit.");
2983 struct obj
*box
; /* null for floor trap */
2985 boolean see_it
= !Blind
;
2988 /* Bug: for box case, the equivalent of burn_floor_objects() ought
2989 * to be done upon its contents.
2992 if ((box
&& !carried(box
)) ? is_pool(box
->ox
, box
->oy
) : Underwater
) {
2993 pline("A cascade of steamy bubbles erupts from %s!",
2994 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
2995 if (Fire_resistance
)
2996 You("are uninjured.");
2998 losehp(rnd(3), "boiling water", KILLED_BY
);
3001 pline("A %s %s from %s!", tower_of_flame
, box
? "bursts" : "erupts",
3002 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
3003 if (Fire_resistance
) {
3004 shieldeff(u
.ux
, u
.uy
);
3006 } else if (Upolyd
) {
3008 switch (u
.umonnum
) {
3009 case PM_PAPER_GOLEM
:
3012 case PM_STRAW_GOLEM
:
3018 case PM_LEATHER_GOLEM
:
3027 if (u
.mhmax
> mons
[u
.umonnum
].mlevel
)
3028 u
.mhmax
-= rn2(min(u
.mhmax
, num
+ 1)), context
.botl
= 1;
3031 if (u
.uhpmax
> u
.ulevel
)
3032 u
.uhpmax
-= rn2(min(u
.uhpmax
, num
+ 1)), context
.botl
= 1;
3035 You("are uninjured.");
3037 losehp(num
, tower_of_flame
, KILLED_BY_AN
); /* fire damage */
3040 if (burnarmor(&youmonst
) || rn2(3)) {
3041 destroy_item(SCROLL_CLASS
, AD_FIRE
);
3042 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
3043 destroy_item(POTION_CLASS
, AD_FIRE
);
3045 if (!box
&& burn_floor_objects(u
.ux
, u
.uy
, see_it
, TRUE
) && !see_it
)
3046 You("smell paper burning.");
3047 if (is_ice(u
.ux
, u
.uy
))
3048 melt_ice(u
.ux
, u
.uy
, (char *) 0);
3054 register int fate
= rnd(20);
3056 /* What happened to the poor sucker? */
3059 /* Most of the time, it creates some monsters. */
3060 register int cnt
= rnd(4);
3062 /* blindness effects */
3063 if (!resists_blnd(&youmonst
)) {
3064 You("are momentarily blinded by a flash of light!");
3065 make_blinded((long) rn1(5, 10), FALSE
);
3067 Your1(vision_clears
);
3068 } else if (!Blind
) {
3069 You_see("a flash of light!");
3072 /* deafness effects */
3074 You_hear("a deafening roar!");
3075 incr_itimeout(&HDeaf
, rn1(20, 30));
3076 context
.botl
= TRUE
;
3078 /* magic vibrations still hit you */
3079 You_feel("rankled.");
3080 incr_itimeout(&HDeaf
, rn1(5, 15));
3081 context
.botl
= TRUE
;
3084 (void) makemon((struct permonst
*) 0, u
.ux
, u
.uy
, NO_MM_FLAGS
);
3089 /* sometimes nothing happens */
3091 case 12: /* a flash of fire */
3092 dofiretrap((struct obj
*) 0);
3097 pline("A shiver runs up and down your %s!", body_part(SPINE
));
3100 You_hear(Hallucination
? "the moon howling at you."
3101 : "distant howling.");
3104 if (on_level(&u
.uz
, &qstart_level
))
3106 "%slike the prodigal son.",
3107 (flags
.female
|| (Upolyd
&& is_neuter(youmonst
.data
)))
3111 You("suddenly yearn for %s.",
3114 : (In_quest(&u
.uz
) || at_dgn_entrance("The Quest"))
3115 ? "your nearby homeland"
3116 : "your distant homeland");
3119 Your("pack shakes violently!");
3122 You(Hallucination
? "smell hamburgers." : "smell charred flesh.");
3128 /* very occasionally something nice happens. */
3129 case 19: { /* tame nearby monsters */
3133 (void) adjattrib(A_CHA
, 1, FALSE
);
3134 for (i
= -1; i
<= 1; i
++)
3135 for (j
= -1; j
<= 1; j
++) {
3136 if (!isok(u
.ux
+ i
, u
.uy
+ j
))
3138 mtmp
= m_at(u
.ux
+ i
, u
.uy
+ j
);
3140 (void) tamedog(mtmp
, (struct obj
*) 0);
3144 case 20: { /* uncurse stuff */
3146 long save_conf
= HConfusion
;
3148 pseudo
= zeroobj
; /* neither cursed nor blessed,
3149 and zero out oextra */
3150 pseudo
.otyp
= SCR_REMOVE_CURSE
;
3152 (void) seffects(&pseudo
);
3153 HConfusion
= save_conf
;
3161 /* Set an item on fire.
3162 * "force" means not to roll a luck-based protection check for the
3164 * "x" and "y" are the coordinates to dump the contents of a
3165 * container, if it burns up.
3167 * Return whether the object was destroyed.
3170 fire_damage(obj
, force
, x
, y
)
3176 struct obj
*otmp
, *ncobj
;
3177 int in_sight
= !Blind
&& couldsee(x
, y
); /* Don't care if it's lit */
3180 /* object might light in a controlled manner */
3184 if (Is_container(obj
)) {
3185 switch (obj
->otyp
) {
3187 return FALSE
; /* Immune */
3198 if ((!force
&& (Luck
+ 5) > rn2(chance
))
3199 || (is_flammable(obj
) && obj
->oerodeproof
))
3201 /* Container is burnt up - dump contents out */
3203 pline("%s catches fire and burns.", Yname2(obj
));
3204 if (Has_contents(obj
)) {
3206 pline("Its contents fall out.");
3207 for (otmp
= obj
->cobj
; otmp
; otmp
= ncobj
) {
3209 obj_extract_self(otmp
);
3210 if (!flooreffects(otmp
, x
, y
, ""))
3211 place_object(otmp
, x
, y
);
3217 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3218 /* chance per item of sustaining damage:
3219 * max luck (Luck==13): 10%
3220 * avg luck (Luck==0): 75%
3221 * awful luck (Luck<-4): 100%
3224 } else if (obj
->oclass
== SCROLL_CLASS
|| obj
->oclass
== SPBOOK_CLASS
) {
3225 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
)
3227 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3229 pline("Smoke rises from %s.", the(xname(obj
)));
3232 dindx
= (obj
->oclass
== SCROLL_CLASS
) ? 3 : 4;
3234 pline("%s %s.", Yname2(obj
),
3235 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3239 } else if (obj
->oclass
== POTION_CLASS
) {
3240 dindx
= (obj
->otyp
!= POT_OIL
) ? 1 : 2;
3242 pline("%s %s.", Yname2(obj
),
3243 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3247 } else if (erode_obj(obj
, (char *) 0, ERODE_BURN
, EF_DESTROY
)
3255 * Apply fire_damage() to an entire chain.
3257 * Return number of objects destroyed. --ALI
3260 fire_damage_chain(chain
, force
, here
, x
, y
)
3262 boolean force
, here
;
3265 struct obj
*obj
, *nobj
;
3267 for (obj
= chain
; obj
; obj
= nobj
) {
3268 nobj
= here
? obj
->nexthere
: obj
->nobj
;
3269 if (fire_damage(obj
, force
, x
, y
))
3273 if (num
&& (Blind
&& !couldsee(x
, y
)))
3274 You("smell smoke.");
3282 /* Scrolls but not spellbooks can be erased by acid. */
3283 struct monst
*victim
;
3289 victim
= carried(obj
) ? &youmonst
: mcarried(obj
) ? obj
->ocarry
: NULL
;
3290 vismon
= victim
&& (victim
!= &youmonst
) && canseemon(victim
);
3293 grease_protect(obj
, (char *) 0, victim
);
3294 } else if (obj
->oclass
== SCROLL_CLASS
&& obj
->otyp
!= SCR_BLANK_PAPER
) {
3295 if (obj
->otyp
!= SCR_BLANK_PAPER
3297 && obj
->otyp
!= SCR_MAIL
3301 if (victim
== &youmonst
)
3302 pline("Your %s.", aobjnam(obj
, "fade"));
3304 pline("%s %s.", s_suffix(Monnam(victim
)),
3305 aobjnam(obj
, "fade"));
3308 obj
->otyp
= SCR_BLANK_PAPER
;
3312 erode_obj(obj
, (char *) 0, ERODE_CORRODE
, EF_GREASE
| EF_VERBOSE
);
3315 /* context for water_damage(), managed by water_damage_chain();
3316 when more than one stack of potions of acid explode while processing
3317 a chain of objects, use alternate phrasing after the first message */
3318 static struct h2o_ctx
{
3319 int dkn_boom
, unk_boom
; /* track dknown, !dknown separately */
3321 } acid_ctx
= { 0, 0, FALSE
};
3323 /* Get an object wet and damage it appropriately.
3324 * "ostr", if present, is used instead of the object name in some
3326 * "force" means not to roll luck to protect some objects.
3327 * Returns an erosion return value (ER_*)
3330 water_damage(obj
, ostr
, force
)
3344 if (obj
->otyp
== CAN_OF_GREASE
&& obj
->spe
> 0) {
3346 } else if (obj
->otyp
== TOWEL
&& obj
->spe
< 7) {
3347 wet_a_towel(obj
, rnd(7), TRUE
);
3349 } else if (obj
->greased
) {
3355 } else if (Is_container(obj
) && !Is_box(obj
)
3356 && (obj
->otyp
!= OILSKIN_SACK
|| (obj
->cursed
&& !rn2(3)))) {
3358 pline("Water gets into your %s!", ostr
);
3360 water_damage_chain(obj
->cobj
, FALSE
);
3362 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3363 /* chance per item of sustaining damage:
3365 * avg luck (Luck==0): 75%
3366 * awful luck (Luck<-4): 100%
3369 } else if (obj
->oclass
== SCROLL_CLASS
) {
3370 if (obj
->otyp
== SCR_BLANK_PAPER
3372 || obj
->otyp
== SCR_MAIL
3376 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3378 obj
->otyp
= SCR_BLANK_PAPER
;
3384 } else if (obj
->oclass
== SPBOOK_CLASS
) {
3385 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3386 pline("Steam rises from %s.", the(xname(obj
)));
3388 } else if (obj
->otyp
== SPE_BLANK_PAPER
) {
3392 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3394 if (obj
->otyp
== SPE_NOVEL
) {
3399 obj
->otyp
= SPE_BLANK_PAPER
;
3404 } else if (obj
->oclass
== POTION_CLASS
) {
3405 if (obj
->otyp
== POT_ACID
) {
3407 boolean one
= (obj
->quan
== 1L), update
= carried(obj
),
3410 if (Blind
&& !carried(obj
))
3412 if (acid_ctx
.ctx_valid
)
3413 exploded
= ((obj
->dknown
? acid_ctx
.dkn_boom
3414 : acid_ctx
.unk_boom
) > 0);
3416 * "a [potion|<color> potion|potion of acid] explodes"
3417 * depending on obj->dknown (potion has been seen) and
3418 * objects[POT_ACID].oc_name_known (fully discovered),
3419 * or "some {plural version} explode" when relevant.
3420 * Second and subsequent messages for same chain and
3421 * matching dknown status are
3422 * "another [potion|<color> &c] explodes" or plural
3425 bufp
= simpleonames(obj
);
3426 pline("%s %s %s!", /* "A potion explodes!" */
3427 !exploded
? (one
? "A" : "Some")
3428 : (one
? "Another" : "More"),
3429 bufp
, vtense(bufp
, "explode"));
3430 if (acid_ctx
.ctx_valid
) {
3432 acid_ctx
.dkn_boom
++;
3434 acid_ctx
.unk_boom
++;
3440 return ER_DESTROYED
;
3441 } else if (obj
->odiluted
) {
3443 pline("Your %s %s further.", ostr
, vtense(ostr
, "dilute"));
3445 obj
->otyp
= POT_WATER
;
3447 obj
->blessed
= obj
->cursed
= 0;
3452 } else if (obj
->otyp
!= POT_WATER
) {
3454 pline("Your %s %s.", ostr
, vtense(ostr
, "dilute"));
3462 return erode_obj(obj
, ostr
, ERODE_RUST
, EF_NONE
);
3468 water_damage_chain(obj
, here
)
3474 /* initialize acid context: so far, neither seen (dknown) potions of
3475 acid nor unseen have exploded during this water damage sequence */
3476 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3477 acid_ctx
.ctx_valid
= TRUE
;
3479 for (; obj
; obj
= otmp
) {
3480 otmp
= here
? obj
->nexthere
: obj
->nobj
;
3481 water_damage(obj
, (char *) 0, FALSE
);
3484 /* reset acid context */
3485 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3486 acid_ctx
.ctx_valid
= FALSE
;
3490 * This function is potentially expensive - rolling
3491 * inventory list multiple times. Luckily it's seldom needed.
3492 * Returns TRUE if disrobing made player unencumbered enough to
3493 * crawl out of the current predicament.
3496 emergency_disrobe(lostsome
)
3499 int invc
= inv_cnt(TRUE
);
3501 while (near_capacity() > (Punished
? UNENCUMBERED
: SLT_ENCUMBER
)) {
3502 register struct obj
*obj
, *otmp
= (struct obj
*) 0;
3505 /* Pick a random object */
3508 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
3510 * Undroppables are: body armor, boots, gloves,
3511 * amulets, and rings because of the time and effort
3512 * in removing them + loadstone and other cursed stuff
3513 * for obvious reasons.
3515 if (!((obj
->otyp
== LOADSTONE
&& obj
->cursed
) || obj
== uamul
3516 || obj
== uleft
|| obj
== uright
|| obj
== ublindf
3517 || obj
== uarm
|| obj
== uarmc
|| obj
== uarmg
3518 || obj
== uarmf
|| obj
== uarmu
3519 || (obj
->cursed
&& (obj
== uarmh
|| obj
== uarms
))
3522 /* reached the mark and found some stuff to drop? */
3523 if (--i
< 0 && otmp
)
3530 return FALSE
; /* nothing to drop! */
3531 if (otmp
->owornmask
)
3532 remove_worn_item(otmp
, FALSE
);
3541 /* return TRUE iff player relocated */
3545 const char *pool_of_water
;
3546 boolean inpool_ok
= FALSE
, crawl_ok
;
3549 /* happily wading in the same contiguous pool */
3550 if (u
.uinwater
&& is_pool(u
.ux
- u
.dx
, u
.uy
- u
.dy
)
3551 && (Swimming
|| Amphibious
)) {
3552 /* water effects on objects every now and then */
3560 You("%s into the water%c", Is_waterlevel(&u
.uz
) ? "plunge" : "fall",
3561 Amphibious
|| Swimming
? '.' : '!');
3562 if (!Swimming
&& !Is_waterlevel(&u
.uz
))
3563 You("sink like %s.", Hallucination
? "the Titanic" : "a rock");
3566 water_damage_chain(invent
, FALSE
);
3568 if (u
.umonnum
== PM_GREMLIN
&& rn2(3))
3569 (void) split_mon(&youmonst
, (struct monst
*) 0);
3570 else if (u
.umonnum
== PM_IRON_GOLEM
) {
3572 i
= Maybe_Half_Phys(d(2, 6));
3575 losehp(i
, "rusting away", KILLED_BY
);
3580 if ((i
= number_leashed()) > 0) {
3581 pline_The("leash%s slip%s loose.", (i
> 1) ? "es" : "",
3582 (i
> 1) ? "" : "s");
3586 if (Amphibious
|| Swimming
) {
3589 pline("But you aren't drowning.");
3590 if (!Is_waterlevel(&u
.uz
)) {
3592 Your("keel hits the bottom.");
3594 You("touch bottom.");
3601 vision_recalc(2); /* unsee old position */
3604 vision_full_recalc
= 1;
3607 if ((Teleportation
|| can_teleport(youmonst
.data
)) && !Unaware
3608 && (Teleport_control
|| rn2(3) < Luck
+ 2)) {
3609 You("attempt a teleport spell."); /* utcsri!carroll */
3610 if (!level
.flags
.noteleport
) {
3612 if (!is_pool(u
.ux
, u
.uy
))
3615 pline_The("attempted teleport spell fails.");
3618 dismount_steed(DISMOUNT_GENERIC
);
3619 if (!is_pool(u
.ux
, u
.uy
))
3623 x
= y
= 0; /* lint suppression */
3624 /* if sleeping, wake up now so that we don't crawl out of water
3625 while still asleep; we can't do that the same way that waking
3626 due to combat is handled; note unmul() clears u.usleep */
3628 unmul("Suddenly you wake up!");
3629 /* being doused will revive from fainting */
3632 /* can't crawl if unable to move (crawl_ok flag stays false) */
3633 if (multi
< 0 || (Upolyd
&& !youmonst
.data
->mmove
))
3635 /* look around for a place to crawl to */
3636 for (i
= 0; i
< 100; i
++) {
3637 x
= rn1(3, u
.ux
- 1);
3638 y
= rn1(3, u
.uy
- 1);
3639 if (crawl_destination(x
, y
)) {
3645 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
3646 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++)
3647 if (crawl_destination(x
, y
)) {
3653 boolean lost
= FALSE
;
3654 /* time to do some strip-tease... */
3655 boolean succ
= Is_waterlevel(&u
.uz
) ? TRUE
: emergency_disrobe(&lost
);
3657 You("try to crawl out of the water.");
3659 You("dump some of your gear to lose weight...");
3661 pline("Pheew! That was close.");
3665 /* still too much weight */
3666 pline("But in vain.");
3670 for (i
= 0; i
< 5; i
++) { /* arbitrary number of loops */
3671 /* killer format and name are reconstructed every iteration
3672 because lifesaving resets them */
3673 pool_of_water
= waterbody_name(u
.ux
, u
.uy
);
3674 killer
.format
= KILLED_BY_AN
;
3675 /* avoid "drowned in [a] water" */
3676 if (!strcmp(pool_of_water
, "water"))
3677 pool_of_water
= "deep water", killer
.format
= KILLED_BY
;
3678 Strcpy(killer
.name
, pool_of_water
);
3680 /* oops, we're still alive. better get out of the water. */
3681 if (safe_teleds(TRUE
))
3682 break; /* successful life-save */
3683 /* nowhere safe to land; repeat drowning loop... */
3684 pline("You're still drowning.");
3688 You("find yourself back %s.",
3689 Is_waterlevel(&u
.uz
) ? "in an air bubble" : "on land");
3699 /* energy is completely gone */
3700 You_feel("momentarily lethargic.");
3702 /* throttle further loss a bit when there's not much left to lose */
3703 if (n
> u
.uenmax
|| n
> u
.ulevel
)
3706 You_feel("your magical energy drain away%c", (n
> u
.uen
) ? '!' : '.');
3709 u
.uenmax
-= rnd(-u
.uen
);
3722 if (near_capacity() >= HVY_ENCUMBER
) {
3723 pline("You're too strained to do that.");
3726 if ((nohands(youmonst
.data
) && !webmaker(youmonst
.data
))
3727 || !youmonst
.data
->mmove
) {
3728 pline("And just how do you expect to do that?");
3730 } else if (u
.ustuck
&& sticks(youmonst
.data
)) {
3731 pline("You'll have to let go of %s first.", mon_nam(u
.ustuck
));
3734 if (u
.ustuck
|| (welded(uwep
) && bimanual(uwep
))) {
3735 Your("%s seem to be too busy for that.", makeplural(body_part(HAND
)));
3738 return untrap(FALSE
);
3741 /* Probability of disabling a trap. Helge Hafting */
3748 /* Only spiders know how to deal with webs reliably */
3749 if (ttmp
->ttyp
== WEB
&& !webmaker(youmonst
.data
))
3751 if (Confusion
|| Hallucination
)
3759 /* Your own traps are better known than others. */
3760 if (ttmp
&& ttmp
->madeby_u
)
3762 if (Role_if(PM_ROGUE
)) {
3763 if (rn2(2 * MAXULEV
) < u
.ulevel
)
3765 if (u
.uhave
.questart
&& chance
> 1)
3767 } else if (Role_if(PM_RANGER
) && chance
> 1)
3772 /* Replace trap with object(s). Helge Hafting */
3774 cnv_trap_obj(otyp
, cnt
, ttmp
, bury_it
)
3780 struct obj
*otmp
= mksobj(otyp
, TRUE
, FALSE
);
3783 otmp
->owt
= weight(otmp
);
3784 /* Only dart traps are capable of being poisonous */
3786 otmp
->opoisoned
= 0;
3787 place_object(otmp
, ttmp
->tx
, ttmp
->ty
);
3789 /* magical digging first disarms this trap, then will unearth it */
3790 (void) bury_an_obj(otmp
, (boolean
*) 0);
3792 /* Sell your own traps only... */
3794 sellobj(otmp
, ttmp
->tx
, ttmp
->ty
);
3797 newsym(ttmp
->tx
, ttmp
->ty
);
3798 if (u
.utrap
&& ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
)
3803 /* while attempting to disarm an adjacent trap, we've fallen into it */
3805 move_into_trap(ttmp
)
3809 xchar x
= ttmp
->tx
, y
= ttmp
->ty
, bx
, by
, cx
, cy
;
3812 bx
= by
= cx
= cy
= 0; /* lint suppression */
3813 /* we know there's no monster in the way, and we're not trapped */
3815 || drag_ball(x
, y
, &bc
, &bx
, &by
, &cx
, &cy
, &unused
, TRUE
)) {
3816 u
.ux0
= u
.ux
, u
.uy0
= u
.uy
;
3819 newsym(u
.ux0
, u
.uy0
);
3821 check_leash(u
.ux0
, u
.uy0
);
3823 move_bc(0, bc
, bx
, by
, cx
, cy
);
3824 /* marking the trap unseen forces dotrap() to treat it like a new
3825 discovery and prevents pickup() -> look_here() -> check_here()
3826 from giving a redundant "there is a <trap> here" message when
3827 there are objects covering this trap */
3828 ttmp
->tseen
= 0; /* hack for check_here() */
3829 /* trigger the trap */
3830 spoteffects(TRUE
); /* pickup() + dotrap() */
3831 exercise(A_WIS
, FALSE
);
3835 /* 0: doesn't even try
3836 * 1: tries and fails
3840 try_disarm(ttmp
, force_failure
)
3842 boolean force_failure
;
3844 struct monst
*mtmp
= m_at(ttmp
->tx
, ttmp
->ty
);
3845 int ttype
= ttmp
->ttyp
;
3846 boolean under_u
= (!u
.dx
&& !u
.dy
);
3847 boolean holdingtrap
= (ttype
== BEAR_TRAP
|| ttype
== WEB
);
3849 /* Test for monster first, monsters are displayed instead of trap. */
3850 if (mtmp
&& (!mtmp
->mtrapped
|| !holdingtrap
)) {
3851 pline("%s is in the way.", Monnam(mtmp
));
3854 /* We might be forced to move onto the trap's location. */
3855 if (sobj_at(BOULDER
, ttmp
->tx
, ttmp
->ty
) && !Passes_walls
&& !under_u
) {
3856 There("is a boulder in your way.");
3859 /* duplicate tight-space checks from test_move */
3860 if (u
.dx
&& u
.dy
&& bad_rock(youmonst
.data
, u
.ux
, ttmp
->ty
)
3861 && bad_rock(youmonst
.data
, ttmp
->tx
, u
.uy
)) {
3862 if ((invent
&& (inv_weight() + weight_cap() > 600))
3863 || bigmonst(youmonst
.data
)) {
3864 /* don't allow untrap if they can't get thru to it */
3865 You("are unable to reach the %s!",
3866 defsyms
[trap_to_defsym(ttype
)].explanation
);
3870 /* untrappable traps are located on the ground. */
3871 if (!can_reach_floor(TRUE
)) {
3872 if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
)
3875 You("are unable to reach the %s!",
3876 defsyms
[trap_to_defsym(ttype
)].explanation
);
3880 /* Will our hero succeed? */
3881 if (force_failure
|| untrap_prob(ttmp
)) {
3884 if (mtmp
) { /* must be a trap that holds monsters */
3885 if (ttype
== BEAR_TRAP
) {
3888 if ((mtmp
->mhp
-= rnd(4)) <= 0)
3890 } else if (ttype
== WEB
) {
3891 if (!webmaker(youmonst
.data
)) {
3892 struct trap
*ttmp2
= maketrap(u
.ux
, u
.uy
, WEB
);
3896 "webbing sticks to you. You're caught too!");
3897 dotrap(ttmp2
, NOWEBMSG
);
3898 if (u
.usteed
&& u
.utrap
) {
3899 /* you, not steed, are trapped */
3900 dismount_steed(DISMOUNT_FELL
);
3904 pline("%s remains entangled.", Monnam(mtmp
));
3906 } else if (under_u
) {
3909 move_into_trap(ttmp
);
3912 pline("%s %s is difficult to %s.",
3913 ttmp
->madeby_u
? "Your" : under_u
? "This" : "That",
3914 defsyms
[trap_to_defsym(ttype
)].explanation
,
3915 (ttype
== WEB
) ? "remove" : "disarm");
3923 reward_untrap(ttmp
, mtmp
)
3927 if (!ttmp
->madeby_u
) {
3928 if (rnl(10) < 8 && !mtmp
->mpeaceful
&& !mtmp
->msleeping
3929 && !mtmp
->mfrozen
&& !mindless(mtmp
->data
)
3930 && mtmp
->data
->mlet
!= S_HUMAN
) {
3931 mtmp
->mpeaceful
= 1;
3932 set_malign(mtmp
); /* reset alignment */
3933 pline("%s is grateful.", Monnam(mtmp
));
3935 /* Helping someone out of a trap is a nice thing to do,
3936 * A lawful may be rewarded, but not too often. */
3937 if (!rn2(3) && !rnl(8) && u
.ualign
.type
== A_LAWFUL
) {
3939 You_feel("that you did the right thing.");
3945 disarm_holdingtrap(ttmp
) /* Helge Hafting */
3949 int fails
= try_disarm(ttmp
, FALSE
);
3954 /* ok, disarm it. */
3956 /* untrap the monster, if any.
3957 There's no need for a cockatrice test, only the trap is touched */
3958 if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
3960 You("remove %s %s from %s.", the_your
[ttmp
->madeby_u
],
3961 (ttmp
->ttyp
== BEAR_TRAP
) ? "bear trap" : "webbing",
3963 reward_untrap(ttmp
, mtmp
);
3965 if (ttmp
->ttyp
== BEAR_TRAP
) {
3966 You("disarm %s bear trap.", the_your
[ttmp
->madeby_u
]);
3967 cnv_trap_obj(BEARTRAP
, 1, ttmp
, FALSE
);
3968 } else /* if (ttmp->ttyp == WEB) */ {
3969 You("succeed in removing %s web.", the_your
[ttmp
->madeby_u
]);
3973 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
3978 disarm_landmine(ttmp
) /* Helge Hafting */
3981 int fails
= try_disarm(ttmp
, FALSE
);
3985 You("disarm %s land mine.", the_your
[ttmp
->madeby_u
]);
3986 cnv_trap_obj(LAND_MINE
, 1, ttmp
, FALSE
);
3990 /* getobj will filter down to cans of grease and known potions of oil */
3991 static NEARDATA
const char oil
[] = { ALL_CLASSES
, TOOL_CLASS
, POTION_CLASS
,
3994 /* it may not make much sense to use grease on floor boards, but so what? */
3996 disarm_squeaky_board(ttmp
)
4003 obj
= getobj(oil
, "untrap with");
4007 bad_tool
= (obj
->cursed
4008 || ((obj
->otyp
!= POT_OIL
|| obj
->lamplit
)
4009 && (obj
->otyp
!= CAN_OF_GREASE
|| !obj
->spe
)));
4010 fails
= try_disarm(ttmp
, bad_tool
);
4014 /* successfully used oil or grease to fix squeaky board */
4015 if (obj
->otyp
== CAN_OF_GREASE
) {
4016 consume_obj_charge(obj
, TRUE
);
4018 useup(obj
); /* oil */
4021 You("repair the squeaky board."); /* no madeby_u */
4023 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
4024 more_experienced(1, 5);
4029 /* removes traps that shoot arrows, darts, etc. */
4031 disarm_shooting_trap(ttmp
, otyp
)
4035 int fails
= try_disarm(ttmp
, FALSE
);
4039 You("disarm %s trap.", the_your
[ttmp
->madeby_u
]);
4040 cnv_trap_obj(otyp
, 50 - rnl(50), ttmp
, FALSE
);
4044 /* Is the weight too heavy?
4045 * Formula as in near_capacity() & check_capacity() */
4047 try_lift(mtmp
, ttmp
, wt
, stuff
)
4053 int wc
= weight_cap();
4055 if (((wt
* 2) / wc
) >= HVY_ENCUMBER
) {
4056 pline("%s is %s for you to lift.", Monnam(mtmp
),
4057 stuff
? "carrying too much" : "too heavy");
4058 if (!ttmp
->madeby_u
&& !mtmp
->mpeaceful
&& mtmp
->mcanmove
4059 && !mindless(mtmp
->data
) && mtmp
->data
->mlet
!= S_HUMAN
4061 mtmp
->mpeaceful
= 1;
4062 set_malign(mtmp
); /* reset alignment */
4063 pline("%s thinks it was nice of you to try.", Monnam(mtmp
));
4070 /* Help trapped monster (out of a (spiked) pit) */
4072 help_monster_out(mtmp
, ttmp
)
4081 * This works when levitating too -- consistent with the ability
4082 * to hit monsters while levitating.
4084 * Should perhaps check that our hero has arms/hands at the
4085 * moment. Helping can also be done by engulfing...
4087 * Test the monster first - monsters are displayed before traps.
4089 if (!mtmp
->mtrapped
) {
4090 pline("%s isn't trapped.", Monnam(mtmp
));
4093 /* Do you have the necessary capacity to lift anything? */
4094 if (check_capacity((char *) 0))
4097 /* Will our hero succeed? */
4098 if ((uprob
= untrap_prob(ttmp
)) && !mtmp
->msleeping
&& mtmp
->mcanmove
) {
4099 You("try to reach out your %s, but %s backs away skeptically.",
4100 makeplural(body_part(ARM
)), mon_nam(mtmp
));
4104 /* is it a cockatrice?... */
4105 if (touch_petrifies(mtmp
->data
) && !uarmg
&& !Stone_resistance
) {
4106 You("grab the trapped %s using your bare %s.", mtmp
->data
->mname
,
4107 makeplural(body_part(HAND
)));
4109 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
)) {
4110 display_nhwindow(WIN_MESSAGE
, FALSE
);
4114 Sprintf(kbuf
, "trying to help %s out of a pit",
4115 an(mtmp
->data
->mname
));
4120 /* need to do cockatrice check first if sleeping or paralyzed */
4122 You("try to grab %s, but cannot get a firm grasp.", mon_nam(mtmp
));
4123 if (mtmp
->msleeping
) {
4124 mtmp
->msleeping
= 0;
4125 pline("%s awakens.", Monnam(mtmp
));
4130 You("reach out your %s and grab %s.", makeplural(body_part(ARM
)),
4133 if (mtmp
->msleeping
) {
4134 mtmp
->msleeping
= 0;
4135 pline("%s awakens.", Monnam(mtmp
));
4136 } else if (mtmp
->mfrozen
&& !rn2(mtmp
->mfrozen
)) {
4137 /* After such manhandling, perhaps the effect wears off */
4140 pline("%s stirs.", Monnam(mtmp
));
4143 /* is the monster too heavy? */
4144 wt
= inv_weight() + mtmp
->data
->cwt
;
4145 if (!try_lift(mtmp
, ttmp
, wt
, FALSE
))
4148 /* is the monster with inventory too heavy? */
4149 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
4151 if (!try_lift(mtmp
, ttmp
, wt
, TRUE
))
4154 You("pull %s out of the pit.", mon_nam(mtmp
));
4156 fill_pit(mtmp
->mx
, mtmp
->my
);
4157 reward_untrap(ttmp
, mtmp
);
4165 register struct obj
*otmp
;
4170 const char *trapdescr
;
4171 boolean here
, useplural
, confused
= (Confusion
|| Hallucination
),
4172 trap_skipped
= FALSE
, deal_with_floor_trap
;
4174 char the_trap
[BUFSZ
], qbuf
[QBUFSZ
];
4176 if (!getdir((char *) 0))
4181 pline_The("perils lurking there are beyond your grasp.");
4185 if (ttmp
&& !ttmp
->tseen
)
4187 trapdescr
= ttmp
? defsyms
[trap_to_defsym(ttmp
->ttyp
)].explanation
: 0;
4188 here
= (x
== u
.ux
&& y
== u
.uy
); /* !u.dx && !u.dy */
4190 if (here
) /* are there are one or more containers here? */
4191 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4197 deal_with_floor_trap
= can_reach_floor(FALSE
);
4198 if (!deal_with_floor_trap
) {
4201 Strcat(the_trap
, an(trapdescr
));
4203 Strcat(the_trap
, " and ");
4205 Strcat(the_trap
, (boxcnt
== 1) ? "a container" : "containers");
4206 useplural
= ((ttmp
&& boxcnt
> 0) || boxcnt
> 1);
4207 /* note: boxcnt and useplural will always be 0 for !here case */
4209 There("%s %s %s but you can't reach %s%s.",
4210 useplural
? "are" : "is", the_trap
, here
? "here" : "there",
4211 useplural
? "them" : "it",
4212 u
.usteed
? " while mounted" : "");
4213 trap_skipped
= (ttmp
!= 0);
4214 } else { /* deal_with_floor_trap */
4217 Strcpy(the_trap
, the(trapdescr
));
4219 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== SPIKED_PIT
) {
4220 You_cant("do much about %s%s.", the_trap
,
4221 u
.utrap
? " that you're stuck in"
4222 : " while standing on the edge of it");
4223 trap_skipped
= TRUE
;
4224 deal_with_floor_trap
= FALSE
;
4227 qbuf
, "There %s and %s here. %s %s?",
4228 (boxcnt
== 1) ? "is a container" : "are containers",
4230 (ttmp
->ttyp
== WEB
) ? "Remove" : "Disarm", the_trap
);
4231 switch (ynq(qbuf
)) {
4235 trap_skipped
= TRUE
;
4236 deal_with_floor_trap
= FALSE
;
4241 if (deal_with_floor_trap
) {
4243 You("cannot deal with %s while trapped%s!", the_trap
,
4244 (x
== u
.ux
&& y
== u
.uy
) ? " in it" : "");
4247 if ((mtmp
= m_at(x
, y
)) != 0
4248 && (mtmp
->m_ap_type
== M_AP_FURNITURE
4249 || mtmp
->m_ap_type
== M_AP_OBJECT
)) {
4250 stumble_onto_mimic(mtmp
);
4253 switch (ttmp
->ttyp
) {
4256 return disarm_holdingtrap(ttmp
);
4258 return disarm_landmine(ttmp
);
4260 return disarm_squeaky_board(ttmp
);
4262 return disarm_shooting_trap(ttmp
, DART
);
4264 return disarm_shooting_trap(ttmp
, ARROW
);
4268 You("are already on the edge of the pit.");
4272 pline("Try filling the pit instead.");
4275 return help_monster_out(mtmp
, ttmp
);
4277 You("cannot disable %s trap.", !here
? "that" : "this");
4284 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4286 (void) safe_qbuf(qbuf
, "There is ",
4287 " here. Check it for traps?", otmp
,
4288 doname
, ansimpleoname
, "a box");
4289 switch (ynq(qbuf
)) {
4297 && (force
|| (!confused
4298 && rn2(MAXULEV
+ 1 - u
.ulevel
) < 10)))
4299 || (!force
&& confused
&& !rn2(3))) {
4300 You("find a trap on %s!", the(xname(otmp
)));
4302 exercise(A_WIS
, TRUE
);
4304 switch (ynq("Disarm it?")) {
4308 trap_skipped
= TRUE
;
4312 if (otmp
->otrapped
) {
4313 exercise(A_DEX
, TRUE
);
4314 ch
= ACURR(A_DEX
) + u
.ulevel
;
4315 if (Role_if(PM_ROGUE
))
4317 if (!force
&& (confused
|| Fumbling
4318 || rnd(75 + level_difficulty() / 2)
4320 (void) chest_trap(otmp
, FINGER
, TRUE
);
4326 pline("That %s was not trapped.", xname(otmp
));
4329 You("find no traps on %s.", the(xname(otmp
)));
4334 You(trap_skipped
? "find no other traps here."
4335 : "know of no traps here.");
4339 if (stumble_on_door_mimic(x
, y
))
4342 } /* deal_with_floor_trap */
4343 /* doors can be manipulated even while levitating/unskilled riding */
4345 if (!IS_DOOR(levl
[x
][y
].typ
)) {
4347 You("know of no traps there.");
4351 switch (levl
[x
][y
].doormask
) {
4353 You("%s no door there.", Blind
? "feel" : "see");
4356 pline("This door is safely open.");
4359 pline("This door is broken.");
4363 if ((levl
[x
][y
].doormask
& D_TRAPPED
4364 && (force
|| (!confused
&& rn2(MAXULEV
- u
.ulevel
+ 11) < 10)))
4365 || (!force
&& confused
&& !rn2(3))) {
4366 You("find a trap on the door!");
4367 exercise(A_WIS
, TRUE
);
4368 if (ynq("Disarm it?") != 'y')
4370 if (levl
[x
][y
].doormask
& D_TRAPPED
) {
4371 ch
= 15 + (Role_if(PM_ROGUE
) ? u
.ulevel
* 3 : u
.ulevel
);
4372 exercise(A_DEX
, TRUE
);
4373 if (!force
&& (confused
|| Fumbling
4374 || rnd(75 + level_difficulty() / 2) > ch
)) {
4376 b_trapped("door", FINGER
);
4377 levl
[x
][y
].doormask
= D_NODOOR
;
4378 unblock_point(x
, y
);
4380 /* (probably ought to charge for this damage...) */
4381 if (*in_rooms(x
, y
, SHOPBASE
))
4382 add_damage(x
, y
, 0L);
4385 levl
[x
][y
].doormask
&= ~D_TRAPPED
;
4388 pline("This door was not trapped.");
4391 You("find no traps on the door.");
4396 /* for magic unlocking; returns true if targetted monster (which might
4397 be hero) gets untrapped; the trap remains intact */
4399 openholdingtrap(mon
, noticed
)
4401 boolean
*noticed
; /* set to true iff hero notices the effect; */
4402 { /* otherwise left with its previous value intact */
4405 const char *trapdescr
, *which
;
4406 boolean ishero
= (mon
== &youmonst
);
4408 if (mon
== u
.usteed
)
4410 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4411 /* if no trap here or it's not a holding trap, we're done */
4412 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4415 trapdescr
= defsyms
[trap_to_defsym(t
->ttyp
)].explanation
;
4416 which
= t
->tseen
? the_your
[t
->madeby_u
]
4417 : index(vowels
, *trapdescr
) ? "an" : "a";
4422 u
.utrap
= 0; /* released regardless of type */
4424 /* give message only if trap was the expected type */
4425 if (u
.utraptype
== TT_BEARTRAP
|| u
.utraptype
== TT_WEB
) {
4427 Sprintf(buf
, "%s is", noit_Monnam(u
.usteed
));
4429 Strcpy(buf
, "You are");
4430 pline("%s released from %s %s.", buf
, which
, trapdescr
);
4436 if (canspotmon(mon
)) {
4438 pline("%s is released from %s %s.", Monnam(mon
), which
,
4440 } else if (cansee(t
->tx
, t
->ty
) && t
->tseen
) {
4443 pline("%s is released from %s %s.", Something
, which
,
4445 else /* BEAR_TRAP */
4446 pline("%s %s opens.", upstart(strcpy(buf
, which
)), trapdescr
);
4448 /* might pacify monster if adjacent */
4449 if (rn2(2) && distu(mon
->mx
, mon
->my
) <= 2)
4450 reward_untrap(t
, mon
);
4455 /* for magic locking; returns true if targetted monster (which might
4456 be hero) gets hit by a trap (might avoid actually becoming trapped) */
4458 closeholdingtrap(mon
, noticed
)
4460 boolean
*noticed
; /* set to true iff hero notices the effect; */
4461 { /* otherwise left with its previous value intact */
4463 unsigned dotrapflags
;
4464 boolean ishero
= (mon
== &youmonst
), result
;
4466 if (mon
== u
.usteed
)
4468 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4469 /* if no trap here or it's not a holding trap, we're done */
4470 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4475 return FALSE
; /* already trapped */
4477 dotrapflags
= FORCETRAP
;
4478 /* dotrap calls mintrap when mounted hero encounters a web */
4480 dotrapflags
|= NOWEBMSG
;
4482 dotrap(t
, dotrapflags
);
4484 result
= (u
.utrap
!= 0);
4487 return FALSE
; /* already trapped */
4488 /* you notice it if you see the trap close/tremble/whatever
4489 or if you sense the monster who becomes trapped */
4490 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4492 result
= (mintrap(mon
) != 0);
4498 /* for magic unlocking; returns true if targetted monster (which might
4499 be hero) gets hit by a trap (target might avoid its effect) */
4501 openfallingtrap(mon
, trapdoor_only
, noticed
)
4503 boolean trapdoor_only
;
4504 boolean
*noticed
; /* set to true iff hero notices the effect; */
4505 { /* otherwise left with its previous value intact */
4507 boolean ishero
= (mon
== &youmonst
), result
;
4509 if (mon
== u
.usteed
)
4511 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4512 /* if no trap here or it's not a falling trap, we're done
4513 (note: falling rock traps have a trapdoor in the ceiling) */
4514 if (!t
|| ((t
->ttyp
!= TRAPDOOR
&& t
->ttyp
!= ROCKTRAP
)
4515 && (trapdoor_only
|| (t
->ttyp
!= HOLE
&& t
->ttyp
!= PIT
4516 && t
->ttyp
!= SPIKED_PIT
))))
4521 return FALSE
; /* already trapped */
4523 dotrap(t
, FORCETRAP
);
4524 result
= (u
.utrap
!= 0);
4527 return FALSE
; /* already trapped */
4528 /* you notice it if you see the trap close/tremble/whatever
4529 or if you sense the monster who becomes trapped */
4530 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4531 /* monster will be angered; mintrap doesn't handle that */
4534 result
= (mintrap(mon
) != 0);
4536 /* mon might now be on the migrating monsters list */
4541 /* only called when the player is doing something to the chest directly */
4543 chest_trap(obj
, bodypart
, disarm
)
4544 register struct obj
*obj
;
4545 register int bodypart
;
4548 register struct obj
*otmp
= obj
, *otmp2
;
4553 if (get_obj_location(obj
, &cc
.x
, &cc
.y
, 0)) /* might be carried */
4554 obj
->ox
= cc
.x
, obj
->oy
= cc
.y
;
4556 otmp
->otrapped
= 0; /* trap is one-shot; clear flag first in case
4557 chest kills you and ends up in bones file */
4558 You(disarm
? "set it off!" : "trigger a trap!");
4559 display_nhwindow(WIN_MESSAGE
, FALSE
);
4560 if (Luck
> -13 && rn2(13 + Luck
) > 7) { /* saved by luck */
4561 /* trap went off, but good luck prevents damage */
4565 msg
= "explosive charge is a dud";
4569 msg
= "electric charge is grounded";
4573 msg
= "flame fizzles out";
4578 msg
= "poisoned needle misses";
4584 msg
= "gas cloud blows away";
4587 impossible("chest disarm bug");
4592 pline("But luckily the %s!", msg
);
4594 switch (rn2(20) ? ((Luck
>= 13) ? 0 : rn2(13 - Luck
)) : rn2(26)) {
4600 struct monst
*shkp
= 0;
4602 boolean costly
, insider
;
4603 register xchar ox
= obj
->ox
, oy
= obj
->oy
;
4605 /* the obj location need not be that of player */
4606 costly
= (costly_spot(ox
, oy
)
4607 && (shkp
= shop_keeper(*in_rooms(ox
, oy
, SHOPBASE
)))
4608 != (struct monst
*) 0);
4609 insider
= (*u
.ushops
&& inside_shop(u
.ux
, u
.uy
)
4610 && *in_rooms(ox
, oy
, SHOPBASE
) == *u
.ushops
);
4612 pline("%s!", Tobjnam(obj
, "explode"));
4613 Sprintf(buf
, "exploding %s", xname(obj
));
4616 loss
+= stolen_value(obj
, ox
, oy
, (boolean
) shkp
->mpeaceful
,
4618 delete_contents(obj
);
4619 /* unpunish() in advance if either ball or chain (or both)
4620 is going to be destroyed */
4621 if (Punished
&& ((uchain
->ox
== u
.ux
&& uchain
->oy
== u
.uy
)
4622 || (uball
->where
== OBJ_FLOOR
4623 && uball
->ox
== u
.ux
&& uball
->oy
== u
.uy
)))
4626 for (otmp
= level
.objects
[u
.ux
][u
.uy
]; otmp
; otmp
= otmp2
) {
4627 otmp2
= otmp
->nexthere
;
4629 loss
+= stolen_value(otmp
, otmp
->ox
, otmp
->oy
,
4630 (boolean
) shkp
->mpeaceful
, TRUE
);
4634 losehp(Maybe_Half_Phys(d(6, 6)), buf
, KILLED_BY_AN
);
4635 exercise(A_STR
, FALSE
);
4636 if (costly
&& loss
) {
4638 You("owe %ld %s for objects destroyed.", loss
,
4641 You("caused %ld %s worth of damage!", loss
,
4643 make_angry_shk(shkp
, ox
, oy
);
4652 pline("A cloud of noxious gas billows from %s.", the(xname(obj
)));
4653 poisoned("gas cloud", A_STR
, "cloud of poison gas", 15, FALSE
);
4654 exercise(A_CON
, FALSE
);
4660 You_feel("a needle prick your %s.", body_part(bodypart
));
4661 poisoned("needle", A_CON
, "poisoned needle", 10, FALSE
);
4662 exercise(A_CON
, FALSE
);
4675 You("are jolted by a surge of electricity!");
4676 if (Shock_resistance
) {
4677 shieldeff(u
.ux
, u
.uy
);
4678 You("don't seem to be affected.");
4682 destroy_item(RING_CLASS
, AD_ELEC
);
4683 destroy_item(WAND_CLASS
, AD_ELEC
);
4685 losehp(dmg
, "electric shock", KILLED_BY_AN
);
4692 pline("Suddenly you are frozen in place!");
4694 multi_reason
= "frozen by a trap";
4695 exercise(A_DEX
, FALSE
);
4696 nomovemsg
= You_can_move_again
;
4698 You("momentarily stiffen.");
4703 pline("A cloud of %s gas billows from %s.",
4704 Blind
? blindgas
[rn2(SIZE(blindgas
))] : rndcolor(),
4708 pline("What a groovy feeling!");
4710 You("%s%s...", stagger(youmonst
.data
, "stagger"),
4711 Halluc_resistance
? ""
4712 : Blind
? " and get dizzy"
4713 : " and your vision blurs");
4715 make_stunned((HStun
& TIMEOUT
) + (long) rn1(7, 16), FALSE
);
4716 (void) make_hallucinated(
4717 (HHallucination
& TIMEOUT
) + (long) rn1(5, 16), FALSE
, 0L);
4720 impossible("bad chest trap");
4723 bot(); /* to get immediate botl re-display */
4733 register struct trap
*trap
= ftrap
;
4736 if (trap
->tx
== x
&& trap
->ty
== y
)
4740 return (struct trap
*) 0;
4745 register struct trap
*trap
;
4747 register struct trap
*ttmp
;
4749 clear_conjoined_pits(trap
);
4750 if (trap
== ftrap
) {
4751 ftrap
= ftrap
->ntrap
;
4753 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
)
4754 if (ttmp
->ntrap
== trap
)
4757 panic("deltrap: no preceding trap!");
4758 ttmp
->ntrap
= trap
->ntrap
;
4760 if (Sokoban
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== HOLE
))
4761 maybe_finish_sokoban();
4766 conjoined_pits(trap2
, trap1
, u_entering_trap2
)
4767 struct trap
*trap2
, *trap1
;
4768 boolean u_entering_trap2
;
4770 int dx
, dy
, diridx
, adjidx
;
4772 if (!trap1
|| !trap2
)
4774 if (!isok(trap2
->tx
, trap2
->ty
) || !isok(trap1
->tx
, trap1
->ty
)
4775 || !(trap2
->ttyp
== PIT
|| trap2
->ttyp
== SPIKED_PIT
)
4776 || !(trap1
->ttyp
== PIT
|| trap1
->ttyp
== SPIKED_PIT
)
4777 || (u_entering_trap2
&& !(u
.utrap
&& u
.utraptype
== TT_PIT
)))
4779 dx
= sgn(trap2
->tx
- trap1
->tx
);
4780 dy
= sgn(trap2
->ty
- trap1
->ty
);
4781 for (diridx
= 0; diridx
< 8; diridx
++)
4782 if (xdir
[diridx
] == dx
&& ydir
[diridx
] == dy
)
4784 /* diridx is valid if < 8 */
4786 adjidx
= (diridx
+ 4) % 8;
4787 if ((trap1
->conjoined
& (1 << diridx
))
4788 && (trap2
->conjoined
& (1 << adjidx
)))
4795 clear_conjoined_pits(trap
)
4798 int diridx
, adjidx
, x
, y
;
4801 if (trap
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
4802 for (diridx
= 0; diridx
< 8; ++diridx
) {
4803 if (trap
->conjoined
& (1 << diridx
)) {
4804 x
= trap
->tx
+ xdir
[diridx
];
4805 y
= trap
->ty
+ ydir
[diridx
];
4807 && (t
= t_at(x
, y
)) != 0
4808 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4809 adjidx
= (diridx
+ 4) % 8;
4810 t
->conjoined
&= ~(1 << adjidx
);
4812 trap
->conjoined
&= ~(1 << diridx
);
4820 * Mark all neighboring pits as conjoined pits.
4821 * (currently not called from anywhere)
4824 join_adjacent_pits(trap
)
4832 for (diridx
= 0; diridx
< 8; ++diridx
) {
4833 x
= trap
->tx
+ xdir
[diridx
];
4834 y
= trap
->ty
+ ydir
[diridx
];
4836 if ((t
= t_at(x
, y
)) != 0
4837 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4838 trap
->conjoined
|= (1 << diridx
);
4839 join_adjacent_pits(t
);
4841 trap
->conjoined
&= ~(1 << diridx
);
4848 * Returns TRUE if you escaped a pit and are standing on the precipice.
4851 uteetering_at_seen_pit(trap
)
4854 if (trap
&& trap
->tseen
&& (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
4855 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
))
4861 /* Destroy a trap that emanates from the floor. */
4864 register struct trap
*ttmp
;
4866 /* some of these are arbitrary -dlc */
4867 if (ttmp
&& ((ttmp
->ttyp
== SQKY_BOARD
) || (ttmp
->ttyp
== BEAR_TRAP
)
4868 || (ttmp
->ttyp
== LANDMINE
) || (ttmp
->ttyp
== FIRE_TRAP
)
4869 || (ttmp
->ttyp
== PIT
) || (ttmp
->ttyp
== SPIKED_PIT
)
4870 || (ttmp
->ttyp
== HOLE
) || (ttmp
->ttyp
== TRAPDOOR
)
4871 || (ttmp
->ttyp
== TELEP_TRAP
) || (ttmp
->ttyp
== LEVEL_TELEP
)
4872 || (ttmp
->ttyp
== WEB
) || (ttmp
->ttyp
== MAGIC_TRAP
)
4873 || (ttmp
->ttyp
== ANTI_MAGIC
))) {
4874 register struct monst
*mtmp
;
4876 if (ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
) {
4879 } else if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
4888 /* used for doors (also tins). can be used for anything else that opens. */
4890 b_trapped(item
, bodypart
)
4894 int lvl
= level_difficulty(),
4895 dmg
= rnd(5 + (lvl
< 5 ? lvl
: 2 + lvl
/ 2));
4897 pline("KABOOM!! %s was booby-trapped!", The(item
));
4899 losehp(Maybe_Half_Phys(dmg
), "explosion", KILLED_BY_AN
);
4900 exercise(A_STR
, FALSE
);
4902 exercise(A_CON
, FALSE
);
4903 make_stunned((HStun
& TIMEOUT
) + (long) dmg
, TRUE
);
4906 /* Monster is hit by trap. */
4907 /* Note: doesn't work if both obj and d_override are null */
4909 thitm(tlev
, mon
, obj
, d_override
, nocorpse
)
4917 boolean trapkilled
= FALSE
;
4922 strike
= (find_mac(mon
) + tlev
+ obj
->spe
<= rnd(20));
4924 strike
= (find_mac(mon
) + tlev
<= rnd(20));
4926 /* Actually more accurate than thitu, which doesn't take
4927 * obj->spe into account.
4930 if (obj
&& cansee(mon
->mx
, mon
->my
))
4931 pline("%s is almost hit by %s!", Monnam(mon
), doname(obj
));
4935 if (obj
&& cansee(mon
->mx
, mon
->my
))
4936 pline("%s is hit by %s!", Monnam(mon
), doname(obj
));
4940 dam
= dmgval(obj
, mon
);
4944 if ((mon
->mhp
-= dam
) <= 0) {
4948 monkilled(mon
, "", nocorpse
? -AD_RBRE
: AD_PHYS
);
4949 if (mon
->mhp
<= 0) {
4955 if (obj
&& (!strike
|| d_override
)) {
4956 place_object(obj
, mon
->mx
, mon
->my
);
4970 return (boolean
) (u
.usleep
4972 && (!strncmp(nomovemsg
, "You awake", 9)
4973 || !strncmp(nomovemsg
, "You regain con", 14)
4974 || !strncmp(nomovemsg
, "You are consci", 14))));
4977 static const char lava_killer
[] = "molten lava";
4982 register struct obj
*obj
, *obj2
;
4983 int dmg
= d(6, 6); /* only applicable for water walking */
4984 boolean usurvive
, boil_away
;
4987 if (likes_lava(youmonst
.data
))
4990 usurvive
= Fire_resistance
|| (Wwalking
&& dmg
< u
.uhp
);
4992 * A timely interrupt might manage to salvage your life
4993 * but not your gear. For scrolls and potions this
4994 * will destroy whole stacks, where fire resistant hero
4995 * survivor only loses partial stacks via destroy_item().
4997 * Flag items to be destroyed before any messages so
4998 * that player causing hangup at --More-- won't get an
4999 * emergency save file created before item destruction.
5002 for (obj
= invent
; obj
; obj
= obj
->nobj
)
5003 if ((is_organic(obj
) || obj
->oclass
== POTION_CLASS
)
5004 && !obj
->oerodeproof
5005 && objects
[obj
->otyp
].oc_oprop
!= FIRE_RES
5006 && obj
->otyp
!= SCR_FIRE
&& obj
->otyp
!= SPE_FIREBALL
5007 && !obj_resists(obj
, 0, 0)) /* for invocation items */
5010 /* Check whether we should burn away boots *first* so we know whether to
5011 * make the player sink into the lava. Assumption: water walking only
5014 if (uarmf
&& is_organic(uarmf
) && !uarmf
->oerodeproof
) {
5016 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5017 iflags
.in_lava_effects
++; /* (see above) */
5020 iflags
.in_lava_effects
--;
5023 if (!Fire_resistance
) {
5025 pline_The("lava here burns you!");
5027 losehp(dmg
, lava_killer
, KILLED_BY
); /* lava damage */
5031 You("fall into the lava!");
5033 usurvive
= Lifesaved
|| discover
;
5037 /* prevent remove_worn_item() -> Boots_off(WATER_WALKING_BOOTS) ->
5038 spoteffects() -> lava_effects() recursion which would
5039 successfully delete (via useupall) the no-longer-worn boots;
5040 once recursive call returned, we would try to delete them again
5041 here in the outer call (and access stale memory, probably panic) */
5042 iflags
.in_lava_effects
++;
5044 for (obj
= invent
; obj
; obj
= obj2
) {
5046 /* above, we set in_use for objects which are to be destroyed */
5047 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
&& !Blind
) {
5049 pline("%s glows a strange %s, but remains intact.",
5050 The(xname(obj
)), hcolor("dark red"));
5051 } else if (obj
->in_use
) {
5052 if (obj
->owornmask
) {
5054 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5055 remove_worn_item(obj
, TRUE
);
5061 iflags
.in_lava_effects
--;
5064 boil_away
= (u
.umonnum
== PM_WATER_ELEMENTAL
5065 || u
.umonnum
== PM_STEAM_VORTEX
5066 || u
.umonnum
== PM_FOG_CLOUD
);
5069 /* killer format and name are reconstructed every iteration
5070 because lifesaving resets them */
5071 killer
.format
= KILLED_BY
;
5072 Strcpy(killer
.name
, lava_killer
);
5073 You("%s...", boil_away
? "boil away" : "burn to a crisp");
5075 if (safe_teleds(TRUE
))
5076 break; /* successful life-save */
5077 /* nowhere safe to land; repeat burning loop */
5078 pline("You're still burning.");
5080 You("find yourself back on solid %s.", surface(u
.ux
, u
.uy
));
5082 } else if (!Wwalking
&& (!u
.utrap
|| u
.utraptype
!= TT_LAVA
)) {
5083 boil_away
= !Fire_resistance
;
5084 /* if not fire resistant, sink_into_lava() will quickly be fatal;
5085 hero needs to escape immediately */
5086 u
.utrap
= rn1(4, 4) + ((boil_away
? 2 : rn1(4, 12)) << 8);
5087 u
.utraptype
= TT_LAVA
;
5088 You("sink into the lava%s!", !boil_away
5089 ? ", but it only burns slightly"
5090 : " and are about to be immolated");
5092 losehp(!boil_away
? 1 : (u
.uhp
/ 2), lava_killer
,
5093 KILLED_BY
); /* lava damage */
5097 destroy_item(SCROLL_CLASS
, AD_FIRE
);
5098 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
5099 destroy_item(POTION_CLASS
, AD_FIRE
);
5103 /* called each turn when trapped in lava */
5107 static const char sink_deeper
[] = "You sink deeper into the lava.";
5109 if (!u
.utrap
|| u
.utraptype
!= TT_LAVA
) {
5110 ; /* do nothing; this shouldn't happen */
5111 } else if (!is_lava(u
.ux
, u
.uy
)) {
5112 u
.utrap
= 0; /* this shouldn't happen either */
5113 } else if (!u
.uinvulnerable
) {
5114 /* ordinarily we'd have to be fire resistant to survive long
5115 enough to become stuck in lava, but it can happen without
5116 resistance if water walking boots allow survival and then
5117 get burned up; u.utrap time will be quite short in that case */
5118 if (!Fire_resistance
)
5119 u
.uhp
= (u
.uhp
+ 2) / 3;
5121 u
.utrap
-= (1 << 8);
5122 if (u
.utrap
< (1 << 8)) {
5123 killer
.format
= KILLED_BY
;
5124 Strcpy(killer
.name
, "molten lava");
5125 You("sink below the surface and die.");
5126 burn_away_slime(); /* add insult to injury? */
5128 /* can only get here via life-saving; try to get away from lava */
5130 (void) safe_teleds(TRUE
);
5131 } else if (!u
.umoved
) {
5132 /* can't fully turn into slime while in lava, but might not
5133 have it be burned away until you've come awfully close */
5134 if (Slimed
&& rnd(10 - 1) >= (int) (Slimed
& TIMEOUT
)) {
5145 /* called when something has been done (breaking a boulder, for instance)
5146 which entails a luck penalty if performed on a sokoban level */
5152 /* TODO: issue some feedback so that player can learn that whatever
5153 he/she just did is a naughty thing to do in sokoban and should
5154 probably be avoided in future....
5155 Caveat: doing this might introduce message sequencing issues,
5156 depending upon feedback during the various actions which trigger
5157 Sokoban luck penalties. */
5161 /* called when a trap has been deleted or had its ttyp replaced */
5163 maybe_finish_sokoban()
5167 if (Sokoban
&& !in_mklev
) {
5168 /* scan all remaining traps, ignoring any created by the hero;
5169 if this level has no more pits or holes, the current sokoban
5170 puzzle has been solved */
5171 for (t
= ftrap
; t
; t
= t
->ntrap
) {
5174 if (t
->ttyp
== PIT
|| t
->ttyp
== HOLE
)
5178 /* we've passed the last trap without finding a pit or hole;
5179 clear the sokoban_rules flag so that luck penalties for
5180 things like breaking boulders or jumping will no longer
5181 be given, and restrictions on diagonal moves are lifted */
5182 Sokoban
= 0; /* clear level.flags.sokoban_rules */
5183 /* TODO: give some feedback about solving the sokoban puzzle
5184 (perhaps say "congratulations" in Japanese?) */