1 /* NetHack 3.6 trap.c $NHDT-Date: 1473665044 2016/09/12 07:24:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.274 $ */
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;
340 (void) memset((genericptr_t
)ttmp
, 0, sizeof(struct trap
));
345 /* [re-]initialize all fields except ntrap (handled below) and <tx,ty> */
347 ttmp
->launch
.x
= ttmp
->launch
.y
= -1; /* force error if used before set */
348 ttmp
->dst
.dnum
= ttmp
->dst
.dlevel
= -1;
351 ttmp
->tseen
= (typ
== HOLE
); /* hide non-holes */
356 int tavail
[12], tpick
[12], tcnt
= 0, k
;
359 for (k
= 0; k
< 12; ++k
)
360 tavail
[k
] = tpick
[k
] = 0;
361 for (t
= ftrap
; t
; t
= t
->ntrap
)
362 if (t
->ttyp
== SQKY_BOARD
&& t
!= ttmp
)
363 tavail
[t
->tnote
] = 1;
364 /* now populate tpick[] with the available indices */
365 for (k
= 0; k
< 12; ++k
)
368 /* choose an unused note; if all are in use, pick a random one */
369 ttmp
->tnote
= (short) ((tcnt
> 0) ? tpick
[rn2(tcnt
)] : rn2(12));
372 case STATUE_TRAP
: { /* create a "living" statue */
374 struct obj
*otmp
, *statue
;
375 struct permonst
*mptr
;
378 do { /* avoid ultimately hostile co-aligned unicorn */
379 mptr
= &mons
[rndmonnum()];
380 } while (--trycount
> 0 && is_unicorn(mptr
)
381 && sgn(u
.ualign
.type
) == sgn(mptr
->maligntyp
));
382 statue
= mkcorpstat(STATUE
, (struct monst
*) 0, mptr
, x
, y
,
384 mtmp
= makemon(&mons
[statue
->corpsenm
], 0, 0, MM_NOCOUNTBIRTH
);
386 break; /* should never happen */
387 while (mtmp
->minvent
) {
388 otmp
= mtmp
->minvent
;
390 obj_extract_self(otmp
);
391 (void) add_to_container(statue
, otmp
);
393 statue
->owt
= weight(statue
);
397 case ROLLING_BOULDER_TRAP
: /* boulder will roll towards trigger */
398 (void) mkroll_launch(ttmp
, x
, y
, BOULDER
, 1L);
406 if (*in_rooms(x
, y
, SHOPBASE
)
407 && (typ
== HOLE
|| typ
== TRAPDOOR
408 || IS_DOOR(lev
->typ
) || IS_WALL(lev
->typ
)))
409 add_damage(x
, y
, /* schedule repair */
410 ((IS_DOOR(lev
->typ
) || IS_WALL(lev
->typ
))
411 && !context
.mon_moving
)
414 lev
->doormask
= 0; /* subsumes altarmask, icedpool... */
415 if (IS_ROOM(lev
->typ
)) /* && !IS_AIR(lev->typ) */
418 * some cases which can happen when digging
419 * down while phazing thru solid areas
421 else if (lev
->typ
== STONE
|| lev
->typ
== SCORR
)
423 else if (IS_WALL(lev
->typ
) || lev
->typ
== SDOOR
)
424 lev
->typ
= level
.flags
.is_maze_lev
426 : level
.flags
.is_cavernous_lev
? CORR
: DOOR
;
437 it shouldn't be possible to override a sokoban pit or hole
438 with some other trap, but we'll check just to be safe */
440 maybe_finish_sokoban();
447 boolean td
; /* td == TRUE : trap door or hole */
451 const char *dont_fall
= 0;
452 int newlevel
, bottom
;
454 /* we'll fall even while levitating in Sokoban; otherwise, if we
455 won't fall and won't be told that we aren't falling, give up now */
456 if (Blind
&& Levitation
&& !Sokoban
)
459 bottom
= dunlevs_in_dungeon(&u
.uz
);
460 /* when in the upper half of the quest, don't fall past the
461 middle "quest locate" level if hero hasn't been there yet */
462 if (In_quest(&u
.uz
)) {
463 int qlocate_depth
= qlocate_level
.dlevel
;
465 /* deepest reached < qlocate implies current < qlocate */
466 if (dunlev_reached(&u
.uz
) < qlocate_depth
)
467 bottom
= qlocate_depth
; /* early cut-off */
469 newlevel
= dunlev(&u
.uz
); /* current level */
472 } while (!rn2(4) && newlevel
< bottom
);
475 struct trap
*t
= t_at(u
.ux
, u
.uy
);
479 if (t
->ttyp
== TRAPDOOR
)
480 pline("A trap door opens up under you!");
482 pline("There's a gaping hole under you!");
485 pline_The("%s opens up under you!", surface(u
.ux
, u
.uy
));
487 if (Sokoban
&& Can_fall_thru(&u
.uz
))
488 ; /* KMH -- You can't escape the Sokoban level traps */
489 else if (Levitation
|| u
.ustuck
490 || (!Can_fall_thru(&u
.uz
) && !levl
[u
.ux
][u
.uy
].candig
) || Flying
491 || is_clinger(youmonst
.data
)
492 || (Inhell
&& !u
.uevent
.invoked
&& newlevel
== bottom
)) {
493 dont_fall
= "don't fall in.";
494 } else if (youmonst
.data
->msize
>= MZ_HUGE
) {
495 dont_fall
= "don't fit through.";
496 } else if (!next_to_u()) {
497 dont_fall
= "are jerked back by your pet!";
501 /* hero didn't fall through, but any objects here might */
502 impact_drop((struct obj
*) 0, u
.ux
, u
.uy
, 0);
504 display_nhwindow(WIN_MESSAGE
, FALSE
);
505 pline_The("opening under you closes up.");
512 if (Is_stronghold(&u
.uz
)) {
515 int dist
= newlevel
- dunlev(&u
.uz
);
516 dtmp
.dnum
= u
.uz
.dnum
;
517 dtmp
.dlevel
= newlevel
;
519 You("fall down a %s%sshaft!", dist
> 3 ? "very " : "",
520 dist
> 2 ? "deep " : "");
523 Sprintf(msgbuf
, "The hole in the %s above you closes up.",
524 ceiling(u
.ux
, u
.uy
));
526 schedule_goto(&dtmp
, FALSE
, TRUE
, 0, (char *) 0,
527 !td
? msgbuf
: (char *) 0);
531 * Animate the given statue. May have been via shatter attempt, trap,
532 * or stone to flesh spell. Return a monster if successfully animated.
533 * If the monster is animated, the object is deleted. If fail_reason
534 * is non-null, then fill in the reason for failure (or success).
536 * The cause of animation is:
538 * ANIMATE_NORMAL - hero "finds" the monster
539 * ANIMATE_SHATTER - hero tries to destroy the statue
540 * ANIMATE_SPELL - stone to flesh spell hits the statue
542 * Perhaps x, y is not needed if we can use get_obj_location() to find
543 * the statue's location... ???
545 * Sequencing matters:
546 * create monster; if it fails, give up with statue intact;
547 * give "statue comes to life" message;
548 * if statue belongs to shop, have shk give "you owe" message;
549 * transfer statue contents to monster (after stolen_value());
551 * [This ordering means that if the statue ends up wearing a cloak of
552 * invisibility or a mummy wrapping, the visibility checks might be
553 * wrong, but to avoid that we'd have to clone the statue contents
554 * first in order to give them to the monster before checking their
555 * shop status--it's not worth the hassle.]
558 animate_statue(statue
, x
, y
, cause
, fail_reason
)
564 int mnum
= statue
->corpsenm
;
565 struct permonst
*mptr
= &mons
[mnum
];
566 struct monst
*mon
= 0, *shkp
;
569 boolean historic
= (Role_if(PM_ARCHEOLOGIST
)
570 && (statue
->spe
& STATUE_HISTORIC
) != 0),
571 golem_xform
= FALSE
, use_saved_traits
;
572 const char *comes_to_life
;
573 char statuename
[BUFSZ
], tmpbuf
[BUFSZ
];
574 static const char historic_statue_is_gone
[] =
575 "that the historic statue is now gone";
577 if (cant_revive(&mnum
, TRUE
, statue
)) {
578 /* mnum has changed; we won't be animating this statue as itself */
579 if (mnum
!= PM_DOPPELGANGER
)
581 use_saved_traits
= FALSE
;
582 } else if (is_golem(mptr
) && cause
== ANIMATE_SPELL
) {
583 /* statue of any golem hit by stone-to-flesh becomes flesh golem */
584 golem_xform
= (mptr
!= &mons
[PM_FLESH_GOLEM
]);
585 mnum
= PM_FLESH_GOLEM
;
586 mptr
= &mons
[PM_FLESH_GOLEM
];
587 use_saved_traits
= (has_omonst(statue
) && !golem_xform
);
589 use_saved_traits
= has_omonst(statue
);
592 if (use_saved_traits
) {
593 /* restore a petrified monster */
595 mon
= montraits(statue
, &cc
);
596 if (mon
&& mon
->mtame
&& !mon
->isminion
)
599 /* statues of unique monsters from bones or wishing end
600 up here (cant_revive() sets mnum to be doppelganger;
601 mptr reflects the original form for use by newcham()) */
602 if ((mnum
== PM_DOPPELGANGER
&& mptr
!= &mons
[PM_DOPPELGANGER
])
603 /* block quest guards from other roles */
604 || (mptr
->msound
== MS_GUARDIAN
605 && quest_info(MS_GUARDIAN
) != mnum
)) {
606 mon
= makemon(&mons
[PM_DOPPELGANGER
], x
, y
,
607 NO_MINVENT
| MM_NOCOUNTBIRTH
| MM_ADJACENTOK
);
608 /* if hero has protection from shape changers, cham field will
609 be NON_PM; otherwise, set form to match the statue */
610 if (mon
&& mon
->cham
>= LOW_PM
)
611 (void) newcham(mon
, mptr
, FALSE
, FALSE
);
613 mon
= makemon(mptr
, x
, y
, (cause
== ANIMATE_SPELL
)
614 ? (NO_MINVENT
| MM_ADJACENTOK
)
620 *fail_reason
= unique_corpstat(&mons
[statue
->corpsenm
])
623 return (struct monst
*) 0;
626 /* a non-montraits() statue might specify gender */
627 if (statue
->spe
& STATUE_MALE
)
629 else if (statue
->spe
& STATUE_FEMALE
)
631 /* if statue has been named, give same name to the monster */
632 if (has_oname(statue
) && !unique_corpstat(mon
->data
))
633 mon
= christen_monst(mon
, ONAME(statue
));
634 /* mimic statue becomes seen mimic; other hiders won't be hidden */
638 mon
->mundetected
= FALSE
;
640 if (cause
== ANIMATE_NORMAL
|| cause
== ANIMATE_SHATTER
) {
641 /* trap always releases hostile monster */
642 mon
->mtame
= 0; /* (might be petrified pet tossed onto trap) */
647 comes_to_life
= !canspotmon(mon
)
651 : (nonliving(mon
->data
) || is_vampshifter(mon
))
654 if ((x
== u
.ux
&& y
== u
.uy
) || cause
== ANIMATE_SPELL
) {
655 /* "the|your|Manlobbi's statue [of a wombat]" */
656 shkp
= shop_keeper(*in_rooms(mon
->mx
, mon
->my
, SHOPBASE
));
657 Sprintf(statuename
, "%s%s", shk_your(tmpbuf
, statue
),
658 (cause
== ANIMATE_SPELL
659 /* avoid "of a shopkeeper" if it's Manlobbi himself
660 (if carried, it can't be unpaid--hence won't be
661 described as "Manlobbi's statue"--because there
662 wasn't any living shk when statue was picked up) */
663 && (mon
!= shkp
|| carried(statue
)))
666 pline("%s %s!", upstart(statuename
), comes_to_life
);
667 } else if (Hallucination
) { /* They don't know it's a statue */
668 pline_The("%s suddenly seems more animated.", rndmonnam((char *) 0));
669 } else if (cause
== ANIMATE_SHATTER
) {
671 Sprintf(statuename
, "%s%s", shk_your(tmpbuf
, statue
),
674 Strcpy(statuename
, "a statue");
675 pline("Instead of shattering, %s suddenly %s!", statuename
,
677 } else { /* cause == ANIMATE_NORMAL */
678 You("find %s posing as a statue.",
679 canspotmon(mon
) ? a_monnam(mon
) : something
);
680 if (!canspotmon(mon
) && Blind
)
685 /* if this isn't caused by a monster using a wand of striking,
686 there might be consequences for the hero */
687 if (!context
.mon_moving
) {
688 /* if statue is owned by a shop, hero will have to pay for it;
689 stolen_value gives a message (about debt or use of credit)
690 which refers to "it" so needs to follow a message describing
691 the object ("the statue comes to life" one above) */
692 if (cause
!= ANIMATE_NORMAL
&& costly_spot(x
, y
)
693 && (carried(statue
) ? statue
->unpaid
: !statue
->no_charge
)
694 && (shkp
= shop_keeper(*in_rooms(x
, y
, SHOPBASE
))) != 0
695 /* avoid charging for Manlobbi's statue of Manlobbi
696 if stone-to-flesh is used on petrified shopkeep */
698 (void) stolen_value(statue
, x
, y
, (boolean
) shkp
->mpeaceful
,
702 You_feel("guilty %s.", historic_statue_is_gone
);
706 if (historic
&& cansee(x
, y
))
707 You_feel("regret %s.", historic_statue_is_gone
);
708 /* no alignment penalty */
711 /* transfer any statue contents to monster's inventory */
712 while ((item
= statue
->cobj
) != 0) {
713 obj_extract_self(item
);
714 (void) mpickobj(mon
, item
);
717 /* in case statue is wielded and hero zaps stone-to-flesh at self */
718 if (statue
->owornmask
)
719 remove_worn_item(statue
, TRUE
);
720 /* statue no longer exists */
723 /* avoid hiding under nothing */
724 if (x
== u
.ux
&& y
== u
.uy
&& Upolyd
&& hides_under(youmonst
.data
)
729 *fail_reason
= AS_OK
;
734 * You've either stepped onto a statue trap's location or you've triggered a
735 * statue trap by searching next to it or by trying to break it with a wand
739 activate_statue_trap(trap
, x
, y
, shatter
)
744 struct monst
*mtmp
= (struct monst
*) 0;
745 struct obj
*otmp
= sobj_at(STATUE
, x
, y
);
749 * Try to animate the first valid statue. Stop the loop when we
750 * actually create something or the failure cause is not because
751 * the mon was unique.
755 mtmp
= animate_statue(otmp
, x
, y
,
756 shatter
? ANIMATE_SHATTER
: ANIMATE_NORMAL
,
758 if (mtmp
|| fail_reason
!= AS_MON_IS_UNIQUE
)
761 otmp
= nxtobj(otmp
, STATUE
, TRUE
);
769 keep_saddle_with_steedcorpse(steed_mid
, objchn
, saddle
)
771 struct obj
*objchn
, *saddle
;
776 if (objchn
->otyp
== CORPSE
&& has_omonst(objchn
)) {
777 struct monst
*mtmp
= OMONST(objchn
);
779 if (mtmp
->m_id
== steed_mid
) {
782 if (get_obj_location(objchn
, &x
, &y
, 0)) {
783 obj_extract_self(saddle
);
784 place_object(saddle
, x
, y
);
790 if (Has_contents(objchn
)
791 && keep_saddle_with_steedcorpse(steed_mid
, objchn
->cobj
, saddle
))
793 objchn
= objchn
->nobj
;
798 /* monster or you go through and possibly destroy a web.
799 return TRUE if could go through. */
801 mu_maybe_destroy_web(mtmp
, domsg
, trap
)
806 boolean isyou
= (mtmp
== &youmonst
);
807 struct permonst
*mptr
= mtmp
->data
;
808 if (amorphous(mptr
) || is_whirly(mptr
) || flaming(mptr
)
809 || unsolid(mptr
) || mptr
== &mons
[PM_GELATINOUS_CUBE
]) {
812 if (flaming(mptr
) || acidic(mptr
)) {
815 You("%s %s spider web!",
816 (flaming(mptr
)) ? "burn" : "dissolve",
817 a_your
[trap
->madeby_u
]);
819 pline("%s %s %s spider web!", Monnam(mtmp
),
820 (flaming(mptr
)) ? "burns" : "dissolves",
821 a_your
[trap
->madeby_u
]);
829 You("flow through %s spider web.", a_your
[trap
->madeby_u
]);
831 pline("%s flows through %s spider web.", Monnam(mtmp
),
832 a_your
[trap
->madeby_u
]);
842 dotrap(trap
, trflags
)
843 register struct trap
*trap
;
846 register int ttype
= trap
->ttyp
;
847 register struct obj
*otmp
;
848 boolean already_seen
= trap
->tseen
,
849 forcetrap
= (trflags
& FORCETRAP
) != 0,
850 webmsgok
= (trflags
& NOWEBMSG
) == 0,
851 forcebungle
= (trflags
& FORCEBUNGLE
) != 0,
852 plunged
= (trflags
& TOOKPLUNGE
) != 0,
853 viasitting
= (trflags
& VIASITTING
) != 0,
854 adj_pit
= conjoined_pits(trap
, t_at(u
.ux0
, u
.uy0
), TRUE
);
856 int steed_article
= ARTICLE_THE
;
860 /* KMH -- You can't escape the Sokoban level traps */
861 if (Sokoban
&& (ttype
== PIT
|| ttype
== SPIKED_PIT
862 || ttype
== HOLE
|| ttype
== TRAPDOOR
)) {
863 /* The "air currents" message is still appropriate -- even when
864 * the hero isn't flying or levitating -- because it conveys the
865 * reason why the player cannot escape the trap with a dexterity
866 * check, clinging to the ceiling, etc.
868 pline("Air currents pull you down into %s %s!",
869 a_your
[trap
->madeby_u
],
870 defsyms
[trap_to_defsym(ttype
)].explanation
);
871 /* then proceed to normal trap effect */
872 } else if (already_seen
&& !forcetrap
) {
873 if ((Levitation
|| (Flying
&& !plunged
))
874 && (ttype
== PIT
|| ttype
== SPIKED_PIT
|| ttype
== HOLE
875 || ttype
== BEAR_TRAP
)) {
876 You("%s over %s %s.", Levitation
? "float" : "fly",
877 a_your
[trap
->madeby_u
],
878 defsyms
[trap_to_defsym(ttype
)].explanation
);
881 if (!Fumbling
&& ttype
!= MAGIC_PORTAL
&& ttype
!= VIBRATING_SQUARE
882 && ttype
!= ANTI_MAGIC
&& !forcebungle
&& !plunged
&& !adj_pit
883 && (!rn2(5) || ((ttype
== PIT
|| ttype
== SPIKED_PIT
)
884 && is_clinger(youmonst
.data
)))) {
885 You("escape %s %s.", (ttype
== ARROW_TRAP
&& !trap
->madeby_u
)
887 : a_your
[trap
->madeby_u
],
888 defsyms
[trap_to_defsym(ttype
)].explanation
);
894 u
.usteed
->mtrapseen
|= (1 << (ttype
- 1));
895 /* suppress article in various steed messages when using its
896 name (which won't occur when hallucinating) */
897 if (has_mname(u
.usteed
) && !Hallucination
)
898 steed_article
= ARTICLE_NONE
;
903 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
904 You_hear("a loud click!");
911 pline("An arrow shoots out at you!");
912 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
914 otmp
->owt
= weight(otmp
);
916 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
918 } else if (thitu(8, dmgval(otmp
, &youmonst
), otmp
, "arrow")) {
919 obfree(otmp
, (struct obj
*) 0);
921 place_object(otmp
, u
.ux
, u
.uy
);
930 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
931 You_hear("a soft click.");
938 pline("A little dart shoots out at you!");
939 otmp
= mksobj(DART
, TRUE
, FALSE
);
941 otmp
->owt
= weight(otmp
);
944 oldumort
= u
.umortality
;
945 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
947 } else if (thitu(7, dmgval(otmp
, &youmonst
), otmp
, "little dart")) {
949 poisoned("dart", A_CON
, "little dart",
950 /* if damage triggered life-saving,
951 poison is limited to attrib loss */
952 (u
.umortality
> oldumort
) ? 0 : 10, TRUE
);
953 obfree(otmp
, (struct obj
*) 0);
955 place_object(otmp
, u
.ux
, u
.uy
);
964 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
965 pline("A trap door in %s opens, but nothing falls out!",
966 the(ceiling(u
.ux
, u
.uy
)));
970 int dmg
= d(2, 6); /* should be std ROCK dmg? */
974 otmp
= mksobj_at(ROCK
, u
.ux
, u
.uy
, TRUE
, FALSE
);
976 otmp
->owt
= weight(otmp
);
978 pline("A trap door in %s opens and %s falls on your %s!",
979 the(ceiling(u
.ux
, u
.uy
)), an(xname(otmp
)), body_part(HEAD
));
982 if (is_metallic(uarmh
)) {
983 pline("Fortunately, you are wearing a hard helmet.");
985 } else if (flags
.verbose
) {
986 pline("%s does not protect you.", Yname2(uarmh
));
993 newsym(u
.ux
, u
.uy
); /* map the rock */
995 losehp(Maybe_Half_Phys(dmg
), "falling rock", KILLED_BY_AN
);
996 exercise(A_STR
, FALSE
);
1000 case SQKY_BOARD
: /* stepped on a squeaky board */
1001 if ((Levitation
|| Flying
) && !forcetrap
) {
1005 You("notice a crease in the linoleum.");
1007 You("notice a loose board below you.");
1011 pline("A board beneath you %s%s%s.",
1012 Deaf
? "vibrates" : "squeaks ",
1013 Deaf
? "" : trapnote(trap
, 0), Deaf
? "" : " loudly");
1021 if ((Levitation
|| Flying
) && !forcetrap
)
1024 if (amorphous(youmonst
.data
) || is_whirly(youmonst
.data
)
1025 || unsolid(youmonst
.data
)) {
1026 pline("%s bear trap closes harmlessly through you.",
1027 A_Your
[trap
->madeby_u
]);
1030 if (!u
.usteed
&& youmonst
.data
->msize
<= MZ_SMALL
) {
1031 pline("%s bear trap closes harmlessly over you.",
1032 A_Your
[trap
->madeby_u
]);
1035 u
.utrap
= rn1(4, 4);
1036 u
.utraptype
= TT_BEARTRAP
;
1038 pline("%s bear trap closes on %s %s!", A_Your
[trap
->madeby_u
],
1039 s_suffix(mon_nam(u
.usteed
)), mbodypart(u
.usteed
, FOOT
));
1040 if (thitm(0, u
.usteed
, (struct obj
*) 0, dmg
, FALSE
))
1041 u
.utrap
= 0; /* steed died, hero not trapped */
1043 pline("%s bear trap closes on your %s!", A_Your
[trap
->madeby_u
],
1045 set_wounded_legs(rn2(2) ? RIGHT_SIDE
: LEFT_SIDE
, rn1(10, 10));
1046 if (u
.umonnum
== PM_OWLBEAR
|| u
.umonnum
== PM_BUGBEAR
)
1047 You("howl in anger!");
1048 losehp(Maybe_Half_Phys(dmg
), "bear trap", KILLED_BY_AN
);
1050 exercise(A_DEX
, FALSE
);
1056 if (Sleep_resistance
|| breathless(youmonst
.data
)) {
1057 You("are enveloped in a cloud of gas!");
1059 pline("A cloud of gas puts you to sleep!");
1060 fall_asleep(-rnd(25), TRUE
);
1062 (void) steedintrap(trap
, (struct obj
*) 0);
1068 /* Unlike monsters, traps cannot aim their rust attacks at
1069 * you, so instead of looping through and taking either the
1070 * first rustable one or the body, we take whatever we get,
1071 * even if it is not rustable.
1075 pline("%s you on the %s!", A_gush_of_water_hits
, body_part(HEAD
));
1076 (void) water_damage(uarmh
, helm_simple_name(uarmh
), TRUE
);
1079 pline("%s your left %s!", A_gush_of_water_hits
, body_part(ARM
));
1080 if (water_damage(uarms
, "shield", TRUE
) != ER_NOTHING
)
1082 if (u
.twoweap
|| (uwep
&& bimanual(uwep
)))
1083 (void) water_damage(u
.twoweap
? uswapwep
: uwep
, 0, TRUE
);
1085 (void) water_damage(uarmg
, "gauntlets", TRUE
);
1086 /* Not "metal gauntlets" since it gets called
1087 * even if it's leather for the message
1091 pline("%s your right %s!", A_gush_of_water_hits
, body_part(ARM
));
1092 (void) water_damage(uwep
, 0, TRUE
);
1095 pline("%s you!", A_gush_of_water_hits
);
1096 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1097 if (otmp
->lamplit
&& otmp
!= uwep
1098 && (otmp
!= uswapwep
|| !u
.twoweap
))
1099 (void) snuff_lit(otmp
);
1101 (void) water_damage(uarmc
, cloak_simple_name(uarmc
), TRUE
);
1103 (void) water_damage(uarm
, "armor", TRUE
);
1105 (void) water_damage(uarmu
, "shirt", TRUE
);
1109 if (u
.umonnum
== PM_IRON_GOLEM
) {
1112 pline("%s you!", A_gush_of_water_hits
);
1113 You("are covered with rust!");
1114 losehp(Maybe_Half_Phys(dam
), "rusting away", KILLED_BY
);
1115 } else if (u
.umonnum
== PM_GREMLIN
&& rn2(3)) {
1116 pline("%s you!", A_gush_of_water_hits
);
1117 (void) split_mon(&youmonst
, (struct monst
*) 0);
1124 dofiretrap((struct obj
*) 0);
1129 /* KMH -- You can't escape the Sokoban level traps */
1130 if (!Sokoban
&& (Levitation
|| (Flying
&& !plunged
)))
1133 if (!Sokoban
&& is_clinger(youmonst
.data
) && !plunged
) {
1135 You_see("%s %spit below you.", a_your
[trap
->madeby_u
],
1136 ttype
== SPIKED_PIT
? "spiked " : "");
1138 pline("%s pit %sopens up under you!", A_Your
[trap
->madeby_u
],
1139 ttype
== SPIKED_PIT
? "full of spikes " : "");
1140 You("don't fall in!");
1145 char verbbuf
[BUFSZ
];
1148 if ((trflags
& RECURSIVETRAP
) != 0)
1149 Sprintf(verbbuf
, "and %s fall",
1150 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1151 SUPPRESS_SADDLE
, FALSE
));
1153 Sprintf(verbbuf
, "lead %s",
1154 x_monnam(u
.usteed
, steed_article
, "poor",
1155 SUPPRESS_SADDLE
, FALSE
));
1156 } else if (adj_pit
) {
1157 You("move into an adjacent pit.");
1160 !plunged
? "fall" : (Flying
? "dive" : "plunge"));
1161 You("%s into %s pit!", verbbuf
, a_your
[trap
->madeby_u
]);
1164 /* wumpus reference */
1165 if (Role_if(PM_RANGER
) && !trap
->madeby_u
&& !trap
->once
1166 && In_quest(&u
.uz
) && Is_qlocate(&u
.uz
)) {
1167 pline("Fortunately it has a bottom after all...");
1169 } else if (u
.umonnum
== PM_PIT_VIPER
|| u
.umonnum
== PM_PIT_FIEND
) {
1170 pline("How pitiful. Isn't that the pits?");
1172 if (ttype
== SPIKED_PIT
) {
1173 const char *predicament
= "on a set of sharp iron spikes";
1177 upstart(x_monnam(u
.usteed
, steed_article
, "poor",
1178 SUPPRESS_SADDLE
, FALSE
)),
1179 adj_pit
? "steps" : "lands", predicament
);
1181 You("%s %s!", adj_pit
? "step" : "land", predicament
);
1183 u
.utrap
= rn1(6, 2);
1184 u
.utraptype
= TT_PIT
;
1185 if (!steedintrap(trap
, (struct obj
*) 0)) {
1186 if (ttype
== SPIKED_PIT
) {
1187 oldumort
= u
.umortality
;
1188 losehp(Maybe_Half_Phys(rnd(adj_pit
? 6 : 10)),
1190 ? "deliberately plunged into a pit of iron spikes"
1191 : adj_pit
? "stepped into a pit of iron spikes"
1192 : "fell into a pit of iron spikes",
1195 poisoned("spikes", A_STR
,
1196 adj_pit
? "stepping on poison spikes"
1197 : "fall onto poison spikes",
1198 /* if damage triggered life-saving,
1199 poison is limited to attrib loss */
1200 (u
.umortality
> oldumort
) ? 0 : 8, FALSE
);
1202 /* plunging flyers take spike damage but not pit damage */
1204 && !(plunged
&& (Flying
|| is_clinger(youmonst
.data
))))
1205 losehp(Maybe_Half_Phys(rnd(6)),
1206 plunged
? "deliberately plunged into a pit"
1207 : "fell into a pit",
1210 if (Punished
&& !carried(uball
)) {
1216 selftouch("Falling, you");
1217 vision_full_recalc
= 1; /* vision limits change */
1218 exercise(A_STR
, FALSE
);
1219 exercise(A_DEX
, FALSE
);
1225 if (!Can_fall_thru(&u
.uz
)) {
1226 seetrap(trap
); /* normally done in fall_through */
1227 impossible("dotrap: %ss cannot exist on this level.",
1228 defsyms
[trap_to_defsym(ttype
)].explanation
);
1229 break; /* don't activate it after all */
1241 level_tele_trap(trap
, trflags
);
1244 case WEB
: /* Our luckless player has stumbled into a web. */
1246 if (mu_maybe_destroy_web(&youmonst
, webmsgok
, trap
))
1248 if (webmaker(youmonst
.data
)) {
1250 pline(trap
->madeby_u
? "You take a walk on your web."
1251 : "There is a spider web here.");
1255 char verbbuf
[BUFSZ
];
1257 if (forcetrap
|| viasitting
) {
1258 Strcpy(verbbuf
, "are caught by");
1259 } else if (u
.usteed
) {
1260 Sprintf(verbbuf
, "lead %s into",
1261 x_monnam(u
.usteed
, steed_article
, "poor",
1262 SUPPRESS_SADDLE
, FALSE
));
1264 Sprintf(verbbuf
, "%s into",
1265 Levitation
? (const char *) "float"
1266 : locomotion(youmonst
.data
, "stumble"));
1268 You("%s %s spider web!", verbbuf
, a_your
[trap
->madeby_u
]);
1270 u
.utraptype
= TT_WEB
;
1272 /* Time stuck in the web depends on your/steed strength. */
1274 register int str
= ACURR(A_STR
);
1276 /* If mounted, the steed gets trapped. Use mintrap
1277 * to do all the work. If mtrapped is set as a result,
1278 * unset it and set utrap instead. In the case of a
1279 * strongmonst and mintrap said it's trapped, use a
1280 * short but non-zero trap time. Otherwise, monsters
1281 * have no specific strength, so use player strength.
1282 * This gets skipped for webmsgok, which implies that
1283 * the steed isn't a factor.
1285 if (u
.usteed
&& webmsgok
) {
1286 /* mtmp location might not be up to date */
1287 u
.usteed
->mx
= u
.ux
;
1288 u
.usteed
->my
= u
.uy
;
1290 /* mintrap currently does not return 2(died) for webs */
1291 if (mintrap(u
.usteed
)) {
1292 u
.usteed
->mtrapped
= 0;
1293 if (strongmonst(u
.usteed
->data
))
1299 webmsgok
= FALSE
; /* mintrap printed the messages */
1302 u
.utrap
= rn1(6, 6);
1304 u
.utrap
= rn1(6, 4);
1306 u
.utrap
= rn1(4, 4);
1308 u
.utrap
= rn1(4, 2);
1310 u
.utrap
= rn1(2, 2);
1318 You("tear through %s web!", a_your
[trap
->madeby_u
]);
1320 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1326 (void) activate_statue_trap(trap
, u
.ux
, u
.uy
, FALSE
);
1329 case MAGIC_TRAP
: /* A magic trap. */
1333 newsym(u
.ux
, u
.uy
); /* update position */
1334 You("are caught in a magical explosion!");
1335 losehp(rnd(10), "magical explosion", KILLED_BY_AN
);
1336 Your("body absorbs some of the magical energy!");
1337 u
.uen
= (u
.uenmax
+= 2);
1342 (void) steedintrap(trap
, (struct obj
*) 0);
1347 /* hero without magic resistance loses spell energy,
1348 hero with magic resistance takes damage instead;
1349 possibly non-intuitive but useful for play balance */
1351 drain_en(rnd(u
.ulevel
) + 1);
1353 int dmgval2
= rnd(4), hp
= Upolyd
? u
.mh
: u
.uhp
;
1355 /* Half_XXX_damage has opposite its usual effect (approx)
1356 but isn't cumulative if hero has more than one */
1357 if (Half_physical_damage
|| Half_spell_damage
)
1359 /* give Magicbane wielder dose of own medicine */
1360 if (uwep
&& uwep
->oartifact
== ART_MAGICBANE
)
1362 /* having an artifact--other than own quest one--which
1363 confers magic resistance simply by being carried
1364 also increases the effect */
1365 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1366 if (otmp
->oartifact
&& !is_quest_artifact(otmp
)
1367 && defends_when_carried(AD_MAGM
, otmp
))
1372 dmgval2
= (dmgval2
+ 3) / 4;
1374 You_feel((dmgval2
>= hp
) ? "unbearably torpid!"
1375 : (dmgval2
>= hp
/ 4) ? "very lethargic."
1377 /* opposite of magical explosion */
1378 losehp(dmgval2
, "anti-magic implosion", KILLED_BY_AN
);
1383 char verbbuf
[BUFSZ
];
1387 Strcpy(verbbuf
, "trigger"); /* follows "You sit down." */
1389 Sprintf(verbbuf
, "lead %s onto",
1390 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1391 SUPPRESS_SADDLE
, FALSE
));
1393 Sprintf(verbbuf
, "%s onto",
1394 Levitation
? (const char *) "float"
1395 : locomotion(youmonst
.data
, "step"));
1396 You("%s a polymorph trap!", verbbuf
);
1397 if (Antimagic
|| Unchanging
) {
1398 shieldeff(u
.ux
, u
.uy
);
1399 You_feel("momentarily different.");
1400 /* Trap did nothing; don't remove it --KAA */
1402 (void) steedintrap(trap
, (struct obj
*) 0);
1403 deltrap(trap
); /* delete trap before polymorph */
1404 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1405 You_feel("a change coming over you.");
1411 unsigned steed_mid
= 0;
1412 struct obj
*saddle
= 0;
1414 if ((Levitation
|| Flying
) && !forcetrap
) {
1415 if (!already_seen
&& rn2(3))
1418 pline("%s %s in a pile of soil below you.",
1419 already_seen
? "There is" : "You discover",
1420 trap
->madeby_u
? "the trigger of your mine" : "a trigger");
1421 if (already_seen
&& rn2(3))
1423 pline("KAABLAMM!!! %s %s%s off!",
1424 forcebungle
? "Your inept attempt sets"
1425 : "The air currents set",
1426 already_seen
? a_your
[trap
->madeby_u
] : "",
1427 already_seen
? " land mine" : "it");
1429 /* prevent landmine from killing steed, throwing you to
1430 * the ground, and you being affected again by the same
1431 * mine because it hasn't been deleted yet
1433 static boolean recursive_mine
= FALSE
;
1438 pline("KAABLAMM!!! You triggered %s land mine!",
1439 a_your
[trap
->madeby_u
]);
1441 steed_mid
= u
.usteed
->m_id
;
1442 recursive_mine
= TRUE
;
1443 (void) steedintrap(trap
, (struct obj
*) 0);
1444 recursive_mine
= FALSE
;
1445 saddle
= sobj_at(SADDLE
, u
.ux
, u
.uy
);
1446 set_wounded_legs(LEFT_SIDE
, rn1(35, 41));
1447 set_wounded_legs(RIGHT_SIDE
, rn1(35, 41));
1448 exercise(A_DEX
, FALSE
);
1450 blow_up_landmine(trap
);
1451 if (steed_mid
&& saddle
&& !u
.usteed
)
1452 (void) keep_saddle_with_steedcorpse(steed_mid
, fobj
, saddle
);
1453 newsym(u
.ux
, u
.uy
); /* update trap symbol */
1454 losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN
);
1455 /* fall recursively into the pit... */
1456 if ((trap
= t_at(u
.ux
, u
.uy
)) != 0)
1457 dotrap(trap
, RECURSIVETRAP
);
1458 fill_pit(u
.ux
, u
.uy
);
1462 case ROLLING_BOULDER_TRAP
: {
1463 int style
= ROLL
| (trap
->tseen
? LAUNCH_KNOWN
: 0);
1466 pline("Click! You trigger a rolling boulder trap!");
1467 if (!launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
1468 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
1470 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1471 pline("Fortunately for you, no boulder was released.");
1478 domagicportal(trap
);
1481 case VIBRATING_SQUARE
:
1483 /* messages handled elsewhere; the trap symbol is merely to mark the
1484 * square for future reference */
1489 impossible("You hit a trap of type %u", trap
->ttyp
);
1494 trapnote(trap
, noprefix
)
1498 static char tnbuf
[12];
1500 *tnnames
[12] = { "C note", "D flat", "D note", "E flat",
1501 "E note", "F note", "F sharp", "G note",
1502 "G sharp", "A note", "B flat", "B note" };
1505 tn
= tnnames
[trap
->tnote
];
1507 Sprintf(tnbuf
, "%s ",
1508 (*tn
== 'A' || *tn
== 'E' || *tn
== 'F') ? "an" : "a");
1509 Sprintf(eos(tnbuf
), "%s", tn
);
1514 steedintrap(trap
, otmp
)
1518 struct monst
*steed
= u
.usteed
;
1520 boolean trapkilled
, steedhit
;
1522 if (!steed
|| !trap
)
1527 trapkilled
= steedhit
= FALSE
;
1532 impossible("steed hit by non-existant arrow?");
1535 trapkilled
= thitm(8, steed
, otmp
, 0, FALSE
);
1540 impossible("steed hit by non-existant dart?");
1543 trapkilled
= thitm(7, steed
, otmp
, 0, FALSE
);
1547 if (!resists_sleep(steed
) && !breathless(steed
->data
)
1548 && !steed
->msleeping
&& steed
->mcanmove
) {
1549 if (sleep_monst(steed
, rnd(25), -1))
1550 /* no in_sight check here; you can feel it even if blind */
1551 pline("%s suddenly falls asleep!", Monnam(steed
));
1556 trapkilled
= thitm(0, steed
, (struct obj
*) 0, rnd(16), FALSE
);
1561 trapkilled
= (steed
->mhp
<= 0
1562 || thitm(0, steed
, (struct obj
*) 0,
1563 rnd((tt
== PIT
) ? 6 : 10), FALSE
));
1567 if (!resists_magm(steed
) && !resist(steed
, WAND_CLASS
, 0, NOTELL
)) {
1568 (void) newcham(steed
, (struct permonst
*) 0, FALSE
, FALSE
);
1569 if (!can_saddle(steed
) || !can_ride(steed
))
1570 dismount_steed(DISMOUNT_POLY
);
1572 You("have to adjust yourself in the saddle on %s.",
1573 x_monnam(steed
, ARTICLE_A
, (char *) 0, SUPPRESS_SADDLE
,
1583 dismount_steed(DISMOUNT_POLY
);
1586 return steedhit
? 1 : 0;
1589 /* some actions common to both player and monsters for triggered landmine */
1591 blow_up_landmine(trap
)
1594 int x
= trap
->tx
, y
= trap
->ty
, dbx
, dby
;
1595 struct rm
*lev
= &levl
[x
][y
];
1597 (void) scatter(x
, y
, 4,
1598 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
| VIS_EFFECTS
,
1601 wake_nearto(x
, y
, 400);
1602 if (IS_DOOR(lev
->typ
))
1603 lev
->doormask
= D_BROKEN
;
1604 /* destroy drawbridge if present */
1605 if (lev
->typ
== DRAWBRIDGE_DOWN
|| is_drawbridge_wall(x
, y
) >= 0) {
1607 /* if under the portcullis, the bridge is adjacent */
1608 if (find_drawbridge(&dbx
, &dby
))
1609 destroy_drawbridge(dbx
, dby
);
1610 trap
= t_at(x
, y
); /* expected to be null after destruction */
1612 /* convert landmine into pit */
1614 if (Is_waterlevel(&u
.uz
) || Is_airlevel(&u
.uz
)) {
1618 trap
->ttyp
= PIT
; /* explosion creates a pit */
1619 trap
->madeby_u
= FALSE
; /* resulting pit isn't yours */
1620 seetrap(trap
); /* and it isn't concealed */
1626 * The following are used to track launched objects to
1627 * prevent them from vanishing if you are killed. They
1628 * will reappear at the launchplace in bones files.
1636 launch_drop_spot(obj
, x
, y
)
1641 launchplace
.obj
= (struct obj
*) 0;
1645 launchplace
.obj
= obj
;
1652 launch_in_progress()
1654 if (launchplace
.obj
)
1660 force_launch_placement()
1662 if (launchplace
.obj
) {
1663 launchplace
.obj
->otrapped
= 0;
1664 place_object(launchplace
.obj
, launchplace
.x
, launchplace
.y
);
1669 * Move obj from (x1,y1) to (x2,y2)
1671 * Return 0 if no object was launched.
1672 * 1 if an object was launched and placed somewhere.
1673 * 2 if an object was launched, but used up.
1676 launch_obj(otyp
, x1
, y1
, x2
, y2
, style
)
1678 register int x1
, y1
, x2
, y2
;
1681 register struct monst
*mtmp
;
1682 register struct obj
*otmp
, *otmp2
;
1683 register int dx
, dy
;
1684 struct obj
*singleobj
;
1685 boolean used_up
= FALSE
;
1686 boolean otherside
= FALSE
;
1691 otmp
= sobj_at(otyp
, x1
, y1
);
1692 /* Try the other side too, for rolling boulder traps */
1693 if (!otmp
&& otyp
== BOULDER
) {
1695 otmp
= sobj_at(otyp
, x2
, y2
);
1699 if (otherside
) { /* swap 'em */
1710 if (otmp
->quan
== 1L) {
1711 obj_extract_self(otmp
);
1713 otmp
= (struct obj
*) 0;
1715 singleobj
= splitobj(otmp
, 1L);
1716 obj_extract_self(singleobj
);
1719 /* in case you're using a pick-axe to chop the boulder that's being
1720 launched (perhaps a monster triggered it), destroy context so that
1721 next dig attempt never thinks you're resuming previous effort */
1722 if ((otyp
== BOULDER
|| otyp
== STATUE
)
1723 && singleobj
->ox
== context
.digging
.pos
.x
1724 && singleobj
->oy
== context
.digging
.pos
.y
)
1725 (void) memset((genericptr_t
) &context
.digging
, 0,
1726 sizeof(struct dig_info
));
1728 dist
= distmin(x1
, y1
, x2
, y2
);
1734 case ROLL
| LAUNCH_UNSEEN
:
1735 if (otyp
== BOULDER
) {
1736 You_hear(Hallucination
? "someone bowling."
1737 : "rumbling in the distance.");
1739 style
&= ~LAUNCH_UNSEEN
;
1741 case ROLL
| LAUNCH_KNOWN
:
1742 /* use otrapped as a flag to ohitmon */
1743 singleobj
->otrapped
= 1;
1744 style
&= ~LAUNCH_KNOWN
;
1753 if (!cansee(bhitpos
.x
, bhitpos
.y
))
1755 tmp_at(DISP_FLASH
, obj_to_glyph(singleobj
));
1756 tmp_at(bhitpos
.x
, bhitpos
.y
);
1758 /* Mark a spot to place object in bones files to prevent
1759 * loss of object. Use the starting spot to ensure that
1760 * a rolling boulder will still launch, which it wouldn't
1761 * do if left midstream. Unfortunately we can't use the
1762 * target resting spot, because there are some things/situations
1763 * that would prevent it from ever getting there (bars), and we
1764 * can't tell that yet.
1766 launch_drop_spot(singleobj
, bhitpos
.x
, bhitpos
.y
);
1768 /* Set the object in motion */
1769 while (dist
-- > 0 && !used_up
) {
1771 tmp_at(bhitpos
.x
, bhitpos
.y
);
1774 /* dstage@u.washington.edu -- Delay only if hero sees it */
1775 if (cansee(bhitpos
.x
, bhitpos
.y
))
1781 t
= t_at(bhitpos
.x
, bhitpos
.y
);
1783 if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
1784 if (otyp
== BOULDER
&& throws_rocks(mtmp
->data
)) {
1786 if (cansee(bhitpos
.x
, bhitpos
.y
))
1787 pline("%s snatches the boulder.", Monnam(mtmp
));
1788 singleobj
->otrapped
= 0;
1789 (void) mpickobj(mtmp
, singleobj
);
1791 launch_drop_spot((struct obj
*) 0, 0, 0);
1795 if (ohitmon(mtmp
, singleobj
, (style
== ROLL
) ? -1 : dist
,
1798 launch_drop_spot((struct obj
*) 0, 0, 0);
1801 } else if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) {
1804 if (thitu(9 + singleobj
->spe
, dmgval(singleobj
, &youmonst
),
1805 singleobj
, (char *) 0))
1808 if (style
== ROLL
) {
1809 if (down_gate(bhitpos
.x
, bhitpos
.y
) != -1) {
1810 if (ship_object(singleobj
, bhitpos
.x
, bhitpos
.y
, FALSE
)) {
1812 launch_drop_spot((struct obj
*) 0, 0, 0);
1816 if (t
&& otyp
== BOULDER
) {
1822 cansee(bhitpos
.x
, bhitpos
.y
)
1823 ? " The rolling boulder triggers a land mine."
1826 del_engr_at(bhitpos
.x
, bhitpos
.y
);
1827 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1828 singleobj
->otrapped
= 0;
1829 fracture_rock(singleobj
);
1830 (void) scatter(bhitpos
.x
, bhitpos
.y
, 4,
1831 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
1834 if (cansee(bhitpos
.x
, bhitpos
.y
))
1835 newsym(bhitpos
.x
, bhitpos
.y
);
1837 launch_drop_spot((struct obj
*) 0, 0, 0);
1842 if (cansee(bhitpos
.x
, bhitpos
.y
))
1843 pline("Suddenly the rolling boulder disappears!");
1845 You_hear("a rumbling stop abruptly.");
1846 singleobj
->otrapped
= 0;
1847 if (t
->ttyp
== TELEP_TRAP
)
1848 (void) rloco(singleobj
);
1850 int newlev
= random_teleport_level();
1853 if (newlev
== depth(&u
.uz
) || In_endgame(&u
.uz
))
1855 add_to_migration(singleobj
);
1856 get_level(&dest
, newlev
);
1857 singleobj
->ox
= dest
.dnum
;
1858 singleobj
->oy
= dest
.dlevel
;
1859 singleobj
->owornmask
= (long) MIGR_RANDOM
;
1863 launch_drop_spot((struct obj
*) 0, 0, 0);
1869 /* the boulder won't be used up if there is a
1870 monster in the trap; stop rolling anyway */
1871 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* stops here */
1872 if (flooreffects(singleobj
, x2
, y2
, "fall")) {
1874 launch_drop_spot((struct obj
*) 0, 0, 0);
1876 dist
= -1; /* stop rolling immediately */
1879 if (used_up
|| dist
== -1)
1882 if (flooreffects(singleobj
, bhitpos
.x
, bhitpos
.y
, "fall")) {
1884 launch_drop_spot((struct obj
*) 0, 0, 0);
1888 && (otmp2
= sobj_at(BOULDER
, bhitpos
.x
, bhitpos
.y
)) != 0) {
1889 const char *bmsg
= " as one boulder sets another in motion";
1891 if (!isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
) || !dist
1892 || IS_ROCK(levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
))
1893 bmsg
= " as one boulder hits another";
1895 You_hear("a loud crash%s!",
1896 cansee(bhitpos
.x
, bhitpos
.y
) ? bmsg
: "");
1897 obj_extract_self(otmp2
);
1898 /* pass off the otrapped flag to the next boulder */
1899 otmp2
->otrapped
= singleobj
->otrapped
;
1900 singleobj
->otrapped
= 0;
1901 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1903 otmp2
= (struct obj
*) 0;
1904 wake_nearto(bhitpos
.x
, bhitpos
.y
, 10 * 10);
1907 if (otyp
== BOULDER
&& closed_door(bhitpos
.x
, bhitpos
.y
)) {
1908 if (cansee(bhitpos
.x
, bhitpos
.y
))
1909 pline_The("boulder crashes through a door.");
1910 levl
[bhitpos
.x
][bhitpos
.y
].doormask
= D_BROKEN
;
1912 unblock_point(bhitpos
.x
, bhitpos
.y
);
1915 /* if about to hit iron bars, do so now */
1916 if (dist
> 0 && isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
)
1917 && levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
== IRONBARS
) {
1918 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* object stops here */
1919 if (hits_bars(&singleobj
,
1920 x2
, y2
, x2
+dx
, y2
+dy
,
1924 launch_drop_spot((struct obj
*) 0, 0, 0);
1930 tmp_at(DISP_END
, 0);
1931 launch_drop_spot((struct obj
*) 0, 0, 0);
1933 singleobj
->otrapped
= 0;
1934 place_object(singleobj
, x2
, y2
);
1947 newsym(trap
->tx
, trap
->ty
);
1951 /* like seetrap() but overrides vision */
1958 /* in case it's beneath something, redisplay the something */
1959 newsym(trap
->tx
, trap
->ty
);
1963 mkroll_launch(ttmp
, x
, y
, otyp
, ocount
)
1976 boolean success
= FALSE
;
1979 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
)
1981 distance
= rn1(5, 4); /* 4..8 away */
1982 tmp
= rn2(8); /* randomly pick a direction to try first */
1983 while (distance
>= mindist
) {
1988 /* Prevent boulder from being placed on water */
1989 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
1990 && is_pool_or_lava(x
+ distance
* dx
, y
+ distance
* dy
))
1993 success
= isclearpath(&cc
, distance
, dx
, dy
);
1994 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
1995 boolean success_otherway
;
1999 success_otherway
= isclearpath(&bcc
, distance
, -(dx
), -(dy
));
2000 if (!success_otherway
)
2007 if ((++trycount
% 8) == 0)
2011 /* create the trap without any ammo, launch pt at trap location */
2015 otmp
= mksobj(otyp
, TRUE
, FALSE
);
2016 otmp
->quan
= ocount
;
2017 otmp
->owt
= weight(otmp
);
2018 place_object(otmp
, cc
.x
, cc
.y
);
2021 ttmp
->launch
.x
= cc
.x
;
2022 ttmp
->launch
.y
= cc
.y
;
2023 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
2024 ttmp
->launch2
.x
= bcc
.x
;
2025 ttmp
->launch2
.y
= bcc
.y
;
2027 ttmp
->launch_otyp
= otyp
;
2028 newsym(ttmp
->launch
.x
, ttmp
->launch
.y
);
2033 isclearpath(cc
, distance
, dx
, dy
)
2043 while (distance
-- > 0) {
2046 typ
= levl
[x
][y
].typ
;
2047 if (!isok(x
, y
) || !ZAP_POS(typ
) || closed_door(x
, y
))
2057 register struct monst
*mtmp
;
2059 register struct trap
*trap
= t_at(mtmp
->mx
, mtmp
->my
);
2060 boolean trapkilled
= FALSE
;
2061 struct permonst
*mptr
= mtmp
->data
;
2065 mtmp
->mtrapped
= 0; /* perhaps teleported? */
2066 } else if (mtmp
->mtrapped
) { /* is currently in the trap */
2067 if (!trap
->tseen
&& cansee(mtmp
->mx
, mtmp
->my
) && canseemon(mtmp
)
2068 && (trap
->ttyp
== SPIKED_PIT
|| trap
->ttyp
== BEAR_TRAP
2069 || trap
->ttyp
== HOLE
|| trap
->ttyp
== PIT
2070 || trap
->ttyp
== WEB
)) {
2071 /* If you come upon an obviously trapped monster, then
2072 * you must be able to see the trap it's in too.
2078 if (sobj_at(BOULDER
, mtmp
->mx
, mtmp
->my
)
2079 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
2082 if (canseemon(mtmp
))
2083 pline("%s pulls free...", Monnam(mtmp
));
2084 fill_pit(mtmp
->mx
, mtmp
->my
);
2089 } else if (metallivorous(mptr
)) {
2090 if (trap
->ttyp
== BEAR_TRAP
) {
2091 if (canseemon(mtmp
))
2092 pline("%s eats a bear trap!", Monnam(mtmp
));
2096 } else if (trap
->ttyp
== SPIKED_PIT
) {
2097 if (canseemon(mtmp
))
2098 pline("%s munches on some spikes!", Monnam(mtmp
));
2104 register int tt
= trap
->ttyp
;
2105 boolean in_sight
, tear_web
, see_it
,
2106 inescapable
= force_mintrap
|| ((tt
== HOLE
|| tt
== PIT
)
2107 && Sokoban
&& !trap
->madeby_u
);
2108 const char *fallverb
;
2110 /* true when called from dotrap, inescapable is not an option */
2111 if (mtmp
== u
.usteed
)
2113 if (!inescapable
&& ((mtmp
->mtrapseen
& (1 << (tt
- 1))) != 0
2114 || (tt
== HOLE
&& !mindless(mptr
)))) {
2115 /* it has been in such a trap - perhaps it escapes */
2119 mtmp
->mtrapseen
|= (1 << (tt
- 1));
2121 /* Monster is aggravated by being trapped by you.
2122 Recognizing who made the trap isn't completely
2123 unreasonable; everybody has their own style. */
2124 if (trap
->madeby_u
&& rnl(5))
2125 setmangry(mtmp
, TRUE
);
2127 in_sight
= canseemon(mtmp
);
2128 see_it
= cansee(mtmp
->mx
, mtmp
->my
);
2129 /* assume hero can tell what's going on for the steed */
2130 if (mtmp
== u
.usteed
)
2134 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2135 if (in_sight
&& see_it
)
2136 pline("%s triggers a trap but nothing happens.",
2139 newsym(mtmp
->mx
, mtmp
->my
);
2143 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
2145 otmp
->owt
= weight(otmp
);
2146 otmp
->opoisoned
= 0;
2149 if (thitm(8, mtmp
, otmp
, 0, FALSE
))
2153 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2154 if (in_sight
&& see_it
)
2155 pline("%s triggers a trap but nothing happens.",
2158 newsym(mtmp
->mx
, mtmp
->my
);
2162 otmp
= mksobj(DART
, TRUE
, FALSE
);
2164 otmp
->owt
= weight(otmp
);
2166 otmp
->opoisoned
= 1;
2169 if (thitm(7, mtmp
, otmp
, 0, FALSE
))
2173 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2174 if (in_sight
&& see_it
)
2176 "A trap door above %s opens, but nothing falls out!",
2179 newsym(mtmp
->mx
, mtmp
->my
);
2183 otmp
= mksobj(ROCK
, TRUE
, FALSE
);
2185 otmp
->owt
= weight(otmp
);
2188 if (thitm(0, mtmp
, otmp
, d(2, 6), FALSE
))
2194 /* stepped on a squeaky board */
2197 pline("A board beneath %s squeaks %s loudly.",
2198 mon_nam(mtmp
), trapnote(trap
, 0));
2201 pline("%s stops momentarily and appears to cringe.",
2205 /* same near/far threshold as mzapmsg() */
2206 int range
= couldsee(mtmp
->mx
, mtmp
->my
) /* 9 or 5 */
2207 ? (BOLT_LIM
+ 1) : (BOLT_LIM
- 3);
2209 You_hear("a %s squeak %s.", trapnote(trap
, 1),
2210 (distu(mtmp
->mx
, mtmp
->my
) <= range
* range
)
2211 ? "nearby" : "in the distance");
2213 /* wake up nearby monsters */
2214 wake_nearto(mtmp
->mx
, mtmp
->my
, 40);
2217 if (mptr
->msize
> MZ_SMALL
&& !amorphous(mptr
) && !is_flyer(mptr
)
2218 && !is_whirly(mptr
) && !unsolid(mptr
)) {
2221 pline("%s is caught in %s bear trap!", Monnam(mtmp
),
2222 a_your
[trap
->madeby_u
]);
2225 if (mptr
== &mons
[PM_OWLBEAR
]
2226 || mptr
== &mons
[PM_BUGBEAR
])
2227 You_hear("the roaring of an angry bear!");
2229 } else if (force_mintrap
) {
2231 pline("%s evades %s bear trap!", Monnam(mtmp
),
2232 a_your
[trap
->madeby_u
]);
2237 trapkilled
= thitm(0, mtmp
, (struct obj
*) 0, d(2, 4), FALSE
);
2240 if (!resists_sleep(mtmp
) && !breathless(mptr
) && !mtmp
->msleeping
2241 && mtmp
->mcanmove
) {
2242 if (sleep_monst(mtmp
, rnd(25), -1) && in_sight
) {
2243 pline("%s suddenly falls asleep!", Monnam(mtmp
));
2256 pline("%s %s on the %s!", A_gush_of_water_hits
,
2257 mon_nam(mtmp
), mbodypart(mtmp
, HEAD
));
2258 target
= which_armor(mtmp
, W_ARMH
);
2259 (void) water_damage(target
, helm_simple_name(target
), TRUE
);
2263 pline("%s %s's left %s!", A_gush_of_water_hits
,
2264 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2265 target
= which_armor(mtmp
, W_ARMS
);
2266 if (water_damage(target
, "shield", TRUE
) != ER_NOTHING
)
2268 target
= MON_WEP(mtmp
);
2269 if (target
&& bimanual(target
))
2270 (void) water_damage(target
, 0, TRUE
);
2272 target
= which_armor(mtmp
, W_ARMG
);
2273 (void) water_damage(target
, "gauntlets", TRUE
);
2277 pline("%s %s's right %s!", A_gush_of_water_hits
,
2278 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2279 (void) water_damage(MON_WEP(mtmp
), 0, TRUE
);
2283 pline("%s %s!", A_gush_of_water_hits
, mon_nam(mtmp
));
2284 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2286 && (otmp
->owornmask
& (W_WEP
| W_SWAPWEP
)) == 0)
2287 (void) snuff_lit(otmp
);
2288 if ((target
= which_armor(mtmp
, W_ARMC
)) != 0)
2289 (void) water_damage(target
, cloak_simple_name(target
),
2291 else if ((target
= which_armor(mtmp
, W_ARM
)) != 0)
2292 (void) water_damage(target
, "armor", TRUE
);
2293 else if ((target
= which_armor(mtmp
, W_ARMU
)) != 0)
2294 (void) water_damage(target
, "shirt", TRUE
);
2297 if (mptr
== &mons
[PM_IRON_GOLEM
]) {
2299 pline("%s falls to pieces!", Monnam(mtmp
));
2300 else if (mtmp
->mtame
)
2301 pline("May %s rust in peace.", mon_nam(mtmp
));
2305 } else if (mptr
== &mons
[PM_GREMLIN
] && rn2(3)) {
2306 (void) split_mon(mtmp
, (struct monst
*) 0);
2313 pline("A %s erupts from the %s under %s!", tower_of_flame
,
2314 surface(mtmp
->mx
, mtmp
->my
), mon_nam(mtmp
));
2315 else if (see_it
) /* evidently `mtmp' is invisible */
2316 You_see("a %s erupt from the %s!", tower_of_flame
,
2317 surface(mtmp
->mx
, mtmp
->my
));
2319 if (resists_fire(mtmp
)) {
2321 shieldeff(mtmp
->mx
, mtmp
->my
);
2322 pline("%s is uninjured.", Monnam(mtmp
));
2325 int num
= d(2, 4), alt
;
2326 boolean immolate
= FALSE
;
2328 /* paper burns very fast, assume straw is tightly
2329 * packed and burns a bit slower */
2330 switch (monsndx(mptr
)) {
2331 case PM_PAPER_GOLEM
:
2335 case PM_STRAW_GOLEM
:
2336 alt
= mtmp
->mhpmax
/ 2;
2339 alt
= mtmp
->mhpmax
/ 4;
2341 case PM_LEATHER_GOLEM
:
2342 alt
= mtmp
->mhpmax
/ 8;
2351 if (thitm(0, mtmp
, (struct obj
*) 0, num
, immolate
))
2354 /* we know mhp is at least `num' below mhpmax,
2355 so no (mhp > mhpmax) check is needed here */
2356 mtmp
->mhpmax
-= rn2(num
+ 1);
2358 if (burnarmor(mtmp
) || rn2(3)) {
2359 (void) destroy_mitem(mtmp
, SCROLL_CLASS
, AD_FIRE
);
2360 (void) destroy_mitem(mtmp
, SPBOOK_CLASS
, AD_FIRE
);
2361 (void) destroy_mitem(mtmp
, POTION_CLASS
, AD_FIRE
);
2363 if (burn_floor_objects(mtmp
->mx
, mtmp
->my
, see_it
, FALSE
)
2364 && !see_it
&& distu(mtmp
->mx
, mtmp
->my
) <= 3 * 3)
2365 You("smell smoke.");
2366 if (is_ice(mtmp
->mx
, mtmp
->my
))
2367 melt_ice(mtmp
->mx
, mtmp
->my
, (char *) 0);
2374 if (is_flyer(mptr
) || is_floater(mptr
)
2375 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2376 || is_clinger(mptr
)) {
2377 if (force_mintrap
&& !Sokoban
) {
2378 /* openfallingtrap; not inescapable here */
2381 pline("%s doesn't fall into the pit.", Monnam(mtmp
));
2383 break; /* inescapable = FALSE; */
2386 break; /* avoids trap */
2387 fallverb
= "is dragged"; /* sokoban pit */
2389 if (!passes_walls(mptr
))
2392 pline("%s %s into %s pit!", Monnam(mtmp
), fallverb
,
2393 a_your
[trap
->madeby_u
]);
2394 if (mptr
== &mons
[PM_PIT_VIPER
]
2395 || mptr
== &mons
[PM_PIT_FIEND
])
2396 pline("How pitiful. Isn't that the pits?");
2399 mselftouch(mtmp
, "Falling, ", FALSE
);
2400 if (mtmp
->mhp
<= 0 || thitm(0, mtmp
, (struct obj
*) 0,
2401 rnd((tt
== PIT
) ? 6 : 10), FALSE
))
2406 if (!Can_fall_thru(&u
.uz
)) {
2407 impossible("mintrap: %ss cannot exist on this level.",
2408 defsyms
[trap_to_defsym(tt
)].explanation
);
2409 break; /* don't activate it after all */
2411 if (is_flyer(mptr
) || is_floater(mptr
) || mptr
== &mons
[PM_WUMPUS
]
2412 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2413 || mptr
->msize
>= MZ_HUGE
) {
2414 if (force_mintrap
&& !Sokoban
) {
2415 /* openfallingtrap; not inescapable here */
2420 "A trap door opens, but %s doesn't fall through.",
2422 else /* (tt == HOLE) */
2423 pline("%s doesn't fall through the hole.",
2426 break; /* inescapable = FALSE; */
2428 if (inescapable
) { /* sokoban hole */
2430 pline("%s seems to be yanked down!", Monnam(mtmp
));
2431 /* suppress message in mlevel_tele_trap() */
2440 case MAGIC_PORTAL
: {
2443 mlev_res
= mlevel_tele_trap(mtmp
, trap
, inescapable
, in_sight
);
2449 mtele_trap(mtmp
, trap
, in_sight
);
2452 /* Monster in a web. */
2455 if (mu_maybe_destroy_web(mtmp
, in_sight
, trap
))
2458 switch (monsndx(mptr
)) {
2459 case PM_OWLBEAR
: /* Eric Backus */
2462 You_hear("the roaring of a confused bear!");
2468 if (mptr
->mlet
== S_GIANT
2469 /* exclude baby dragons and relatively short worms */
2470 || (mptr
->mlet
== S_DRAGON
&& extra_nasty(mptr
))
2471 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)) {
2473 } else if (in_sight
) {
2474 pline("%s is caught in %s spider web.", Monnam(mtmp
),
2475 a_your
[trap
->madeby_u
]);
2478 mtmp
->mtrapped
= tear_web
? 0 : 1;
2480 /* this list is fairly arbitrary; it deliberately
2481 excludes wumpus & giant/ettin zombies/mummies */
2482 case PM_TITANOTHERE
:
2483 case PM_BALUCHITHERIUM
:
2484 case PM_PURPLE_WORM
:
2493 case PM_LORD_SURTUR
:
2499 pline("%s tears through %s spider web!", Monnam(mtmp
),
2500 a_your
[trap
->madeby_u
]);
2502 newsym(mtmp
->mx
, mtmp
->my
);
2503 } else if (force_mintrap
&& !mtmp
->mtrapped
) {
2505 pline("%s avoids %s spider web!", Monnam(mtmp
),
2506 a_your
[trap
->madeby_u
]);
2514 /* A magic trap. Monsters usually immune. */
2519 /* similar to hero's case, more or less */
2520 if (!resists_magm(mtmp
)) { /* lose spell energy */
2521 if (!mtmp
->mcan
&& (attacktype(mptr
, AT_MAGC
)
2522 || attacktype(mptr
, AT_BREA
))) {
2523 mtmp
->mspec_used
+= d(2, 2);
2526 pline("%s seems lethargic.", Monnam(mtmp
));
2529 } else { /* take some damage */
2530 int dmgval2
= rnd(4);
2532 if ((otmp
= MON_WEP(mtmp
)) != 0
2533 && otmp
->oartifact
== ART_MAGICBANE
)
2535 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2537 && defends_when_carried(AD_MAGM
, otmp
))
2541 if (passes_walls(mptr
))
2542 dmgval2
= (dmgval2
+ 3) / 4;
2546 mtmp
->mhp
-= dmgval2
;
2550 ? "compression from an anti-magic field"
2556 newsym(trap
->tx
, trap
->ty
);
2561 break; /* monsters usually don't set it off */
2562 if (is_flyer(mptr
)) {
2563 boolean already_seen
= trap
->tseen
;
2565 if (in_sight
&& !already_seen
) {
2566 pline("A trigger appears in a pile of soil below %s.",
2573 newsym(mtmp
->mx
, mtmp
->my
);
2574 pline_The("air currents set %s off!",
2575 already_seen
? "a land mine" : "it");
2577 } else if (in_sight
) {
2578 newsym(mtmp
->mx
, mtmp
->my
);
2579 pline("%s%s triggers %s land mine!",
2580 !Deaf
? "KAABLAMM!!! " : "", Monnam(mtmp
),
2581 a_your
[trap
->madeby_u
]);
2583 if (!in_sight
&& !Deaf
)
2584 pline("Kaablamm! You hear an explosion in the distance!");
2585 blow_up_landmine(trap
);
2586 /* explosion might have destroyed a drawbridge; don't
2587 dish out more damage if monster is already dead */
2589 || thitm(0, mtmp
, (struct obj
*) 0, rnd(16), FALSE
)) {
2592 /* monsters recursively fall into new pit */
2593 if (mintrap(mtmp
) == 2)
2596 /* a boulder may fill the new pit, crushing monster */
2597 fill_pit(trap
->tx
, trap
->ty
);
2600 if (unconscious()) {
2602 nomovemsg
= "The explosion awakens you!";
2606 if (resists_magm(mtmp
)) {
2607 shieldeff(mtmp
->mx
, mtmp
->my
);
2608 } else if (!resist(mtmp
, WAND_CLASS
, 0, NOTELL
)) {
2609 if (newcham(mtmp
, (struct permonst
*) 0, FALSE
, FALSE
))
2610 /* we're done with mptr but keep it up to date */
2616 case ROLLING_BOULDER_TRAP
:
2617 if (!is_flyer(mptr
)) {
2618 int style
= ROLL
| (in_sight
? 0 : LAUNCH_UNSEEN
);
2620 newsym(mtmp
->mx
, mtmp
->my
);
2622 pline("Click! %s triggers %s.", Monnam(mtmp
),
2623 trap
->tseen
? "a rolling boulder trap" : something
);
2624 if (launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
2625 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
2632 newsym(mtmp
->mx
, mtmp
->my
);
2636 case VIBRATING_SQUARE
:
2637 if (see_it
&& !Blind
) {
2639 pline("You see a strange vibration beneath %s %s.",
2640 s_suffix(mon_nam(mtmp
)),
2641 makeplural(mbodypart(mtmp
, FOOT
)));
2643 pline("You see the ground vibrate in the distance.");
2648 impossible("Some monster encountered a strange trap of type %d.",
2654 return mtmp
->mtrapped
;
2657 /* Combine cockatrice checks into single functions to avoid repeating code. */
2662 if (Stone_resistance
)
2664 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
))
2666 You("turn to stone...");
2667 killer
.format
= KILLED_BY
;
2668 if (str
!= killer
.name
)
2669 Strcpy(killer
.name
, str
? str
: "");
2674 minstapetrify(mon
, byplayer
)
2678 if (resists_ston(mon
))
2680 if (poly_when_stoned(mon
->data
)) {
2684 if (!vamp_stone(mon
))
2687 /* give a "<mon> is slowing down" message and also remove
2688 intrinsic speed (comparable to similar effect on the hero) */
2689 mon_adjust_speed(mon
, -3, (struct obj
*) 0);
2691 if (cansee(mon
->mx
, mon
->my
))
2692 pline("%s turns to stone.", Monnam(mon
));
2695 xkilled(mon
, XKILL_NOMSG
);
2706 if (uwep
&& uwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[uwep
->corpsenm
])
2707 && !Stone_resistance
) {
2708 pline("%s touch the %s corpse.", arg
, mons
[uwep
->corpsenm
].mname
);
2709 Sprintf(kbuf
, "%s corpse", an(mons
[uwep
->corpsenm
].mname
));
2711 /* life-saved; unwield the corpse if we can't handle it */
2712 if (!uarmg
&& !Stone_resistance
)
2715 /* Or your secondary weapon, if wielded [hypothetical; we don't
2716 allow two-weapon combat when either weapon is a corpse] */
2717 if (u
.twoweap
&& uswapwep
&& uswapwep
->otyp
== CORPSE
2718 && touch_petrifies(&mons
[uswapwep
->corpsenm
]) && !Stone_resistance
) {
2719 pline("%s touch the %s corpse.", arg
, mons
[uswapwep
->corpsenm
].mname
);
2720 Sprintf(kbuf
, "%s corpse", an(mons
[uswapwep
->corpsenm
].mname
));
2722 /* life-saved; unwield the corpse */
2723 if (!uarmg
&& !Stone_resistance
)
2729 mselftouch(mon
, arg
, byplayer
)
2734 struct obj
*mwep
= MON_WEP(mon
);
2736 if (mwep
&& mwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[mwep
->corpsenm
])
2737 && !resists_ston(mon
)) {
2738 if (cansee(mon
->mx
, mon
->my
)) {
2739 pline("%s%s touches %s.", arg
? arg
: "",
2740 arg
? mon_nam(mon
) : Monnam(mon
),
2741 corpse_xname(mwep
, (const char *) 0, CXN_PFX_THE
));
2743 minstapetrify(mon
, byplayer
);
2744 /* if life-saved, might not be able to continue wielding */
2745 if (mon
->mhp
> 0 && !which_armor(mon
, W_ARMG
) && !resists_ston(mon
))
2750 /* start levitating */
2754 context
.botl
= TRUE
;
2756 if (u
.utraptype
== TT_PIT
) {
2758 You("float up, out of the pit!");
2759 vision_full_recalc
= 1; /* vision limits change */
2760 fill_pit(u
.ux
, u
.uy
);
2761 } else if (u
.utraptype
== TT_INFLOOR
) {
2762 Your("body pulls upward, but your %s are still stuck.",
2763 makeplural(body_part(LEG
)));
2765 You("float up, only your %s is still stuck.", body_part(LEG
));
2768 } else if (Is_waterlevel(&u
.uz
)) {
2769 pline("It feels as though you've lost some weight.");
2771 } else if (u
.uinwater
) {
2773 } else if (u
.uswallow
) {
2774 You(is_animal(u
.ustuck
->data
) ? "float away from the %s."
2775 : "spiral up into %s.",
2776 is_animal(u
.ustuck
->data
) ? surface(u
.ux
, u
.uy
)
2777 : mon_nam(u
.ustuck
));
2778 } else if (Hallucination
) {
2779 pline("Up, up, and awaaaay! You're walking on air!");
2780 } else if (Is_airlevel(&u
.uz
)) {
2781 You("gain control over your movements.");
2783 You("start to float in the air!");
2785 if (u
.usteed
&& !is_floater(u
.usteed
->data
)
2786 && !is_flyer(u
.usteed
->data
)) {
2788 pline("%s magically floats up!", Monnam(u
.usteed
));
2790 You("cannot stay on %s.", mon_nam(u
.usteed
));
2791 dismount_steed(DISMOUNT_GENERIC
);
2795 You("are no longer able to control your flight.");
2796 BFlying
|= I_SPECIAL
;
2807 if ((t
= t_at(x
, y
)) && ((t
->ttyp
== PIT
) || (t
->ttyp
== SPIKED_PIT
))
2808 && (otmp
= sobj_at(BOULDER
, x
, y
))) {
2809 obj_extract_self(otmp
);
2810 (void) flooreffects(otmp
, x
, y
, "settle");
2814 /* stop levitating */
2816 float_down(hmask
, emask
)
2817 long hmask
, emask
; /* might cancel timeout */
2819 register struct trap
*trap
= (struct trap
*) 0;
2820 d_level current_dungeon_level
;
2821 boolean no_msg
= FALSE
;
2823 HLevitation
&= ~hmask
;
2824 ELevitation
&= ~emask
;
2826 return 0; /* maybe another ring/potion/boots */
2828 /* Levitation is blocked, so hero is not actually floating
2829 hence shouldn't have float_down effects and feedback */
2830 float_vs_flight(); /* before nomul() rather than after */
2833 context
.botl
= TRUE
;
2834 nomul(0); /* stop running or resting */
2836 /* controlled flight no longer overridden by levitation */
2837 BFlying
&= ~I_SPECIAL
;
2839 You("have stopped levitating and are now flying.");
2844 You("float down, but you are still %s.",
2845 is_animal(u
.ustuck
->data
) ? "swallowed" : "engulfed");
2849 if (Punished
&& !carried(uball
)
2850 && (is_pool(uball
->ox
, uball
->oy
)
2851 || ((trap
= t_at(uball
->ox
, uball
->oy
))
2852 && ((trap
->ttyp
== PIT
) || (trap
->ttyp
== SPIKED_PIT
)
2853 || (trap
->ttyp
== TRAPDOOR
) || (trap
->ttyp
== HOLE
))))) {
2858 movobj(uchain
, uball
->ox
, uball
->oy
);
2859 newsym(u
.ux0
, u
.uy0
);
2860 vision_full_recalc
= 1; /* in case the hero moved. */
2862 /* check for falling into pool - added by GAN 10/20/86 */
2864 if (!u
.uswallow
&& u
.ustuck
) {
2865 if (sticks(youmonst
.data
))
2866 You("aren't able to maintain your hold on %s.",
2869 pline("Startled, %s can no longer hold you!",
2874 * drown() and lava_effects() print various messages almost
2875 * every time they're called which conflict with the "fall
2876 * into" message below. Thus, we want to avoid printing
2877 * confusing, duplicate or out-of-order messages.
2878 * Use knowledge of the two routines as a hack -- this
2879 * should really be handled differently -dlc
2881 if (is_pool(u
.ux
, u
.uy
) && !Wwalking
&& !Swimming
&& !u
.uinwater
)
2884 if (is_lava(u
.ux
, u
.uy
)) {
2885 (void) lava_effects();
2890 trap
= t_at(u
.ux
, u
.uy
);
2891 if (Is_airlevel(&u
.uz
)) {
2892 You("begin to tumble in place.");
2893 } else if (Is_waterlevel(&u
.uz
) && !no_msg
) {
2894 You_feel("heavier.");
2895 /* u.uinwater msgs already in spoteffects()/drown() */
2896 } else if (!u
.uinwater
&& !no_msg
) {
2897 if (!(emask
& W_SADDLE
)) {
2898 if (Sokoban
&& trap
) {
2899 /* Justification elsewhere for Sokoban traps is based
2900 * on air currents. This is consistent with that.
2901 * The unexpected additional force of the air currents
2902 * once levitation ceases knocks you off your feet.
2905 pline("Bummer! You've crashed.");
2908 losehp(rnd(2), "dangerous winds", KILLED_BY
);
2910 dismount_steed(DISMOUNT_FELL
);
2911 selftouch("As you fall, you");
2912 } else if (u
.usteed
&& (is_floater(u
.usteed
->data
)
2913 || is_flyer(u
.usteed
->data
))) {
2914 You("settle more firmly in the saddle.");
2915 } else if (Hallucination
) {
2916 pline("Bummer! You've %s.",
2919 : "hit the ground");
2921 You("float gently to the %s.", surface(u
.ux
, u
.uy
));
2927 /* can't rely on u.uz0 for detecting trap door-induced level change;
2928 it gets changed to reflect the new level before we can check it */
2929 assign_level(¤t_dungeon_level
, &u
.uz
);
2931 switch (trap
->ttyp
) {
2936 if (!Can_fall_thru(&u
.uz
) || u
.ustuck
)
2940 if (!u
.utrap
) /* not already in the trap */
2944 if (!Is_airlevel(&u
.uz
) && !Is_waterlevel(&u
.uz
) && !u
.uswallow
2945 /* falling through trap door calls goto_level,
2946 and goto_level does its own pickup() call */
2947 && on_level(&u
.uz
, ¤t_dungeon_level
))
2952 /* shared code for climbing out of a pit */
2956 if (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
2960 /* marked as trapped so they can pick things up */
2961 You("ascend from the pit.");
2963 fill_pit(u
.ux
, u
.uy
);
2964 vision_full_recalc
= 1; /* vision limits change */
2965 } else if (!rn2(2) && sobj_at(BOULDER
, u
.ux
, u
.uy
)) {
2966 Your("%s gets stuck in a crevice.", body_part(LEG
));
2967 display_nhwindow(WIN_MESSAGE
, FALSE
);
2968 clear_nhwindow(WIN_MESSAGE
);
2969 You("free your %s.", body_part(LEG
));
2970 } else if ((Flying
|| is_clinger(youmonst
.data
)) && !Sokoban
) {
2971 /* eg fell in pit, then poly'd to a flying monster;
2972 or used '>' to deliberately enter it */
2973 You("%s from the pit.", Flying
? "fly" : "climb");
2975 fill_pit(u
.ux
, u
.uy
);
2976 vision_full_recalc
= 1; /* vision limits change */
2977 } else if (!(--u
.utrap
)) {
2978 You("%s to the edge of the pit.",
2979 (Sokoban
&& Levitation
)
2980 ? "struggle against the air currents and float"
2981 : u
.usteed
? "ride" : "crawl");
2982 fill_pit(u
.ux
, u
.uy
);
2983 vision_full_recalc
= 1; /* vision limits change */
2984 } else if (u
.dz
|| flags
.verbose
) {
2986 Norep("%s is still in a pit.", upstart(y_monnam(u
.usteed
)));
2988 Norep((Hallucination
&& !rn2(5))
2989 ? "You've fallen, and you can't get up."
2990 : "You are still in a pit.");
2996 struct obj
*box
; /* null for floor trap */
2998 boolean see_it
= !Blind
;
3001 /* Bug: for box case, the equivalent of burn_floor_objects() ought
3002 * to be done upon its contents.
3005 if ((box
&& !carried(box
)) ? is_pool(box
->ox
, box
->oy
) : Underwater
) {
3006 pline("A cascade of steamy bubbles erupts from %s!",
3007 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
3008 if (Fire_resistance
)
3009 You("are uninjured.");
3011 losehp(rnd(3), "boiling water", KILLED_BY
);
3014 pline("A %s %s from %s!", tower_of_flame
, box
? "bursts" : "erupts",
3015 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
3016 if (Fire_resistance
) {
3017 shieldeff(u
.ux
, u
.uy
);
3019 } else if (Upolyd
) {
3021 switch (u
.umonnum
) {
3022 case PM_PAPER_GOLEM
:
3025 case PM_STRAW_GOLEM
:
3031 case PM_LEATHER_GOLEM
:
3040 if (u
.mhmax
> mons
[u
.umonnum
].mlevel
)
3041 u
.mhmax
-= rn2(min(u
.mhmax
, num
+ 1)), context
.botl
= 1;
3044 if (u
.uhpmax
> u
.ulevel
)
3045 u
.uhpmax
-= rn2(min(u
.uhpmax
, num
+ 1)), context
.botl
= 1;
3048 You("are uninjured.");
3050 losehp(num
, tower_of_flame
, KILLED_BY_AN
); /* fire damage */
3053 if (burnarmor(&youmonst
) || rn2(3)) {
3054 destroy_item(SCROLL_CLASS
, AD_FIRE
);
3055 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
3056 destroy_item(POTION_CLASS
, AD_FIRE
);
3058 if (!box
&& burn_floor_objects(u
.ux
, u
.uy
, see_it
, TRUE
) && !see_it
)
3059 You("smell paper burning.");
3060 if (is_ice(u
.ux
, u
.uy
))
3061 melt_ice(u
.ux
, u
.uy
, (char *) 0);
3067 register int fate
= rnd(20);
3069 /* What happened to the poor sucker? */
3072 /* Most of the time, it creates some monsters. */
3073 register int cnt
= rnd(4);
3075 /* blindness effects */
3076 if (!resists_blnd(&youmonst
)) {
3077 You("are momentarily blinded by a flash of light!");
3078 make_blinded((long) rn1(5, 10), FALSE
);
3080 Your1(vision_clears
);
3081 } else if (!Blind
) {
3082 You_see("a flash of light!");
3085 /* deafness effects */
3087 You_hear("a deafening roar!");
3088 incr_itimeout(&HDeaf
, rn1(20, 30));
3089 context
.botl
= TRUE
;
3091 /* magic vibrations still hit you */
3092 You_feel("rankled.");
3093 incr_itimeout(&HDeaf
, rn1(5, 15));
3094 context
.botl
= TRUE
;
3097 (void) makemon((struct permonst
*) 0, u
.ux
, u
.uy
, NO_MM_FLAGS
);
3102 /* sometimes nothing happens */
3104 case 12: /* a flash of fire */
3105 dofiretrap((struct obj
*) 0);
3110 pline("A shiver runs up and down your %s!", body_part(SPINE
));
3113 You_hear(Hallucination
? "the moon howling at you."
3114 : "distant howling.");
3117 if (on_level(&u
.uz
, &qstart_level
))
3119 "%slike the prodigal son.",
3120 (flags
.female
|| (Upolyd
&& is_neuter(youmonst
.data
)))
3124 You("suddenly yearn for %s.",
3127 : (In_quest(&u
.uz
) || at_dgn_entrance("The Quest"))
3128 ? "your nearby homeland"
3129 : "your distant homeland");
3132 Your("pack shakes violently!");
3135 You(Hallucination
? "smell hamburgers." : "smell charred flesh.");
3141 /* very occasionally something nice happens. */
3142 case 19: { /* tame nearby monsters */
3146 (void) adjattrib(A_CHA
, 1, FALSE
);
3147 for (i
= -1; i
<= 1; i
++)
3148 for (j
= -1; j
<= 1; j
++) {
3149 if (!isok(u
.ux
+ i
, u
.uy
+ j
))
3151 mtmp
= m_at(u
.ux
+ i
, u
.uy
+ j
);
3153 (void) tamedog(mtmp
, (struct obj
*) 0);
3157 case 20: { /* uncurse stuff */
3159 long save_conf
= HConfusion
;
3161 pseudo
= zeroobj
; /* neither cursed nor blessed,
3162 and zero out oextra */
3163 pseudo
.otyp
= SCR_REMOVE_CURSE
;
3165 (void) seffects(&pseudo
);
3166 HConfusion
= save_conf
;
3174 /* Set an item on fire.
3175 * "force" means not to roll a luck-based protection check for the
3177 * "x" and "y" are the coordinates to dump the contents of a
3178 * container, if it burns up.
3180 * Return whether the object was destroyed.
3183 fire_damage(obj
, force
, x
, y
)
3189 struct obj
*otmp
, *ncobj
;
3190 int in_sight
= !Blind
&& couldsee(x
, y
); /* Don't care if it's lit */
3193 /* object might light in a controlled manner */
3197 if (Is_container(obj
)) {
3198 switch (obj
->otyp
) {
3200 return FALSE
; /* Immune */
3211 if ((!force
&& (Luck
+ 5) > rn2(chance
))
3212 || (is_flammable(obj
) && obj
->oerodeproof
))
3214 /* Container is burnt up - dump contents out */
3216 pline("%s catches fire and burns.", Yname2(obj
));
3217 if (Has_contents(obj
)) {
3219 pline("Its contents fall out.");
3220 for (otmp
= obj
->cobj
; otmp
; otmp
= ncobj
) {
3222 obj_extract_self(otmp
);
3223 if (!flooreffects(otmp
, x
, y
, ""))
3224 place_object(otmp
, x
, y
);
3230 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3231 /* chance per item of sustaining damage:
3232 * max luck (Luck==13): 10%
3233 * avg luck (Luck==0): 75%
3234 * awful luck (Luck<-4): 100%
3237 } else if (obj
->oclass
== SCROLL_CLASS
|| obj
->oclass
== SPBOOK_CLASS
) {
3238 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
)
3240 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3242 pline("Smoke rises from %s.", the(xname(obj
)));
3245 dindx
= (obj
->oclass
== SCROLL_CLASS
) ? 3 : 4;
3247 pline("%s %s.", Yname2(obj
),
3248 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3252 } else if (obj
->oclass
== POTION_CLASS
) {
3253 dindx
= (obj
->otyp
!= POT_OIL
) ? 1 : 2;
3255 pline("%s %s.", Yname2(obj
),
3256 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3260 } else if (erode_obj(obj
, (char *) 0, ERODE_BURN
, EF_DESTROY
)
3268 * Apply fire_damage() to an entire chain.
3270 * Return number of objects destroyed. --ALI
3273 fire_damage_chain(chain
, force
, here
, x
, y
)
3275 boolean force
, here
;
3278 struct obj
*obj
, *nobj
;
3280 for (obj
= chain
; obj
; obj
= nobj
) {
3281 nobj
= here
? obj
->nexthere
: obj
->nobj
;
3282 if (fire_damage(obj
, force
, x
, y
))
3286 if (num
&& (Blind
&& !couldsee(x
, y
)))
3287 You("smell smoke.");
3295 /* Scrolls but not spellbooks can be erased by acid. */
3296 struct monst
*victim
;
3302 victim
= carried(obj
) ? &youmonst
: mcarried(obj
) ? obj
->ocarry
: NULL
;
3303 vismon
= victim
&& (victim
!= &youmonst
) && canseemon(victim
);
3306 grease_protect(obj
, (char *) 0, victim
);
3307 } else if (obj
->oclass
== SCROLL_CLASS
&& obj
->otyp
!= SCR_BLANK_PAPER
) {
3308 if (obj
->otyp
!= SCR_BLANK_PAPER
3310 && obj
->otyp
!= SCR_MAIL
3314 if (victim
== &youmonst
)
3315 pline("Your %s.", aobjnam(obj
, "fade"));
3317 pline("%s %s.", s_suffix(Monnam(victim
)),
3318 aobjnam(obj
, "fade"));
3321 obj
->otyp
= SCR_BLANK_PAPER
;
3325 erode_obj(obj
, (char *) 0, ERODE_CORRODE
, EF_GREASE
| EF_VERBOSE
);
3328 /* context for water_damage(), managed by water_damage_chain();
3329 when more than one stack of potions of acid explode while processing
3330 a chain of objects, use alternate phrasing after the first message */
3331 static struct h2o_ctx
{
3332 int dkn_boom
, unk_boom
; /* track dknown, !dknown separately */
3334 } acid_ctx
= { 0, 0, FALSE
};
3336 /* Get an object wet and damage it appropriately.
3337 * "ostr", if present, is used instead of the object name in some
3339 * "force" means not to roll luck to protect some objects.
3340 * Returns an erosion return value (ER_*)
3343 water_damage(obj
, ostr
, force
)
3357 if (obj
->otyp
== CAN_OF_GREASE
&& obj
->spe
> 0) {
3359 } else if (obj
->otyp
== TOWEL
&& obj
->spe
< 7) {
3360 wet_a_towel(obj
, rnd(7), TRUE
);
3362 } else if (obj
->greased
) {
3368 } else if (Is_container(obj
) && !Is_box(obj
)
3369 && (obj
->otyp
!= OILSKIN_SACK
|| (obj
->cursed
&& !rn2(3)))) {
3371 pline("Water gets into your %s!", ostr
);
3373 water_damage_chain(obj
->cobj
, FALSE
);
3375 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3376 /* chance per item of sustaining damage:
3378 * avg luck (Luck==0): 75%
3379 * awful luck (Luck<-4): 100%
3382 } else if (obj
->oclass
== SCROLL_CLASS
) {
3383 if (obj
->otyp
== SCR_BLANK_PAPER
3385 || obj
->otyp
== SCR_MAIL
3389 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3391 obj
->otyp
= SCR_BLANK_PAPER
;
3397 } else if (obj
->oclass
== SPBOOK_CLASS
) {
3398 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3399 pline("Steam rises from %s.", the(xname(obj
)));
3401 } else if (obj
->otyp
== SPE_BLANK_PAPER
) {
3405 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3407 if (obj
->otyp
== SPE_NOVEL
) {
3412 obj
->otyp
= SPE_BLANK_PAPER
;
3417 } else if (obj
->oclass
== POTION_CLASS
) {
3418 if (obj
->otyp
== POT_ACID
) {
3420 boolean one
= (obj
->quan
== 1L), update
= carried(obj
),
3423 if (Blind
&& !carried(obj
))
3425 if (acid_ctx
.ctx_valid
)
3426 exploded
= ((obj
->dknown
? acid_ctx
.dkn_boom
3427 : acid_ctx
.unk_boom
) > 0);
3429 * "a [potion|<color> potion|potion of acid] explodes"
3430 * depending on obj->dknown (potion has been seen) and
3431 * objects[POT_ACID].oc_name_known (fully discovered),
3432 * or "some {plural version} explode" when relevant.
3433 * Second and subsequent messages for same chain and
3434 * matching dknown status are
3435 * "another [potion|<color> &c] explodes" or plural
3438 bufp
= simpleonames(obj
);
3439 pline("%s %s %s!", /* "A potion explodes!" */
3440 !exploded
? (one
? "A" : "Some")
3441 : (one
? "Another" : "More"),
3442 bufp
, vtense(bufp
, "explode"));
3443 if (acid_ctx
.ctx_valid
) {
3445 acid_ctx
.dkn_boom
++;
3447 acid_ctx
.unk_boom
++;
3453 return ER_DESTROYED
;
3454 } else if (obj
->odiluted
) {
3456 pline("Your %s %s further.", ostr
, vtense(ostr
, "dilute"));
3458 obj
->otyp
= POT_WATER
;
3460 obj
->blessed
= obj
->cursed
= 0;
3465 } else if (obj
->otyp
!= POT_WATER
) {
3467 pline("Your %s %s.", ostr
, vtense(ostr
, "dilute"));
3475 return erode_obj(obj
, ostr
, ERODE_RUST
, EF_NONE
);
3481 water_damage_chain(obj
, here
)
3487 /* initialize acid context: so far, neither seen (dknown) potions of
3488 acid nor unseen have exploded during this water damage sequence */
3489 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3490 acid_ctx
.ctx_valid
= TRUE
;
3492 for (; obj
; obj
= otmp
) {
3493 otmp
= here
? obj
->nexthere
: obj
->nobj
;
3494 water_damage(obj
, (char *) 0, FALSE
);
3497 /* reset acid context */
3498 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3499 acid_ctx
.ctx_valid
= FALSE
;
3503 * This function is potentially expensive - rolling
3504 * inventory list multiple times. Luckily it's seldom needed.
3505 * Returns TRUE if disrobing made player unencumbered enough to
3506 * crawl out of the current predicament.
3509 emergency_disrobe(lostsome
)
3512 int invc
= inv_cnt(TRUE
);
3514 while (near_capacity() > (Punished
? UNENCUMBERED
: SLT_ENCUMBER
)) {
3515 register struct obj
*obj
, *otmp
= (struct obj
*) 0;
3518 /* Pick a random object */
3521 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
3523 * Undroppables are: body armor, boots, gloves,
3524 * amulets, and rings because of the time and effort
3525 * in removing them + loadstone and other cursed stuff
3526 * for obvious reasons.
3528 if (!((obj
->otyp
== LOADSTONE
&& obj
->cursed
) || obj
== uamul
3529 || obj
== uleft
|| obj
== uright
|| obj
== ublindf
3530 || obj
== uarm
|| obj
== uarmc
|| obj
== uarmg
3531 || obj
== uarmf
|| obj
== uarmu
3532 || (obj
->cursed
&& (obj
== uarmh
|| obj
== uarms
))
3535 /* reached the mark and found some stuff to drop? */
3536 if (--i
< 0 && otmp
)
3543 return FALSE
; /* nothing to drop! */
3544 if (otmp
->owornmask
)
3545 remove_worn_item(otmp
, FALSE
);
3554 /* return TRUE iff player relocated */
3558 const char *pool_of_water
;
3559 boolean inpool_ok
= FALSE
, crawl_ok
;
3562 /* happily wading in the same contiguous pool */
3563 if (u
.uinwater
&& is_pool(u
.ux
- u
.dx
, u
.uy
- u
.dy
)
3564 && (Swimming
|| Amphibious
)) {
3565 /* water effects on objects every now and then */
3573 You("%s into the %s%c", Is_waterlevel(&u
.uz
) ? "plunge" : "fall",
3575 Amphibious
|| Swimming
? '.' : '!');
3576 if (!Swimming
&& !Is_waterlevel(&u
.uz
))
3577 You("sink like %s.", Hallucination
? "the Titanic" : "a rock");
3580 water_damage_chain(invent
, FALSE
);
3582 if (u
.umonnum
== PM_GREMLIN
&& rn2(3))
3583 (void) split_mon(&youmonst
, (struct monst
*) 0);
3584 else if (u
.umonnum
== PM_IRON_GOLEM
) {
3586 i
= Maybe_Half_Phys(d(2, 6));
3589 losehp(i
, "rusting away", KILLED_BY
);
3594 if ((i
= number_leashed()) > 0) {
3595 pline_The("leash%s slip%s loose.", (i
> 1) ? "es" : "",
3596 (i
> 1) ? "" : "s");
3600 if (Amphibious
|| Swimming
) {
3603 pline("But you aren't drowning.");
3604 if (!Is_waterlevel(&u
.uz
)) {
3606 Your("keel hits the bottom.");
3608 You("touch bottom.");
3615 vision_recalc(2); /* unsee old position */
3618 vision_full_recalc
= 1;
3621 if ((Teleportation
|| can_teleport(youmonst
.data
)) && !Unaware
3622 && (Teleport_control
|| rn2(3) < Luck
+ 2)) {
3623 You("attempt a teleport spell."); /* utcsri!carroll */
3624 if (!level
.flags
.noteleport
) {
3626 if (!is_pool(u
.ux
, u
.uy
))
3629 pline_The("attempted teleport spell fails.");
3632 dismount_steed(DISMOUNT_GENERIC
);
3633 if (!is_pool(u
.ux
, u
.uy
))
3637 x
= y
= 0; /* lint suppression */
3638 /* if sleeping, wake up now so that we don't crawl out of water
3639 while still asleep; we can't do that the same way that waking
3640 due to combat is handled; note unmul() clears u.usleep */
3642 unmul("Suddenly you wake up!");
3643 /* being doused will revive from fainting */
3646 /* can't crawl if unable to move (crawl_ok flag stays false) */
3647 if (multi
< 0 || (Upolyd
&& !youmonst
.data
->mmove
))
3649 /* look around for a place to crawl to */
3650 for (i
= 0; i
< 100; i
++) {
3651 x
= rn1(3, u
.ux
- 1);
3652 y
= rn1(3, u
.uy
- 1);
3653 if (crawl_destination(x
, y
)) {
3659 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
3660 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++)
3661 if (crawl_destination(x
, y
)) {
3667 boolean lost
= FALSE
;
3668 /* time to do some strip-tease... */
3669 boolean succ
= Is_waterlevel(&u
.uz
) ? TRUE
: emergency_disrobe(&lost
);
3671 You("try to crawl out of the %s.", hliquid("water"));
3673 You("dump some of your gear to lose weight...");
3675 pline("Pheew! That was close.");
3679 /* still too much weight */
3680 pline("But in vain.");
3684 for (i
= 0; i
< 5; i
++) { /* arbitrary number of loops */
3685 /* killer format and name are reconstructed every iteration
3686 because lifesaving resets them */
3687 pool_of_water
= waterbody_name(u
.ux
, u
.uy
);
3688 killer
.format
= KILLED_BY_AN
;
3689 /* avoid "drowned in [a] water" */
3690 if (!strcmp(pool_of_water
, "water"))
3691 pool_of_water
= "deep water", killer
.format
= KILLED_BY
;
3692 Strcpy(killer
.name
, pool_of_water
);
3694 /* oops, we're still alive. better get out of the water. */
3695 if (safe_teleds(TRUE
))
3696 break; /* successful life-save */
3697 /* nowhere safe to land; repeat drowning loop... */
3698 pline("You're still drowning.");
3702 You("find yourself back %s.",
3703 Is_waterlevel(&u
.uz
) ? "in an air bubble" : "on land");
3713 /* energy is completely gone */
3714 You_feel("momentarily lethargic.");
3716 /* throttle further loss a bit when there's not much left to lose */
3717 if (n
> u
.uenmax
|| n
> u
.ulevel
)
3720 You_feel("your magical energy drain away%c", (n
> u
.uen
) ? '!' : '.');
3723 u
.uenmax
-= rnd(-u
.uen
);
3736 if (near_capacity() >= HVY_ENCUMBER
) {
3737 pline("You're too strained to do that.");
3740 if ((nohands(youmonst
.data
) && !webmaker(youmonst
.data
))
3741 || !youmonst
.data
->mmove
) {
3742 pline("And just how do you expect to do that?");
3744 } else if (u
.ustuck
&& sticks(youmonst
.data
)) {
3745 pline("You'll have to let go of %s first.", mon_nam(u
.ustuck
));
3748 if (u
.ustuck
|| (welded(uwep
) && bimanual(uwep
))) {
3749 Your("%s seem to be too busy for that.", makeplural(body_part(HAND
)));
3752 return untrap(FALSE
);
3755 /* Probability of disabling a trap. Helge Hafting */
3762 /* Only spiders know how to deal with webs reliably */
3763 if (ttmp
->ttyp
== WEB
&& !webmaker(youmonst
.data
))
3765 if (Confusion
|| Hallucination
)
3773 /* Your own traps are better known than others. */
3774 if (ttmp
&& ttmp
->madeby_u
)
3776 if (Role_if(PM_ROGUE
)) {
3777 if (rn2(2 * MAXULEV
) < u
.ulevel
)
3779 if (u
.uhave
.questart
&& chance
> 1)
3781 } else if (Role_if(PM_RANGER
) && chance
> 1)
3786 /* Replace trap with object(s). Helge Hafting */
3788 cnv_trap_obj(otyp
, cnt
, ttmp
, bury_it
)
3794 struct obj
*otmp
= mksobj(otyp
, TRUE
, FALSE
);
3797 otmp
->owt
= weight(otmp
);
3798 /* Only dart traps are capable of being poisonous */
3800 otmp
->opoisoned
= 0;
3801 place_object(otmp
, ttmp
->tx
, ttmp
->ty
);
3803 /* magical digging first disarms this trap, then will unearth it */
3804 (void) bury_an_obj(otmp
, (boolean
*) 0);
3806 /* Sell your own traps only... */
3808 sellobj(otmp
, ttmp
->tx
, ttmp
->ty
);
3811 newsym(ttmp
->tx
, ttmp
->ty
);
3812 if (u
.utrap
&& ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
)
3817 /* while attempting to disarm an adjacent trap, we've fallen into it */
3819 move_into_trap(ttmp
)
3823 xchar x
= ttmp
->tx
, y
= ttmp
->ty
, bx
, by
, cx
, cy
;
3826 bx
= by
= cx
= cy
= 0; /* lint suppression */
3827 /* we know there's no monster in the way, and we're not trapped */
3829 || drag_ball(x
, y
, &bc
, &bx
, &by
, &cx
, &cy
, &unused
, TRUE
)) {
3830 u
.ux0
= u
.ux
, u
.uy0
= u
.uy
;
3833 newsym(u
.ux0
, u
.uy0
);
3835 check_leash(u
.ux0
, u
.uy0
);
3837 move_bc(0, bc
, bx
, by
, cx
, cy
);
3838 /* marking the trap unseen forces dotrap() to treat it like a new
3839 discovery and prevents pickup() -> look_here() -> check_here()
3840 from giving a redundant "there is a <trap> here" message when
3841 there are objects covering this trap */
3842 ttmp
->tseen
= 0; /* hack for check_here() */
3843 /* trigger the trap */
3844 spoteffects(TRUE
); /* pickup() + dotrap() */
3845 exercise(A_WIS
, FALSE
);
3849 /* 0: doesn't even try
3850 * 1: tries and fails
3854 try_disarm(ttmp
, force_failure
)
3856 boolean force_failure
;
3858 struct monst
*mtmp
= m_at(ttmp
->tx
, ttmp
->ty
);
3859 int ttype
= ttmp
->ttyp
;
3860 boolean under_u
= (!u
.dx
&& !u
.dy
);
3861 boolean holdingtrap
= (ttype
== BEAR_TRAP
|| ttype
== WEB
);
3863 /* Test for monster first, monsters are displayed instead of trap. */
3864 if (mtmp
&& (!mtmp
->mtrapped
|| !holdingtrap
)) {
3865 pline("%s is in the way.", Monnam(mtmp
));
3868 /* We might be forced to move onto the trap's location. */
3869 if (sobj_at(BOULDER
, ttmp
->tx
, ttmp
->ty
) && !Passes_walls
&& !under_u
) {
3870 There("is a boulder in your way.");
3873 /* duplicate tight-space checks from test_move */
3874 if (u
.dx
&& u
.dy
&& bad_rock(youmonst
.data
, u
.ux
, ttmp
->ty
)
3875 && bad_rock(youmonst
.data
, ttmp
->tx
, u
.uy
)) {
3876 if ((invent
&& (inv_weight() + weight_cap() > 600))
3877 || bigmonst(youmonst
.data
)) {
3878 /* don't allow untrap if they can't get thru to it */
3879 You("are unable to reach the %s!",
3880 defsyms
[trap_to_defsym(ttype
)].explanation
);
3884 /* untrappable traps are located on the ground. */
3885 if (!can_reach_floor(TRUE
)) {
3886 if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
)
3889 You("are unable to reach the %s!",
3890 defsyms
[trap_to_defsym(ttype
)].explanation
);
3894 /* Will our hero succeed? */
3895 if (force_failure
|| untrap_prob(ttmp
)) {
3898 if (mtmp
) { /* must be a trap that holds monsters */
3899 if (ttype
== BEAR_TRAP
) {
3902 mtmp
->mhp
-= rnd(4);
3905 } else if (ttype
== WEB
) {
3906 if (!webmaker(youmonst
.data
)) {
3907 struct trap
*ttmp2
= maketrap(u
.ux
, u
.uy
, WEB
);
3911 "webbing sticks to you. You're caught too!");
3912 dotrap(ttmp2
, NOWEBMSG
);
3913 if (u
.usteed
&& u
.utrap
) {
3914 /* you, not steed, are trapped */
3915 dismount_steed(DISMOUNT_FELL
);
3919 pline("%s remains entangled.", Monnam(mtmp
));
3921 } else if (under_u
) {
3924 move_into_trap(ttmp
);
3927 pline("%s %s is difficult to %s.",
3928 ttmp
->madeby_u
? "Your" : under_u
? "This" : "That",
3929 defsyms
[trap_to_defsym(ttype
)].explanation
,
3930 (ttype
== WEB
) ? "remove" : "disarm");
3938 reward_untrap(ttmp
, mtmp
)
3942 if (!ttmp
->madeby_u
) {
3943 if (rnl(10) < 8 && !mtmp
->mpeaceful
&& !mtmp
->msleeping
3944 && !mtmp
->mfrozen
&& !mindless(mtmp
->data
)
3945 && mtmp
->data
->mlet
!= S_HUMAN
) {
3946 mtmp
->mpeaceful
= 1;
3947 set_malign(mtmp
); /* reset alignment */
3948 pline("%s is grateful.", Monnam(mtmp
));
3950 /* Helping someone out of a trap is a nice thing to do,
3951 * A lawful may be rewarded, but not too often. */
3952 if (!rn2(3) && !rnl(8) && u
.ualign
.type
== A_LAWFUL
) {
3954 You_feel("that you did the right thing.");
3960 disarm_holdingtrap(ttmp
) /* Helge Hafting */
3964 int fails
= try_disarm(ttmp
, FALSE
);
3969 /* ok, disarm it. */
3971 /* untrap the monster, if any.
3972 There's no need for a cockatrice test, only the trap is touched */
3973 if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
3975 You("remove %s %s from %s.", the_your
[ttmp
->madeby_u
],
3976 (ttmp
->ttyp
== BEAR_TRAP
) ? "bear trap" : "webbing",
3978 reward_untrap(ttmp
, mtmp
);
3980 if (ttmp
->ttyp
== BEAR_TRAP
) {
3981 You("disarm %s bear trap.", the_your
[ttmp
->madeby_u
]);
3982 cnv_trap_obj(BEARTRAP
, 1, ttmp
, FALSE
);
3983 } else /* if (ttmp->ttyp == WEB) */ {
3984 You("succeed in removing %s web.", the_your
[ttmp
->madeby_u
]);
3988 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
3993 disarm_landmine(ttmp
) /* Helge Hafting */
3996 int fails
= try_disarm(ttmp
, FALSE
);
4000 You("disarm %s land mine.", the_your
[ttmp
->madeby_u
]);
4001 cnv_trap_obj(LAND_MINE
, 1, ttmp
, FALSE
);
4005 /* getobj will filter down to cans of grease and known potions of oil */
4006 static NEARDATA
const char oil
[] = { ALL_CLASSES
, TOOL_CLASS
, POTION_CLASS
,
4009 /* it may not make much sense to use grease on floor boards, but so what? */
4011 disarm_squeaky_board(ttmp
)
4018 obj
= getobj(oil
, "untrap with");
4022 bad_tool
= (obj
->cursed
4023 || ((obj
->otyp
!= POT_OIL
|| obj
->lamplit
)
4024 && (obj
->otyp
!= CAN_OF_GREASE
|| !obj
->spe
)));
4025 fails
= try_disarm(ttmp
, bad_tool
);
4029 /* successfully used oil or grease to fix squeaky board */
4030 if (obj
->otyp
== CAN_OF_GREASE
) {
4031 consume_obj_charge(obj
, TRUE
);
4033 useup(obj
); /* oil */
4036 You("repair the squeaky board."); /* no madeby_u */
4038 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
4039 more_experienced(1, 5);
4044 /* removes traps that shoot arrows, darts, etc. */
4046 disarm_shooting_trap(ttmp
, otyp
)
4050 int fails
= try_disarm(ttmp
, FALSE
);
4054 You("disarm %s trap.", the_your
[ttmp
->madeby_u
]);
4055 cnv_trap_obj(otyp
, 50 - rnl(50), ttmp
, FALSE
);
4059 /* Is the weight too heavy?
4060 * Formula as in near_capacity() & check_capacity() */
4062 try_lift(mtmp
, ttmp
, wt
, stuff
)
4068 int wc
= weight_cap();
4070 if (((wt
* 2) / wc
) >= HVY_ENCUMBER
) {
4071 pline("%s is %s for you to lift.", Monnam(mtmp
),
4072 stuff
? "carrying too much" : "too heavy");
4073 if (!ttmp
->madeby_u
&& !mtmp
->mpeaceful
&& mtmp
->mcanmove
4074 && !mindless(mtmp
->data
) && mtmp
->data
->mlet
!= S_HUMAN
4076 mtmp
->mpeaceful
= 1;
4077 set_malign(mtmp
); /* reset alignment */
4078 pline("%s thinks it was nice of you to try.", Monnam(mtmp
));
4085 /* Help trapped monster (out of a (spiked) pit) */
4087 help_monster_out(mtmp
, ttmp
)
4096 * This works when levitating too -- consistent with the ability
4097 * to hit monsters while levitating.
4099 * Should perhaps check that our hero has arms/hands at the
4100 * moment. Helping can also be done by engulfing...
4102 * Test the monster first - monsters are displayed before traps.
4104 if (!mtmp
->mtrapped
) {
4105 pline("%s isn't trapped.", Monnam(mtmp
));
4108 /* Do you have the necessary capacity to lift anything? */
4109 if (check_capacity((char *) 0))
4112 /* Will our hero succeed? */
4113 if ((uprob
= untrap_prob(ttmp
)) && !mtmp
->msleeping
&& mtmp
->mcanmove
) {
4114 You("try to reach out your %s, but %s backs away skeptically.",
4115 makeplural(body_part(ARM
)), mon_nam(mtmp
));
4119 /* is it a cockatrice?... */
4120 if (touch_petrifies(mtmp
->data
) && !uarmg
&& !Stone_resistance
) {
4121 You("grab the trapped %s using your bare %s.", mtmp
->data
->mname
,
4122 makeplural(body_part(HAND
)));
4124 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
)) {
4125 display_nhwindow(WIN_MESSAGE
, FALSE
);
4129 Sprintf(kbuf
, "trying to help %s out of a pit",
4130 an(mtmp
->data
->mname
));
4135 /* need to do cockatrice check first if sleeping or paralyzed */
4137 You("try to grab %s, but cannot get a firm grasp.", mon_nam(mtmp
));
4138 if (mtmp
->msleeping
) {
4139 mtmp
->msleeping
= 0;
4140 pline("%s awakens.", Monnam(mtmp
));
4145 You("reach out your %s and grab %s.", makeplural(body_part(ARM
)),
4148 if (mtmp
->msleeping
) {
4149 mtmp
->msleeping
= 0;
4150 pline("%s awakens.", Monnam(mtmp
));
4151 } else if (mtmp
->mfrozen
&& !rn2(mtmp
->mfrozen
)) {
4152 /* After such manhandling, perhaps the effect wears off */
4155 pline("%s stirs.", Monnam(mtmp
));
4158 /* is the monster too heavy? */
4159 wt
= inv_weight() + mtmp
->data
->cwt
;
4160 if (!try_lift(mtmp
, ttmp
, wt
, FALSE
))
4163 /* is the monster with inventory too heavy? */
4164 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
4166 if (!try_lift(mtmp
, ttmp
, wt
, TRUE
))
4169 You("pull %s out of the pit.", mon_nam(mtmp
));
4171 fill_pit(mtmp
->mx
, mtmp
->my
);
4172 reward_untrap(ttmp
, mtmp
);
4180 register struct obj
*otmp
;
4185 const char *trapdescr
;
4186 boolean here
, useplural
, confused
= (Confusion
|| Hallucination
),
4187 trap_skipped
= FALSE
, deal_with_floor_trap
;
4189 char the_trap
[BUFSZ
], qbuf
[QBUFSZ
];
4191 if (!getdir((char *) 0))
4196 pline_The("perils lurking there are beyond your grasp.");
4200 if (ttmp
&& !ttmp
->tseen
)
4202 trapdescr
= ttmp
? defsyms
[trap_to_defsym(ttmp
->ttyp
)].explanation
: 0;
4203 here
= (x
== u
.ux
&& y
== u
.uy
); /* !u.dx && !u.dy */
4205 if (here
) /* are there are one or more containers here? */
4206 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4212 deal_with_floor_trap
= can_reach_floor(FALSE
);
4213 if (!deal_with_floor_trap
) {
4216 Strcat(the_trap
, an(trapdescr
));
4218 Strcat(the_trap
, " and ");
4220 Strcat(the_trap
, (boxcnt
== 1) ? "a container" : "containers");
4221 useplural
= ((ttmp
&& boxcnt
> 0) || boxcnt
> 1);
4222 /* note: boxcnt and useplural will always be 0 for !here case */
4224 There("%s %s %s but you can't reach %s%s.",
4225 useplural
? "are" : "is", the_trap
, here
? "here" : "there",
4226 useplural
? "them" : "it",
4227 u
.usteed
? " while mounted" : "");
4228 trap_skipped
= (ttmp
!= 0);
4229 } else { /* deal_with_floor_trap */
4232 Strcpy(the_trap
, the(trapdescr
));
4234 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== SPIKED_PIT
) {
4235 You_cant("do much about %s%s.", the_trap
,
4236 u
.utrap
? " that you're stuck in"
4237 : " while standing on the edge of it");
4238 trap_skipped
= TRUE
;
4239 deal_with_floor_trap
= FALSE
;
4242 qbuf
, "There %s and %s here. %s %s?",
4243 (boxcnt
== 1) ? "is a container" : "are containers",
4245 (ttmp
->ttyp
== WEB
) ? "Remove" : "Disarm", the_trap
);
4246 switch (ynq(qbuf
)) {
4250 trap_skipped
= TRUE
;
4251 deal_with_floor_trap
= FALSE
;
4256 if (deal_with_floor_trap
) {
4258 You("cannot deal with %s while trapped%s!", the_trap
,
4259 (x
== u
.ux
&& y
== u
.uy
) ? " in it" : "");
4262 if ((mtmp
= m_at(x
, y
)) != 0
4263 && (mtmp
->m_ap_type
== M_AP_FURNITURE
4264 || mtmp
->m_ap_type
== M_AP_OBJECT
)) {
4265 stumble_onto_mimic(mtmp
);
4268 switch (ttmp
->ttyp
) {
4271 return disarm_holdingtrap(ttmp
);
4273 return disarm_landmine(ttmp
);
4275 return disarm_squeaky_board(ttmp
);
4277 return disarm_shooting_trap(ttmp
, DART
);
4279 return disarm_shooting_trap(ttmp
, ARROW
);
4283 You("are already on the edge of the pit.");
4287 pline("Try filling the pit instead.");
4290 return help_monster_out(mtmp
, ttmp
);
4292 You("cannot disable %s trap.", !here
? "that" : "this");
4299 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4301 (void) safe_qbuf(qbuf
, "There is ",
4302 " here. Check it for traps?", otmp
,
4303 doname
, ansimpleoname
, "a box");
4304 switch (ynq(qbuf
)) {
4312 && (force
|| (!confused
4313 && rn2(MAXULEV
+ 1 - u
.ulevel
) < 10)))
4314 || (!force
&& confused
&& !rn2(3))) {
4315 You("find a trap on %s!", the(xname(otmp
)));
4317 exercise(A_WIS
, TRUE
);
4319 switch (ynq("Disarm it?")) {
4323 trap_skipped
= TRUE
;
4327 if (otmp
->otrapped
) {
4328 exercise(A_DEX
, TRUE
);
4329 ch
= ACURR(A_DEX
) + u
.ulevel
;
4330 if (Role_if(PM_ROGUE
))
4332 if (!force
&& (confused
|| Fumbling
4333 || rnd(75 + level_difficulty() / 2)
4335 (void) chest_trap(otmp
, FINGER
, TRUE
);
4341 pline("That %s was not trapped.", xname(otmp
));
4344 You("find no traps on %s.", the(xname(otmp
)));
4349 You(trap_skipped
? "find no other traps here."
4350 : "know of no traps here.");
4354 if (stumble_on_door_mimic(x
, y
))
4357 } /* deal_with_floor_trap */
4358 /* doors can be manipulated even while levitating/unskilled riding */
4360 if (!IS_DOOR(levl
[x
][y
].typ
)) {
4362 You("know of no traps there.");
4366 switch (levl
[x
][y
].doormask
) {
4368 You("%s no door there.", Blind
? "feel" : "see");
4371 pline("This door is safely open.");
4374 pline("This door is broken.");
4378 if ((levl
[x
][y
].doormask
& D_TRAPPED
4379 && (force
|| (!confused
&& rn2(MAXULEV
- u
.ulevel
+ 11) < 10)))
4380 || (!force
&& confused
&& !rn2(3))) {
4381 You("find a trap on the door!");
4382 exercise(A_WIS
, TRUE
);
4383 if (ynq("Disarm it?") != 'y')
4385 if (levl
[x
][y
].doormask
& D_TRAPPED
) {
4386 ch
= 15 + (Role_if(PM_ROGUE
) ? u
.ulevel
* 3 : u
.ulevel
);
4387 exercise(A_DEX
, TRUE
);
4388 if (!force
&& (confused
|| Fumbling
4389 || rnd(75 + level_difficulty() / 2) > ch
)) {
4391 b_trapped("door", FINGER
);
4392 levl
[x
][y
].doormask
= D_NODOOR
;
4393 unblock_point(x
, y
);
4395 /* (probably ought to charge for this damage...) */
4396 if (*in_rooms(x
, y
, SHOPBASE
))
4397 add_damage(x
, y
, 0L);
4400 levl
[x
][y
].doormask
&= ~D_TRAPPED
;
4403 pline("This door was not trapped.");
4406 You("find no traps on the door.");
4411 /* for magic unlocking; returns true if targetted monster (which might
4412 be hero) gets untrapped; the trap remains intact */
4414 openholdingtrap(mon
, noticed
)
4416 boolean
*noticed
; /* set to true iff hero notices the effect; */
4417 { /* otherwise left with its previous value intact */
4420 const char *trapdescr
, *which
;
4421 boolean ishero
= (mon
== &youmonst
);
4423 if (mon
== u
.usteed
)
4425 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4426 /* if no trap here or it's not a holding trap, we're done */
4427 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4430 trapdescr
= defsyms
[trap_to_defsym(t
->ttyp
)].explanation
;
4431 which
= t
->tseen
? the_your
[t
->madeby_u
]
4432 : index(vowels
, *trapdescr
) ? "an" : "a";
4437 u
.utrap
= 0; /* released regardless of type */
4439 /* give message only if trap was the expected type */
4440 if (u
.utraptype
== TT_BEARTRAP
|| u
.utraptype
== TT_WEB
) {
4442 Sprintf(buf
, "%s is", noit_Monnam(u
.usteed
));
4444 Strcpy(buf
, "You are");
4445 pline("%s released from %s %s.", buf
, which
, trapdescr
);
4451 if (canspotmon(mon
)) {
4453 pline("%s is released from %s %s.", Monnam(mon
), which
,
4455 } else if (cansee(t
->tx
, t
->ty
) && t
->tseen
) {
4458 pline("%s is released from %s %s.", Something
, which
,
4460 else /* BEAR_TRAP */
4461 pline("%s %s opens.", upstart(strcpy(buf
, which
)), trapdescr
);
4463 /* might pacify monster if adjacent */
4464 if (rn2(2) && distu(mon
->mx
, mon
->my
) <= 2)
4465 reward_untrap(t
, mon
);
4470 /* for magic locking; returns true if targetted monster (which might
4471 be hero) gets hit by a trap (might avoid actually becoming trapped) */
4473 closeholdingtrap(mon
, noticed
)
4475 boolean
*noticed
; /* set to true iff hero notices the effect; */
4476 { /* otherwise left with its previous value intact */
4478 unsigned dotrapflags
;
4479 boolean ishero
= (mon
== &youmonst
), result
;
4481 if (mon
== u
.usteed
)
4483 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4484 /* if no trap here or it's not a holding trap, we're done */
4485 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4490 return FALSE
; /* already trapped */
4492 dotrapflags
= FORCETRAP
;
4493 /* dotrap calls mintrap when mounted hero encounters a web */
4495 dotrapflags
|= NOWEBMSG
;
4497 dotrap(t
, dotrapflags
);
4499 result
= (u
.utrap
!= 0);
4502 return FALSE
; /* already trapped */
4503 /* you notice it if you see the trap close/tremble/whatever
4504 or if you sense the monster who becomes trapped */
4505 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4507 result
= (mintrap(mon
) != 0);
4513 /* for magic unlocking; returns true if targetted monster (which might
4514 be hero) gets hit by a trap (target might avoid its effect) */
4516 openfallingtrap(mon
, trapdoor_only
, noticed
)
4518 boolean trapdoor_only
;
4519 boolean
*noticed
; /* set to true iff hero notices the effect; */
4520 { /* otherwise left with its previous value intact */
4522 boolean ishero
= (mon
== &youmonst
), result
;
4524 if (mon
== u
.usteed
)
4526 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4527 /* if no trap here or it's not a falling trap, we're done
4528 (note: falling rock traps have a trapdoor in the ceiling) */
4529 if (!t
|| ((t
->ttyp
!= TRAPDOOR
&& t
->ttyp
!= ROCKTRAP
)
4530 && (trapdoor_only
|| (t
->ttyp
!= HOLE
&& t
->ttyp
!= PIT
4531 && t
->ttyp
!= SPIKED_PIT
))))
4536 return FALSE
; /* already trapped */
4538 dotrap(t
, FORCETRAP
);
4539 result
= (u
.utrap
!= 0);
4542 return FALSE
; /* already trapped */
4543 /* you notice it if you see the trap close/tremble/whatever
4544 or if you sense the monster who becomes trapped */
4545 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4546 /* monster will be angered; mintrap doesn't handle that */
4549 result
= (mintrap(mon
) != 0);
4551 /* mon might now be on the migrating monsters list */
4556 /* only called when the player is doing something to the chest directly */
4558 chest_trap(obj
, bodypart
, disarm
)
4559 register struct obj
*obj
;
4560 register int bodypart
;
4563 register struct obj
*otmp
= obj
, *otmp2
;
4568 if (get_obj_location(obj
, &cc
.x
, &cc
.y
, 0)) /* might be carried */
4569 obj
->ox
= cc
.x
, obj
->oy
= cc
.y
;
4571 otmp
->otrapped
= 0; /* trap is one-shot; clear flag first in case
4572 chest kills you and ends up in bones file */
4573 You(disarm
? "set it off!" : "trigger a trap!");
4574 display_nhwindow(WIN_MESSAGE
, FALSE
);
4575 if (Luck
> -13 && rn2(13 + Luck
) > 7) { /* saved by luck */
4576 /* trap went off, but good luck prevents damage */
4580 msg
= "explosive charge is a dud";
4584 msg
= "electric charge is grounded";
4588 msg
= "flame fizzles out";
4593 msg
= "poisoned needle misses";
4599 msg
= "gas cloud blows away";
4602 impossible("chest disarm bug");
4607 pline("But luckily the %s!", msg
);
4609 switch (rn2(20) ? ((Luck
>= 13) ? 0 : rn2(13 - Luck
)) : rn2(26)) {
4615 struct monst
*shkp
= 0;
4617 boolean costly
, insider
;
4618 register xchar ox
= obj
->ox
, oy
= obj
->oy
;
4620 /* the obj location need not be that of player */
4621 costly
= (costly_spot(ox
, oy
)
4622 && (shkp
= shop_keeper(*in_rooms(ox
, oy
, SHOPBASE
)))
4623 != (struct monst
*) 0);
4624 insider
= (*u
.ushops
&& inside_shop(u
.ux
, u
.uy
)
4625 && *in_rooms(ox
, oy
, SHOPBASE
) == *u
.ushops
);
4627 pline("%s!", Tobjnam(obj
, "explode"));
4628 Sprintf(buf
, "exploding %s", xname(obj
));
4631 loss
+= stolen_value(obj
, ox
, oy
, (boolean
) shkp
->mpeaceful
,
4633 delete_contents(obj
);
4634 /* unpunish() in advance if either ball or chain (or both)
4635 is going to be destroyed */
4636 if (Punished
&& ((uchain
->ox
== u
.ux
&& uchain
->oy
== u
.uy
)
4637 || (uball
->where
== OBJ_FLOOR
4638 && uball
->ox
== u
.ux
&& uball
->oy
== u
.uy
)))
4641 for (otmp
= level
.objects
[u
.ux
][u
.uy
]; otmp
; otmp
= otmp2
) {
4642 otmp2
= otmp
->nexthere
;
4644 loss
+= stolen_value(otmp
, otmp
->ox
, otmp
->oy
,
4645 (boolean
) shkp
->mpeaceful
, TRUE
);
4649 losehp(Maybe_Half_Phys(d(6, 6)), buf
, KILLED_BY_AN
);
4650 exercise(A_STR
, FALSE
);
4651 if (costly
&& loss
) {
4653 You("owe %ld %s for objects destroyed.", loss
,
4656 You("caused %ld %s worth of damage!", loss
,
4658 make_angry_shk(shkp
, ox
, oy
);
4667 pline("A cloud of noxious gas billows from %s.", the(xname(obj
)));
4668 poisoned("gas cloud", A_STR
, "cloud of poison gas", 15, FALSE
);
4669 exercise(A_CON
, FALSE
);
4675 You_feel("a needle prick your %s.", body_part(bodypart
));
4676 poisoned("needle", A_CON
, "poisoned needle", 10, FALSE
);
4677 exercise(A_CON
, FALSE
);
4690 You("are jolted by a surge of electricity!");
4691 if (Shock_resistance
) {
4692 shieldeff(u
.ux
, u
.uy
);
4693 You("don't seem to be affected.");
4697 destroy_item(RING_CLASS
, AD_ELEC
);
4698 destroy_item(WAND_CLASS
, AD_ELEC
);
4700 losehp(dmg
, "electric shock", KILLED_BY_AN
);
4707 pline("Suddenly you are frozen in place!");
4709 multi_reason
= "frozen by a trap";
4710 exercise(A_DEX
, FALSE
);
4711 nomovemsg
= You_can_move_again
;
4713 You("momentarily stiffen.");
4718 pline("A cloud of %s gas billows from %s.",
4719 Blind
? blindgas
[rn2(SIZE(blindgas
))] : rndcolor(),
4723 pline("What a groovy feeling!");
4725 You("%s%s...", stagger(youmonst
.data
, "stagger"),
4726 Halluc_resistance
? ""
4727 : Blind
? " and get dizzy"
4728 : " and your vision blurs");
4730 make_stunned((HStun
& TIMEOUT
) + (long) rn1(7, 16), FALSE
);
4731 (void) make_hallucinated(
4732 (HHallucination
& TIMEOUT
) + (long) rn1(5, 16), FALSE
, 0L);
4735 impossible("bad chest trap");
4738 bot(); /* to get immediate botl re-display */
4748 register struct trap
*trap
= ftrap
;
4751 if (trap
->tx
== x
&& trap
->ty
== y
)
4755 return (struct trap
*) 0;
4760 register struct trap
*trap
;
4762 register struct trap
*ttmp
;
4764 clear_conjoined_pits(trap
);
4765 if (trap
== ftrap
) {
4766 ftrap
= ftrap
->ntrap
;
4768 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
)
4769 if (ttmp
->ntrap
== trap
)
4772 panic("deltrap: no preceding trap!");
4773 ttmp
->ntrap
= trap
->ntrap
;
4775 if (Sokoban
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== HOLE
))
4776 maybe_finish_sokoban();
4781 conjoined_pits(trap2
, trap1
, u_entering_trap2
)
4782 struct trap
*trap2
, *trap1
;
4783 boolean u_entering_trap2
;
4785 int dx
, dy
, diridx
, adjidx
;
4787 if (!trap1
|| !trap2
)
4789 if (!isok(trap2
->tx
, trap2
->ty
) || !isok(trap1
->tx
, trap1
->ty
)
4790 || !(trap2
->ttyp
== PIT
|| trap2
->ttyp
== SPIKED_PIT
)
4791 || !(trap1
->ttyp
== PIT
|| trap1
->ttyp
== SPIKED_PIT
)
4792 || (u_entering_trap2
&& !(u
.utrap
&& u
.utraptype
== TT_PIT
)))
4794 dx
= sgn(trap2
->tx
- trap1
->tx
);
4795 dy
= sgn(trap2
->ty
- trap1
->ty
);
4796 for (diridx
= 0; diridx
< 8; diridx
++)
4797 if (xdir
[diridx
] == dx
&& ydir
[diridx
] == dy
)
4799 /* diridx is valid if < 8 */
4801 adjidx
= (diridx
+ 4) % 8;
4802 if ((trap1
->conjoined
& (1 << diridx
))
4803 && (trap2
->conjoined
& (1 << adjidx
)))
4810 clear_conjoined_pits(trap
)
4813 int diridx
, adjidx
, x
, y
;
4816 if (trap
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
4817 for (diridx
= 0; diridx
< 8; ++diridx
) {
4818 if (trap
->conjoined
& (1 << diridx
)) {
4819 x
= trap
->tx
+ xdir
[diridx
];
4820 y
= trap
->ty
+ ydir
[diridx
];
4822 && (t
= t_at(x
, y
)) != 0
4823 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4824 adjidx
= (diridx
+ 4) % 8;
4825 t
->conjoined
&= ~(1 << adjidx
);
4827 trap
->conjoined
&= ~(1 << diridx
);
4835 * Mark all neighboring pits as conjoined pits.
4836 * (currently not called from anywhere)
4839 join_adjacent_pits(trap
)
4847 for (diridx
= 0; diridx
< 8; ++diridx
) {
4848 x
= trap
->tx
+ xdir
[diridx
];
4849 y
= trap
->ty
+ ydir
[diridx
];
4851 if ((t
= t_at(x
, y
)) != 0
4852 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4853 trap
->conjoined
|= (1 << diridx
);
4854 join_adjacent_pits(t
);
4856 trap
->conjoined
&= ~(1 << diridx
);
4863 * Returns TRUE if you escaped a pit and are standing on the precipice.
4866 uteetering_at_seen_pit(trap
)
4869 if (trap
&& trap
->tseen
&& (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
4870 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
))
4876 /* Destroy a trap that emanates from the floor. */
4879 register struct trap
*ttmp
;
4881 /* some of these are arbitrary -dlc */
4882 if (ttmp
&& ((ttmp
->ttyp
== SQKY_BOARD
) || (ttmp
->ttyp
== BEAR_TRAP
)
4883 || (ttmp
->ttyp
== LANDMINE
) || (ttmp
->ttyp
== FIRE_TRAP
)
4884 || (ttmp
->ttyp
== PIT
) || (ttmp
->ttyp
== SPIKED_PIT
)
4885 || (ttmp
->ttyp
== HOLE
) || (ttmp
->ttyp
== TRAPDOOR
)
4886 || (ttmp
->ttyp
== TELEP_TRAP
) || (ttmp
->ttyp
== LEVEL_TELEP
)
4887 || (ttmp
->ttyp
== WEB
) || (ttmp
->ttyp
== MAGIC_TRAP
)
4888 || (ttmp
->ttyp
== ANTI_MAGIC
))) {
4889 register struct monst
*mtmp
;
4891 if (ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
) {
4894 } else if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
4903 /* used for doors (also tins). can be used for anything else that opens. */
4905 b_trapped(item
, bodypart
)
4909 int lvl
= level_difficulty(),
4910 dmg
= rnd(5 + (lvl
< 5 ? lvl
: 2 + lvl
/ 2));
4912 pline("KABOOM!! %s was booby-trapped!", The(item
));
4914 losehp(Maybe_Half_Phys(dmg
), "explosion", KILLED_BY_AN
);
4915 exercise(A_STR
, FALSE
);
4917 exercise(A_CON
, FALSE
);
4918 make_stunned((HStun
& TIMEOUT
) + (long) dmg
, TRUE
);
4921 /* Monster is hit by trap. */
4922 /* Note: doesn't work if both obj and d_override are null */
4924 thitm(tlev
, mon
, obj
, d_override
, nocorpse
)
4932 boolean trapkilled
= FALSE
;
4937 strike
= (find_mac(mon
) + tlev
+ obj
->spe
<= rnd(20));
4939 strike
= (find_mac(mon
) + tlev
<= rnd(20));
4941 /* Actually more accurate than thitu, which doesn't take
4942 * obj->spe into account.
4945 if (obj
&& cansee(mon
->mx
, mon
->my
))
4946 pline("%s is almost hit by %s!", Monnam(mon
), doname(obj
));
4950 if (obj
&& cansee(mon
->mx
, mon
->my
))
4951 pline("%s is hit by %s!", Monnam(mon
), doname(obj
));
4955 dam
= dmgval(obj
, mon
);
4960 if (mon
->mhp
<= 0) {
4961 int xx
= mon
->mx
, yy
= mon
->my
;
4963 monkilled(mon
, "", nocorpse
? -AD_RBRE
: AD_PHYS
);
4964 if (mon
->mhp
<= 0) {
4970 if (obj
&& (!strike
|| d_override
)) {
4971 place_object(obj
, mon
->mx
, mon
->my
);
4985 return (boolean
) (u
.usleep
4987 && (!strncmp(nomovemsg
, "You awake", 9)
4988 || !strncmp(nomovemsg
, "You regain con", 14)
4989 || !strncmp(nomovemsg
, "You are consci", 14))));
4992 static const char lava_killer
[] = "molten lava";
4997 register struct obj
*obj
, *obj2
;
4998 int dmg
= d(6, 6); /* only applicable for water walking */
4999 boolean usurvive
, boil_away
;
5002 if (likes_lava(youmonst
.data
))
5005 usurvive
= Fire_resistance
|| (Wwalking
&& dmg
< u
.uhp
);
5007 * A timely interrupt might manage to salvage your life
5008 * but not your gear. For scrolls and potions this
5009 * will destroy whole stacks, where fire resistant hero
5010 * survivor only loses partial stacks via destroy_item().
5012 * Flag items to be destroyed before any messages so
5013 * that player causing hangup at --More-- won't get an
5014 * emergency save file created before item destruction.
5017 for (obj
= invent
; obj
; obj
= obj
->nobj
)
5018 if ((is_organic(obj
) || obj
->oclass
== POTION_CLASS
)
5019 && !obj
->oerodeproof
5020 && objects
[obj
->otyp
].oc_oprop
!= FIRE_RES
5021 && obj
->otyp
!= SCR_FIRE
&& obj
->otyp
!= SPE_FIREBALL
5022 && !obj_resists(obj
, 0, 0)) /* for invocation items */
5025 /* Check whether we should burn away boots *first* so we know whether to
5026 * make the player sink into the lava. Assumption: water walking only
5029 if (uarmf
&& is_organic(uarmf
) && !uarmf
->oerodeproof
) {
5031 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5032 iflags
.in_lava_effects
++; /* (see above) */
5035 iflags
.in_lava_effects
--;
5038 if (!Fire_resistance
) {
5040 pline_The("%s here burns you!", hliquid("lava"));
5042 losehp(dmg
, lava_killer
, KILLED_BY
); /* lava damage */
5046 You("fall into the %s!", hliquid("lava"));
5048 usurvive
= Lifesaved
|| discover
;
5052 /* prevent remove_worn_item() -> Boots_off(WATER_WALKING_BOOTS) ->
5053 spoteffects() -> lava_effects() recursion which would
5054 successfully delete (via useupall) the no-longer-worn boots;
5055 once recursive call returned, we would try to delete them again
5056 here in the outer call (and access stale memory, probably panic) */
5057 iflags
.in_lava_effects
++;
5059 for (obj
= invent
; obj
; obj
= obj2
) {
5061 /* above, we set in_use for objects which are to be destroyed */
5062 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
&& !Blind
) {
5064 pline("%s glows a strange %s, but remains intact.",
5065 The(xname(obj
)), hcolor("dark red"));
5066 } else if (obj
->in_use
) {
5067 if (obj
->owornmask
) {
5069 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5070 remove_worn_item(obj
, TRUE
);
5076 iflags
.in_lava_effects
--;
5079 boil_away
= (u
.umonnum
== PM_WATER_ELEMENTAL
5080 || u
.umonnum
== PM_STEAM_VORTEX
5081 || u
.umonnum
== PM_FOG_CLOUD
);
5084 /* killer format and name are reconstructed every iteration
5085 because lifesaving resets them */
5086 killer
.format
= KILLED_BY
;
5087 Strcpy(killer
.name
, lava_killer
);
5088 You("%s...", boil_away
? "boil away" : "burn to a crisp");
5090 if (safe_teleds(TRUE
))
5091 break; /* successful life-save */
5092 /* nowhere safe to land; repeat burning loop */
5093 pline("You're still burning.");
5095 You("find yourself back on solid %s.", surface(u
.ux
, u
.uy
));
5097 } else if (!Wwalking
&& (!u
.utrap
|| u
.utraptype
!= TT_LAVA
)) {
5098 boil_away
= !Fire_resistance
;
5099 /* if not fire resistant, sink_into_lava() will quickly be fatal;
5100 hero needs to escape immediately */
5101 u
.utrap
= rn1(4, 4) + ((boil_away
? 2 : rn1(4, 12)) << 8);
5102 u
.utraptype
= TT_LAVA
;
5103 You("sink into the %s%s!", hliquid("lava"),
5105 ? ", but it only burns slightly"
5106 : " and are about to be immolated");
5108 losehp(!boil_away
? 1 : (u
.uhp
/ 2), lava_killer
,
5109 KILLED_BY
); /* lava damage */
5113 destroy_item(SCROLL_CLASS
, AD_FIRE
);
5114 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
5115 destroy_item(POTION_CLASS
, AD_FIRE
);
5119 /* called each turn when trapped in lava */
5123 static const char sink_deeper
[] = "You sink deeper into the lava.";
5125 if (!u
.utrap
|| u
.utraptype
!= TT_LAVA
) {
5126 ; /* do nothing; this shouldn't happen */
5127 } else if (!is_lava(u
.ux
, u
.uy
)) {
5128 u
.utrap
= 0; /* this shouldn't happen either */
5129 } else if (!u
.uinvulnerable
) {
5130 /* ordinarily we'd have to be fire resistant to survive long
5131 enough to become stuck in lava, but it can happen without
5132 resistance if water walking boots allow survival and then
5133 get burned up; u.utrap time will be quite short in that case */
5134 if (!Fire_resistance
)
5135 u
.uhp
= (u
.uhp
+ 2) / 3;
5137 u
.utrap
-= (1 << 8);
5138 if (u
.utrap
< (1 << 8)) {
5139 killer
.format
= KILLED_BY
;
5140 Strcpy(killer
.name
, "molten lava");
5141 You("sink below the surface and die.");
5142 burn_away_slime(); /* add insult to injury? */
5144 /* can only get here via life-saving; try to get away from lava */
5146 (void) safe_teleds(TRUE
);
5147 } else if (!u
.umoved
) {
5148 /* can't fully turn into slime while in lava, but might not
5149 have it be burned away until you've come awfully close */
5150 if (Slimed
&& rnd(10 - 1) >= (int) (Slimed
& TIMEOUT
)) {
5161 /* called when something has been done (breaking a boulder, for instance)
5162 which entails a luck penalty if performed on a sokoban level */
5168 /* TODO: issue some feedback so that player can learn that whatever
5169 he/she just did is a naughty thing to do in sokoban and should
5170 probably be avoided in future....
5171 Caveat: doing this might introduce message sequencing issues,
5172 depending upon feedback during the various actions which trigger
5173 Sokoban luck penalties. */
5177 /* called when a trap has been deleted or had its ttyp replaced */
5179 maybe_finish_sokoban()
5183 if (Sokoban
&& !in_mklev
) {
5184 /* scan all remaining traps, ignoring any created by the hero;
5185 if this level has no more pits or holes, the current sokoban
5186 puzzle has been solved */
5187 for (t
= ftrap
; t
; t
= t
->ntrap
) {
5190 if (t
->ttyp
== PIT
|| t
->ttyp
== HOLE
)
5194 /* we've passed the last trap without finding a pit or hole;
5195 clear the sokoban_rules flag so that luck penalties for
5196 things like breaking boulders or jumping will no longer
5197 be given, and restrictions on diagonal moves are lifted */
5198 Sokoban
= 0; /* clear level.flags.sokoban_rules */
5199 /* TODO: give some feedback about solving the sokoban puzzle
5200 (perhaps say "congratulations" in Japanese?) */