1 /* NetHack 3.6 trap.c $NHDT-Date: 1464138044 2016/05/25 01:00:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.272 $ */
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 adj_pit
= conjoined_pits(trap
, t_at(u
.ux0
, u
.uy0
), TRUE
);
855 int steed_article
= ARTICLE_THE
;
859 /* KMH -- You can't escape the Sokoban level traps */
860 if (Sokoban
&& (ttype
== PIT
|| ttype
== SPIKED_PIT
861 || ttype
== HOLE
|| ttype
== TRAPDOOR
)) {
862 /* The "air currents" message is still appropriate -- even when
863 * the hero isn't flying or levitating -- because it conveys the
864 * reason why the player cannot escape the trap with a dexterity
865 * check, clinging to the ceiling, etc.
867 pline("Air currents pull you down into %s %s!",
868 a_your
[trap
->madeby_u
],
869 defsyms
[trap_to_defsym(ttype
)].explanation
);
870 /* then proceed to normal trap effect */
871 } else if (already_seen
&& !forcetrap
) {
872 if ((Levitation
|| (Flying
&& !plunged
))
873 && (ttype
== PIT
|| ttype
== SPIKED_PIT
|| ttype
== HOLE
874 || ttype
== BEAR_TRAP
)) {
875 You("%s over %s %s.", Levitation
? "float" : "fly",
876 a_your
[trap
->madeby_u
],
877 defsyms
[trap_to_defsym(ttype
)].explanation
);
880 if (!Fumbling
&& ttype
!= MAGIC_PORTAL
&& ttype
!= VIBRATING_SQUARE
881 && ttype
!= ANTI_MAGIC
&& !forcebungle
&& !plunged
&& !adj_pit
882 && (!rn2(5) || ((ttype
== PIT
|| ttype
== SPIKED_PIT
)
883 && is_clinger(youmonst
.data
)))) {
884 You("escape %s %s.", (ttype
== ARROW_TRAP
&& !trap
->madeby_u
)
886 : a_your
[trap
->madeby_u
],
887 defsyms
[trap_to_defsym(ttype
)].explanation
);
893 u
.usteed
->mtrapseen
|= (1 << (ttype
- 1));
894 /* suppress article in various steed messages when using its
895 name (which won't occur when hallucinating) */
896 if (has_mname(u
.usteed
) && !Hallucination
)
897 steed_article
= ARTICLE_NONE
;
902 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
903 You_hear("a loud click!");
910 pline("An arrow shoots out at you!");
911 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
913 otmp
->owt
= weight(otmp
);
915 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
917 } else if (thitu(8, dmgval(otmp
, &youmonst
), otmp
, "arrow")) {
918 obfree(otmp
, (struct obj
*) 0);
920 place_object(otmp
, u
.ux
, u
.uy
);
929 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
930 You_hear("a soft click.");
937 pline("A little dart shoots out at you!");
938 otmp
= mksobj(DART
, TRUE
, FALSE
);
940 otmp
->owt
= weight(otmp
);
943 oldumort
= u
.umortality
;
944 if (u
.usteed
&& !rn2(2) && steedintrap(trap
, otmp
)) { /* nothing */
946 } else if (thitu(7, dmgval(otmp
, &youmonst
), otmp
, "little dart")) {
948 poisoned("dart", A_CON
, "little dart",
949 /* if damage triggered life-saving,
950 poison is limited to attrib loss */
951 (u
.umortality
> oldumort
) ? 0 : 10, TRUE
);
952 obfree(otmp
, (struct obj
*) 0);
954 place_object(otmp
, u
.ux
, u
.uy
);
963 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
964 pline("A trap door in %s opens, but nothing falls out!",
965 the(ceiling(u
.ux
, u
.uy
)));
969 int dmg
= d(2, 6); /* should be std ROCK dmg? */
973 otmp
= mksobj_at(ROCK
, u
.ux
, u
.uy
, TRUE
, FALSE
);
975 otmp
->owt
= weight(otmp
);
977 pline("A trap door in %s opens and %s falls on your %s!",
978 the(ceiling(u
.ux
, u
.uy
)), an(xname(otmp
)), body_part(HEAD
));
981 if (is_metallic(uarmh
)) {
982 pline("Fortunately, you are wearing a hard helmet.");
984 } else if (flags
.verbose
) {
985 pline("%s does not protect you.", Yname2(uarmh
));
992 newsym(u
.ux
, u
.uy
); /* map the rock */
994 losehp(Maybe_Half_Phys(dmg
), "falling rock", KILLED_BY_AN
);
995 exercise(A_STR
, FALSE
);
999 case SQKY_BOARD
: /* stepped on a squeaky board */
1000 if ((Levitation
|| Flying
) && !forcetrap
) {
1004 You("notice a crease in the linoleum.");
1006 You("notice a loose board below you.");
1010 pline("A board beneath you %s%s%s.",
1011 Deaf
? "vibrates" : "squeaks ",
1012 Deaf
? "" : trapnote(trap
, 0), Deaf
? "" : " loudly");
1020 if ((Levitation
|| Flying
) && !forcetrap
)
1023 if (amorphous(youmonst
.data
) || is_whirly(youmonst
.data
)
1024 || unsolid(youmonst
.data
)) {
1025 pline("%s bear trap closes harmlessly through you.",
1026 A_Your
[trap
->madeby_u
]);
1029 if (!u
.usteed
&& youmonst
.data
->msize
<= MZ_SMALL
) {
1030 pline("%s bear trap closes harmlessly over you.",
1031 A_Your
[trap
->madeby_u
]);
1034 u
.utrap
= rn1(4, 4);
1035 u
.utraptype
= TT_BEARTRAP
;
1037 pline("%s bear trap closes on %s %s!", A_Your
[trap
->madeby_u
],
1038 s_suffix(mon_nam(u
.usteed
)), mbodypart(u
.usteed
, FOOT
));
1039 if (thitm(0, u
.usteed
, (struct obj
*) 0, dmg
, FALSE
))
1040 u
.utrap
= 0; /* steed died, hero not trapped */
1042 pline("%s bear trap closes on your %s!", A_Your
[trap
->madeby_u
],
1044 set_wounded_legs(rn2(2) ? RIGHT_SIDE
: LEFT_SIDE
, rn1(10, 10));
1045 if (u
.umonnum
== PM_OWLBEAR
|| u
.umonnum
== PM_BUGBEAR
)
1046 You("howl in anger!");
1047 losehp(Maybe_Half_Phys(dmg
), "bear trap", KILLED_BY_AN
);
1049 exercise(A_DEX
, FALSE
);
1055 if (Sleep_resistance
|| breathless(youmonst
.data
)) {
1056 You("are enveloped in a cloud of gas!");
1058 pline("A cloud of gas puts you to sleep!");
1059 fall_asleep(-rnd(25), TRUE
);
1061 (void) steedintrap(trap
, (struct obj
*) 0);
1067 /* Unlike monsters, traps cannot aim their rust attacks at
1068 * you, so instead of looping through and taking either the
1069 * first rustable one or the body, we take whatever we get,
1070 * even if it is not rustable.
1074 pline("%s you on the %s!", A_gush_of_water_hits
, body_part(HEAD
));
1075 (void) water_damage(uarmh
, helm_simple_name(uarmh
), TRUE
);
1078 pline("%s your left %s!", A_gush_of_water_hits
, body_part(ARM
));
1079 if (water_damage(uarms
, "shield", TRUE
) != ER_NOTHING
)
1081 if (u
.twoweap
|| (uwep
&& bimanual(uwep
)))
1082 (void) water_damage(u
.twoweap
? uswapwep
: uwep
, 0, TRUE
);
1084 (void) water_damage(uarmg
, "gauntlets", TRUE
);
1085 /* Not "metal gauntlets" since it gets called
1086 * even if it's leather for the message
1090 pline("%s your right %s!", A_gush_of_water_hits
, body_part(ARM
));
1091 (void) water_damage(uwep
, 0, TRUE
);
1094 pline("%s you!", A_gush_of_water_hits
);
1095 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1096 if (otmp
->lamplit
&& otmp
!= uwep
1097 && (otmp
!= uswapwep
|| !u
.twoweap
))
1098 (void) snuff_lit(otmp
);
1100 (void) water_damage(uarmc
, cloak_simple_name(uarmc
), TRUE
);
1102 (void) water_damage(uarm
, "armor", TRUE
);
1104 (void) water_damage(uarmu
, "shirt", TRUE
);
1108 if (u
.umonnum
== PM_IRON_GOLEM
) {
1111 pline("%s you!", A_gush_of_water_hits
);
1112 You("are covered with rust!");
1113 losehp(Maybe_Half_Phys(dam
), "rusting away", KILLED_BY
);
1114 } else if (u
.umonnum
== PM_GREMLIN
&& rn2(3)) {
1115 pline("%s you!", A_gush_of_water_hits
);
1116 (void) split_mon(&youmonst
, (struct monst
*) 0);
1123 dofiretrap((struct obj
*) 0);
1128 /* KMH -- You can't escape the Sokoban level traps */
1129 if (!Sokoban
&& (Levitation
|| (Flying
&& !plunged
)))
1132 if (!Sokoban
&& is_clinger(youmonst
.data
) && !plunged
) {
1134 You_see("%s %spit below you.", a_your
[trap
->madeby_u
],
1135 ttype
== SPIKED_PIT
? "spiked " : "");
1137 pline("%s pit %sopens up under you!", A_Your
[trap
->madeby_u
],
1138 ttype
== SPIKED_PIT
? "full of spikes " : "");
1139 You("don't fall in!");
1144 char verbbuf
[BUFSZ
];
1147 if ((trflags
& RECURSIVETRAP
) != 0)
1148 Sprintf(verbbuf
, "and %s fall",
1149 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1150 SUPPRESS_SADDLE
, FALSE
));
1152 Sprintf(verbbuf
, "lead %s",
1153 x_monnam(u
.usteed
, steed_article
, "poor",
1154 SUPPRESS_SADDLE
, FALSE
));
1155 } else if (adj_pit
) {
1156 You("move into an adjacent pit.");
1159 !plunged
? "fall" : (Flying
? "dive" : "plunge"));
1160 You("%s into %s pit!", verbbuf
, a_your
[trap
->madeby_u
]);
1163 /* wumpus reference */
1164 if (Role_if(PM_RANGER
) && !trap
->madeby_u
&& !trap
->once
1165 && In_quest(&u
.uz
) && Is_qlocate(&u
.uz
)) {
1166 pline("Fortunately it has a bottom after all...");
1168 } else if (u
.umonnum
== PM_PIT_VIPER
|| u
.umonnum
== PM_PIT_FIEND
) {
1169 pline("How pitiful. Isn't that the pits?");
1171 if (ttype
== SPIKED_PIT
) {
1172 const char *predicament
= "on a set of sharp iron spikes";
1176 upstart(x_monnam(u
.usteed
, steed_article
, "poor",
1177 SUPPRESS_SADDLE
, FALSE
)),
1178 adj_pit
? "steps" : "lands", predicament
);
1180 You("%s %s!", adj_pit
? "step" : "land", predicament
);
1182 u
.utrap
= rn1(6, 2);
1183 u
.utraptype
= TT_PIT
;
1184 if (!steedintrap(trap
, (struct obj
*) 0)) {
1185 if (ttype
== SPIKED_PIT
) {
1186 oldumort
= u
.umortality
;
1187 losehp(Maybe_Half_Phys(rnd(adj_pit
? 6 : 10)),
1189 ? "deliberately plunged into a pit of iron spikes"
1190 : adj_pit
? "stepped into a pit of iron spikes"
1191 : "fell into a pit of iron spikes",
1194 poisoned("spikes", A_STR
,
1195 adj_pit
? "stepping on poison spikes"
1196 : "fall onto poison spikes",
1197 /* if damage triggered life-saving,
1198 poison is limited to attrib loss */
1199 (u
.umortality
> oldumort
) ? 0 : 8, FALSE
);
1201 /* plunging flyers take spike damage but not pit damage */
1203 && !(plunged
&& (Flying
|| is_clinger(youmonst
.data
))))
1204 losehp(Maybe_Half_Phys(rnd(6)),
1205 plunged
? "deliberately plunged into a pit"
1206 : "fell into a pit",
1209 if (Punished
&& !carried(uball
)) {
1215 selftouch("Falling, you");
1216 vision_full_recalc
= 1; /* vision limits change */
1217 exercise(A_STR
, FALSE
);
1218 exercise(A_DEX
, FALSE
);
1224 if (!Can_fall_thru(&u
.uz
)) {
1225 seetrap(trap
); /* normally done in fall_through */
1226 impossible("dotrap: %ss cannot exist on this level.",
1227 defsyms
[trap_to_defsym(ttype
)].explanation
);
1228 break; /* don't activate it after all */
1240 level_tele_trap(trap
);
1243 case WEB
: /* Our luckless player has stumbled into a web. */
1245 if (mu_maybe_destroy_web(&youmonst
, webmsgok
, trap
))
1247 if (webmaker(youmonst
.data
)) {
1249 pline(trap
->madeby_u
? "You take a walk on your web."
1250 : "There is a spider web here.");
1254 char verbbuf
[BUFSZ
];
1257 Strcpy(verbbuf
, "are caught by");
1258 } else if (u
.usteed
) {
1259 Sprintf(verbbuf
, "lead %s into",
1260 x_monnam(u
.usteed
, steed_article
, "poor",
1261 SUPPRESS_SADDLE
, FALSE
));
1263 Sprintf(verbbuf
, "%s into",
1264 Levitation
? (const char *) "float"
1265 : locomotion(youmonst
.data
, "stumble"));
1267 You("%s %s spider web!", verbbuf
, a_your
[trap
->madeby_u
]);
1269 u
.utraptype
= TT_WEB
;
1271 /* Time stuck in the web depends on your/steed strength. */
1273 register int str
= ACURR(A_STR
);
1275 /* If mounted, the steed gets trapped. Use mintrap
1276 * to do all the work. If mtrapped is set as a result,
1277 * unset it and set utrap instead. In the case of a
1278 * strongmonst and mintrap said it's trapped, use a
1279 * short but non-zero trap time. Otherwise, monsters
1280 * have no specific strength, so use player strength.
1281 * This gets skipped for webmsgok, which implies that
1282 * the steed isn't a factor.
1284 if (u
.usteed
&& webmsgok
) {
1285 /* mtmp location might not be up to date */
1286 u
.usteed
->mx
= u
.ux
;
1287 u
.usteed
->my
= u
.uy
;
1289 /* mintrap currently does not return 2(died) for webs */
1290 if (mintrap(u
.usteed
)) {
1291 u
.usteed
->mtrapped
= 0;
1292 if (strongmonst(u
.usteed
->data
))
1298 webmsgok
= FALSE
; /* mintrap printed the messages */
1301 u
.utrap
= rn1(6, 6);
1303 u
.utrap
= rn1(6, 4);
1305 u
.utrap
= rn1(4, 4);
1307 u
.utrap
= rn1(4, 2);
1309 u
.utrap
= rn1(2, 2);
1317 You("tear through %s web!", a_your
[trap
->madeby_u
]);
1319 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1325 (void) activate_statue_trap(trap
, u
.ux
, u
.uy
, FALSE
);
1328 case MAGIC_TRAP
: /* A magic trap. */
1332 newsym(u
.ux
, u
.uy
); /* update position */
1333 You("are caught in a magical explosion!");
1334 losehp(rnd(10), "magical explosion", KILLED_BY_AN
);
1335 Your("body absorbs some of the magical energy!");
1336 u
.uen
= (u
.uenmax
+= 2);
1341 (void) steedintrap(trap
, (struct obj
*) 0);
1346 /* hero without magic resistance loses spell energy,
1347 hero with magic resistance takes damage instead;
1348 possibly non-intuitive but useful for play balance */
1350 drain_en(rnd(u
.ulevel
) + 1);
1352 int dmgval2
= rnd(4), hp
= Upolyd
? u
.mh
: u
.uhp
;
1354 /* Half_XXX_damage has opposite its usual effect (approx)
1355 but isn't cumulative if hero has more than one */
1356 if (Half_physical_damage
|| Half_spell_damage
)
1358 /* give Magicbane wielder dose of own medicine */
1359 if (uwep
&& uwep
->oartifact
== ART_MAGICBANE
)
1361 /* having an artifact--other than own quest one--which
1362 confers magic resistance simply by being carried
1363 also increases the effect */
1364 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
1365 if (otmp
->oartifact
&& !is_quest_artifact(otmp
)
1366 && defends_when_carried(AD_MAGM
, otmp
))
1371 dmgval2
= (dmgval2
+ 3) / 4;
1373 You_feel((dmgval2
>= hp
) ? "unbearably torpid!"
1374 : (dmgval2
>= hp
/ 4) ? "very lethargic."
1376 /* opposite of magical explosion */
1377 losehp(dmgval2
, "anti-magic implosion", KILLED_BY_AN
);
1382 char verbbuf
[BUFSZ
];
1386 Sprintf(verbbuf
, "lead %s",
1387 x_monnam(u
.usteed
, steed_article
, (char *) 0,
1388 SUPPRESS_SADDLE
, FALSE
));
1390 Sprintf(verbbuf
, "%s", Levitation
1391 ? (const char *) "float"
1392 : locomotion(youmonst
.data
, "step"));
1393 You("%s onto a polymorph trap!", verbbuf
);
1394 if (Antimagic
|| Unchanging
) {
1395 shieldeff(u
.ux
, u
.uy
);
1396 You_feel("momentarily different.");
1397 /* Trap did nothing; don't remove it --KAA */
1399 (void) steedintrap(trap
, (struct obj
*) 0);
1400 deltrap(trap
); /* delete trap before polymorph */
1401 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1402 You_feel("a change coming over you.");
1408 unsigned steed_mid
= 0;
1409 struct obj
*saddle
= 0;
1411 if ((Levitation
|| Flying
) && !forcetrap
) {
1412 if (!already_seen
&& rn2(3))
1415 pline("%s %s in a pile of soil below you.",
1416 already_seen
? "There is" : "You discover",
1417 trap
->madeby_u
? "the trigger of your mine" : "a trigger");
1418 if (already_seen
&& rn2(3))
1420 pline("KAABLAMM!!! %s %s%s off!",
1421 forcebungle
? "Your inept attempt sets"
1422 : "The air currents set",
1423 already_seen
? a_your
[trap
->madeby_u
] : "",
1424 already_seen
? " land mine" : "it");
1426 /* prevent landmine from killing steed, throwing you to
1427 * the ground, and you being affected again by the same
1428 * mine because it hasn't been deleted yet
1430 static boolean recursive_mine
= FALSE
;
1435 pline("KAABLAMM!!! You triggered %s land mine!",
1436 a_your
[trap
->madeby_u
]);
1438 steed_mid
= u
.usteed
->m_id
;
1439 recursive_mine
= TRUE
;
1440 (void) steedintrap(trap
, (struct obj
*) 0);
1441 recursive_mine
= FALSE
;
1442 saddle
= sobj_at(SADDLE
, u
.ux
, u
.uy
);
1443 set_wounded_legs(LEFT_SIDE
, rn1(35, 41));
1444 set_wounded_legs(RIGHT_SIDE
, rn1(35, 41));
1445 exercise(A_DEX
, FALSE
);
1447 blow_up_landmine(trap
);
1448 if (steed_mid
&& saddle
&& !u
.usteed
)
1449 (void) keep_saddle_with_steedcorpse(steed_mid
, fobj
, saddle
);
1450 newsym(u
.ux
, u
.uy
); /* update trap symbol */
1451 losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN
);
1452 /* fall recursively into the pit... */
1453 if ((trap
= t_at(u
.ux
, u
.uy
)) != 0)
1454 dotrap(trap
, RECURSIVETRAP
);
1455 fill_pit(u
.ux
, u
.uy
);
1459 case ROLLING_BOULDER_TRAP
: {
1460 int style
= ROLL
| (trap
->tseen
? LAUNCH_KNOWN
: 0);
1463 pline("Click! You trigger a rolling boulder trap!");
1464 if (!launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
1465 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
1467 newsym(u
.ux
, u
.uy
); /* get rid of trap symbol */
1468 pline("Fortunately for you, no boulder was released.");
1475 domagicportal(trap
);
1478 case VIBRATING_SQUARE
:
1480 /* messages handled elsewhere; the trap symbol is merely to mark the
1481 * square for future reference */
1486 impossible("You hit a trap of type %u", trap
->ttyp
);
1491 trapnote(trap
, noprefix
)
1495 static char tnbuf
[12];
1497 *tnnames
[12] = { "C note", "D flat", "D note", "E flat",
1498 "E note", "F note", "F sharp", "G note",
1499 "G sharp", "A note", "B flat", "B note" };
1502 tn
= tnnames
[trap
->tnote
];
1504 Sprintf(tnbuf
, "%s ",
1505 (*tn
== 'A' || *tn
== 'E' || *tn
== 'F') ? "an" : "a");
1506 Sprintf(eos(tnbuf
), "%s", tn
);
1511 steedintrap(trap
, otmp
)
1515 struct monst
*steed
= u
.usteed
;
1517 boolean trapkilled
, steedhit
;
1519 if (!steed
|| !trap
)
1524 trapkilled
= steedhit
= FALSE
;
1529 impossible("steed hit by non-existant arrow?");
1532 trapkilled
= thitm(8, steed
, otmp
, 0, FALSE
);
1537 impossible("steed hit by non-existant dart?");
1540 trapkilled
= thitm(7, steed
, otmp
, 0, FALSE
);
1544 if (!resists_sleep(steed
) && !breathless(steed
->data
)
1545 && !steed
->msleeping
&& steed
->mcanmove
) {
1546 if (sleep_monst(steed
, rnd(25), -1))
1547 /* no in_sight check here; you can feel it even if blind */
1548 pline("%s suddenly falls asleep!", Monnam(steed
));
1553 trapkilled
= thitm(0, steed
, (struct obj
*) 0, rnd(16), FALSE
);
1558 trapkilled
= (steed
->mhp
<= 0
1559 || thitm(0, steed
, (struct obj
*) 0,
1560 rnd((tt
== PIT
) ? 6 : 10), FALSE
));
1564 if (!resists_magm(steed
) && !resist(steed
, WAND_CLASS
, 0, NOTELL
)) {
1565 (void) newcham(steed
, (struct permonst
*) 0, FALSE
, FALSE
);
1566 if (!can_saddle(steed
) || !can_ride(steed
))
1567 dismount_steed(DISMOUNT_POLY
);
1569 You("have to adjust yourself in the saddle on %s.",
1570 x_monnam(steed
, ARTICLE_A
, (char *) 0, SUPPRESS_SADDLE
,
1580 dismount_steed(DISMOUNT_POLY
);
1583 return steedhit
? 1 : 0;
1586 /* some actions common to both player and monsters for triggered landmine */
1588 blow_up_landmine(trap
)
1591 int x
= trap
->tx
, y
= trap
->ty
, dbx
, dby
;
1592 struct rm
*lev
= &levl
[x
][y
];
1594 (void) scatter(x
, y
, 4,
1595 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
| VIS_EFFECTS
,
1598 wake_nearto(x
, y
, 400);
1599 if (IS_DOOR(lev
->typ
))
1600 lev
->doormask
= D_BROKEN
;
1601 /* destroy drawbridge if present */
1602 if (lev
->typ
== DRAWBRIDGE_DOWN
|| is_drawbridge_wall(x
, y
) >= 0) {
1604 /* if under the portcullis, the bridge is adjacent */
1605 if (find_drawbridge(&dbx
, &dby
))
1606 destroy_drawbridge(dbx
, dby
);
1607 trap
= t_at(x
, y
); /* expected to be null after destruction */
1609 /* convert landmine into pit */
1611 if (Is_waterlevel(&u
.uz
) || Is_airlevel(&u
.uz
)) {
1615 trap
->ttyp
= PIT
; /* explosion creates a pit */
1616 trap
->madeby_u
= FALSE
; /* resulting pit isn't yours */
1617 seetrap(trap
); /* and it isn't concealed */
1623 * The following are used to track launched objects to
1624 * prevent them from vanishing if you are killed. They
1625 * will reappear at the launchplace in bones files.
1633 launch_drop_spot(obj
, x
, y
)
1638 launchplace
.obj
= (struct obj
*) 0;
1642 launchplace
.obj
= obj
;
1649 launch_in_progress()
1651 if (launchplace
.obj
)
1657 force_launch_placement()
1659 if (launchplace
.obj
) {
1660 launchplace
.obj
->otrapped
= 0;
1661 place_object(launchplace
.obj
, launchplace
.x
, launchplace
.y
);
1666 * Move obj from (x1,y1) to (x2,y2)
1668 * Return 0 if no object was launched.
1669 * 1 if an object was launched and placed somewhere.
1670 * 2 if an object was launched, but used up.
1673 launch_obj(otyp
, x1
, y1
, x2
, y2
, style
)
1675 register int x1
, y1
, x2
, y2
;
1678 register struct monst
*mtmp
;
1679 register struct obj
*otmp
, *otmp2
;
1680 register int dx
, dy
;
1681 struct obj
*singleobj
;
1682 boolean used_up
= FALSE
;
1683 boolean otherside
= FALSE
;
1688 otmp
= sobj_at(otyp
, x1
, y1
);
1689 /* Try the other side too, for rolling boulder traps */
1690 if (!otmp
&& otyp
== BOULDER
) {
1692 otmp
= sobj_at(otyp
, x2
, y2
);
1696 if (otherside
) { /* swap 'em */
1707 if (otmp
->quan
== 1L) {
1708 obj_extract_self(otmp
);
1710 otmp
= (struct obj
*) 0;
1712 singleobj
= splitobj(otmp
, 1L);
1713 obj_extract_self(singleobj
);
1716 /* in case you're using a pick-axe to chop the boulder that's being
1717 launched (perhaps a monster triggered it), destroy context so that
1718 next dig attempt never thinks you're resuming previous effort */
1719 if ((otyp
== BOULDER
|| otyp
== STATUE
)
1720 && singleobj
->ox
== context
.digging
.pos
.x
1721 && singleobj
->oy
== context
.digging
.pos
.y
)
1722 (void) memset((genericptr_t
) &context
.digging
, 0,
1723 sizeof(struct dig_info
));
1725 dist
= distmin(x1
, y1
, x2
, y2
);
1731 case ROLL
| LAUNCH_UNSEEN
:
1732 if (otyp
== BOULDER
) {
1733 You_hear(Hallucination
? "someone bowling."
1734 : "rumbling in the distance.");
1736 style
&= ~LAUNCH_UNSEEN
;
1738 case ROLL
| LAUNCH_KNOWN
:
1739 /* use otrapped as a flag to ohitmon */
1740 singleobj
->otrapped
= 1;
1741 style
&= ~LAUNCH_KNOWN
;
1750 if (!cansee(bhitpos
.x
, bhitpos
.y
))
1752 tmp_at(DISP_FLASH
, obj_to_glyph(singleobj
));
1753 tmp_at(bhitpos
.x
, bhitpos
.y
);
1755 /* Mark a spot to place object in bones files to prevent
1756 * loss of object. Use the starting spot to ensure that
1757 * a rolling boulder will still launch, which it wouldn't
1758 * do if left midstream. Unfortunately we can't use the
1759 * target resting spot, because there are some things/situations
1760 * that would prevent it from ever getting there (bars), and we
1761 * can't tell that yet.
1763 launch_drop_spot(singleobj
, bhitpos
.x
, bhitpos
.y
);
1765 /* Set the object in motion */
1766 while (dist
-- > 0 && !used_up
) {
1768 tmp_at(bhitpos
.x
, bhitpos
.y
);
1771 /* dstage@u.washington.edu -- Delay only if hero sees it */
1772 if (cansee(bhitpos
.x
, bhitpos
.y
))
1778 t
= t_at(bhitpos
.x
, bhitpos
.y
);
1780 if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
1781 if (otyp
== BOULDER
&& throws_rocks(mtmp
->data
)) {
1783 if (cansee(bhitpos
.x
, bhitpos
.y
))
1784 pline("%s snatches the boulder.", Monnam(mtmp
));
1785 singleobj
->otrapped
= 0;
1786 (void) mpickobj(mtmp
, singleobj
);
1788 launch_drop_spot((struct obj
*) 0, 0, 0);
1792 if (ohitmon(mtmp
, singleobj
, (style
== ROLL
) ? -1 : dist
,
1795 launch_drop_spot((struct obj
*) 0, 0, 0);
1798 } else if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) {
1801 if (thitu(9 + singleobj
->spe
, dmgval(singleobj
, &youmonst
),
1802 singleobj
, (char *) 0))
1805 if (style
== ROLL
) {
1806 if (down_gate(bhitpos
.x
, bhitpos
.y
) != -1) {
1807 if (ship_object(singleobj
, bhitpos
.x
, bhitpos
.y
, FALSE
)) {
1809 launch_drop_spot((struct obj
*) 0, 0, 0);
1813 if (t
&& otyp
== BOULDER
) {
1819 cansee(bhitpos
.x
, bhitpos
.y
)
1820 ? " The rolling boulder triggers a land mine."
1823 del_engr_at(bhitpos
.x
, bhitpos
.y
);
1824 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1825 singleobj
->otrapped
= 0;
1826 fracture_rock(singleobj
);
1827 (void) scatter(bhitpos
.x
, bhitpos
.y
, 4,
1828 MAY_DESTROY
| MAY_HIT
| MAY_FRACTURE
1831 if (cansee(bhitpos
.x
, bhitpos
.y
))
1832 newsym(bhitpos
.x
, bhitpos
.y
);
1834 launch_drop_spot((struct obj
*) 0, 0, 0);
1839 if (cansee(bhitpos
.x
, bhitpos
.y
))
1840 pline("Suddenly the rolling boulder disappears!");
1842 You_hear("a rumbling stop abruptly.");
1843 singleobj
->otrapped
= 0;
1844 if (t
->ttyp
== TELEP_TRAP
)
1845 (void) rloco(singleobj
);
1847 int newlev
= random_teleport_level();
1850 if (newlev
== depth(&u
.uz
) || In_endgame(&u
.uz
))
1852 add_to_migration(singleobj
);
1853 get_level(&dest
, newlev
);
1854 singleobj
->ox
= dest
.dnum
;
1855 singleobj
->oy
= dest
.dlevel
;
1856 singleobj
->owornmask
= (long) MIGR_RANDOM
;
1860 launch_drop_spot((struct obj
*) 0, 0, 0);
1866 /* the boulder won't be used up if there is a
1867 monster in the trap; stop rolling anyway */
1868 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* stops here */
1869 if (flooreffects(singleobj
, x2
, y2
, "fall")) {
1871 launch_drop_spot((struct obj
*) 0, 0, 0);
1873 dist
= -1; /* stop rolling immediately */
1876 if (used_up
|| dist
== -1)
1879 if (flooreffects(singleobj
, bhitpos
.x
, bhitpos
.y
, "fall")) {
1881 launch_drop_spot((struct obj
*) 0, 0, 0);
1885 && (otmp2
= sobj_at(BOULDER
, bhitpos
.x
, bhitpos
.y
)) != 0) {
1886 const char *bmsg
= " as one boulder sets another in motion";
1888 if (!isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
) || !dist
1889 || IS_ROCK(levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
))
1890 bmsg
= " as one boulder hits another";
1892 You_hear("a loud crash%s!",
1893 cansee(bhitpos
.x
, bhitpos
.y
) ? bmsg
: "");
1894 obj_extract_self(otmp2
);
1895 /* pass off the otrapped flag to the next boulder */
1896 otmp2
->otrapped
= singleobj
->otrapped
;
1897 singleobj
->otrapped
= 0;
1898 place_object(singleobj
, bhitpos
.x
, bhitpos
.y
);
1900 otmp2
= (struct obj
*) 0;
1901 wake_nearto(bhitpos
.x
, bhitpos
.y
, 10 * 10);
1904 if (otyp
== BOULDER
&& closed_door(bhitpos
.x
, bhitpos
.y
)) {
1905 if (cansee(bhitpos
.x
, bhitpos
.y
))
1906 pline_The("boulder crashes through a door.");
1907 levl
[bhitpos
.x
][bhitpos
.y
].doormask
= D_BROKEN
;
1909 unblock_point(bhitpos
.x
, bhitpos
.y
);
1912 /* if about to hit iron bars, do so now */
1913 if (dist
> 0 && isok(bhitpos
.x
+ dx
, bhitpos
.y
+ dy
)
1914 && levl
[bhitpos
.x
+ dx
][bhitpos
.y
+ dy
].typ
== IRONBARS
) {
1915 x2
= bhitpos
.x
, y2
= bhitpos
.y
; /* object stops here */
1916 if (hits_bars(&singleobj
,
1917 x2
, y2
, x2
+dx
, y2
+dy
,
1921 launch_drop_spot((struct obj
*) 0, 0, 0);
1927 tmp_at(DISP_END
, 0);
1928 launch_drop_spot((struct obj
*) 0, 0, 0);
1930 singleobj
->otrapped
= 0;
1931 place_object(singleobj
, x2
, y2
);
1944 newsym(trap
->tx
, trap
->ty
);
1948 /* like seetrap() but overrides vision */
1955 /* in case it's beneath something, redisplay the something */
1956 newsym(trap
->tx
, trap
->ty
);
1960 mkroll_launch(ttmp
, x
, y
, otyp
, ocount
)
1973 boolean success
= FALSE
;
1976 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
)
1978 distance
= rn1(5, 4); /* 4..8 away */
1979 tmp
= rn2(8); /* randomly pick a direction to try first */
1980 while (distance
>= mindist
) {
1985 /* Prevent boulder from being placed on water */
1986 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
1987 && is_pool_or_lava(x
+ distance
* dx
, y
+ distance
* dy
))
1990 success
= isclearpath(&cc
, distance
, dx
, dy
);
1991 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
1992 boolean success_otherway
;
1996 success_otherway
= isclearpath(&bcc
, distance
, -(dx
), -(dy
));
1997 if (!success_otherway
)
2004 if ((++trycount
% 8) == 0)
2008 /* create the trap without any ammo, launch pt at trap location */
2012 otmp
= mksobj(otyp
, TRUE
, FALSE
);
2013 otmp
->quan
= ocount
;
2014 otmp
->owt
= weight(otmp
);
2015 place_object(otmp
, cc
.x
, cc
.y
);
2018 ttmp
->launch
.x
= cc
.x
;
2019 ttmp
->launch
.y
= cc
.y
;
2020 if (ttmp
->ttyp
== ROLLING_BOULDER_TRAP
) {
2021 ttmp
->launch2
.x
= bcc
.x
;
2022 ttmp
->launch2
.y
= bcc
.y
;
2024 ttmp
->launch_otyp
= otyp
;
2025 newsym(ttmp
->launch
.x
, ttmp
->launch
.y
);
2030 isclearpath(cc
, distance
, dx
, dy
)
2040 while (distance
-- > 0) {
2043 typ
= levl
[x
][y
].typ
;
2044 if (!isok(x
, y
) || !ZAP_POS(typ
) || closed_door(x
, y
))
2054 register struct monst
*mtmp
;
2056 register struct trap
*trap
= t_at(mtmp
->mx
, mtmp
->my
);
2057 boolean trapkilled
= FALSE
;
2058 struct permonst
*mptr
= mtmp
->data
;
2062 mtmp
->mtrapped
= 0; /* perhaps teleported? */
2063 } else if (mtmp
->mtrapped
) { /* is currently in the trap */
2064 if (!trap
->tseen
&& cansee(mtmp
->mx
, mtmp
->my
) && canseemon(mtmp
)
2065 && (trap
->ttyp
== SPIKED_PIT
|| trap
->ttyp
== BEAR_TRAP
2066 || trap
->ttyp
== HOLE
|| trap
->ttyp
== PIT
2067 || trap
->ttyp
== WEB
)) {
2068 /* If you come upon an obviously trapped monster, then
2069 * you must be able to see the trap it's in too.
2075 if (sobj_at(BOULDER
, mtmp
->mx
, mtmp
->my
)
2076 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
2079 if (canseemon(mtmp
))
2080 pline("%s pulls free...", Monnam(mtmp
));
2081 fill_pit(mtmp
->mx
, mtmp
->my
);
2086 } else if (metallivorous(mptr
)) {
2087 if (trap
->ttyp
== BEAR_TRAP
) {
2088 if (canseemon(mtmp
))
2089 pline("%s eats a bear trap!", Monnam(mtmp
));
2093 } else if (trap
->ttyp
== SPIKED_PIT
) {
2094 if (canseemon(mtmp
))
2095 pline("%s munches on some spikes!", Monnam(mtmp
));
2101 register int tt
= trap
->ttyp
;
2102 boolean in_sight
, tear_web
, see_it
,
2103 inescapable
= force_mintrap
|| ((tt
== HOLE
|| tt
== PIT
)
2104 && Sokoban
&& !trap
->madeby_u
);
2105 const char *fallverb
;
2107 /* true when called from dotrap, inescapable is not an option */
2108 if (mtmp
== u
.usteed
)
2110 if (!inescapable
&& ((mtmp
->mtrapseen
& (1 << (tt
- 1))) != 0
2111 || (tt
== HOLE
&& !mindless(mptr
)))) {
2112 /* it has been in such a trap - perhaps it escapes */
2116 mtmp
->mtrapseen
|= (1 << (tt
- 1));
2118 /* Monster is aggravated by being trapped by you.
2119 Recognizing who made the trap isn't completely
2120 unreasonable; everybody has their own style. */
2121 if (trap
->madeby_u
&& rnl(5))
2124 in_sight
= canseemon(mtmp
);
2125 see_it
= cansee(mtmp
->mx
, mtmp
->my
);
2126 /* assume hero can tell what's going on for the steed */
2127 if (mtmp
== u
.usteed
)
2131 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2132 if (in_sight
&& see_it
)
2133 pline("%s triggers a trap but nothing happens.",
2136 newsym(mtmp
->mx
, mtmp
->my
);
2140 otmp
= mksobj(ARROW
, TRUE
, FALSE
);
2142 otmp
->owt
= weight(otmp
);
2143 otmp
->opoisoned
= 0;
2146 if (thitm(8, mtmp
, otmp
, 0, FALSE
))
2150 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2151 if (in_sight
&& see_it
)
2152 pline("%s triggers a trap but nothing happens.",
2155 newsym(mtmp
->mx
, mtmp
->my
);
2159 otmp
= mksobj(DART
, TRUE
, FALSE
);
2161 otmp
->owt
= weight(otmp
);
2163 otmp
->opoisoned
= 1;
2166 if (thitm(7, mtmp
, otmp
, 0, FALSE
))
2170 if (trap
->once
&& trap
->tseen
&& !rn2(15)) {
2171 if (in_sight
&& see_it
)
2173 "A trap door above %s opens, but nothing falls out!",
2176 newsym(mtmp
->mx
, mtmp
->my
);
2180 otmp
= mksobj(ROCK
, TRUE
, FALSE
);
2182 otmp
->owt
= weight(otmp
);
2185 if (thitm(0, mtmp
, otmp
, d(2, 6), FALSE
))
2191 /* stepped on a squeaky board */
2194 pline("A board beneath %s squeaks %s loudly.",
2195 mon_nam(mtmp
), trapnote(trap
, 0));
2198 pline("%s stops momentarily and appears to cringe.",
2202 You_hear("a distant %s squeak.", trapnote(trap
, 1));
2203 /* wake up nearby monsters */
2204 wake_nearto(mtmp
->mx
, mtmp
->my
, 40);
2207 if (mptr
->msize
> MZ_SMALL
&& !amorphous(mptr
) && !is_flyer(mptr
)
2208 && !is_whirly(mptr
) && !unsolid(mptr
)) {
2211 pline("%s is caught in %s bear trap!", Monnam(mtmp
),
2212 a_your
[trap
->madeby_u
]);
2215 if (mptr
== &mons
[PM_OWLBEAR
]
2216 || mptr
== &mons
[PM_BUGBEAR
])
2217 You_hear("the roaring of an angry bear!");
2219 } else if (force_mintrap
) {
2221 pline("%s evades %s bear trap!", Monnam(mtmp
),
2222 a_your
[trap
->madeby_u
]);
2227 trapkilled
= thitm(0, mtmp
, (struct obj
*) 0, d(2, 4), FALSE
);
2230 if (!resists_sleep(mtmp
) && !breathless(mptr
) && !mtmp
->msleeping
2231 && mtmp
->mcanmove
) {
2232 if (sleep_monst(mtmp
, rnd(25), -1) && in_sight
) {
2233 pline("%s suddenly falls asleep!", Monnam(mtmp
));
2246 pline("%s %s on the %s!", A_gush_of_water_hits
,
2247 mon_nam(mtmp
), mbodypart(mtmp
, HEAD
));
2248 target
= which_armor(mtmp
, W_ARMH
);
2249 (void) water_damage(target
, helm_simple_name(target
), TRUE
);
2253 pline("%s %s's left %s!", A_gush_of_water_hits
,
2254 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2255 target
= which_armor(mtmp
, W_ARMS
);
2256 if (water_damage(target
, "shield", TRUE
) != ER_NOTHING
)
2258 target
= MON_WEP(mtmp
);
2259 if (target
&& bimanual(target
))
2260 (void) water_damage(target
, 0, TRUE
);
2262 target
= which_armor(mtmp
, W_ARMG
);
2263 (void) water_damage(target
, "gauntlets", TRUE
);
2267 pline("%s %s's right %s!", A_gush_of_water_hits
,
2268 mon_nam(mtmp
), mbodypart(mtmp
, ARM
));
2269 (void) water_damage(MON_WEP(mtmp
), 0, TRUE
);
2273 pline("%s %s!", A_gush_of_water_hits
, mon_nam(mtmp
));
2274 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2276 && (otmp
->owornmask
& (W_WEP
| W_SWAPWEP
)) == 0)
2277 (void) snuff_lit(otmp
);
2278 if ((target
= which_armor(mtmp
, W_ARMC
)) != 0)
2279 (void) water_damage(target
, cloak_simple_name(target
),
2281 else if ((target
= which_armor(mtmp
, W_ARM
)) != 0)
2282 (void) water_damage(target
, "armor", TRUE
);
2283 else if ((target
= which_armor(mtmp
, W_ARMU
)) != 0)
2284 (void) water_damage(target
, "shirt", TRUE
);
2287 if (mptr
== &mons
[PM_IRON_GOLEM
]) {
2289 pline("%s falls to pieces!", Monnam(mtmp
));
2290 else if (mtmp
->mtame
)
2291 pline("May %s rust in peace.", mon_nam(mtmp
));
2295 } else if (mptr
== &mons
[PM_GREMLIN
] && rn2(3)) {
2296 (void) split_mon(mtmp
, (struct monst
*) 0);
2303 pline("A %s erupts from the %s under %s!", tower_of_flame
,
2304 surface(mtmp
->mx
, mtmp
->my
), mon_nam(mtmp
));
2305 else if (see_it
) /* evidently `mtmp' is invisible */
2306 You_see("a %s erupt from the %s!", tower_of_flame
,
2307 surface(mtmp
->mx
, mtmp
->my
));
2309 if (resists_fire(mtmp
)) {
2311 shieldeff(mtmp
->mx
, mtmp
->my
);
2312 pline("%s is uninjured.", Monnam(mtmp
));
2315 int num
= d(2, 4), alt
;
2316 boolean immolate
= FALSE
;
2318 /* paper burns very fast, assume straw is tightly
2319 * packed and burns a bit slower */
2320 switch (monsndx(mptr
)) {
2321 case PM_PAPER_GOLEM
:
2325 case PM_STRAW_GOLEM
:
2326 alt
= mtmp
->mhpmax
/ 2;
2329 alt
= mtmp
->mhpmax
/ 4;
2331 case PM_LEATHER_GOLEM
:
2332 alt
= mtmp
->mhpmax
/ 8;
2341 if (thitm(0, mtmp
, (struct obj
*) 0, num
, immolate
))
2344 /* we know mhp is at least `num' below mhpmax,
2345 so no (mhp > mhpmax) check is needed here */
2346 mtmp
->mhpmax
-= rn2(num
+ 1);
2348 if (burnarmor(mtmp
) || rn2(3)) {
2349 (void) destroy_mitem(mtmp
, SCROLL_CLASS
, AD_FIRE
);
2350 (void) destroy_mitem(mtmp
, SPBOOK_CLASS
, AD_FIRE
);
2351 (void) destroy_mitem(mtmp
, POTION_CLASS
, AD_FIRE
);
2353 if (burn_floor_objects(mtmp
->mx
, mtmp
->my
, see_it
, FALSE
)
2354 && !see_it
&& distu(mtmp
->mx
, mtmp
->my
) <= 3 * 3)
2355 You("smell smoke.");
2356 if (is_ice(mtmp
->mx
, mtmp
->my
))
2357 melt_ice(mtmp
->mx
, mtmp
->my
, (char *) 0);
2364 if (is_flyer(mptr
) || is_floater(mptr
)
2365 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2366 || is_clinger(mptr
)) {
2367 if (force_mintrap
&& !Sokoban
) {
2368 /* openfallingtrap; not inescapable here */
2371 pline("%s doesn't fall into the pit.", Monnam(mtmp
));
2373 break; /* inescapable = FALSE; */
2376 break; /* avoids trap */
2377 fallverb
= "is dragged"; /* sokoban pit */
2379 if (!passes_walls(mptr
))
2382 pline("%s %s into %s pit!", Monnam(mtmp
), fallverb
,
2383 a_your
[trap
->madeby_u
]);
2384 if (mptr
== &mons
[PM_PIT_VIPER
]
2385 || mptr
== &mons
[PM_PIT_FIEND
])
2386 pline("How pitiful. Isn't that the pits?");
2389 mselftouch(mtmp
, "Falling, ", FALSE
);
2390 if (mtmp
->mhp
<= 0 || thitm(0, mtmp
, (struct obj
*) 0,
2391 rnd((tt
== PIT
) ? 6 : 10), FALSE
))
2396 if (!Can_fall_thru(&u
.uz
)) {
2397 impossible("mintrap: %ss cannot exist on this level.",
2398 defsyms
[trap_to_defsym(tt
)].explanation
);
2399 break; /* don't activate it after all */
2401 if (is_flyer(mptr
) || is_floater(mptr
) || mptr
== &mons
[PM_WUMPUS
]
2402 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)
2403 || mptr
->msize
>= MZ_HUGE
) {
2404 if (force_mintrap
&& !Sokoban
) {
2405 /* openfallingtrap; not inescapable here */
2410 "A trap door opens, but %s doesn't fall through.",
2412 else /* (tt == HOLE) */
2413 pline("%s doesn't fall through the hole.",
2416 break; /* inescapable = FALSE; */
2418 if (inescapable
) { /* sokoban hole */
2420 pline("%s seems to be yanked down!", Monnam(mtmp
));
2421 /* suppress message in mlevel_tele_trap() */
2430 case MAGIC_PORTAL
: {
2433 mlev_res
= mlevel_tele_trap(mtmp
, trap
, inescapable
, in_sight
);
2439 mtele_trap(mtmp
, trap
, in_sight
);
2442 /* Monster in a web. */
2445 if (mu_maybe_destroy_web(mtmp
, in_sight
, trap
))
2448 switch (monsndx(mptr
)) {
2449 case PM_OWLBEAR
: /* Eric Backus */
2452 You_hear("the roaring of a confused bear!");
2458 if (mptr
->mlet
== S_GIANT
2459 /* exclude baby dragons and relatively short worms */
2460 || (mptr
->mlet
== S_DRAGON
&& extra_nasty(mptr
))
2461 || (mtmp
->wormno
&& count_wsegs(mtmp
) > 5)) {
2463 } else if (in_sight
) {
2464 pline("%s is caught in %s spider web.", Monnam(mtmp
),
2465 a_your
[trap
->madeby_u
]);
2468 mtmp
->mtrapped
= tear_web
? 0 : 1;
2470 /* this list is fairly arbitrary; it deliberately
2471 excludes wumpus & giant/ettin zombies/mummies */
2472 case PM_TITANOTHERE
:
2473 case PM_BALUCHITHERIUM
:
2474 case PM_PURPLE_WORM
:
2483 case PM_LORD_SURTUR
:
2489 pline("%s tears through %s spider web!", Monnam(mtmp
),
2490 a_your
[trap
->madeby_u
]);
2492 newsym(mtmp
->mx
, mtmp
->my
);
2493 } else if (force_mintrap
&& !mtmp
->mtrapped
) {
2495 pline("%s avoids %s spider web!", Monnam(mtmp
),
2496 a_your
[trap
->madeby_u
]);
2504 /* A magic trap. Monsters usually immune. */
2509 /* similar to hero's case, more or less */
2510 if (!resists_magm(mtmp
)) { /* lose spell energy */
2511 if (!mtmp
->mcan
&& (attacktype(mptr
, AT_MAGC
)
2512 || attacktype(mptr
, AT_BREA
))) {
2513 mtmp
->mspec_used
+= d(2, 2);
2516 pline("%s seems lethargic.", Monnam(mtmp
));
2519 } else { /* take some damage */
2520 int dmgval2
= rnd(4);
2522 if ((otmp
= MON_WEP(mtmp
)) != 0
2523 && otmp
->oartifact
== ART_MAGICBANE
)
2525 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
2527 && defends_when_carried(AD_MAGM
, otmp
))
2531 if (passes_walls(mptr
))
2532 dmgval2
= (dmgval2
+ 3) / 4;
2536 mtmp
->mhp
-= dmgval2
;
2540 ? "compression from an anti-magic field"
2546 newsym(trap
->tx
, trap
->ty
);
2551 break; /* monsters usually don't set it off */
2552 if (is_flyer(mptr
)) {
2553 boolean already_seen
= trap
->tseen
;
2555 if (in_sight
&& !already_seen
) {
2556 pline("A trigger appears in a pile of soil below %s.",
2563 newsym(mtmp
->mx
, mtmp
->my
);
2564 pline_The("air currents set %s off!",
2565 already_seen
? "a land mine" : "it");
2567 } else if (in_sight
) {
2568 newsym(mtmp
->mx
, mtmp
->my
);
2569 pline("%s%s triggers %s land mine!",
2570 !Deaf
? "KAABLAMM!!! " : "", Monnam(mtmp
),
2571 a_your
[trap
->madeby_u
]);
2573 if (!in_sight
&& !Deaf
)
2574 pline("Kaablamm! You hear an explosion in the distance!");
2575 blow_up_landmine(trap
);
2576 /* explosion might have destroyed a drawbridge; don't
2577 dish out more damage if monster is already dead */
2579 || thitm(0, mtmp
, (struct obj
*) 0, rnd(16), FALSE
)) {
2582 /* monsters recursively fall into new pit */
2583 if (mintrap(mtmp
) == 2)
2586 /* a boulder may fill the new pit, crushing monster */
2587 fill_pit(trap
->tx
, trap
->ty
);
2590 if (unconscious()) {
2592 nomovemsg
= "The explosion awakens you!";
2596 if (resists_magm(mtmp
)) {
2597 shieldeff(mtmp
->mx
, mtmp
->my
);
2598 } else if (!resist(mtmp
, WAND_CLASS
, 0, NOTELL
)) {
2599 if (newcham(mtmp
, (struct permonst
*) 0, FALSE
, FALSE
))
2600 /* we're done with mptr but keep it up to date */
2606 case ROLLING_BOULDER_TRAP
:
2607 if (!is_flyer(mptr
)) {
2608 int style
= ROLL
| (in_sight
? 0 : LAUNCH_UNSEEN
);
2610 newsym(mtmp
->mx
, mtmp
->my
);
2612 pline("Click! %s triggers %s.", Monnam(mtmp
),
2613 trap
->tseen
? "a rolling boulder trap" : something
);
2614 if (launch_obj(BOULDER
, trap
->launch
.x
, trap
->launch
.y
,
2615 trap
->launch2
.x
, trap
->launch2
.y
, style
)) {
2622 newsym(mtmp
->mx
, mtmp
->my
);
2626 case VIBRATING_SQUARE
:
2627 if (see_it
&& !Blind
) {
2629 pline("You see a strange vibration beneath %s %s.",
2630 s_suffix(mon_nam(mtmp
)),
2631 makeplural(mbodypart(mtmp
, FOOT
)));
2633 pline("You see the ground vibrate in the distance.");
2638 impossible("Some monster encountered a strange trap of type %d.",
2644 return mtmp
->mtrapped
;
2647 /* Combine cockatrice checks into single functions to avoid repeating code. */
2652 if (Stone_resistance
)
2654 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
))
2656 You("turn to stone...");
2657 killer
.format
= KILLED_BY
;
2658 if (str
!= killer
.name
)
2659 Strcpy(killer
.name
, str
? str
: "");
2664 minstapetrify(mon
, byplayer
)
2668 if (resists_ston(mon
))
2670 if (poly_when_stoned(mon
->data
)) {
2674 if (!vamp_stone(mon
))
2677 /* give a "<mon> is slowing down" message and also remove
2678 intrinsic speed (comparable to similar effect on the hero) */
2679 mon_adjust_speed(mon
, -3, (struct obj
*) 0);
2681 if (cansee(mon
->mx
, mon
->my
))
2682 pline("%s turns to stone.", Monnam(mon
));
2685 xkilled(mon
, XKILL_NOMSG
);
2696 if (uwep
&& uwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[uwep
->corpsenm
])
2697 && !Stone_resistance
) {
2698 pline("%s touch the %s corpse.", arg
, mons
[uwep
->corpsenm
].mname
);
2699 Sprintf(kbuf
, "%s corpse", an(mons
[uwep
->corpsenm
].mname
));
2701 /* life-saved; unwield the corpse if we can't handle it */
2702 if (!uarmg
&& !Stone_resistance
)
2705 /* Or your secondary weapon, if wielded [hypothetical; we don't
2706 allow two-weapon combat when either weapon is a corpse] */
2707 if (u
.twoweap
&& uswapwep
&& uswapwep
->otyp
== CORPSE
2708 && touch_petrifies(&mons
[uswapwep
->corpsenm
]) && !Stone_resistance
) {
2709 pline("%s touch the %s corpse.", arg
, mons
[uswapwep
->corpsenm
].mname
);
2710 Sprintf(kbuf
, "%s corpse", an(mons
[uswapwep
->corpsenm
].mname
));
2712 /* life-saved; unwield the corpse */
2713 if (!uarmg
&& !Stone_resistance
)
2719 mselftouch(mon
, arg
, byplayer
)
2724 struct obj
*mwep
= MON_WEP(mon
);
2726 if (mwep
&& mwep
->otyp
== CORPSE
&& touch_petrifies(&mons
[mwep
->corpsenm
])
2727 && !resists_ston(mon
)) {
2728 if (cansee(mon
->mx
, mon
->my
)) {
2729 pline("%s%s touches %s.", arg
? arg
: "",
2730 arg
? mon_nam(mon
) : Monnam(mon
),
2731 corpse_xname(mwep
, (const char *) 0, CXN_PFX_THE
));
2733 minstapetrify(mon
, byplayer
);
2734 /* if life-saved, might not be able to continue wielding */
2735 if (mon
->mhp
> 0 && !which_armor(mon
, W_ARMG
) && !resists_ston(mon
))
2740 /* start levitating */
2744 context
.botl
= TRUE
;
2746 if (u
.utraptype
== TT_PIT
) {
2748 You("float up, out of the pit!");
2749 vision_full_recalc
= 1; /* vision limits change */
2750 fill_pit(u
.ux
, u
.uy
);
2751 } else if (u
.utraptype
== TT_INFLOOR
) {
2752 Your("body pulls upward, but your %s are still stuck.",
2753 makeplural(body_part(LEG
)));
2755 You("float up, only your %s is still stuck.", body_part(LEG
));
2758 } else if (Is_waterlevel(&u
.uz
)) {
2759 pline("It feels as though you've lost some weight.");
2761 } else if (u
.uinwater
) {
2763 } else if (u
.uswallow
) {
2764 You(is_animal(u
.ustuck
->data
) ? "float away from the %s."
2765 : "spiral up into %s.",
2766 is_animal(u
.ustuck
->data
) ? surface(u
.ux
, u
.uy
)
2767 : mon_nam(u
.ustuck
));
2768 } else if (Hallucination
) {
2769 pline("Up, up, and awaaaay! You're walking on air!");
2770 } else if (Is_airlevel(&u
.uz
)) {
2771 You("gain control over your movements.");
2773 You("start to float in the air!");
2775 if (u
.usteed
&& !is_floater(u
.usteed
->data
)
2776 && !is_flyer(u
.usteed
->data
)) {
2778 pline("%s magically floats up!", Monnam(u
.usteed
));
2780 You("cannot stay on %s.", mon_nam(u
.usteed
));
2781 dismount_steed(DISMOUNT_GENERIC
);
2785 You("are no longer able to control your flight.");
2786 BFlying
|= I_SPECIAL
;
2797 if ((t
= t_at(x
, y
)) && ((t
->ttyp
== PIT
) || (t
->ttyp
== SPIKED_PIT
))
2798 && (otmp
= sobj_at(BOULDER
, x
, y
))) {
2799 obj_extract_self(otmp
);
2800 (void) flooreffects(otmp
, x
, y
, "settle");
2804 /* stop levitating */
2806 float_down(hmask
, emask
)
2807 long hmask
, emask
; /* might cancel timeout */
2809 register struct trap
*trap
= (struct trap
*) 0;
2810 d_level current_dungeon_level
;
2811 boolean no_msg
= FALSE
;
2813 HLevitation
&= ~hmask
;
2814 ELevitation
&= ~emask
;
2816 return 0; /* maybe another ring/potion/boots */
2818 /* Levitation is blocked, so hero is not actually floating
2819 hence shouldn't have float_down effects and feedback */
2820 float_vs_flight(); /* before nomul() rather than after */
2823 context
.botl
= TRUE
;
2824 nomul(0); /* stop running or resting */
2826 /* controlled flight no longer overridden by levitation */
2827 BFlying
&= ~I_SPECIAL
;
2829 You("have stopped levitating and are now flying.");
2834 You("float down, but you are still %s.",
2835 is_animal(u
.ustuck
->data
) ? "swallowed" : "engulfed");
2839 if (Punished
&& !carried(uball
)
2840 && (is_pool(uball
->ox
, uball
->oy
)
2841 || ((trap
= t_at(uball
->ox
, uball
->oy
))
2842 && ((trap
->ttyp
== PIT
) || (trap
->ttyp
== SPIKED_PIT
)
2843 || (trap
->ttyp
== TRAPDOOR
) || (trap
->ttyp
== HOLE
))))) {
2848 movobj(uchain
, uball
->ox
, uball
->oy
);
2849 newsym(u
.ux0
, u
.uy0
);
2850 vision_full_recalc
= 1; /* in case the hero moved. */
2852 /* check for falling into pool - added by GAN 10/20/86 */
2854 if (!u
.uswallow
&& u
.ustuck
) {
2855 if (sticks(youmonst
.data
))
2856 You("aren't able to maintain your hold on %s.",
2859 pline("Startled, %s can no longer hold you!",
2864 * drown() and lava_effects() print various messages almost
2865 * every time they're called which conflict with the "fall
2866 * into" message below. Thus, we want to avoid printing
2867 * confusing, duplicate or out-of-order messages.
2868 * Use knowledge of the two routines as a hack -- this
2869 * should really be handled differently -dlc
2871 if (is_pool(u
.ux
, u
.uy
) && !Wwalking
&& !Swimming
&& !u
.uinwater
)
2874 if (is_lava(u
.ux
, u
.uy
)) {
2875 (void) lava_effects();
2880 trap
= t_at(u
.ux
, u
.uy
);
2881 if (Is_airlevel(&u
.uz
)) {
2882 You("begin to tumble in place.");
2883 } else if (Is_waterlevel(&u
.uz
) && !no_msg
) {
2884 You_feel("heavier.");
2885 /* u.uinwater msgs already in spoteffects()/drown() */
2886 } else if (!u
.uinwater
&& !no_msg
) {
2887 if (!(emask
& W_SADDLE
)) {
2888 if (Sokoban
&& trap
) {
2889 /* Justification elsewhere for Sokoban traps is based
2890 * on air currents. This is consistent with that.
2891 * The unexpected additional force of the air currents
2892 * once levitation ceases knocks you off your feet.
2895 pline("Bummer! You've crashed.");
2898 losehp(rnd(2), "dangerous winds", KILLED_BY
);
2900 dismount_steed(DISMOUNT_FELL
);
2901 selftouch("As you fall, you");
2902 } else if (u
.usteed
&& (is_floater(u
.usteed
->data
)
2903 || is_flyer(u
.usteed
->data
))) {
2904 You("settle more firmly in the saddle.");
2905 } else if (Hallucination
) {
2906 pline("Bummer! You've %s.",
2909 : "hit the ground");
2911 You("float gently to the %s.", surface(u
.ux
, u
.uy
));
2917 /* can't rely on u.uz0 for detecting trap door-induced level change;
2918 it gets changed to reflect the new level before we can check it */
2919 assign_level(¤t_dungeon_level
, &u
.uz
);
2921 switch (trap
->ttyp
) {
2926 if (!Can_fall_thru(&u
.uz
) || u
.ustuck
)
2930 if (!u
.utrap
) /* not already in the trap */
2934 if (!Is_airlevel(&u
.uz
) && !Is_waterlevel(&u
.uz
) && !u
.uswallow
2935 /* falling through trap door calls goto_level,
2936 and goto_level does its own pickup() call */
2937 && on_level(&u
.uz
, ¤t_dungeon_level
))
2942 /* shared code for climbing out of a pit */
2946 if (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
2950 /* marked as trapped so they can pick things up */
2951 You("ascend from the pit.");
2953 fill_pit(u
.ux
, u
.uy
);
2954 vision_full_recalc
= 1; /* vision limits change */
2955 } else if (!rn2(2) && sobj_at(BOULDER
, u
.ux
, u
.uy
)) {
2956 Your("%s gets stuck in a crevice.", body_part(LEG
));
2957 display_nhwindow(WIN_MESSAGE
, FALSE
);
2958 clear_nhwindow(WIN_MESSAGE
);
2959 You("free your %s.", body_part(LEG
));
2960 } else if ((Flying
|| is_clinger(youmonst
.data
)) && !Sokoban
) {
2961 /* eg fell in pit, then poly'd to a flying monster;
2962 or used '>' to deliberately enter it */
2963 You("%s from the pit.", Flying
? "fly" : "climb");
2965 fill_pit(u
.ux
, u
.uy
);
2966 vision_full_recalc
= 1; /* vision limits change */
2967 } else if (!(--u
.utrap
)) {
2968 You("%s to the edge of the pit.",
2969 (Sokoban
&& Levitation
)
2970 ? "struggle against the air currents and float"
2971 : u
.usteed
? "ride" : "crawl");
2972 fill_pit(u
.ux
, u
.uy
);
2973 vision_full_recalc
= 1; /* vision limits change */
2974 } else if (u
.dz
|| flags
.verbose
) {
2976 Norep("%s is still in a pit.", upstart(y_monnam(u
.usteed
)));
2978 Norep((Hallucination
&& !rn2(5))
2979 ? "You've fallen, and you can't get up."
2980 : "You are still in a pit.");
2986 struct obj
*box
; /* null for floor trap */
2988 boolean see_it
= !Blind
;
2991 /* Bug: for box case, the equivalent of burn_floor_objects() ought
2992 * to be done upon its contents.
2995 if ((box
&& !carried(box
)) ? is_pool(box
->ox
, box
->oy
) : Underwater
) {
2996 pline("A cascade of steamy bubbles erupts from %s!",
2997 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
2998 if (Fire_resistance
)
2999 You("are uninjured.");
3001 losehp(rnd(3), "boiling water", KILLED_BY
);
3004 pline("A %s %s from %s!", tower_of_flame
, box
? "bursts" : "erupts",
3005 the(box
? xname(box
) : surface(u
.ux
, u
.uy
)));
3006 if (Fire_resistance
) {
3007 shieldeff(u
.ux
, u
.uy
);
3009 } else if (Upolyd
) {
3011 switch (u
.umonnum
) {
3012 case PM_PAPER_GOLEM
:
3015 case PM_STRAW_GOLEM
:
3021 case PM_LEATHER_GOLEM
:
3030 if (u
.mhmax
> mons
[u
.umonnum
].mlevel
)
3031 u
.mhmax
-= rn2(min(u
.mhmax
, num
+ 1)), context
.botl
= 1;
3034 if (u
.uhpmax
> u
.ulevel
)
3035 u
.uhpmax
-= rn2(min(u
.uhpmax
, num
+ 1)), context
.botl
= 1;
3038 You("are uninjured.");
3040 losehp(num
, tower_of_flame
, KILLED_BY_AN
); /* fire damage */
3043 if (burnarmor(&youmonst
) || rn2(3)) {
3044 destroy_item(SCROLL_CLASS
, AD_FIRE
);
3045 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
3046 destroy_item(POTION_CLASS
, AD_FIRE
);
3048 if (!box
&& burn_floor_objects(u
.ux
, u
.uy
, see_it
, TRUE
) && !see_it
)
3049 You("smell paper burning.");
3050 if (is_ice(u
.ux
, u
.uy
))
3051 melt_ice(u
.ux
, u
.uy
, (char *) 0);
3057 register int fate
= rnd(20);
3059 /* What happened to the poor sucker? */
3062 /* Most of the time, it creates some monsters. */
3063 register int cnt
= rnd(4);
3065 /* blindness effects */
3066 if (!resists_blnd(&youmonst
)) {
3067 You("are momentarily blinded by a flash of light!");
3068 make_blinded((long) rn1(5, 10), FALSE
);
3070 Your1(vision_clears
);
3071 } else if (!Blind
) {
3072 You_see("a flash of light!");
3075 /* deafness effects */
3077 You_hear("a deafening roar!");
3078 incr_itimeout(&HDeaf
, rn1(20, 30));
3079 context
.botl
= TRUE
;
3081 /* magic vibrations still hit you */
3082 You_feel("rankled.");
3083 incr_itimeout(&HDeaf
, rn1(5, 15));
3084 context
.botl
= TRUE
;
3087 (void) makemon((struct permonst
*) 0, u
.ux
, u
.uy
, NO_MM_FLAGS
);
3092 /* sometimes nothing happens */
3094 case 12: /* a flash of fire */
3095 dofiretrap((struct obj
*) 0);
3100 pline("A shiver runs up and down your %s!", body_part(SPINE
));
3103 You_hear(Hallucination
? "the moon howling at you."
3104 : "distant howling.");
3107 if (on_level(&u
.uz
, &qstart_level
))
3109 "%slike the prodigal son.",
3110 (flags
.female
|| (Upolyd
&& is_neuter(youmonst
.data
)))
3114 You("suddenly yearn for %s.",
3117 : (In_quest(&u
.uz
) || at_dgn_entrance("The Quest"))
3118 ? "your nearby homeland"
3119 : "your distant homeland");
3122 Your("pack shakes violently!");
3125 You(Hallucination
? "smell hamburgers." : "smell charred flesh.");
3131 /* very occasionally something nice happens. */
3132 case 19: { /* tame nearby monsters */
3136 (void) adjattrib(A_CHA
, 1, FALSE
);
3137 for (i
= -1; i
<= 1; i
++)
3138 for (j
= -1; j
<= 1; j
++) {
3139 if (!isok(u
.ux
+ i
, u
.uy
+ j
))
3141 mtmp
= m_at(u
.ux
+ i
, u
.uy
+ j
);
3143 (void) tamedog(mtmp
, (struct obj
*) 0);
3147 case 20: { /* uncurse stuff */
3149 long save_conf
= HConfusion
;
3151 pseudo
= zeroobj
; /* neither cursed nor blessed,
3152 and zero out oextra */
3153 pseudo
.otyp
= SCR_REMOVE_CURSE
;
3155 (void) seffects(&pseudo
);
3156 HConfusion
= save_conf
;
3164 /* Set an item on fire.
3165 * "force" means not to roll a luck-based protection check for the
3167 * "x" and "y" are the coordinates to dump the contents of a
3168 * container, if it burns up.
3170 * Return whether the object was destroyed.
3173 fire_damage(obj
, force
, x
, y
)
3179 struct obj
*otmp
, *ncobj
;
3180 int in_sight
= !Blind
&& couldsee(x
, y
); /* Don't care if it's lit */
3183 /* object might light in a controlled manner */
3187 if (Is_container(obj
)) {
3188 switch (obj
->otyp
) {
3190 return FALSE
; /* Immune */
3201 if ((!force
&& (Luck
+ 5) > rn2(chance
))
3202 || (is_flammable(obj
) && obj
->oerodeproof
))
3204 /* Container is burnt up - dump contents out */
3206 pline("%s catches fire and burns.", Yname2(obj
));
3207 if (Has_contents(obj
)) {
3209 pline("Its contents fall out.");
3210 for (otmp
= obj
->cobj
; otmp
; otmp
= ncobj
) {
3212 obj_extract_self(otmp
);
3213 if (!flooreffects(otmp
, x
, y
, ""))
3214 place_object(otmp
, x
, y
);
3220 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3221 /* chance per item of sustaining damage:
3222 * max luck (Luck==13): 10%
3223 * avg luck (Luck==0): 75%
3224 * awful luck (Luck<-4): 100%
3227 } else if (obj
->oclass
== SCROLL_CLASS
|| obj
->oclass
== SPBOOK_CLASS
) {
3228 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
)
3230 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3232 pline("Smoke rises from %s.", the(xname(obj
)));
3235 dindx
= (obj
->oclass
== SCROLL_CLASS
) ? 3 : 4;
3237 pline("%s %s.", Yname2(obj
),
3238 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3242 } else if (obj
->oclass
== POTION_CLASS
) {
3243 dindx
= (obj
->otyp
!= POT_OIL
) ? 1 : 2;
3245 pline("%s %s.", Yname2(obj
),
3246 destroy_strings
[dindx
][(obj
->quan
> 1L)]);
3250 } else if (erode_obj(obj
, (char *) 0, ERODE_BURN
, EF_DESTROY
)
3258 * Apply fire_damage() to an entire chain.
3260 * Return number of objects destroyed. --ALI
3263 fire_damage_chain(chain
, force
, here
, x
, y
)
3265 boolean force
, here
;
3268 struct obj
*obj
, *nobj
;
3270 for (obj
= chain
; obj
; obj
= nobj
) {
3271 nobj
= here
? obj
->nexthere
: obj
->nobj
;
3272 if (fire_damage(obj
, force
, x
, y
))
3276 if (num
&& (Blind
&& !couldsee(x
, y
)))
3277 You("smell smoke.");
3285 /* Scrolls but not spellbooks can be erased by acid. */
3286 struct monst
*victim
;
3292 victim
= carried(obj
) ? &youmonst
: mcarried(obj
) ? obj
->ocarry
: NULL
;
3293 vismon
= victim
&& (victim
!= &youmonst
) && canseemon(victim
);
3296 grease_protect(obj
, (char *) 0, victim
);
3297 } else if (obj
->oclass
== SCROLL_CLASS
&& obj
->otyp
!= SCR_BLANK_PAPER
) {
3298 if (obj
->otyp
!= SCR_BLANK_PAPER
3300 && obj
->otyp
!= SCR_MAIL
3304 if (victim
== &youmonst
)
3305 pline("Your %s.", aobjnam(obj
, "fade"));
3307 pline("%s %s.", s_suffix(Monnam(victim
)),
3308 aobjnam(obj
, "fade"));
3311 obj
->otyp
= SCR_BLANK_PAPER
;
3315 erode_obj(obj
, (char *) 0, ERODE_CORRODE
, EF_GREASE
| EF_VERBOSE
);
3318 /* context for water_damage(), managed by water_damage_chain();
3319 when more than one stack of potions of acid explode while processing
3320 a chain of objects, use alternate phrasing after the first message */
3321 static struct h2o_ctx
{
3322 int dkn_boom
, unk_boom
; /* track dknown, !dknown separately */
3324 } acid_ctx
= { 0, 0, FALSE
};
3326 /* Get an object wet and damage it appropriately.
3327 * "ostr", if present, is used instead of the object name in some
3329 * "force" means not to roll luck to protect some objects.
3330 * Returns an erosion return value (ER_*)
3333 water_damage(obj
, ostr
, force
)
3347 if (obj
->otyp
== CAN_OF_GREASE
&& obj
->spe
> 0) {
3349 } else if (obj
->otyp
== TOWEL
&& obj
->spe
< 7) {
3350 wet_a_towel(obj
, rnd(7), TRUE
);
3352 } else if (obj
->greased
) {
3358 } else if (Is_container(obj
) && !Is_box(obj
)
3359 && (obj
->otyp
!= OILSKIN_SACK
|| (obj
->cursed
&& !rn2(3)))) {
3361 pline("Water gets into your %s!", ostr
);
3363 water_damage_chain(obj
->cobj
, FALSE
);
3365 } else if (!force
&& (Luck
+ 5) > rn2(20)) {
3366 /* chance per item of sustaining damage:
3368 * avg luck (Luck==0): 75%
3369 * awful luck (Luck<-4): 100%
3372 } else if (obj
->oclass
== SCROLL_CLASS
) {
3373 if (obj
->otyp
== SCR_BLANK_PAPER
3375 || obj
->otyp
== SCR_MAIL
3379 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3381 obj
->otyp
= SCR_BLANK_PAPER
;
3387 } else if (obj
->oclass
== SPBOOK_CLASS
) {
3388 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
3389 pline("Steam rises from %s.", the(xname(obj
)));
3391 } else if (obj
->otyp
== SPE_BLANK_PAPER
) {
3395 pline("Your %s %s.", ostr
, vtense(ostr
, "fade"));
3397 if (obj
->otyp
== SPE_NOVEL
) {
3402 obj
->otyp
= SPE_BLANK_PAPER
;
3407 } else if (obj
->oclass
== POTION_CLASS
) {
3408 if (obj
->otyp
== POT_ACID
) {
3410 boolean one
= (obj
->quan
== 1L), update
= carried(obj
),
3413 if (Blind
&& !carried(obj
))
3415 if (acid_ctx
.ctx_valid
)
3416 exploded
= ((obj
->dknown
? acid_ctx
.dkn_boom
3417 : acid_ctx
.unk_boom
) > 0);
3419 * "a [potion|<color> potion|potion of acid] explodes"
3420 * depending on obj->dknown (potion has been seen) and
3421 * objects[POT_ACID].oc_name_known (fully discovered),
3422 * or "some {plural version} explode" when relevant.
3423 * Second and subsequent messages for same chain and
3424 * matching dknown status are
3425 * "another [potion|<color> &c] explodes" or plural
3428 bufp
= simpleonames(obj
);
3429 pline("%s %s %s!", /* "A potion explodes!" */
3430 !exploded
? (one
? "A" : "Some")
3431 : (one
? "Another" : "More"),
3432 bufp
, vtense(bufp
, "explode"));
3433 if (acid_ctx
.ctx_valid
) {
3435 acid_ctx
.dkn_boom
++;
3437 acid_ctx
.unk_boom
++;
3443 return ER_DESTROYED
;
3444 } else if (obj
->odiluted
) {
3446 pline("Your %s %s further.", ostr
, vtense(ostr
, "dilute"));
3448 obj
->otyp
= POT_WATER
;
3450 obj
->blessed
= obj
->cursed
= 0;
3455 } else if (obj
->otyp
!= POT_WATER
) {
3457 pline("Your %s %s.", ostr
, vtense(ostr
, "dilute"));
3465 return erode_obj(obj
, ostr
, ERODE_RUST
, EF_NONE
);
3471 water_damage_chain(obj
, here
)
3477 /* initialize acid context: so far, neither seen (dknown) potions of
3478 acid nor unseen have exploded during this water damage sequence */
3479 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3480 acid_ctx
.ctx_valid
= TRUE
;
3482 for (; obj
; obj
= otmp
) {
3483 otmp
= here
? obj
->nexthere
: obj
->nobj
;
3484 water_damage(obj
, (char *) 0, FALSE
);
3487 /* reset acid context */
3488 acid_ctx
.dkn_boom
= acid_ctx
.unk_boom
= 0;
3489 acid_ctx
.ctx_valid
= FALSE
;
3493 * This function is potentially expensive - rolling
3494 * inventory list multiple times. Luckily it's seldom needed.
3495 * Returns TRUE if disrobing made player unencumbered enough to
3496 * crawl out of the current predicament.
3499 emergency_disrobe(lostsome
)
3502 int invc
= inv_cnt(TRUE
);
3504 while (near_capacity() > (Punished
? UNENCUMBERED
: SLT_ENCUMBER
)) {
3505 register struct obj
*obj
, *otmp
= (struct obj
*) 0;
3508 /* Pick a random object */
3511 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
3513 * Undroppables are: body armor, boots, gloves,
3514 * amulets, and rings because of the time and effort
3515 * in removing them + loadstone and other cursed stuff
3516 * for obvious reasons.
3518 if (!((obj
->otyp
== LOADSTONE
&& obj
->cursed
) || obj
== uamul
3519 || obj
== uleft
|| obj
== uright
|| obj
== ublindf
3520 || obj
== uarm
|| obj
== uarmc
|| obj
== uarmg
3521 || obj
== uarmf
|| obj
== uarmu
3522 || (obj
->cursed
&& (obj
== uarmh
|| obj
== uarms
))
3525 /* reached the mark and found some stuff to drop? */
3526 if (--i
< 0 && otmp
)
3533 return FALSE
; /* nothing to drop! */
3534 if (otmp
->owornmask
)
3535 remove_worn_item(otmp
, FALSE
);
3544 /* return TRUE iff player relocated */
3548 const char *pool_of_water
;
3549 boolean inpool_ok
= FALSE
, crawl_ok
;
3552 /* happily wading in the same contiguous pool */
3553 if (u
.uinwater
&& is_pool(u
.ux
- u
.dx
, u
.uy
- u
.dy
)
3554 && (Swimming
|| Amphibious
)) {
3555 /* water effects on objects every now and then */
3563 You("%s into the %s%c", Is_waterlevel(&u
.uz
) ? "plunge" : "fall",
3565 Amphibious
|| Swimming
? '.' : '!');
3566 if (!Swimming
&& !Is_waterlevel(&u
.uz
))
3567 You("sink like %s.", Hallucination
? "the Titanic" : "a rock");
3570 water_damage_chain(invent
, FALSE
);
3572 if (u
.umonnum
== PM_GREMLIN
&& rn2(3))
3573 (void) split_mon(&youmonst
, (struct monst
*) 0);
3574 else if (u
.umonnum
== PM_IRON_GOLEM
) {
3576 i
= Maybe_Half_Phys(d(2, 6));
3579 losehp(i
, "rusting away", KILLED_BY
);
3584 if ((i
= number_leashed()) > 0) {
3585 pline_The("leash%s slip%s loose.", (i
> 1) ? "es" : "",
3586 (i
> 1) ? "" : "s");
3590 if (Amphibious
|| Swimming
) {
3593 pline("But you aren't drowning.");
3594 if (!Is_waterlevel(&u
.uz
)) {
3596 Your("keel hits the bottom.");
3598 You("touch bottom.");
3605 vision_recalc(2); /* unsee old position */
3608 vision_full_recalc
= 1;
3611 if ((Teleportation
|| can_teleport(youmonst
.data
)) && !Unaware
3612 && (Teleport_control
|| rn2(3) < Luck
+ 2)) {
3613 You("attempt a teleport spell."); /* utcsri!carroll */
3614 if (!level
.flags
.noteleport
) {
3616 if (!is_pool(u
.ux
, u
.uy
))
3619 pline_The("attempted teleport spell fails.");
3622 dismount_steed(DISMOUNT_GENERIC
);
3623 if (!is_pool(u
.ux
, u
.uy
))
3627 x
= y
= 0; /* lint suppression */
3628 /* if sleeping, wake up now so that we don't crawl out of water
3629 while still asleep; we can't do that the same way that waking
3630 due to combat is handled; note unmul() clears u.usleep */
3632 unmul("Suddenly you wake up!");
3633 /* being doused will revive from fainting */
3636 /* can't crawl if unable to move (crawl_ok flag stays false) */
3637 if (multi
< 0 || (Upolyd
&& !youmonst
.data
->mmove
))
3639 /* look around for a place to crawl to */
3640 for (i
= 0; i
< 100; i
++) {
3641 x
= rn1(3, u
.ux
- 1);
3642 y
= rn1(3, u
.uy
- 1);
3643 if (crawl_destination(x
, y
)) {
3649 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
3650 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++)
3651 if (crawl_destination(x
, y
)) {
3657 boolean lost
= FALSE
;
3658 /* time to do some strip-tease... */
3659 boolean succ
= Is_waterlevel(&u
.uz
) ? TRUE
: emergency_disrobe(&lost
);
3661 You("try to crawl out of the %s.", hliquid("water"));
3663 You("dump some of your gear to lose weight...");
3665 pline("Pheew! That was close.");
3669 /* still too much weight */
3670 pline("But in vain.");
3674 for (i
= 0; i
< 5; i
++) { /* arbitrary number of loops */
3675 /* killer format and name are reconstructed every iteration
3676 because lifesaving resets them */
3677 pool_of_water
= waterbody_name(u
.ux
, u
.uy
);
3678 killer
.format
= KILLED_BY_AN
;
3679 /* avoid "drowned in [a] water" */
3680 if (!strcmp(pool_of_water
, "water"))
3681 pool_of_water
= "deep water", killer
.format
= KILLED_BY
;
3682 Strcpy(killer
.name
, pool_of_water
);
3684 /* oops, we're still alive. better get out of the water. */
3685 if (safe_teleds(TRUE
))
3686 break; /* successful life-save */
3687 /* nowhere safe to land; repeat drowning loop... */
3688 pline("You're still drowning.");
3692 You("find yourself back %s.",
3693 Is_waterlevel(&u
.uz
) ? "in an air bubble" : "on land");
3703 /* energy is completely gone */
3704 You_feel("momentarily lethargic.");
3706 /* throttle further loss a bit when there's not much left to lose */
3707 if (n
> u
.uenmax
|| n
> u
.ulevel
)
3710 You_feel("your magical energy drain away%c", (n
> u
.uen
) ? '!' : '.');
3713 u
.uenmax
-= rnd(-u
.uen
);
3726 if (near_capacity() >= HVY_ENCUMBER
) {
3727 pline("You're too strained to do that.");
3730 if ((nohands(youmonst
.data
) && !webmaker(youmonst
.data
))
3731 || !youmonst
.data
->mmove
) {
3732 pline("And just how do you expect to do that?");
3734 } else if (u
.ustuck
&& sticks(youmonst
.data
)) {
3735 pline("You'll have to let go of %s first.", mon_nam(u
.ustuck
));
3738 if (u
.ustuck
|| (welded(uwep
) && bimanual(uwep
))) {
3739 Your("%s seem to be too busy for that.", makeplural(body_part(HAND
)));
3742 return untrap(FALSE
);
3745 /* Probability of disabling a trap. Helge Hafting */
3752 /* Only spiders know how to deal with webs reliably */
3753 if (ttmp
->ttyp
== WEB
&& !webmaker(youmonst
.data
))
3755 if (Confusion
|| Hallucination
)
3763 /* Your own traps are better known than others. */
3764 if (ttmp
&& ttmp
->madeby_u
)
3766 if (Role_if(PM_ROGUE
)) {
3767 if (rn2(2 * MAXULEV
) < u
.ulevel
)
3769 if (u
.uhave
.questart
&& chance
> 1)
3771 } else if (Role_if(PM_RANGER
) && chance
> 1)
3776 /* Replace trap with object(s). Helge Hafting */
3778 cnv_trap_obj(otyp
, cnt
, ttmp
, bury_it
)
3784 struct obj
*otmp
= mksobj(otyp
, TRUE
, FALSE
);
3787 otmp
->owt
= weight(otmp
);
3788 /* Only dart traps are capable of being poisonous */
3790 otmp
->opoisoned
= 0;
3791 place_object(otmp
, ttmp
->tx
, ttmp
->ty
);
3793 /* magical digging first disarms this trap, then will unearth it */
3794 (void) bury_an_obj(otmp
, (boolean
*) 0);
3796 /* Sell your own traps only... */
3798 sellobj(otmp
, ttmp
->tx
, ttmp
->ty
);
3801 newsym(ttmp
->tx
, ttmp
->ty
);
3802 if (u
.utrap
&& ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
)
3807 /* while attempting to disarm an adjacent trap, we've fallen into it */
3809 move_into_trap(ttmp
)
3813 xchar x
= ttmp
->tx
, y
= ttmp
->ty
, bx
, by
, cx
, cy
;
3816 bx
= by
= cx
= cy
= 0; /* lint suppression */
3817 /* we know there's no monster in the way, and we're not trapped */
3819 || drag_ball(x
, y
, &bc
, &bx
, &by
, &cx
, &cy
, &unused
, TRUE
)) {
3820 u
.ux0
= u
.ux
, u
.uy0
= u
.uy
;
3823 newsym(u
.ux0
, u
.uy0
);
3825 check_leash(u
.ux0
, u
.uy0
);
3827 move_bc(0, bc
, bx
, by
, cx
, cy
);
3828 /* marking the trap unseen forces dotrap() to treat it like a new
3829 discovery and prevents pickup() -> look_here() -> check_here()
3830 from giving a redundant "there is a <trap> here" message when
3831 there are objects covering this trap */
3832 ttmp
->tseen
= 0; /* hack for check_here() */
3833 /* trigger the trap */
3834 spoteffects(TRUE
); /* pickup() + dotrap() */
3835 exercise(A_WIS
, FALSE
);
3839 /* 0: doesn't even try
3840 * 1: tries and fails
3844 try_disarm(ttmp
, force_failure
)
3846 boolean force_failure
;
3848 struct monst
*mtmp
= m_at(ttmp
->tx
, ttmp
->ty
);
3849 int ttype
= ttmp
->ttyp
;
3850 boolean under_u
= (!u
.dx
&& !u
.dy
);
3851 boolean holdingtrap
= (ttype
== BEAR_TRAP
|| ttype
== WEB
);
3853 /* Test for monster first, monsters are displayed instead of trap. */
3854 if (mtmp
&& (!mtmp
->mtrapped
|| !holdingtrap
)) {
3855 pline("%s is in the way.", Monnam(mtmp
));
3858 /* We might be forced to move onto the trap's location. */
3859 if (sobj_at(BOULDER
, ttmp
->tx
, ttmp
->ty
) && !Passes_walls
&& !under_u
) {
3860 There("is a boulder in your way.");
3863 /* duplicate tight-space checks from test_move */
3864 if (u
.dx
&& u
.dy
&& bad_rock(youmonst
.data
, u
.ux
, ttmp
->ty
)
3865 && bad_rock(youmonst
.data
, ttmp
->tx
, u
.uy
)) {
3866 if ((invent
&& (inv_weight() + weight_cap() > 600))
3867 || bigmonst(youmonst
.data
)) {
3868 /* don't allow untrap if they can't get thru to it */
3869 You("are unable to reach the %s!",
3870 defsyms
[trap_to_defsym(ttype
)].explanation
);
3874 /* untrappable traps are located on the ground. */
3875 if (!can_reach_floor(TRUE
)) {
3876 if (u
.usteed
&& P_SKILL(P_RIDING
) < P_BASIC
)
3879 You("are unable to reach the %s!",
3880 defsyms
[trap_to_defsym(ttype
)].explanation
);
3884 /* Will our hero succeed? */
3885 if (force_failure
|| untrap_prob(ttmp
)) {
3888 if (mtmp
) { /* must be a trap that holds monsters */
3889 if (ttype
== BEAR_TRAP
) {
3892 mtmp
->mhp
-= rnd(4);
3895 } else if (ttype
== WEB
) {
3896 if (!webmaker(youmonst
.data
)) {
3897 struct trap
*ttmp2
= maketrap(u
.ux
, u
.uy
, WEB
);
3901 "webbing sticks to you. You're caught too!");
3902 dotrap(ttmp2
, NOWEBMSG
);
3903 if (u
.usteed
&& u
.utrap
) {
3904 /* you, not steed, are trapped */
3905 dismount_steed(DISMOUNT_FELL
);
3909 pline("%s remains entangled.", Monnam(mtmp
));
3911 } else if (under_u
) {
3914 move_into_trap(ttmp
);
3917 pline("%s %s is difficult to %s.",
3918 ttmp
->madeby_u
? "Your" : under_u
? "This" : "That",
3919 defsyms
[trap_to_defsym(ttype
)].explanation
,
3920 (ttype
== WEB
) ? "remove" : "disarm");
3928 reward_untrap(ttmp
, mtmp
)
3932 if (!ttmp
->madeby_u
) {
3933 if (rnl(10) < 8 && !mtmp
->mpeaceful
&& !mtmp
->msleeping
3934 && !mtmp
->mfrozen
&& !mindless(mtmp
->data
)
3935 && mtmp
->data
->mlet
!= S_HUMAN
) {
3936 mtmp
->mpeaceful
= 1;
3937 set_malign(mtmp
); /* reset alignment */
3938 pline("%s is grateful.", Monnam(mtmp
));
3940 /* Helping someone out of a trap is a nice thing to do,
3941 * A lawful may be rewarded, but not too often. */
3942 if (!rn2(3) && !rnl(8) && u
.ualign
.type
== A_LAWFUL
) {
3944 You_feel("that you did the right thing.");
3950 disarm_holdingtrap(ttmp
) /* Helge Hafting */
3954 int fails
= try_disarm(ttmp
, FALSE
);
3959 /* ok, disarm it. */
3961 /* untrap the monster, if any.
3962 There's no need for a cockatrice test, only the trap is touched */
3963 if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
3965 You("remove %s %s from %s.", the_your
[ttmp
->madeby_u
],
3966 (ttmp
->ttyp
== BEAR_TRAP
) ? "bear trap" : "webbing",
3968 reward_untrap(ttmp
, mtmp
);
3970 if (ttmp
->ttyp
== BEAR_TRAP
) {
3971 You("disarm %s bear trap.", the_your
[ttmp
->madeby_u
]);
3972 cnv_trap_obj(BEARTRAP
, 1, ttmp
, FALSE
);
3973 } else /* if (ttmp->ttyp == WEB) */ {
3974 You("succeed in removing %s web.", the_your
[ttmp
->madeby_u
]);
3978 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
3983 disarm_landmine(ttmp
) /* Helge Hafting */
3986 int fails
= try_disarm(ttmp
, FALSE
);
3990 You("disarm %s land mine.", the_your
[ttmp
->madeby_u
]);
3991 cnv_trap_obj(LAND_MINE
, 1, ttmp
, FALSE
);
3995 /* getobj will filter down to cans of grease and known potions of oil */
3996 static NEARDATA
const char oil
[] = { ALL_CLASSES
, TOOL_CLASS
, POTION_CLASS
,
3999 /* it may not make much sense to use grease on floor boards, but so what? */
4001 disarm_squeaky_board(ttmp
)
4008 obj
= getobj(oil
, "untrap with");
4012 bad_tool
= (obj
->cursed
4013 || ((obj
->otyp
!= POT_OIL
|| obj
->lamplit
)
4014 && (obj
->otyp
!= CAN_OF_GREASE
|| !obj
->spe
)));
4015 fails
= try_disarm(ttmp
, bad_tool
);
4019 /* successfully used oil or grease to fix squeaky board */
4020 if (obj
->otyp
== CAN_OF_GREASE
) {
4021 consume_obj_charge(obj
, TRUE
);
4023 useup(obj
); /* oil */
4026 You("repair the squeaky board."); /* no madeby_u */
4028 newsym(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
);
4029 more_experienced(1, 5);
4034 /* removes traps that shoot arrows, darts, etc. */
4036 disarm_shooting_trap(ttmp
, otyp
)
4040 int fails
= try_disarm(ttmp
, FALSE
);
4044 You("disarm %s trap.", the_your
[ttmp
->madeby_u
]);
4045 cnv_trap_obj(otyp
, 50 - rnl(50), ttmp
, FALSE
);
4049 /* Is the weight too heavy?
4050 * Formula as in near_capacity() & check_capacity() */
4052 try_lift(mtmp
, ttmp
, wt
, stuff
)
4058 int wc
= weight_cap();
4060 if (((wt
* 2) / wc
) >= HVY_ENCUMBER
) {
4061 pline("%s is %s for you to lift.", Monnam(mtmp
),
4062 stuff
? "carrying too much" : "too heavy");
4063 if (!ttmp
->madeby_u
&& !mtmp
->mpeaceful
&& mtmp
->mcanmove
4064 && !mindless(mtmp
->data
) && mtmp
->data
->mlet
!= S_HUMAN
4066 mtmp
->mpeaceful
= 1;
4067 set_malign(mtmp
); /* reset alignment */
4068 pline("%s thinks it was nice of you to try.", Monnam(mtmp
));
4075 /* Help trapped monster (out of a (spiked) pit) */
4077 help_monster_out(mtmp
, ttmp
)
4086 * This works when levitating too -- consistent with the ability
4087 * to hit monsters while levitating.
4089 * Should perhaps check that our hero has arms/hands at the
4090 * moment. Helping can also be done by engulfing...
4092 * Test the monster first - monsters are displayed before traps.
4094 if (!mtmp
->mtrapped
) {
4095 pline("%s isn't trapped.", Monnam(mtmp
));
4098 /* Do you have the necessary capacity to lift anything? */
4099 if (check_capacity((char *) 0))
4102 /* Will our hero succeed? */
4103 if ((uprob
= untrap_prob(ttmp
)) && !mtmp
->msleeping
&& mtmp
->mcanmove
) {
4104 You("try to reach out your %s, but %s backs away skeptically.",
4105 makeplural(body_part(ARM
)), mon_nam(mtmp
));
4109 /* is it a cockatrice?... */
4110 if (touch_petrifies(mtmp
->data
) && !uarmg
&& !Stone_resistance
) {
4111 You("grab the trapped %s using your bare %s.", mtmp
->data
->mname
,
4112 makeplural(body_part(HAND
)));
4114 if (poly_when_stoned(youmonst
.data
) && polymon(PM_STONE_GOLEM
)) {
4115 display_nhwindow(WIN_MESSAGE
, FALSE
);
4119 Sprintf(kbuf
, "trying to help %s out of a pit",
4120 an(mtmp
->data
->mname
));
4125 /* need to do cockatrice check first if sleeping or paralyzed */
4127 You("try to grab %s, but cannot get a firm grasp.", mon_nam(mtmp
));
4128 if (mtmp
->msleeping
) {
4129 mtmp
->msleeping
= 0;
4130 pline("%s awakens.", Monnam(mtmp
));
4135 You("reach out your %s and grab %s.", makeplural(body_part(ARM
)),
4138 if (mtmp
->msleeping
) {
4139 mtmp
->msleeping
= 0;
4140 pline("%s awakens.", Monnam(mtmp
));
4141 } else if (mtmp
->mfrozen
&& !rn2(mtmp
->mfrozen
)) {
4142 /* After such manhandling, perhaps the effect wears off */
4145 pline("%s stirs.", Monnam(mtmp
));
4148 /* is the monster too heavy? */
4149 wt
= inv_weight() + mtmp
->data
->cwt
;
4150 if (!try_lift(mtmp
, ttmp
, wt
, FALSE
))
4153 /* is the monster with inventory too heavy? */
4154 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
4156 if (!try_lift(mtmp
, ttmp
, wt
, TRUE
))
4159 You("pull %s out of the pit.", mon_nam(mtmp
));
4161 fill_pit(mtmp
->mx
, mtmp
->my
);
4162 reward_untrap(ttmp
, mtmp
);
4170 register struct obj
*otmp
;
4175 const char *trapdescr
;
4176 boolean here
, useplural
, confused
= (Confusion
|| Hallucination
),
4177 trap_skipped
= FALSE
, deal_with_floor_trap
;
4179 char the_trap
[BUFSZ
], qbuf
[QBUFSZ
];
4181 if (!getdir((char *) 0))
4186 pline_The("perils lurking there are beyond your grasp.");
4190 if (ttmp
&& !ttmp
->tseen
)
4192 trapdescr
= ttmp
? defsyms
[trap_to_defsym(ttmp
->ttyp
)].explanation
: 0;
4193 here
= (x
== u
.ux
&& y
== u
.uy
); /* !u.dx && !u.dy */
4195 if (here
) /* are there are one or more containers here? */
4196 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4202 deal_with_floor_trap
= can_reach_floor(FALSE
);
4203 if (!deal_with_floor_trap
) {
4206 Strcat(the_trap
, an(trapdescr
));
4208 Strcat(the_trap
, " and ");
4210 Strcat(the_trap
, (boxcnt
== 1) ? "a container" : "containers");
4211 useplural
= ((ttmp
&& boxcnt
> 0) || boxcnt
> 1);
4212 /* note: boxcnt and useplural will always be 0 for !here case */
4214 There("%s %s %s but you can't reach %s%s.",
4215 useplural
? "are" : "is", the_trap
, here
? "here" : "there",
4216 useplural
? "them" : "it",
4217 u
.usteed
? " while mounted" : "");
4218 trap_skipped
= (ttmp
!= 0);
4219 } else { /* deal_with_floor_trap */
4222 Strcpy(the_trap
, the(trapdescr
));
4224 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== SPIKED_PIT
) {
4225 You_cant("do much about %s%s.", the_trap
,
4226 u
.utrap
? " that you're stuck in"
4227 : " while standing on the edge of it");
4228 trap_skipped
= TRUE
;
4229 deal_with_floor_trap
= FALSE
;
4232 qbuf
, "There %s and %s here. %s %s?",
4233 (boxcnt
== 1) ? "is a container" : "are containers",
4235 (ttmp
->ttyp
== WEB
) ? "Remove" : "Disarm", the_trap
);
4236 switch (ynq(qbuf
)) {
4240 trap_skipped
= TRUE
;
4241 deal_with_floor_trap
= FALSE
;
4246 if (deal_with_floor_trap
) {
4248 You("cannot deal with %s while trapped%s!", the_trap
,
4249 (x
== u
.ux
&& y
== u
.uy
) ? " in it" : "");
4252 if ((mtmp
= m_at(x
, y
)) != 0
4253 && (mtmp
->m_ap_type
== M_AP_FURNITURE
4254 || mtmp
->m_ap_type
== M_AP_OBJECT
)) {
4255 stumble_onto_mimic(mtmp
);
4258 switch (ttmp
->ttyp
) {
4261 return disarm_holdingtrap(ttmp
);
4263 return disarm_landmine(ttmp
);
4265 return disarm_squeaky_board(ttmp
);
4267 return disarm_shooting_trap(ttmp
, DART
);
4269 return disarm_shooting_trap(ttmp
, ARROW
);
4273 You("are already on the edge of the pit.");
4277 pline("Try filling the pit instead.");
4280 return help_monster_out(mtmp
, ttmp
);
4282 You("cannot disable %s trap.", !here
? "that" : "this");
4289 for (otmp
= level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
4291 (void) safe_qbuf(qbuf
, "There is ",
4292 " here. Check it for traps?", otmp
,
4293 doname
, ansimpleoname
, "a box");
4294 switch (ynq(qbuf
)) {
4302 && (force
|| (!confused
4303 && rn2(MAXULEV
+ 1 - u
.ulevel
) < 10)))
4304 || (!force
&& confused
&& !rn2(3))) {
4305 You("find a trap on %s!", the(xname(otmp
)));
4307 exercise(A_WIS
, TRUE
);
4309 switch (ynq("Disarm it?")) {
4313 trap_skipped
= TRUE
;
4317 if (otmp
->otrapped
) {
4318 exercise(A_DEX
, TRUE
);
4319 ch
= ACURR(A_DEX
) + u
.ulevel
;
4320 if (Role_if(PM_ROGUE
))
4322 if (!force
&& (confused
|| Fumbling
4323 || rnd(75 + level_difficulty() / 2)
4325 (void) chest_trap(otmp
, FINGER
, TRUE
);
4331 pline("That %s was not trapped.", xname(otmp
));
4334 You("find no traps on %s.", the(xname(otmp
)));
4339 You(trap_skipped
? "find no other traps here."
4340 : "know of no traps here.");
4344 if (stumble_on_door_mimic(x
, y
))
4347 } /* deal_with_floor_trap */
4348 /* doors can be manipulated even while levitating/unskilled riding */
4350 if (!IS_DOOR(levl
[x
][y
].typ
)) {
4352 You("know of no traps there.");
4356 switch (levl
[x
][y
].doormask
) {
4358 You("%s no door there.", Blind
? "feel" : "see");
4361 pline("This door is safely open.");
4364 pline("This door is broken.");
4368 if ((levl
[x
][y
].doormask
& D_TRAPPED
4369 && (force
|| (!confused
&& rn2(MAXULEV
- u
.ulevel
+ 11) < 10)))
4370 || (!force
&& confused
&& !rn2(3))) {
4371 You("find a trap on the door!");
4372 exercise(A_WIS
, TRUE
);
4373 if (ynq("Disarm it?") != 'y')
4375 if (levl
[x
][y
].doormask
& D_TRAPPED
) {
4376 ch
= 15 + (Role_if(PM_ROGUE
) ? u
.ulevel
* 3 : u
.ulevel
);
4377 exercise(A_DEX
, TRUE
);
4378 if (!force
&& (confused
|| Fumbling
4379 || rnd(75 + level_difficulty() / 2) > ch
)) {
4381 b_trapped("door", FINGER
);
4382 levl
[x
][y
].doormask
= D_NODOOR
;
4383 unblock_point(x
, y
);
4385 /* (probably ought to charge for this damage...) */
4386 if (*in_rooms(x
, y
, SHOPBASE
))
4387 add_damage(x
, y
, 0L);
4390 levl
[x
][y
].doormask
&= ~D_TRAPPED
;
4393 pline("This door was not trapped.");
4396 You("find no traps on the door.");
4401 /* for magic unlocking; returns true if targetted monster (which might
4402 be hero) gets untrapped; the trap remains intact */
4404 openholdingtrap(mon
, noticed
)
4406 boolean
*noticed
; /* set to true iff hero notices the effect; */
4407 { /* otherwise left with its previous value intact */
4410 const char *trapdescr
, *which
;
4411 boolean ishero
= (mon
== &youmonst
);
4413 if (mon
== u
.usteed
)
4415 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4416 /* if no trap here or it's not a holding trap, we're done */
4417 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4420 trapdescr
= defsyms
[trap_to_defsym(t
->ttyp
)].explanation
;
4421 which
= t
->tseen
? the_your
[t
->madeby_u
]
4422 : index(vowels
, *trapdescr
) ? "an" : "a";
4427 u
.utrap
= 0; /* released regardless of type */
4429 /* give message only if trap was the expected type */
4430 if (u
.utraptype
== TT_BEARTRAP
|| u
.utraptype
== TT_WEB
) {
4432 Sprintf(buf
, "%s is", noit_Monnam(u
.usteed
));
4434 Strcpy(buf
, "You are");
4435 pline("%s released from %s %s.", buf
, which
, trapdescr
);
4441 if (canspotmon(mon
)) {
4443 pline("%s is released from %s %s.", Monnam(mon
), which
,
4445 } else if (cansee(t
->tx
, t
->ty
) && t
->tseen
) {
4448 pline("%s is released from %s %s.", Something
, which
,
4450 else /* BEAR_TRAP */
4451 pline("%s %s opens.", upstart(strcpy(buf
, which
)), trapdescr
);
4453 /* might pacify monster if adjacent */
4454 if (rn2(2) && distu(mon
->mx
, mon
->my
) <= 2)
4455 reward_untrap(t
, mon
);
4460 /* for magic locking; returns true if targetted monster (which might
4461 be hero) gets hit by a trap (might avoid actually becoming trapped) */
4463 closeholdingtrap(mon
, noticed
)
4465 boolean
*noticed
; /* set to true iff hero notices the effect; */
4466 { /* otherwise left with its previous value intact */
4468 unsigned dotrapflags
;
4469 boolean ishero
= (mon
== &youmonst
), result
;
4471 if (mon
== u
.usteed
)
4473 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4474 /* if no trap here or it's not a holding trap, we're done */
4475 if (!t
|| (t
->ttyp
!= BEAR_TRAP
&& t
->ttyp
!= WEB
))
4480 return FALSE
; /* already trapped */
4482 dotrapflags
= FORCETRAP
;
4483 /* dotrap calls mintrap when mounted hero encounters a web */
4485 dotrapflags
|= NOWEBMSG
;
4487 dotrap(t
, dotrapflags
);
4489 result
= (u
.utrap
!= 0);
4492 return FALSE
; /* already trapped */
4493 /* you notice it if you see the trap close/tremble/whatever
4494 or if you sense the monster who becomes trapped */
4495 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4497 result
= (mintrap(mon
) != 0);
4503 /* for magic unlocking; returns true if targetted monster (which might
4504 be hero) gets hit by a trap (target might avoid its effect) */
4506 openfallingtrap(mon
, trapdoor_only
, noticed
)
4508 boolean trapdoor_only
;
4509 boolean
*noticed
; /* set to true iff hero notices the effect; */
4510 { /* otherwise left with its previous value intact */
4512 boolean ishero
= (mon
== &youmonst
), result
;
4514 if (mon
== u
.usteed
)
4516 t
= t_at(ishero
? u
.ux
: mon
->mx
, ishero
? u
.uy
: mon
->my
);
4517 /* if no trap here or it's not a falling trap, we're done
4518 (note: falling rock traps have a trapdoor in the ceiling) */
4519 if (!t
|| ((t
->ttyp
!= TRAPDOOR
&& t
->ttyp
!= ROCKTRAP
)
4520 && (trapdoor_only
|| (t
->ttyp
!= HOLE
&& t
->ttyp
!= PIT
4521 && t
->ttyp
!= SPIKED_PIT
))))
4526 return FALSE
; /* already trapped */
4528 dotrap(t
, FORCETRAP
);
4529 result
= (u
.utrap
!= 0);
4532 return FALSE
; /* already trapped */
4533 /* you notice it if you see the trap close/tremble/whatever
4534 or if you sense the monster who becomes trapped */
4535 *noticed
= cansee(t
->tx
, t
->ty
) || canspotmon(mon
);
4536 /* monster will be angered; mintrap doesn't handle that */
4539 result
= (mintrap(mon
) != 0);
4541 /* mon might now be on the migrating monsters list */
4546 /* only called when the player is doing something to the chest directly */
4548 chest_trap(obj
, bodypart
, disarm
)
4549 register struct obj
*obj
;
4550 register int bodypart
;
4553 register struct obj
*otmp
= obj
, *otmp2
;
4558 if (get_obj_location(obj
, &cc
.x
, &cc
.y
, 0)) /* might be carried */
4559 obj
->ox
= cc
.x
, obj
->oy
= cc
.y
;
4561 otmp
->otrapped
= 0; /* trap is one-shot; clear flag first in case
4562 chest kills you and ends up in bones file */
4563 You(disarm
? "set it off!" : "trigger a trap!");
4564 display_nhwindow(WIN_MESSAGE
, FALSE
);
4565 if (Luck
> -13 && rn2(13 + Luck
) > 7) { /* saved by luck */
4566 /* trap went off, but good luck prevents damage */
4570 msg
= "explosive charge is a dud";
4574 msg
= "electric charge is grounded";
4578 msg
= "flame fizzles out";
4583 msg
= "poisoned needle misses";
4589 msg
= "gas cloud blows away";
4592 impossible("chest disarm bug");
4597 pline("But luckily the %s!", msg
);
4599 switch (rn2(20) ? ((Luck
>= 13) ? 0 : rn2(13 - Luck
)) : rn2(26)) {
4605 struct monst
*shkp
= 0;
4607 boolean costly
, insider
;
4608 register xchar ox
= obj
->ox
, oy
= obj
->oy
;
4610 /* the obj location need not be that of player */
4611 costly
= (costly_spot(ox
, oy
)
4612 && (shkp
= shop_keeper(*in_rooms(ox
, oy
, SHOPBASE
)))
4613 != (struct monst
*) 0);
4614 insider
= (*u
.ushops
&& inside_shop(u
.ux
, u
.uy
)
4615 && *in_rooms(ox
, oy
, SHOPBASE
) == *u
.ushops
);
4617 pline("%s!", Tobjnam(obj
, "explode"));
4618 Sprintf(buf
, "exploding %s", xname(obj
));
4621 loss
+= stolen_value(obj
, ox
, oy
, (boolean
) shkp
->mpeaceful
,
4623 delete_contents(obj
);
4624 /* unpunish() in advance if either ball or chain (or both)
4625 is going to be destroyed */
4626 if (Punished
&& ((uchain
->ox
== u
.ux
&& uchain
->oy
== u
.uy
)
4627 || (uball
->where
== OBJ_FLOOR
4628 && uball
->ox
== u
.ux
&& uball
->oy
== u
.uy
)))
4631 for (otmp
= level
.objects
[u
.ux
][u
.uy
]; otmp
; otmp
= otmp2
) {
4632 otmp2
= otmp
->nexthere
;
4634 loss
+= stolen_value(otmp
, otmp
->ox
, otmp
->oy
,
4635 (boolean
) shkp
->mpeaceful
, TRUE
);
4639 losehp(Maybe_Half_Phys(d(6, 6)), buf
, KILLED_BY_AN
);
4640 exercise(A_STR
, FALSE
);
4641 if (costly
&& loss
) {
4643 You("owe %ld %s for objects destroyed.", loss
,
4646 You("caused %ld %s worth of damage!", loss
,
4648 make_angry_shk(shkp
, ox
, oy
);
4657 pline("A cloud of noxious gas billows from %s.", the(xname(obj
)));
4658 poisoned("gas cloud", A_STR
, "cloud of poison gas", 15, FALSE
);
4659 exercise(A_CON
, FALSE
);
4665 You_feel("a needle prick your %s.", body_part(bodypart
));
4666 poisoned("needle", A_CON
, "poisoned needle", 10, FALSE
);
4667 exercise(A_CON
, FALSE
);
4680 You("are jolted by a surge of electricity!");
4681 if (Shock_resistance
) {
4682 shieldeff(u
.ux
, u
.uy
);
4683 You("don't seem to be affected.");
4687 destroy_item(RING_CLASS
, AD_ELEC
);
4688 destroy_item(WAND_CLASS
, AD_ELEC
);
4690 losehp(dmg
, "electric shock", KILLED_BY_AN
);
4697 pline("Suddenly you are frozen in place!");
4699 multi_reason
= "frozen by a trap";
4700 exercise(A_DEX
, FALSE
);
4701 nomovemsg
= You_can_move_again
;
4703 You("momentarily stiffen.");
4708 pline("A cloud of %s gas billows from %s.",
4709 Blind
? blindgas
[rn2(SIZE(blindgas
))] : rndcolor(),
4713 pline("What a groovy feeling!");
4715 You("%s%s...", stagger(youmonst
.data
, "stagger"),
4716 Halluc_resistance
? ""
4717 : Blind
? " and get dizzy"
4718 : " and your vision blurs");
4720 make_stunned((HStun
& TIMEOUT
) + (long) rn1(7, 16), FALSE
);
4721 (void) make_hallucinated(
4722 (HHallucination
& TIMEOUT
) + (long) rn1(5, 16), FALSE
, 0L);
4725 impossible("bad chest trap");
4728 bot(); /* to get immediate botl re-display */
4738 register struct trap
*trap
= ftrap
;
4741 if (trap
->tx
== x
&& trap
->ty
== y
)
4745 return (struct trap
*) 0;
4750 register struct trap
*trap
;
4752 register struct trap
*ttmp
;
4754 clear_conjoined_pits(trap
);
4755 if (trap
== ftrap
) {
4756 ftrap
= ftrap
->ntrap
;
4758 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
)
4759 if (ttmp
->ntrap
== trap
)
4762 panic("deltrap: no preceding trap!");
4763 ttmp
->ntrap
= trap
->ntrap
;
4765 if (Sokoban
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== HOLE
))
4766 maybe_finish_sokoban();
4771 conjoined_pits(trap2
, trap1
, u_entering_trap2
)
4772 struct trap
*trap2
, *trap1
;
4773 boolean u_entering_trap2
;
4775 int dx
, dy
, diridx
, adjidx
;
4777 if (!trap1
|| !trap2
)
4779 if (!isok(trap2
->tx
, trap2
->ty
) || !isok(trap1
->tx
, trap1
->ty
)
4780 || !(trap2
->ttyp
== PIT
|| trap2
->ttyp
== SPIKED_PIT
)
4781 || !(trap1
->ttyp
== PIT
|| trap1
->ttyp
== SPIKED_PIT
)
4782 || (u_entering_trap2
&& !(u
.utrap
&& u
.utraptype
== TT_PIT
)))
4784 dx
= sgn(trap2
->tx
- trap1
->tx
);
4785 dy
= sgn(trap2
->ty
- trap1
->ty
);
4786 for (diridx
= 0; diridx
< 8; diridx
++)
4787 if (xdir
[diridx
] == dx
&& ydir
[diridx
] == dy
)
4789 /* diridx is valid if < 8 */
4791 adjidx
= (diridx
+ 4) % 8;
4792 if ((trap1
->conjoined
& (1 << diridx
))
4793 && (trap2
->conjoined
& (1 << adjidx
)))
4800 clear_conjoined_pits(trap
)
4803 int diridx
, adjidx
, x
, y
;
4806 if (trap
&& (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
)) {
4807 for (diridx
= 0; diridx
< 8; ++diridx
) {
4808 if (trap
->conjoined
& (1 << diridx
)) {
4809 x
= trap
->tx
+ xdir
[diridx
];
4810 y
= trap
->ty
+ ydir
[diridx
];
4812 && (t
= t_at(x
, y
)) != 0
4813 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4814 adjidx
= (diridx
+ 4) % 8;
4815 t
->conjoined
&= ~(1 << adjidx
);
4817 trap
->conjoined
&= ~(1 << diridx
);
4825 * Mark all neighboring pits as conjoined pits.
4826 * (currently not called from anywhere)
4829 join_adjacent_pits(trap
)
4837 for (diridx
= 0; diridx
< 8; ++diridx
) {
4838 x
= trap
->tx
+ xdir
[diridx
];
4839 y
= trap
->ty
+ ydir
[diridx
];
4841 if ((t
= t_at(x
, y
)) != 0
4842 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
)) {
4843 trap
->conjoined
|= (1 << diridx
);
4844 join_adjacent_pits(t
);
4846 trap
->conjoined
&= ~(1 << diridx
);
4853 * Returns TRUE if you escaped a pit and are standing on the precipice.
4856 uteetering_at_seen_pit(trap
)
4859 if (trap
&& trap
->tseen
&& (!u
.utrap
|| u
.utraptype
!= TT_PIT
)
4860 && (trap
->ttyp
== PIT
|| trap
->ttyp
== SPIKED_PIT
))
4866 /* Destroy a trap that emanates from the floor. */
4869 register struct trap
*ttmp
;
4871 /* some of these are arbitrary -dlc */
4872 if (ttmp
&& ((ttmp
->ttyp
== SQKY_BOARD
) || (ttmp
->ttyp
== BEAR_TRAP
)
4873 || (ttmp
->ttyp
== LANDMINE
) || (ttmp
->ttyp
== FIRE_TRAP
)
4874 || (ttmp
->ttyp
== PIT
) || (ttmp
->ttyp
== SPIKED_PIT
)
4875 || (ttmp
->ttyp
== HOLE
) || (ttmp
->ttyp
== TRAPDOOR
)
4876 || (ttmp
->ttyp
== TELEP_TRAP
) || (ttmp
->ttyp
== LEVEL_TELEP
)
4877 || (ttmp
->ttyp
== WEB
) || (ttmp
->ttyp
== MAGIC_TRAP
)
4878 || (ttmp
->ttyp
== ANTI_MAGIC
))) {
4879 register struct monst
*mtmp
;
4881 if (ttmp
->tx
== u
.ux
&& ttmp
->ty
== u
.uy
) {
4884 } else if ((mtmp
= m_at(ttmp
->tx
, ttmp
->ty
)) != 0) {
4893 /* used for doors (also tins). can be used for anything else that opens. */
4895 b_trapped(item
, bodypart
)
4899 int lvl
= level_difficulty(),
4900 dmg
= rnd(5 + (lvl
< 5 ? lvl
: 2 + lvl
/ 2));
4902 pline("KABOOM!! %s was booby-trapped!", The(item
));
4904 losehp(Maybe_Half_Phys(dmg
), "explosion", KILLED_BY_AN
);
4905 exercise(A_STR
, FALSE
);
4907 exercise(A_CON
, FALSE
);
4908 make_stunned((HStun
& TIMEOUT
) + (long) dmg
, TRUE
);
4911 /* Monster is hit by trap. */
4912 /* Note: doesn't work if both obj and d_override are null */
4914 thitm(tlev
, mon
, obj
, d_override
, nocorpse
)
4922 boolean trapkilled
= FALSE
;
4927 strike
= (find_mac(mon
) + tlev
+ obj
->spe
<= rnd(20));
4929 strike
= (find_mac(mon
) + tlev
<= rnd(20));
4931 /* Actually more accurate than thitu, which doesn't take
4932 * obj->spe into account.
4935 if (obj
&& cansee(mon
->mx
, mon
->my
))
4936 pline("%s is almost hit by %s!", Monnam(mon
), doname(obj
));
4940 if (obj
&& cansee(mon
->mx
, mon
->my
))
4941 pline("%s is hit by %s!", Monnam(mon
), doname(obj
));
4945 dam
= dmgval(obj
, mon
);
4950 if (mon
->mhp
<= 0) {
4951 int xx
= mon
->mx
, yy
= mon
->my
;
4953 monkilled(mon
, "", nocorpse
? -AD_RBRE
: AD_PHYS
);
4954 if (mon
->mhp
<= 0) {
4960 if (obj
&& (!strike
|| d_override
)) {
4961 place_object(obj
, mon
->mx
, mon
->my
);
4975 return (boolean
) (u
.usleep
4977 && (!strncmp(nomovemsg
, "You awake", 9)
4978 || !strncmp(nomovemsg
, "You regain con", 14)
4979 || !strncmp(nomovemsg
, "You are consci", 14))));
4982 static const char lava_killer
[] = "molten lava";
4987 register struct obj
*obj
, *obj2
;
4988 int dmg
= d(6, 6); /* only applicable for water walking */
4989 boolean usurvive
, boil_away
;
4992 if (likes_lava(youmonst
.data
))
4995 usurvive
= Fire_resistance
|| (Wwalking
&& dmg
< u
.uhp
);
4997 * A timely interrupt might manage to salvage your life
4998 * but not your gear. For scrolls and potions this
4999 * will destroy whole stacks, where fire resistant hero
5000 * survivor only loses partial stacks via destroy_item().
5002 * Flag items to be destroyed before any messages so
5003 * that player causing hangup at --More-- won't get an
5004 * emergency save file created before item destruction.
5007 for (obj
= invent
; obj
; obj
= obj
->nobj
)
5008 if ((is_organic(obj
) || obj
->oclass
== POTION_CLASS
)
5009 && !obj
->oerodeproof
5010 && objects
[obj
->otyp
].oc_oprop
!= FIRE_RES
5011 && obj
->otyp
!= SCR_FIRE
&& obj
->otyp
!= SPE_FIREBALL
5012 && !obj_resists(obj
, 0, 0)) /* for invocation items */
5015 /* Check whether we should burn away boots *first* so we know whether to
5016 * make the player sink into the lava. Assumption: water walking only
5019 if (uarmf
&& is_organic(uarmf
) && !uarmf
->oerodeproof
) {
5021 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5022 iflags
.in_lava_effects
++; /* (see above) */
5025 iflags
.in_lava_effects
--;
5028 if (!Fire_resistance
) {
5030 pline_The("%s here burns you!", hliquid("lava"));
5032 losehp(dmg
, lava_killer
, KILLED_BY
); /* lava damage */
5036 You("fall into the %s!", hliquid("lava"));
5038 usurvive
= Lifesaved
|| discover
;
5042 /* prevent remove_worn_item() -> Boots_off(WATER_WALKING_BOOTS) ->
5043 spoteffects() -> lava_effects() recursion which would
5044 successfully delete (via useupall) the no-longer-worn boots;
5045 once recursive call returned, we would try to delete them again
5046 here in the outer call (and access stale memory, probably panic) */
5047 iflags
.in_lava_effects
++;
5049 for (obj
= invent
; obj
; obj
= obj2
) {
5051 /* above, we set in_use for objects which are to be destroyed */
5052 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
&& !Blind
) {
5054 pline("%s glows a strange %s, but remains intact.",
5055 The(xname(obj
)), hcolor("dark red"));
5056 } else if (obj
->in_use
) {
5057 if (obj
->owornmask
) {
5059 pline("%s into flame!", Yobjnam2(obj
, "burst"));
5060 remove_worn_item(obj
, TRUE
);
5066 iflags
.in_lava_effects
--;
5069 boil_away
= (u
.umonnum
== PM_WATER_ELEMENTAL
5070 || u
.umonnum
== PM_STEAM_VORTEX
5071 || u
.umonnum
== PM_FOG_CLOUD
);
5074 /* killer format and name are reconstructed every iteration
5075 because lifesaving resets them */
5076 killer
.format
= KILLED_BY
;
5077 Strcpy(killer
.name
, lava_killer
);
5078 You("%s...", boil_away
? "boil away" : "burn to a crisp");
5080 if (safe_teleds(TRUE
))
5081 break; /* successful life-save */
5082 /* nowhere safe to land; repeat burning loop */
5083 pline("You're still burning.");
5085 You("find yourself back on solid %s.", surface(u
.ux
, u
.uy
));
5087 } else if (!Wwalking
&& (!u
.utrap
|| u
.utraptype
!= TT_LAVA
)) {
5088 boil_away
= !Fire_resistance
;
5089 /* if not fire resistant, sink_into_lava() will quickly be fatal;
5090 hero needs to escape immediately */
5091 u
.utrap
= rn1(4, 4) + ((boil_away
? 2 : rn1(4, 12)) << 8);
5092 u
.utraptype
= TT_LAVA
;
5093 You("sink into the %s%s!", hliquid("lava"),
5095 ? ", but it only burns slightly"
5096 : " and are about to be immolated");
5098 losehp(!boil_away
? 1 : (u
.uhp
/ 2), lava_killer
,
5099 KILLED_BY
); /* lava damage */
5103 destroy_item(SCROLL_CLASS
, AD_FIRE
);
5104 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
5105 destroy_item(POTION_CLASS
, AD_FIRE
);
5109 /* called each turn when trapped in lava */
5113 static const char sink_deeper
[] = "You sink deeper into the lava.";
5115 if (!u
.utrap
|| u
.utraptype
!= TT_LAVA
) {
5116 ; /* do nothing; this shouldn't happen */
5117 } else if (!is_lava(u
.ux
, u
.uy
)) {
5118 u
.utrap
= 0; /* this shouldn't happen either */
5119 } else if (!u
.uinvulnerable
) {
5120 /* ordinarily we'd have to be fire resistant to survive long
5121 enough to become stuck in lava, but it can happen without
5122 resistance if water walking boots allow survival and then
5123 get burned up; u.utrap time will be quite short in that case */
5124 if (!Fire_resistance
)
5125 u
.uhp
= (u
.uhp
+ 2) / 3;
5127 u
.utrap
-= (1 << 8);
5128 if (u
.utrap
< (1 << 8)) {
5129 killer
.format
= KILLED_BY
;
5130 Strcpy(killer
.name
, "molten lava");
5131 You("sink below the surface and die.");
5132 burn_away_slime(); /* add insult to injury? */
5134 /* can only get here via life-saving; try to get away from lava */
5136 (void) safe_teleds(TRUE
);
5137 } else if (!u
.umoved
) {
5138 /* can't fully turn into slime while in lava, but might not
5139 have it be burned away until you've come awfully close */
5140 if (Slimed
&& rnd(10 - 1) >= (int) (Slimed
& TIMEOUT
)) {
5151 /* called when something has been done (breaking a boulder, for instance)
5152 which entails a luck penalty if performed on a sokoban level */
5158 /* TODO: issue some feedback so that player can learn that whatever
5159 he/she just did is a naughty thing to do in sokoban and should
5160 probably be avoided in future....
5161 Caveat: doing this might introduce message sequencing issues,
5162 depending upon feedback during the various actions which trigger
5163 Sokoban luck penalties. */
5167 /* called when a trap has been deleted or had its ttyp replaced */
5169 maybe_finish_sokoban()
5173 if (Sokoban
&& !in_mklev
) {
5174 /* scan all remaining traps, ignoring any created by the hero;
5175 if this level has no more pits or holes, the current sokoban
5176 puzzle has been solved */
5177 for (t
= ftrap
; t
; t
= t
->ntrap
) {
5180 if (t
->ttyp
== PIT
|| t
->ttyp
== HOLE
)
5184 /* we've passed the last trap without finding a pit or hole;
5185 clear the sokoban_rules flag so that luck penalties for
5186 things like breaking boulders or jumping will no longer
5187 be given, and restrictions on diagonal moves are lifted */
5188 Sokoban
= 0; /* clear level.flags.sokoban_rules */
5189 /* TODO: give some feedback about solving the sokoban puzzle
5190 (perhaps say "congratulations" in Japanese?) */