1 /* NetHack 3.6 polyself.c $NHDT-Date: 1457572516 2016/03/10 01:15:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.108 $ */
2 /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
3 /* NetHack may be freely redistributed. See license for details. */
6 * Polymorph self routine.
8 * Note: the light source handling code assumes that both youmonst.m_id
9 * and youmonst.mx will always remain 0 when it handles the case of the
10 * player polymorphed into a light-emitting monster.
12 * Transformation sequences:
13 * /-> polymon poly into monster form
15 * \-> newman -> polyman fail to poly, get human form
17 * rehumanize -> polyman return to original form
19 * polymon (called directly) usually golem petrification
24 STATIC_DCL
void FDECL(check_strangling
, (BOOLEAN_P
));
25 STATIC_DCL
void FDECL(polyman
, (const char *, const char *));
26 STATIC_DCL
void NDECL(break_armor
);
27 STATIC_DCL
void FDECL(drop_weapon
, (int));
28 STATIC_DCL
void NDECL(uunstick
);
29 STATIC_DCL
int FDECL(armor_to_dragon
, (int));
30 STATIC_DCL
void NDECL(newman
);
31 STATIC_DCL
void NDECL(polysense
);
33 STATIC_VAR
const char no_longer_petrify_resistant
[] =
34 "No longer petrify-resistant, you";
36 /* controls whether taking on new form or becoming new man can also
37 change sex (ought to be an arg to polymon() and newman() instead) */
38 STATIC_VAR
int sex_change_ok
= 0;
40 /* update the youmonst.data structure pointer and intrinsics */
44 struct permonst
*mdat
= &mons
[u
.umonnum
];
45 int new_speed
, old_speed
= youmonst
.data
? youmonst
.data
->mmove
: 0;
47 set_mon_data(&youmonst
, mdat
, 0);
49 #define PROPSET(PropIndx, ON) \
52 u.uprops[PropIndx].intrinsic |= FROMFORM; \
54 u.uprops[PropIndx].intrinsic &= ~FROMFORM; \
57 PROPSET(FIRE_RES
, resists_fire(&youmonst
));
58 PROPSET(COLD_RES
, resists_cold(&youmonst
));
59 PROPSET(SLEEP_RES
, resists_sleep(&youmonst
));
60 PROPSET(DISINT_RES
, resists_disint(&youmonst
));
61 PROPSET(SHOCK_RES
, resists_elec(&youmonst
));
62 PROPSET(POISON_RES
, resists_poison(&youmonst
));
63 PROPSET(ACID_RES
, resists_acid(&youmonst
));
64 PROPSET(STONE_RES
, resists_ston(&youmonst
));
66 /* resists_drli() takes wielded weapon into account; suppress it */
67 struct obj
*save_uwep
= uwep
;
70 PROPSET(DRAIN_RES
, resists_drli(&youmonst
));
73 /* resists_magm() takes wielded, worn, and carried equipment into
74 into account; cheat and duplicate its monster-specific part */
75 PROPSET(ANTIMAGIC
, (dmgtype(mdat
, AD_MAGM
)
76 || mdat
== &mons
[PM_BABY_GRAY_DRAGON
]
77 || dmgtype(mdat
, AD_RBRE
)));
78 PROPSET(SICK_RES
, (mdat
->mlet
== S_FUNGUS
|| mdat
== &mons
[PM_GHOUL
]));
80 PROPSET(STUNNED
, (mdat
== &mons
[PM_STALKER
] || is_bat(mdat
)));
81 PROPSET(HALLUC_RES
, dmgtype(mdat
, AD_HALU
));
82 PROPSET(SEE_INVIS
, perceives(mdat
));
83 PROPSET(TELEPAT
, telepathic(mdat
));
84 PROPSET(INFRAVISION
, infravision(mdat
));
85 PROPSET(INVIS
, pm_invisible(mdat
));
86 PROPSET(TELEPORT
, can_teleport(mdat
));
87 PROPSET(TELEPORT_CONTROL
, control_teleport(mdat
));
88 PROPSET(LEVITATION
, is_floater(mdat
));
89 PROPSET(FLYING
, is_flyer(mdat
));
90 PROPSET(SWIMMING
, is_swimmer(mdat
));
91 /* [don't touch MAGICAL_BREATHING here; both Amphibious and Breathless
92 key off of it but include different monster forms...] */
93 PROPSET(PASSES_WALLS
, passes_walls(mdat
));
94 PROPSET(REGENERATION
, regenerates(mdat
));
95 PROPSET(REFLECTING
, (mdat
== &mons
[PM_SILVER_DRAGON
]));
98 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
101 if (youmonst
.movement
) {
102 new_speed
= mdat
->mmove
;
103 /* prorate unused movement if new form is slower so that
104 it doesn't get extra moves leftover from previous form;
105 if new form is faster, leave unused movement as is */
106 if (new_speed
< old_speed
)
107 youmonst
.movement
= new_speed
* youmonst
.movement
/ old_speed
;
110 #ifdef STATUS_VIA_WINDOWPORT
111 status_initialize(REASSESS_ONLY
);
115 /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */
119 /* floating overrides flight; normally float_up() and float_down()
120 handle this, but sometimes they're skipped */
121 if (HLevitation
|| ELevitation
)
122 BFlying
|= I_SPECIAL
;
124 BFlying
&= ~I_SPECIAL
;
128 /* for changing into form that's immune to strangulation */
133 /* on -- maybe resume strangling */
135 /* when Strangled is already set, polymorphing from one
136 vulnerable form into another causes the counter to be reset */
137 if (uamul
&& uamul
->otyp
== AMULET_OF_STRANGULATION
138 && can_be_strangled(&youmonst
)) {
141 Your("%s %s your %s!", simpleonames(uamul
),
142 Strangled
? "still constricts" : "begins constricting",
143 body_part(NECK
)); /* "throat" */
144 makeknown(AMULET_OF_STRANGULATION
);
147 /* off -- maybe block strangling */
149 if (Strangled
&& !can_be_strangled(&youmonst
)) {
152 You("are no longer being strangled.");
157 /* make a (new) human out of the player */
160 const char *fmt
, *arg
;
162 boolean sticky
= (sticks(youmonst
.data
) && u
.ustuck
&& !u
.uswallow
),
163 was_mimicking
= (youmonst
.m_ap_type
== M_AP_OBJECT
);
164 boolean was_blind
= !!Blind
;
167 u
.acurr
= u
.macurr
; /* restore old attribs */
169 u
.umonnum
= u
.umonster
;
170 flags
.female
= u
.mfemale
;
185 youmonst
.m_ap_type
= M_AP_NOTHING
;
191 /* check whether player foolishly genocided self while poly'd */
192 if ((mvitals
[urole
.malenum
].mvflags
& G_GENOD
)
193 || (urole
.femalenum
!= NON_PM
194 && (mvitals
[urole
.femalenum
].mvflags
& G_GENOD
))
195 || (mvitals
[urace
.malenum
].mvflags
& G_GENOD
)
196 || (urace
.femalenum
!= NON_PM
197 && (mvitals
[urace
.femalenum
].mvflags
& G_GENOD
))) {
198 /* intervening activity might have clobbered genocide info */
199 struct kinfo
*kptr
= find_delayed_killer(POLYMORPH
);
201 if (kptr
!= (struct kinfo
*) 0 && kptr
->name
[0]) {
202 killer
.format
= kptr
->format
;
203 Strcpy(killer
.name
, kptr
->name
);
205 killer
.format
= KILLED_BY
;
206 Strcpy(killer
.name
, "self-genocide");
208 dealloc_killer(kptr
);
212 if (u
.twoweap
&& !could_twoweap(youmonst
.data
))
215 if (u
.utraptype
== TT_PIT
&& u
.utrap
) {
216 u
.utrap
= rn1(6, 2); /* time to escape resets */
218 if (was_blind
&& !Blind
) { /* reverting from eyeless */
220 make_blinded(0L, TRUE
); /* remove blindness */
222 check_strangling(TRUE
);
224 if (!Levitation
&& !u
.ustuck
&& is_pool_or_lava(u
.ux
, u
.uy
))
233 /* setting u.umonster for caveman/cavewoman or priest/priestess
234 swap unintentionally makes `Upolyd' appear to be true */
235 boolean already_polyd
= (boolean
) Upolyd
;
237 /* Some monsters are always of one sex and their sex can't be changed;
238 * Succubi/incubi can change, but are handled below.
240 * !already_polyd check necessary because is_male() and is_female()
241 * are true if the player is a priest/priestess.
244 || (!is_male(youmonst
.data
) && !is_female(youmonst
.data
)
245 && !is_neuter(youmonst
.data
)))
246 flags
.female
= !flags
.female
;
247 if (already_polyd
) /* poly'd: also change saved sex */
248 u
.mfemale
= !u
.mfemale
;
249 max_rank_sz(); /* [this appears to be superfluous] */
250 if ((already_polyd
? u
.mfemale
: flags
.female
) && urole
.name
.f
)
251 Strcpy(pl_character
, urole
.name
.f
);
253 Strcpy(pl_character
, urole
.name
.m
);
254 u
.umonster
= ((already_polyd
? u
.mfemale
: flags
.female
)
255 && urole
.femalenum
!= NON_PM
)
258 if (!already_polyd
) {
259 u
.umonnum
= u
.umonster
;
260 } else if (u
.umonnum
== PM_SUCCUBUS
|| u
.umonnum
== PM_INCUBUS
) {
261 flags
.female
= !flags
.female
;
262 /* change monster type to match new sex */
263 u
.umonnum
= (u
.umonnum
== PM_SUCCUBUS
) ? PM_INCUBUS
: PM_SUCCUBUS
;
271 int i
, oldlvl
, newlvl
, hpmax
, enmax
;
274 newlvl
= oldlvl
+ rn1(5, -2); /* new = old + {-2,-1,0,+1,+2} */
275 if (newlvl
> 127 || newlvl
< 1) { /* level went below 0? */
276 goto dead
; /* old level is still intact (in case of lifesaving) */
278 if (newlvl
> MAXULEV
)
280 /* If your level goes down, your peak level goes down by
281 the same amount so that you can't simply use blessed
282 full healing to undo the decrease. But if your level
283 goes up, your peak level does *not* undergo the same
284 adjustment; you might end up losing out on the chance
285 to regain some levels previously lost to other causes. */
287 u
.ulevelmax
-= (oldlvl
- newlvl
);
288 if (u
.ulevelmax
< newlvl
)
289 u
.ulevelmax
= newlvl
;
292 if (sex_change_ok
&& !rn2(10))
295 adjabil(oldlvl
, (int) u
.ulevel
);
296 reset_rndmonst(NON_PM
); /* new monster generation criteria */
298 /* random experience points for the new experience level */
299 u
.uexp
= rndexp(FALSE
);
301 /* set up new attribute points (particularly Con) */
306 * remove level-gain based HP from any extra HP accumulated
307 * (the "extra" might actually be negative);
308 * modify the extra, retaining {80%, 90%, 100%, or 110%};
309 * add in newly generated set of level-gain HP.
311 * (This used to calculate new HP in direct proportion to old HP,
312 * but that was subject to abuse: accumulate a large amount of
313 * extra HP, drain level down to 1, then polyself to level 2 or 3
314 * [lifesaving capability needed to handle level 0 and -1 cases]
315 * and the extra got multiplied by 2 or 3. Repeat the level
316 * drain and polyself steps until out of lifesaving capability.)
319 for (i
= 0; i
< oldlvl
; i
++)
320 hpmax
-= (int) u
.uhpinc
[i
];
321 /* hpmax * rn1(4,8) / 10; 0.95*hpmax on average */
322 hpmax
= rounddiv((long) hpmax
* (long) rn1(4, 8), 10);
323 for (i
= 0; (u
.ulevel
= i
) < newlvl
; i
++)
325 if (hpmax
< u
.ulevel
)
326 hpmax
= u
.ulevel
; /* min of 1 HP per level */
327 /* retain same proportion for current HP; u.uhp * hpmax / u.uhpmax */
328 u
.uhp
= rounddiv((long) u
.uhp
* (long) hpmax
, u
.uhpmax
);
331 * Do the same for spell power.
334 for (i
= 0; i
< oldlvl
; i
++)
335 enmax
-= (int) u
.ueninc
[i
];
336 enmax
= rounddiv((long) enmax
* (long) rn1(4, 8), 10);
337 for (i
= 0; (u
.ulevel
= i
) < newlvl
; i
++)
339 if (enmax
< u
.ulevel
)
341 u
.uen
= rounddiv((long) u
.uen
* (long) enmax
,
342 ((u
.uenmax
< 1) ? 1 : u
.uenmax
));
344 /* [should alignment record be tweaked too?] */
346 u
.uhunger
= rn1(500, 500);
348 make_sick(0L, (char *) 0, FALSE
, SICK_ALL
);
350 make_stoned(0L, (char *) 0, 0, (char *) 0);
352 if (Polymorph_control
) { /* even when Stunned || Unaware */
356 dead
: /* we come directly here if their experience level went to 0 or
358 Your("new form doesn't seem healthy enough to survive.");
359 killer
.format
= KILLED_BY_AN
;
360 Strcpy(killer
.name
, "unsuccessful polymorph");
363 return; /* lifesaved */
367 polyman("feel like a new %s!",
368 /* use saved gender we're about to revert to, not current */
369 (u
.mfemale
&& urace
.individual
.f
)
371 : (urace
.individual
.m
) ? urace
.individual
.m
: urace
.noun
);
373 Your("body transforms, but there is still slime on you.");
374 make_slimed(10L, (const char *) 0);
379 (void) encumber_msg();
381 retouch_equipment(2);
383 selftouch(no_longer_petrify_resistant
);
391 int old_light
, new_light
, mntmp
, class, tryct
;
392 boolean forcecontrol
= (psflags
== 1), monsterpoly
= (psflags
== 2),
393 draconian
= (uarm
&& Is_dragon_armor(uarm
)),
394 iswere
= (u
.ulycn
>= LOW_PM
), isvamp
= is_vampire(youmonst
.data
),
395 controllable_poly
= Polymorph_control
&& !(Stunned
|| Unaware
);
398 pline("You fail to transform!");
401 /* being Stunned|Unaware doesn't negate this aspect of Poly_control */
402 if (!Polymorph_control
&& !forcecontrol
&& !draconian
&& !iswere
404 if (rn2(20) > ACURR(A_CON
)) {
405 You1(shudder_for_moment
);
406 losehp(rnd(30), "system shock", KILLED_BY_AN
);
407 exercise(A_CON
, FALSE
);
411 old_light
= emits_light(youmonst
.data
);
414 if (monsterpoly
&& isvamp
)
417 if (controllable_poly
|| forcecontrol
) {
421 getlin("Become what kind of monster? [type the name]", buf
);
422 (void) mungspaces(buf
);
423 if (*buf
== '\033') {
424 /* user is cancelling controlled poly */
425 if (forcecontrol
) { /* wizard mode #polyself */
429 Strcpy(buf
, "*"); /* resort to random */
431 if (!strcmp(buf
, "*") || !strcmp(buf
, "random")) {
432 /* explicitly requesting random result */
433 tryct
= 0; /* will skip thats_enough_tries */
434 continue; /* end do-while(--tryct > 0) loop */
437 mntmp
= name_to_mon(buf
);
438 if (mntmp
< LOW_PM
) {
440 class = name_to_monclass(buf
, &mntmp
);
441 if (class && mntmp
== NON_PM
)
442 mntmp
= mkclass_poly(class);
444 if (mntmp
< LOW_PM
) {
446 pline("I've never heard of such monsters.");
448 You_cant("polymorph into any of those.");
449 } else if (iswere
&& (were_beastie(mntmp
) == u
.ulycn
450 || mntmp
== counter_were(u
.ulycn
)
451 || (Upolyd
&& mntmp
== PM_HUMAN
))) {
453 /* Note: humans are illegal as monsters, but an
454 * illegal monster forces newman(), which is what we
455 * want if they specified a human.... */
456 } else if (!polyok(&mons
[mntmp
])
457 && !(mntmp
== PM_HUMAN
|| your_race(&mons
[mntmp
])
458 || mntmp
== urole
.malenum
459 || mntmp
== urole
.femalenum
)) {
462 /* mkclass_poly() can pick a !polyok()
463 candidate; if so, usually try again */
465 if (rn2(3) || --tryct
> 0)
467 /* no retries left; put one back on counter
468 so that end of loop decrement will yield
469 0 and trigger thats_enough_tries message */
472 pm_name
= mons
[mntmp
].mname
;
473 if (the_unique_pm(&mons
[mntmp
]))
474 pm_name
= the(pm_name
);
475 else if (!type_is_pname(&mons
[mntmp
]))
476 pm_name
= an(pm_name
);
477 You_cant("polymorph into %s.", pm_name
);
480 } while (--tryct
> 0);
482 pline1(thats_enough_tries
);
483 /* allow skin merging, even when polymorph is controlled */
484 if (draconian
&& (tryct
<= 0 || mntmp
== armor_to_dragon(uarm
->otyp
)))
486 if (isvamp
&& (tryct
<= 0 || mntmp
== PM_WOLF
|| mntmp
== PM_FOG_CLOUD
487 || is_bat(&mons
[mntmp
])))
489 } else if (draconian
|| iswere
|| isvamp
) {
490 /* special changes that don't require polyok() */
493 mntmp
= armor_to_dragon(uarm
->otyp
);
494 if (!(mvitals
[mntmp
].mvflags
& G_GENOD
)) {
495 /* allow G_EXTINCT */
496 if (Is_dragon_scales(uarm
)) {
497 /* dragon scales remain intact as uskin */
498 You("merge with your scaly armor.");
499 } else { /* dragon scale mail */
500 /* d.scale mail first reverts to scales */
503 /* similar to noarmor(invent.c),
504 shorten to "<color> scale mail" */
505 dsmail
= strcpy(buf
, simpleonames(uarm
));
506 if ((p
= strstri(dsmail
, " dragon ")) != 0)
507 while ((p
[1] = p
[8]) != '\0')
509 /* tricky phrasing; dragon scale mail
510 is singular, dragon scales are plural */
511 Your("%s reverts to scales as you merge with them.",
513 /* uarm->spe enchantment remains unchanged;
514 re-converting scales to mail poses risk
515 of evaporation due to over enchanting */
516 uarm
->otyp
+= GRAY_DRAGON_SCALES
- GRAY_DRAGON_SCALE_MAIL
;
518 context
.botl
= 1; /* AC is changing */
521 uarm
= (struct obj
*) 0;
522 /* save/restore hack */
523 uskin
->owornmask
|= I_SPECIAL
;
528 if (Upolyd
&& were_beastie(mntmp
) != u
.ulycn
)
529 mntmp
= PM_HUMAN
; /* Illegal; force newman() */
534 if (mntmp
< LOW_PM
|| (mons
[mntmp
].geno
& G_UNIQ
))
535 mntmp
= (youmonst
.data
!= &mons
[PM_VAMPIRE
] && !rn2(10))
537 : !rn2(4) ? PM_FOG_CLOUD
: PM_VAMPIRE_BAT
;
538 if (controllable_poly
) {
539 Sprintf(buf
, "Become %s?", an(mons
[mntmp
].mname
));
544 /* if polymon fails, "you feel" message has been given
545 so don't follow up with another polymon or newman;
546 sex_change_ok left disabled here */
547 if (mntmp
== PM_HUMAN
)
548 newman(); /* werecritter */
550 (void) polymon(mntmp
);
551 goto made_change
; /* maybe not, but this is right anyway */
554 if (mntmp
< LOW_PM
) {
557 /* randomly pick an "ordinary" monster */
558 mntmp
= rn1(SPECIAL_PM
- LOW_PM
, LOW_PM
);
559 if (polyok(&mons
[mntmp
]) && !is_placeholder(&mons
[mntmp
]))
561 } while (--tryct
> 0);
564 /* The below polyok() fails either if everything is genocided, or if
565 * we deliberately chose something illegal to force newman().
568 if (!polyok(&mons
[mntmp
]) || (!forcecontrol
&& !rn2(5))
569 || your_race(&mons
[mntmp
])) {
572 (void) polymon(mntmp
);
574 sex_change_ok
--; /* reset */
577 new_light
= emits_light(youmonst
.data
);
578 if (old_light
!= new_light
) {
580 del_light_source(LS_MONSTER
, monst_to_any(&youmonst
));
582 ++new_light
; /* otherwise it's undetectable */
584 new_light_source(u
.ux
, u
.uy
, new_light
, LS_MONSTER
,
585 monst_to_any(&youmonst
));
589 /* (try to) make a mntmp monster out of the player;
590 returns 1 if polymorph successful */
596 boolean sticky
= sticks(youmonst
.data
) && u
.ustuck
&& !u
.uswallow
,
597 was_blind
= !!Blind
, dochange
= FALSE
;
600 if (mvitals
[mntmp
].mvflags
& G_GENOD
) { /* allow G_EXTINCT */
601 You_feel("rather %s-ish.", mons
[mntmp
].mname
);
602 exercise(A_WIS
, TRUE
);
607 u
.uconduct
.polyselfs
++;
609 /* exercise used to be at the very end but only Wis was affected
610 there since the polymorph was always in effect by then */
611 exercise(A_CON
, FALSE
);
612 exercise(A_WIS
, TRUE
);
615 /* Human to monster; save human stats */
618 u
.mfemale
= flags
.female
;
620 /* Monster to monster; restore human stats, to be
621 * immediately changed to provide stats for the new monster
625 flags
.female
= u
.mfemale
;
628 /* if stuck mimicking gold, stop immediately */
629 if (multi
< 0 && youmonst
.m_ap_type
== M_AP_OBJECT
630 && youmonst
.data
->mlet
!= S_MIMIC
)
632 /* if becoming a non-mimic, stop mimicking anything */
633 if (mons
[mntmp
].mlet
!= S_MIMIC
) {
634 /* as in polyman() */
635 youmonst
.m_ap_type
= M_AP_NOTHING
;
637 if (is_male(&mons
[mntmp
])) {
640 } else if (is_female(&mons
[mntmp
])) {
643 } else if (!is_neuter(&mons
[mntmp
]) && mntmp
!= u
.ulycn
) {
644 if (sex_change_ok
&& !rn2(10))
648 Strcpy(buf
, (u
.umonnum
!= mntmp
) ? "" : "new ");
650 flags
.female
= !flags
.female
;
651 Strcat(buf
, (is_male(&mons
[mntmp
]) || is_female(&mons
[mntmp
]))
652 ? "" : flags
.female
? "female " : "male ");
654 Strcat(buf
, mons
[mntmp
].mname
);
655 You("%s %s!", (u
.umonnum
!= mntmp
) ? "turn into" : "feel like", an(buf
));
657 if (Stoned
&& poly_when_stoned(&mons
[mntmp
])) {
658 /* poly_when_stoned already checked stone golem genocide */
659 mntmp
= PM_STONE_GOLEM
;
660 make_stoned(0L, "You turn to stone!", 0, (char *) 0);
663 u
.mtimedone
= rn1(500, 500);
667 /* New stats for monster, to last only as long as polymorphed.
668 * Currently only strength gets changed.
670 if (strongmonst(&mons
[mntmp
]))
671 ABASE(A_STR
) = AMAX(A_STR
) = STR18(100);
673 if (Stone_resistance
&& Stoned
) { /* parnes@eniac.seas.upenn.edu */
674 make_stoned(0L, "You no longer seem to be petrifying.", 0,
677 if (Sick_resistance
&& Sick
) {
678 make_sick(0L, (char *) 0, FALSE
, SICK_ALL
);
679 You("no longer feel sick.");
682 if (flaming(youmonst
.data
)) {
683 make_slimed(0L, "The slime burns away!");
684 } else if (mntmp
== PM_GREEN_SLIME
) {
686 make_slimed(0L, (char *) 0);
689 check_strangling(FALSE
); /* maybe stop strangling */
690 if (nohands(youmonst
.data
))
694 mlvl = adj_lev(&mons[mntmp]);
695 * We can't do the above, since there's no such thing as an
696 * "experience level of you as a monster" for a polymorphed character.
698 mlvl
= (int) mons
[mntmp
].mlevel
;
699 if (youmonst
.data
->mlet
== S_DRAGON
&& mntmp
>= PM_GRAY_DRAGON
) {
700 u
.mhmax
= In_endgame(&u
.uz
) ? (8 * mlvl
) : (4 * mlvl
+ d(mlvl
, 4));
701 } else if (is_golem(youmonst
.data
)) {
702 u
.mhmax
= golemhp(mntmp
);
707 u
.mhmax
= d(mlvl
, 8);
708 if (is_home_elemental(&mons
[mntmp
]))
713 if (u
.ulevel
< mlvl
) {
714 /* Low level characters can't become high level monsters for long */
716 /* DRS/NS 2.2.6 messes up -- Peter Kendell */
717 int mtd
= u
.mtimedone
, ulv
= u
.ulevel
;
719 u
.mtimedone
= mtd
* ulv
/ mlvl
;
721 u
.mtimedone
= u
.mtimedone
* u
.ulevel
/ mlvl
;
725 if (uskin
&& mntmp
!= armor_to_dragon(uskin
->otyp
))
729 (void) hideunder(&youmonst
);
731 if (u
.utraptype
== TT_PIT
&& u
.utrap
) {
732 u
.utrap
= rn1(6, 2); /* time to escape resets */
734 if (was_blind
&& !Blind
) { /* previous form was eyeless */
736 make_blinded(0L, TRUE
); /* remove blindness */
738 newsym(u
.ux
, u
.uy
); /* Change symbol */
740 if (!sticky
&& !u
.uswallow
&& u
.ustuck
&& sticks(youmonst
.data
))
742 else if (sticky
&& !sticks(youmonst
.data
))
745 if (touch_petrifies(u
.usteed
->data
) && !Stone_resistance
&& rnl(3)) {
746 pline("%s touch %s.", no_longer_petrify_resistant
,
748 Sprintf(buf
, "riding %s", an(u
.usteed
->data
->mname
));
751 if (!can_ride(u
.usteed
))
752 dismount_steed(DISMOUNT_POLY
);
756 static const char use_thec
[] = "Use the command #%s to %s.";
757 static const char monsterc
[] = "monster";
759 if (can_breathe(youmonst
.data
))
760 pline(use_thec
, monsterc
, "use your breath weapon");
761 if (attacktype(youmonst
.data
, AT_SPIT
))
762 pline(use_thec
, monsterc
, "spit venom");
763 if (youmonst
.data
->mlet
== S_NYMPH
)
764 pline(use_thec
, monsterc
, "remove an iron ball");
765 if (attacktype(youmonst
.data
, AT_GAZE
))
766 pline(use_thec
, monsterc
, "gaze at monsters");
767 if (is_hider(youmonst
.data
))
768 pline(use_thec
, monsterc
, "hide");
769 if (is_were(youmonst
.data
))
770 pline(use_thec
, monsterc
, "summon help");
771 if (webmaker(youmonst
.data
))
772 pline(use_thec
, monsterc
, "spin a web");
773 if (u
.umonnum
== PM_GREMLIN
)
774 pline(use_thec
, monsterc
, "multiply in a fountain");
775 if (is_unicorn(youmonst
.data
))
776 pline(use_thec
, monsterc
, "use your horn");
777 if (is_mind_flayer(youmonst
.data
))
778 pline(use_thec
, monsterc
, "emit a mental blast");
779 if (youmonst
.data
->msound
== MS_SHRIEK
) /* worthless, actually */
780 pline(use_thec
, monsterc
, "shriek");
781 if (is_vampire(youmonst
.data
))
782 pline(use_thec
, monsterc
, "change shape");
784 if (lays_eggs(youmonst
.data
) && flags
.female
)
785 pline(use_thec
, "sit", "lay an egg");
788 /* you now know what an egg of your type looks like */
789 if (lays_eggs(youmonst
.data
)) {
790 learn_egg_type(u
.umonnum
);
791 /* make queen bees recognize killer bee eggs */
792 learn_egg_type(egg_type_from_parent(u
.umonnum
, TRUE
));
795 if ((!Levitation
&& !u
.ustuck
&& !Flying
&& is_pool_or_lava(u
.ux
, u
.uy
))
796 || (Underwater
&& !Swimming
))
798 if (Passes_walls
&& u
.utrap
799 && (u
.utraptype
== TT_INFLOOR
|| u
.utraptype
== TT_BURIEDBALL
)) {
801 if (u
.utraptype
== TT_INFLOOR
)
802 pline_The("rock seems to no longer trap you.");
804 pline_The("buried ball is no longer bound to you.");
805 buried_ball_to_freedom();
807 } else if (likes_lava(youmonst
.data
) && u
.utrap
808 && u
.utraptype
== TT_LAVA
) {
810 pline_The("lava now feels soothing.");
812 if (amorphous(youmonst
.data
) || is_whirly(youmonst
.data
)
813 || unsolid(youmonst
.data
)) {
815 You("slip out of the iron chain.");
817 } else if (u
.utrap
&& u
.utraptype
== TT_BURIEDBALL
) {
818 You("slip free of the buried ball and chain.");
819 buried_ball_to_freedom();
822 if (u
.utrap
&& (u
.utraptype
== TT_WEB
|| u
.utraptype
== TT_BEARTRAP
)
823 && (amorphous(youmonst
.data
) || is_whirly(youmonst
.data
)
824 || unsolid(youmonst
.data
) || (youmonst
.data
->msize
<= MZ_SMALL
825 && u
.utraptype
== TT_BEARTRAP
))) {
826 You("are no longer stuck in the %s.",
827 u
.utraptype
== TT_WEB
? "web" : "bear trap");
828 /* probably should burn webs too if PM_FIRE_ELEMENTAL */
831 if (webmaker(youmonst
.data
) && u
.utrap
&& u
.utraptype
== TT_WEB
) {
832 You("orient yourself on the web.");
835 check_strangling(TRUE
); /* maybe start strangling */
838 vision_full_recalc
= 1;
840 (void) encumber_msg();
842 retouch_equipment(2);
843 /* this might trigger a recursive call to polymon() [stone golem
844 wielding cockatrice corpse and hit by stone-to-flesh, becomes
845 flesh golem above, now gets transformed back into stone golem] */
847 selftouch(no_longer_petrify_resistant
);
854 register struct obj
*otmp
;
856 if (breakarm(youmonst
.data
)) {
857 if ((otmp
= uarm
) != 0) {
860 You("break out of your armor!");
861 exercise(A_STR
, FALSE
);
865 if ((otmp
= uarmc
) != 0) {
866 if (otmp
->oartifact
) {
867 Your("%s falls off!", cloak_simple_name(otmp
));
871 Your("%s tears apart!", cloak_simple_name(otmp
));
877 Your("shirt rips to shreds!");
880 } else if (sliparm(youmonst
.data
)) {
881 if (((otmp
= uarm
) != 0) && (racial_exception(&youmonst
, otmp
) < 1)) {
884 Your("armor falls around you!");
888 if ((otmp
= uarmc
) != 0) {
889 if (is_whirly(youmonst
.data
))
890 Your("%s falls, unsupported!", cloak_simple_name(otmp
));
892 You("shrink out of your %s!", cloak_simple_name(otmp
));
896 if ((otmp
= uarmu
) != 0) {
897 if (is_whirly(youmonst
.data
))
898 You("seep right through your shirt!");
900 You("become much too small for your shirt!");
901 setworn((struct obj
*) 0, otmp
->owornmask
& W_ARMU
);
905 if (has_horns(youmonst
.data
)) {
906 if ((otmp
= uarmh
) != 0) {
907 if (is_flimsy(otmp
) && !donning(otmp
)) {
910 /* Future possibilities: This could damage/destroy helmet */
911 Sprintf(hornbuf
, "horn%s", plur(num_horns(youmonst
.data
)));
912 Your("%s %s through %s.", hornbuf
, vtense(hornbuf
, "pierce"),
917 Your("%s falls to the %s!", helm_simple_name(otmp
),
918 surface(u
.ux
, u
.uy
));
924 if (nohands(youmonst
.data
) || verysmall(youmonst
.data
)) {
925 if ((otmp
= uarmg
) != 0) {
928 /* Drop weapon along with gloves */
929 You("drop your gloves%s!", uwep
? " and weapon" : "");
934 if ((otmp
= uarms
) != 0) {
935 You("can no longer hold your shield!");
939 if ((otmp
= uarmh
) != 0) {
942 Your("%s falls to the %s!", helm_simple_name(otmp
),
943 surface(u
.ux
, u
.uy
));
948 if (nohands(youmonst
.data
) || verysmall(youmonst
.data
)
949 || slithy(youmonst
.data
) || youmonst
.data
->mlet
== S_CENTAUR
) {
950 if ((otmp
= uarmf
) != 0) {
953 if (is_whirly(youmonst
.data
))
954 Your("boots fall away!");
956 Your("boots %s off your feet!",
957 verysmall(youmonst
.data
) ? "slide" : "are pushed");
969 const char *what
, *which
, *whichtoo
;
970 boolean candropwep
, candropswapwep
;
973 /* !alone check below is currently superfluous but in the
974 * future it might not be so if there are monsters which cannot
975 * wear gloves but can wield weapons
977 if (!alone
|| cantwield(youmonst
.data
)) {
978 candropwep
= canletgo(uwep
, "");
979 candropswapwep
= !u
.twoweap
|| canletgo(uswapwep
, "");
981 what
= (candropwep
&& candropswapwep
) ? "drop" : "release";
982 which
= is_sword(uwep
) ? "sword" : weapon_descr(uwep
);
985 is_sword(uswapwep
) ? "sword" : weapon_descr(uswapwep
);
986 if (strcmp(which
, whichtoo
))
989 if (uwep
->quan
!= 1L || u
.twoweap
)
990 which
= makeplural(which
);
992 You("find you must %s %s %s!", what
,
993 the_your
[!!strncmp(which
, "corpse", 6)], which
);
1006 } else if (!could_twoweap(youmonst
.data
)) {
1015 /* You can't revert back while unchanging */
1016 if (Unchanging
&& (u
.mh
< 1)) {
1017 killer
.format
= NO_KILLER_PREFIX
;
1018 Strcpy(killer
.name
, "killed while stuck in creature form");
1022 if (emits_light(youmonst
.data
))
1023 del_light_source(LS_MONSTER
, monst_to_any(&youmonst
));
1024 polyman("return to %s form!", urace
.adj
);
1027 /* can only happen if some bit of code reduces u.uhp
1028 instead of u.mh while poly'd */
1029 Your("old form was not healthy enough to survive.");
1030 Sprintf(killer
.name
, "reverting to unhealthy %s form", urace
.adj
);
1031 killer
.format
= KILLED_BY
;
1037 vision_full_recalc
= 1;
1038 (void) encumber_msg();
1040 retouch_equipment(2);
1042 selftouch(no_longer_petrify_resistant
);
1048 struct attack
*mattk
;
1051 You_cant("breathe. Sorry.");
1055 You("don't have enough energy to breathe!");
1061 if (!getdir((char *) 0))
1064 mattk
= attacktype_fordmg(youmonst
.data
, AT_BREA
, AD_ANY
);
1066 impossible("bad breath attack?"); /* mouthwash needed... */
1067 else if (!u
.dx
&& !u
.dy
&& !u
.dz
)
1070 buzz((int) (20 + mattk
->adtyp
- 1), (int) mattk
->damn
, u
.ux
, u
.uy
,
1079 struct attack
*mattk
;
1081 if (!getdir((char *) 0))
1083 mattk
= attacktype_fordmg(youmonst
.data
, AT_SPIT
, AD_ANY
);
1085 impossible("bad spit attack?");
1087 switch (mattk
->adtyp
) {
1090 otmp
= mksobj(BLINDING_VENOM
, TRUE
, FALSE
);
1093 impossible("bad attack type in dospit");
1096 otmp
= mksobj(ACID_VENOM
, TRUE
, FALSE
);
1099 otmp
->spe
= 1; /* to indicate it's yours */
1100 throwit(otmp
, 0L, FALSE
);
1109 if (u
.utrap
&& u
.utraptype
== TT_BURIEDBALL
) {
1110 pline_The("ball and chain are buried firmly in the %s.",
1111 surface(u
.ux
, u
.uy
));
1114 You("are not chained to anything!");
1124 register struct trap
*ttmp
= t_at(u
.ux
, u
.uy
);
1126 if (Levitation
|| Is_airlevel(&u
.uz
) || Underwater
1127 || Is_waterlevel(&u
.uz
)) {
1128 You("must be on the ground to spin a web.");
1132 You("release web fluid inside %s.", mon_nam(u
.ustuck
));
1133 if (is_animal(u
.ustuck
->data
)) {
1134 expels(u
.ustuck
, u
.ustuck
->data
, TRUE
);
1137 if (is_whirly(u
.ustuck
->data
)) {
1140 for (i
= 0; i
< NATTK
; i
++)
1141 if (u
.ustuck
->data
->mattk
[i
].aatyp
== AT_ENGL
)
1144 impossible("Swallower has no engulfing attack?");
1149 switch (u
.ustuck
->data
->mattk
[i
].adtyp
) {
1151 Strcpy(sweep
, "ignites and ");
1154 Strcpy(sweep
, "fries and ");
1157 Strcpy(sweep
, "freezes, shatters and ");
1160 pline_The("web %sis swept away!", sweep
);
1163 } /* default: a nasty jelly-like creature */
1164 pline_The("web dissolves into %s.", mon_nam(u
.ustuck
));
1168 You("cannot spin webs while stuck in a trap.");
1171 exercise(A_DEX
, TRUE
);
1173 switch (ttmp
->ttyp
) {
1176 You("spin a web, covering up the pit.");
1178 bury_objs(u
.ux
, u
.uy
);
1182 pline_The("squeaky board is muffled.");
1189 case VIBRATING_SQUARE
:
1190 Your("webbing vanishes!");
1193 You("make the web thicker.");
1197 You("web over the %s.",
1198 (ttmp
->ttyp
== TRAPDOOR
) ? "trap door" : "hole");
1202 case ROLLING_BOULDER_TRAP
:
1203 You("spin a web, jamming the trigger.");
1218 You("have triggered a trap!");
1222 impossible("Webbing over trap type %d?", ttmp
->ttyp
);
1225 } else if (On_stairs(u
.ux
, u
.uy
)) {
1226 /* cop out: don't let them hide the stairs */
1227 Your("web fails to impede access to the %s.",
1228 (levl
[u
.ux
][u
.uy
].typ
== STAIRS
) ? "stairs" : "ladder");
1231 ttmp
= maketrap(u
.ux
, u
.uy
, WEB
);
1244 You("lack the energy to send forth a call for help!");
1250 You("call upon your brethren for help!");
1251 exercise(A_WIS
, TRUE
);
1252 if (!were_summon(youmonst
.data
, TRUE
, &placeholder
, (char *) 0))
1253 pline("But none arrive.");
1260 register struct monst
*mtmp
;
1266 for (i
= 0; i
< NATTK
; i
++) {
1267 if (youmonst
.data
->mattk
[i
].aatyp
== AT_GAZE
) {
1268 adtyp
= youmonst
.data
->mattk
[i
].adtyp
;
1272 if (adtyp
!= AD_CONF
&& adtyp
!= AD_FIRE
) {
1273 impossible("gaze attack %d?", adtyp
);
1278 You_cant("see anything to gaze at.");
1280 } else if (Hallucination
) {
1281 You_cant("gaze at anything you can see.");
1285 You("lack the energy to use your special gaze!");
1291 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
1292 if (DEADMONSTER(mtmp
))
1294 if (canseemon(mtmp
) && couldsee(mtmp
->mx
, mtmp
->my
)) {
1296 if (Invis
&& !perceives(mtmp
->data
)) {
1297 pline("%s seems not to notice your gaze.", Monnam(mtmp
));
1298 } else if (mtmp
->minvis
&& !See_invisible
) {
1299 You_cant("see where to gaze at %s.", Monnam(mtmp
));
1300 } else if (mtmp
->m_ap_type
== M_AP_FURNITURE
1301 || mtmp
->m_ap_type
== M_AP_OBJECT
) {
1304 } else if (flags
.safe_dog
&& mtmp
->mtame
&& !Confusion
) {
1305 You("avoid gazing at %s.", y_monnam(mtmp
));
1307 if (flags
.confirm
&& mtmp
->mpeaceful
&& !Confusion
) {
1308 Sprintf(qbuf
, "Really %s %s?",
1309 (adtyp
== AD_CONF
) ? "confuse" : "attack",
1311 if (yn(qbuf
) != 'y')
1315 if (!mtmp
->mcanmove
|| mtmp
->mstun
|| mtmp
->msleeping
1316 || !mtmp
->mcansee
|| !haseyes(mtmp
->data
)) {
1320 /* No reflection check for consistency with when a monster
1321 * gazes at *you*--only medusa gaze gets reflected then.
1323 if (adtyp
== AD_CONF
) {
1325 Your("gaze confuses %s!", mon_nam(mtmp
));
1327 pline("%s is getting more and more confused.",
1330 } else if (adtyp
== AD_FIRE
) {
1331 int dmg
= d(2, 6), lev
= (int) u
.ulevel
;
1333 You("attack %s with a fiery gaze!", mon_nam(mtmp
));
1334 if (resists_fire(mtmp
)) {
1335 pline_The("fire doesn't burn %s!", mon_nam(mtmp
));
1339 (void) destroy_mitem(mtmp
, SCROLL_CLASS
, AD_FIRE
);
1341 (void) destroy_mitem(mtmp
, POTION_CLASS
, AD_FIRE
);
1343 (void) destroy_mitem(mtmp
, SPBOOK_CLASS
, AD_FIRE
);
1349 /* For consistency with passive() in uhitm.c, this only
1350 * affects you if the monster is still alive.
1352 if (DEADMONSTER(mtmp
))
1355 if (mtmp
->data
== &mons
[PM_FLOATING_EYE
] && !mtmp
->mcan
) {
1357 You("are frozen by %s gaze!",
1358 s_suffix(mon_nam(mtmp
)));
1359 nomul((u
.ulevel
> 6 || rn2(4))
1360 ? -d((int) mtmp
->m_lev
+ 1,
1361 (int) mtmp
->data
->mattk
[0].damd
)
1363 multi_reason
= "frozen by a monster's gaze";
1367 You("stiffen momentarily under %s gaze.",
1368 s_suffix(mon_nam(mtmp
)));
1370 /* Technically this one shouldn't affect you at all because
1371 * the Medusa gaze is an active monster attack that only
1372 * works on the monster's turn, but for it to *not* have an
1373 * effect would be too weird.
1375 if (mtmp
->data
== &mons
[PM_MEDUSA
] && !mtmp
->mcan
) {
1376 pline("Gazing at the awake %s is not a very good idea.",
1378 /* as if gazing at a sleeping anything is fruitful... */
1379 You("turn to stone...");
1380 killer
.format
= KILLED_BY
;
1381 Strcpy(killer
.name
, "deliberately meeting Medusa's gaze");
1388 You("gaze at no place in particular.");
1395 boolean ismimic
= youmonst
.data
->mlet
== S_MIMIC
,
1396 on_ceiling
= is_clinger(youmonst
.data
) || Flying
;
1398 /* can't hide while being held (or holding) or while trapped
1399 (except for floor hiders [trapper or mimic] in pits) */
1400 if (u
.ustuck
|| (u
.utrap
&& (u
.utraptype
!= TT_PIT
|| on_ceiling
))) {
1401 You_cant("hide while you're %s.",
1402 !u
.ustuck
? "trapped" : !sticks(youmonst
.data
)
1404 : humanoid(u
.ustuck
->data
)
1406 : "holding that creature");
1408 || (ismimic
&& youmonst
.m_ap_type
!= M_AP_NOTHING
)) {
1410 youmonst
.m_ap_type
= M_AP_NOTHING
;
1415 /* note: the eel and hides_under cases are hypothetical;
1416 such critters aren't offered the option of hiding via #monster */
1417 if (youmonst
.data
->mlet
== S_EEL
&& !is_pool(u
.ux
, u
.uy
)) {
1418 if (IS_FOUNTAIN(levl
[u
.ux
][u
.uy
].typ
))
1419 The("fountain is not deep enough to hide in.");
1421 There("is no water to hide in here.");
1425 if (hides_under(youmonst
.data
) && !level
.objects
[u
.ux
][u
.uy
]) {
1426 There("is nothing to hide under here.");
1430 /* Planes of Air and Water */
1431 if (on_ceiling
&& !has_ceiling(&u
.uz
)) {
1432 There("is nowhere to hide above you.");
1436 if ((is_hider(youmonst
.data
) && !Flying
) /* floor hider */
1437 && (Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
))) {
1438 There("is nowhere to hide beneath you.");
1442 /* TODO? inhibit floor hiding at furniture locations, or
1443 * else make youhiding() give smarter messages at such spots.
1446 if (u
.uundetected
|| (ismimic
&& youmonst
.m_ap_type
!= M_AP_NOTHING
)) {
1447 youhiding(FALSE
, 1); /* "you are already hiding" */
1452 /* should bring up a dialog "what would you like to imitate?" */
1453 youmonst
.m_ap_type
= M_AP_OBJECT
;
1454 youmonst
.mappearance
= STRANGE_OBJECT
;
1458 youhiding(FALSE
, 0); /* "you are now hiding" */
1465 struct permonst
*savedat
= youmonst
.data
;
1467 if (is_vampire(youmonst
.data
)) {
1469 if (savedat
!= youmonst
.data
) {
1470 You("transform into %s.", an(youmonst
.data
->mname
));
1480 struct monst
*mtmp
, *nmon
;
1483 You("concentrate but lack the energy to maintain doing so.");
1489 You("concentrate.");
1490 pline("A wave of psychic energy pours out.");
1491 for (mtmp
= fmon
; mtmp
; mtmp
= nmon
) {
1495 if (DEADMONSTER(mtmp
))
1497 if (distu(mtmp
->mx
, mtmp
->my
) > BOLT_LIM
* BOLT_LIM
)
1499 if (mtmp
->mpeaceful
)
1501 u_sen
= telepathic(mtmp
->data
) && !mtmp
->mcansee
;
1502 if (u_sen
|| (telepathic(mtmp
->data
) && rn2(2)) || !rn2(10)) {
1503 You("lock in on %s %s.", s_suffix(mon_nam(mtmp
)),
1505 : telepathic(mtmp
->data
) ? "latent telepathy" : "mind");
1506 mtmp
->mhp
-= rnd(15);
1517 pline("%s is no longer in your clutches.", Monnam(u
.ustuck
));
1527 Your("skin returns to its original form.");
1529 uskin
= (struct obj
*) 0;
1530 /* undo save/restore hack */
1531 uarm
->owornmask
&= ~I_SPECIAL
;
1536 mbodypart(mon
, part
)
1540 static NEARDATA
const char
1541 *humanoid_parts
[] = { "arm", "eye", "face", "finger",
1542 "fingertip", "foot", "hand", "handed",
1543 "head", "leg", "light headed", "neck",
1544 "spine", "toe", "hair", "blood",
1545 "lung", "nose", "stomach" },
1546 *jelly_parts
[] = { "pseudopod", "dark spot", "front",
1547 "pseudopod extension", "pseudopod extremity",
1548 "pseudopod root", "grasp", "grasped",
1549 "cerebral area", "lower pseudopod", "viscous",
1550 "middle", "surface", "pseudopod extremity",
1551 "ripples", "juices", "surface", "sensor",
1553 *animal_parts
[] = { "forelimb", "eye", "face",
1554 "foreclaw", "claw tip", "rear claw",
1555 "foreclaw", "clawed", "head",
1556 "rear limb", "light headed", "neck",
1557 "spine", "rear claw tip", "fur",
1558 "blood", "lung", "nose",
1560 *bird_parts
[] = { "wing", "eye", "face", "wing",
1561 "wing tip", "foot", "wing", "winged",
1562 "head", "leg", "light headed", "neck",
1563 "spine", "toe", "feathers", "blood",
1564 "lung", "bill", "stomach" },
1565 *horse_parts
[] = { "foreleg", "eye", "face",
1566 "forehoof", "hoof tip", "rear hoof",
1567 "forehoof", "hooved", "head",
1568 "rear leg", "light headed", "neck",
1569 "backbone", "rear hoof tip", "mane",
1570 "blood", "lung", "nose",
1572 *sphere_parts
[] = { "appendage", "optic nerve", "body", "tentacle",
1573 "tentacle tip", "lower appendage", "tentacle",
1574 "tentacled", "body", "lower tentacle",
1575 "rotational", "equator", "body",
1576 "lower tentacle tip", "cilia", "life force",
1577 "retina", "olfactory nerve", "interior" },
1578 *fungus_parts
[] = { "mycelium", "visual area", "front",
1579 "hypha", "hypha", "root",
1580 "strand", "stranded", "cap area",
1581 "rhizome", "sporulated", "stalk",
1582 "root", "rhizome tip", "spores",
1583 "juices", "gill", "gill",
1585 *vortex_parts
[] = { "region", "eye", "front",
1586 "minor current", "minor current", "lower current",
1587 "swirl", "swirled", "central core",
1588 "lower current", "addled", "center",
1589 "currents", "edge", "currents",
1590 "life force", "center", "leading edge",
1592 *snake_parts
[] = { "vestigial limb", "eye", "face", "large scale",
1593 "large scale tip", "rear region", "scale gap",
1594 "scale gapped", "head", "rear region",
1595 "light headed", "neck", "length", "rear scale",
1596 "scales", "blood", "lung", "forked tongue",
1598 *worm_parts
[] = { "anterior segment", "light sensitive cell",
1599 "clitellum", "setae", "setae", "posterior segment",
1600 "segment", "segmented", "anterior segment",
1601 "posterior", "over stretched", "clitellum",
1602 "length", "posterior setae", "setae", "blood",
1603 "skin", "prostomium", "stomach" },
1604 *fish_parts
[] = { "fin", "eye", "premaxillary", "pelvic axillary",
1605 "pelvic fin", "anal fin", "pectoral fin", "finned",
1606 "head", "peduncle", "played out", "gills",
1607 "dorsal fin", "caudal fin", "scales", "blood",
1608 "gill", "nostril", "stomach" };
1609 /* claw attacks are overloaded in mons[]; most humanoids with
1610 such attacks should still reference hands rather than claws */
1611 static const char not_claws
[] = {
1612 S_HUMAN
, S_MUMMY
, S_ZOMBIE
, S_ANGEL
, S_NYMPH
, S_LEPRECHAUN
,
1613 S_QUANTMECH
, S_VAMPIRE
, S_ORC
, S_GIANT
, /* quest nemeses */
1614 '\0' /* string terminator; assert( S_xxx != 0 ); */
1616 struct permonst
*mptr
= mon
->data
;
1618 /* some special cases */
1619 if (mptr
->mlet
== S_DOG
|| mptr
->mlet
== S_FELINE
1620 || mptr
->mlet
== S_RODENT
|| mptr
== &mons
[PM_OWLBEAR
]) {
1630 return horse_parts
[part
]; /* "foreleg", "rear leg" */
1632 break; /* for other parts, use animal_parts[] below */
1634 } else if (mptr
->mlet
== S_YETI
) { /* excl. owlbear due to 'if' above */
1635 /* opposable thumbs, hence "hands", "arms", "legs", &c */
1636 return humanoid_parts
[part
]; /* yeti/sasquatch, monkey/ape */
1638 if ((part
== HAND
|| part
== HANDED
)
1639 && (humanoid(mptr
) && attacktype(mptr
, AT_CLAW
)
1640 && !index(not_claws
, mptr
->mlet
) && mptr
!= &mons
[PM_STONE_GOLEM
]
1641 && mptr
!= &mons
[PM_INCUBUS
] && mptr
!= &mons
[PM_SUCCUBUS
]))
1642 return (part
== HAND
) ? "claw" : "clawed";
1643 if ((mptr
== &mons
[PM_MUMAK
] || mptr
== &mons
[PM_MASTODON
])
1646 if (mptr
== &mons
[PM_SHARK
] && part
== HAIR
)
1647 return "skin"; /* sharks don't have scales */
1648 if ((mptr
== &mons
[PM_JELLYFISH
] || mptr
== &mons
[PM_KRAKEN
])
1649 && (part
== ARM
|| part
== FINGER
|| part
== HAND
|| part
== FOOT
1652 if (mptr
== &mons
[PM_FLOATING_EYE
] && part
== EYE
)
1654 if (humanoid(mptr
) && (part
== ARM
|| part
== FINGER
|| part
== FINGERTIP
1655 || part
== HAND
|| part
== HANDED
))
1656 return humanoid_parts
[part
];
1657 if (mptr
== &mons
[PM_RAVEN
])
1658 return bird_parts
[part
];
1659 if (mptr
->mlet
== S_CENTAUR
|| mptr
->mlet
== S_UNICORN
1660 || (mptr
== &mons
[PM_ROTHE
] && part
!= HAIR
))
1661 return horse_parts
[part
];
1662 if (mptr
->mlet
== S_LIGHT
) {
1665 else if (part
== ARM
|| part
== FINGER
|| part
== FINGERTIP
1671 if (mptr
== &mons
[PM_STALKER
] && part
== HEAD
)
1673 if (mptr
->mlet
== S_EEL
&& mptr
!= &mons
[PM_JELLYFISH
])
1674 return fish_parts
[part
];
1675 if (mptr
->mlet
== S_WORM
)
1676 return worm_parts
[part
];
1677 if (slithy(mptr
) || (mptr
->mlet
== S_DRAGON
&& part
== HAIR
))
1678 return snake_parts
[part
];
1679 if (mptr
->mlet
== S_EYE
)
1680 return sphere_parts
[part
];
1681 if (mptr
->mlet
== S_JELLY
|| mptr
->mlet
== S_PUDDING
1682 || mptr
->mlet
== S_BLOB
|| mptr
== &mons
[PM_JELLYFISH
])
1683 return jelly_parts
[part
];
1684 if (mptr
->mlet
== S_VORTEX
|| mptr
->mlet
== S_ELEMENTAL
)
1685 return vortex_parts
[part
];
1686 if (mptr
->mlet
== S_FUNGUS
)
1687 return fungus_parts
[part
];
1689 return humanoid_parts
[part
];
1690 return animal_parts
[part
];
1697 return mbodypart(&youmonst
, part
);
1703 /* Returns gender of polymorphed player;
1704 * 0/1=same meaning as flags.female, 2=none.
1706 if (is_neuter(youmonst
.data
) || !humanoid(youmonst
.data
))
1708 return flags
.female
;
1712 ugolemeffects(damtype
, dam
)
1717 /* We won't bother with "slow"/"haste" since players do not
1718 * have a monster-specific slow/haste so there is no way to
1719 * restore the old velocity once they are back to human.
1721 if (u
.umonnum
!= PM_FLESH_GOLEM
&& u
.umonnum
!= PM_IRON_GOLEM
)
1725 if (u
.umonnum
== PM_FLESH_GOLEM
)
1726 heal
= (dam
+ 5) / 6; /* Approx 1 per die */
1729 if (u
.umonnum
== PM_IRON_GOLEM
)
1733 if (heal
&& (u
.mh
< u
.mhmax
)) {
1738 pline("Strangely, you feel better than before.");
1739 exercise(A_STR
, TRUE
);
1744 armor_to_dragon(atyp
)
1748 case GRAY_DRAGON_SCALE_MAIL
:
1749 case GRAY_DRAGON_SCALES
:
1750 return PM_GRAY_DRAGON
;
1751 case SILVER_DRAGON_SCALE_MAIL
:
1752 case SILVER_DRAGON_SCALES
:
1753 return PM_SILVER_DRAGON
;
1754 #if 0 /* DEFERRED */
1755 case SHIMMERING_DRAGON_SCALE_MAIL
:
1756 case SHIMMERING_DRAGON_SCALES
:
1757 return PM_SHIMMERING_DRAGON
;
1759 case RED_DRAGON_SCALE_MAIL
:
1760 case RED_DRAGON_SCALES
:
1761 return PM_RED_DRAGON
;
1762 case ORANGE_DRAGON_SCALE_MAIL
:
1763 case ORANGE_DRAGON_SCALES
:
1764 return PM_ORANGE_DRAGON
;
1765 case WHITE_DRAGON_SCALE_MAIL
:
1766 case WHITE_DRAGON_SCALES
:
1767 return PM_WHITE_DRAGON
;
1768 case BLACK_DRAGON_SCALE_MAIL
:
1769 case BLACK_DRAGON_SCALES
:
1770 return PM_BLACK_DRAGON
;
1771 case BLUE_DRAGON_SCALE_MAIL
:
1772 case BLUE_DRAGON_SCALES
:
1773 return PM_BLUE_DRAGON
;
1774 case GREEN_DRAGON_SCALE_MAIL
:
1775 case GREEN_DRAGON_SCALES
:
1776 return PM_GREEN_DRAGON
;
1777 case YELLOW_DRAGON_SCALE_MAIL
:
1778 case YELLOW_DRAGON_SCALES
:
1779 return PM_YELLOW_DRAGON
;
1785 /* some species have awareness of other species */
1789 short warnidx
= NON_PM
;
1791 context
.warntype
.speciesidx
= NON_PM
;
1792 context
.warntype
.species
= 0;
1793 context
.warntype
.polyd
= 0;
1794 HWarn_of_mon
&= ~FROMRACE
;
1796 switch (u
.umonnum
) {
1797 case PM_PURPLE_WORM
:
1798 warnidx
= PM_SHRIEKER
;
1801 case PM_VAMPIRE_LORD
:
1802 context
.warntype
.polyd
= M2_HUMAN
| M2_ELF
;
1803 HWarn_of_mon
|= FROMRACE
;
1806 if (warnidx
>= LOW_PM
) {
1807 context
.warntype
.speciesidx
= warnidx
;
1808 context
.warntype
.species
= &mons
[warnidx
];
1809 HWarn_of_mon
|= FROMRACE
;