option parsing buffer overflow vulnerability
[aNetHack.git] / src / polyself.c
blobb67cc133ad2c59dba52c7dcbab03bc30e5314174
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. */
5 /*
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
14 * polyself =
15 * \-> newman -> polyman fail to poly, get human form
17 * rehumanize -> polyman return to original form
19 * polymon (called directly) usually golem petrification
22 #include "hack.h"
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 */
41 void
42 set_uasmon()
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) \
50 do { \
51 if (ON) \
52 u.uprops[PropIndx].intrinsic |= FROMFORM; \
53 else \
54 u.uprops[PropIndx].intrinsic &= ~FROMFORM; \
55 } while (0)
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;
69 uwep = 0;
70 PROPSET(DRAIN_RES, resists_drli(&youmonst));
71 uwep = save_uwep;
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]));
96 #undef PROPSET
98 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
99 polysense();
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);
112 #endif
115 /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */
116 void
117 float_vs_flight()
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;
123 else
124 BFlying &= ~I_SPECIAL;
125 context.botl = TRUE;
128 /* for changing into form that's immune to strangulation */
129 STATIC_OVL void
130 check_strangling(on)
131 boolean on;
133 /* on -- maybe resume strangling */
134 if (on) {
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)) {
139 Strangled = 6L;
140 context.botl = TRUE;
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 */
148 } else {
149 if (Strangled && !can_be_strangled(&youmonst)) {
150 Strangled = 0L;
151 context.botl = TRUE;
152 You("are no longer being strangled.");
157 /* make a (new) human out of the player */
158 STATIC_OVL void
159 polyman(fmt, arg)
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;
166 if (Upolyd) {
167 u.acurr = u.macurr; /* restore old attribs */
168 u.amax = u.mamax;
169 u.umonnum = u.umonster;
170 flags.female = u.mfemale;
172 set_uasmon();
174 u.mh = u.mhmax = 0;
175 u.mtimedone = 0;
176 skinback(FALSE);
177 u.uundetected = 0;
179 if (sticky)
180 uunstick();
181 find_ac();
182 if (was_mimicking) {
183 if (multi < 0)
184 unmul("");
185 youmonst.m_ap_type = M_AP_NOTHING;
188 newsym(u.ux, u.uy);
190 You(fmt, arg);
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);
204 } else {
205 killer.format = KILLED_BY;
206 Strcpy(killer.name, "self-genocide");
208 dealloc_killer(kptr);
209 done(GENOCIDED);
212 if (u.twoweap && !could_twoweap(youmonst.data))
213 untwoweapon();
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 */
219 Blinded = 1L;
220 make_blinded(0L, TRUE); /* remove blindness */
222 check_strangling(TRUE);
224 if (!Levitation && !u.ustuck && is_pool_or_lava(u.ux, u.uy))
225 spoteffects(TRUE);
227 see_monsters();
230 void
231 change_sex()
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.
243 if (!already_polyd
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);
252 else
253 Strcpy(pl_character, urole.name.m);
254 u.umonster = ((already_polyd ? u.mfemale : flags.female)
255 && urole.femalenum != NON_PM)
256 ? urole.femalenum
257 : urole.malenum;
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;
264 set_uasmon();
268 STATIC_OVL void
269 newman()
271 int i, oldlvl, newlvl, hpmax, enmax;
273 oldlvl = u.ulevel;
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)
279 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. */
286 if (newlvl < oldlvl)
287 u.ulevelmax -= (oldlvl - newlvl);
288 if (u.ulevelmax < newlvl)
289 u.ulevelmax = newlvl;
290 u.ulevel = newlvl;
292 if (sex_change_ok && !rn2(10))
293 change_sex();
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) */
302 redist_attr();
305 * New hit points:
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.)
318 hpmax = u.uhpmax;
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++)
324 hpmax += newhp();
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);
329 u.uhpmax = hpmax;
331 * Do the same for spell power.
333 enmax = u.uenmax;
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++)
338 enmax += newpw();
339 if (enmax < u.ulevel)
340 enmax = u.ulevel;
341 u.uen = rounddiv((long) u.uen * (long) enmax,
342 ((u.uenmax < 1) ? 1 : u.uenmax));
343 u.uenmax = enmax;
344 /* [should alignment record be tweaked too?] */
346 u.uhunger = rn1(500, 500);
347 if (Sick)
348 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
349 if (Stoned)
350 make_stoned(0L, (char *) 0, 0, (char *) 0);
351 if (u.uhp <= 0) {
352 if (Polymorph_control) { /* even when Stunned || Unaware */
353 if (u.uhp <= 0)
354 u.uhp = 1;
355 } else {
356 dead: /* we come directly here if their experience level went to 0 or
357 less */
358 Your("new form doesn't seem healthy enough to survive.");
359 killer.format = KILLED_BY_AN;
360 Strcpy(killer.name, "unsuccessful polymorph");
361 done(DIED);
362 newuhs(FALSE);
363 return; /* lifesaved */
366 newuhs(FALSE);
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)
370 ? urace.individual.f
371 : (urace.individual.m) ? urace.individual.m : urace.noun);
372 if (Slimed) {
373 Your("body transforms, but there is still slime on you.");
374 make_slimed(10L, (const char *) 0);
377 context.botl = 1;
378 see_monsters();
379 (void) encumber_msg();
381 retouch_equipment(2);
382 if (!uarmg)
383 selftouch(no_longer_petrify_resistant);
386 void
387 polyself(psflags)
388 int psflags;
390 char buf[BUFSZ];
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);
397 if (Unchanging) {
398 pline("You fail to transform!");
399 return;
401 /* being Stunned|Unaware doesn't negate this aspect of Poly_control */
402 if (!Polymorph_control && !forcecontrol && !draconian && !iswere
403 && !isvamp) {
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);
408 return;
411 old_light = emits_light(youmonst.data);
412 mntmp = NON_PM;
414 if (monsterpoly && isvamp)
415 goto do_vampyr;
417 if (controllable_poly || forcecontrol) {
418 tryct = 5;
419 do {
420 mntmp = NON_PM;
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 */
426 pline1(Never_mind);
427 return;
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 */
436 class = 0;
437 mntmp = name_to_mon(buf);
438 if (mntmp < LOW_PM) {
439 by_class:
440 class = name_to_monclass(buf, &mntmp);
441 if (class && mntmp == NON_PM)
442 mntmp = mkclass_poly(class);
444 if (mntmp < LOW_PM) {
445 if (!class)
446 pline("I've never heard of such monsters.");
447 else
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))) {
452 goto do_shift;
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)) {
460 const char *pm_name;
462 /* mkclass_poly() can pick a !polyok()
463 candidate; if so, usually try again */
464 if (class) {
465 if (rn2(3) || --tryct > 0)
466 goto by_class;
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 */
470 ++tryct;
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);
478 } else
479 break;
480 } while (--tryct > 0);
481 if (!tryct)
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)))
485 goto do_merge;
486 if (isvamp && (tryct <= 0 || mntmp == PM_WOLF || mntmp == PM_FOG_CLOUD
487 || is_bat(&mons[mntmp])))
488 goto do_vampyr;
489 } else if (draconian || iswere || isvamp) {
490 /* special changes that don't require polyok() */
491 if (draconian) {
492 do_merge:
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 */
501 char *p, *dsmail;
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')
508 ++p;
509 /* tricky phrasing; dragon scale mail
510 is singular, dragon scales are plural */
511 Your("%s reverts to scales as you merge with them.",
512 dsmail);
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;
517 uarm->dknown = 1;
518 context.botl = 1; /* AC is changing */
520 uskin = uarm;
521 uarm = (struct obj *) 0;
522 /* save/restore hack */
523 uskin->owornmask |= I_SPECIAL;
524 update_inventory();
526 } else if (iswere) {
527 do_shift:
528 if (Upolyd && were_beastie(mntmp) != u.ulycn)
529 mntmp = PM_HUMAN; /* Illegal; force newman() */
530 else
531 mntmp = u.ulycn;
532 } else if (isvamp) {
533 do_vampyr:
534 if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ))
535 mntmp = (youmonst.data != &mons[PM_VAMPIRE] && !rn2(10))
536 ? PM_WOLF
537 : !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT;
538 if (controllable_poly) {
539 Sprintf(buf, "Become %s?", an(mons[mntmp].mname));
540 if (yn(buf) != 'y')
541 return;
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 */
549 else
550 (void) polymon(mntmp);
551 goto made_change; /* maybe not, but this is right anyway */
554 if (mntmp < LOW_PM) {
555 tryct = 200;
556 do {
557 /* randomly pick an "ordinary" monster */
558 mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
559 if (polyok(&mons[mntmp]) && !is_placeholder(&mons[mntmp]))
560 break;
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().
567 sex_change_ok++;
568 if (!polyok(&mons[mntmp]) || (!forcecontrol && !rn2(5))
569 || your_race(&mons[mntmp])) {
570 newman();
571 } else {
572 (void) polymon(mntmp);
574 sex_change_ok--; /* reset */
576 made_change:
577 new_light = emits_light(youmonst.data);
578 if (old_light != new_light) {
579 if (old_light)
580 del_light_source(LS_MONSTER, monst_to_any(&youmonst));
581 if (new_light == 1)
582 ++new_light; /* otherwise it's undetectable */
583 if (new_light)
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 */
592 polymon(mntmp)
593 int mntmp;
595 char buf[BUFSZ];
596 boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
597 was_blind = !!Blind, dochange = FALSE;
598 int mlvl;
600 if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
601 You_feel("rather %s-ish.", mons[mntmp].mname);
602 exercise(A_WIS, TRUE);
603 return 0;
606 /* KMH, conduct */
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);
614 if (!Upolyd) {
615 /* Human to monster; save human stats */
616 u.macurr = u.acurr;
617 u.mamax = u.amax;
618 u.mfemale = flags.female;
619 } else {
620 /* Monster to monster; restore human stats, to be
621 * immediately changed to provide stats for the new monster
623 u.acurr = u.macurr;
624 u.amax = u.mamax;
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)
631 unmul("");
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])) {
638 if (flags.female)
639 dochange = TRUE;
640 } else if (is_female(&mons[mntmp])) {
641 if (!flags.female)
642 dochange = TRUE;
643 } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
644 if (sex_change_ok && !rn2(10))
645 dochange = TRUE;
648 Strcpy(buf, (u.umonnum != mntmp) ? "" : "new ");
649 if (dochange) {
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);
664 u.umonnum = mntmp;
665 set_uasmon();
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,
675 (char *) 0);
677 if (Sick_resistance && Sick) {
678 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
679 You("no longer feel sick.");
681 if (Slimed) {
682 if (flaming(youmonst.data)) {
683 make_slimed(0L, "The slime burns away!");
684 } else if (mntmp == PM_GREEN_SLIME) {
685 /* do it silently */
686 make_slimed(0L, (char *) 0);
689 check_strangling(FALSE); /* maybe stop strangling */
690 if (nohands(youmonst.data))
691 Glib = 0;
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);
703 } else {
704 if (!mlvl)
705 u.mhmax = rnd(4);
706 else
707 u.mhmax = d(mlvl, 8);
708 if (is_home_elemental(&mons[mntmp]))
709 u.mhmax *= 3;
711 u.mh = u.mhmax;
713 if (u.ulevel < mlvl) {
714 /* Low level characters can't become high level monsters for long */
715 #ifdef DUMB
716 /* DRS/NS 2.2.6 messes up -- Peter Kendell */
717 int mtd = u.mtimedone, ulv = u.ulevel;
719 u.mtimedone = mtd * ulv / mlvl;
720 #else
721 u.mtimedone = u.mtimedone * u.ulevel / mlvl;
722 #endif
725 if (uskin && mntmp != armor_to_dragon(uskin->otyp))
726 skinback(FALSE);
727 break_armor();
728 drop_weapon(1);
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 */
735 Blinded = 1L;
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))
741 u.ustuck = 0;
742 else if (sticky && !sticks(youmonst.data))
743 uunstick();
744 if (u.usteed) {
745 if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) {
746 pline("%s touch %s.", no_longer_petrify_resistant,
747 mon_nam(u.usteed));
748 Sprintf(buf, "riding %s", an(u.usteed->data->mname));
749 instapetrify(buf);
751 if (!can_ride(u.usteed))
752 dismount_steed(DISMOUNT_POLY);
755 if (flags.verbose) {
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));
794 find_ac();
795 if ((!Levitation && !u.ustuck && !Flying && is_pool_or_lava(u.ux, u.uy))
796 || (Underwater && !Swimming))
797 spoteffects(TRUE);
798 if (Passes_walls && u.utrap
799 && (u.utraptype == TT_INFLOOR || u.utraptype == TT_BURIEDBALL)) {
800 u.utrap = 0;
801 if (u.utraptype == TT_INFLOOR)
802 pline_The("rock seems to no longer trap you.");
803 else {
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) {
809 u.utrap = 0;
810 pline_The("lava now feels soothing.");
812 if (amorphous(youmonst.data) || is_whirly(youmonst.data)
813 || unsolid(youmonst.data)) {
814 if (Punished) {
815 You("slip out of the iron chain.");
816 unpunish();
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 */
829 u.utrap = 0;
831 if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
832 You("orient yourself on the web.");
833 u.utrap = 0;
835 check_strangling(TRUE); /* maybe start strangling */
837 context.botl = 1;
838 vision_full_recalc = 1;
839 see_monsters();
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] */
846 if (!uarmg)
847 selftouch(no_longer_petrify_resistant);
848 return 1;
851 STATIC_OVL void
852 break_armor()
854 register struct obj *otmp;
856 if (breakarm(youmonst.data)) {
857 if ((otmp = uarm) != 0) {
858 if (donning(otmp))
859 cancel_don();
860 You("break out of your armor!");
861 exercise(A_STR, FALSE);
862 (void) Armor_gone();
863 useup(otmp);
865 if ((otmp = uarmc) != 0) {
866 if (otmp->oartifact) {
867 Your("%s falls off!", cloak_simple_name(otmp));
868 (void) Cloak_off();
869 dropx(otmp);
870 } else {
871 Your("%s tears apart!", cloak_simple_name(otmp));
872 (void) Cloak_off();
873 useup(otmp);
876 if (uarmu) {
877 Your("shirt rips to shreds!");
878 useup(uarmu);
880 } else if (sliparm(youmonst.data)) {
881 if (((otmp = uarm) != 0) && (racial_exception(&youmonst, otmp) < 1)) {
882 if (donning(otmp))
883 cancel_don();
884 Your("armor falls around you!");
885 (void) Armor_gone();
886 dropx(otmp);
888 if ((otmp = uarmc) != 0) {
889 if (is_whirly(youmonst.data))
890 Your("%s falls, unsupported!", cloak_simple_name(otmp));
891 else
892 You("shrink out of your %s!", cloak_simple_name(otmp));
893 (void) Cloak_off();
894 dropx(otmp);
896 if ((otmp = uarmu) != 0) {
897 if (is_whirly(youmonst.data))
898 You("seep right through your shirt!");
899 else
900 You("become much too small for your shirt!");
901 setworn((struct obj *) 0, otmp->owornmask & W_ARMU);
902 dropx(otmp);
905 if (has_horns(youmonst.data)) {
906 if ((otmp = uarmh) != 0) {
907 if (is_flimsy(otmp) && !donning(otmp)) {
908 char hornbuf[BUFSZ];
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"),
913 yname(otmp));
914 } else {
915 if (donning(otmp))
916 cancel_don();
917 Your("%s falls to the %s!", helm_simple_name(otmp),
918 surface(u.ux, u.uy));
919 (void) Helmet_off();
920 dropx(otmp);
924 if (nohands(youmonst.data) || verysmall(youmonst.data)) {
925 if ((otmp = uarmg) != 0) {
926 if (donning(otmp))
927 cancel_don();
928 /* Drop weapon along with gloves */
929 You("drop your gloves%s!", uwep ? " and weapon" : "");
930 drop_weapon(0);
931 (void) Gloves_off();
932 dropx(otmp);
934 if ((otmp = uarms) != 0) {
935 You("can no longer hold your shield!");
936 (void) Shield_off();
937 dropx(otmp);
939 if ((otmp = uarmh) != 0) {
940 if (donning(otmp))
941 cancel_don();
942 Your("%s falls to the %s!", helm_simple_name(otmp),
943 surface(u.ux, u.uy));
944 (void) Helmet_off();
945 dropx(otmp);
948 if (nohands(youmonst.data) || verysmall(youmonst.data)
949 || slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) {
950 if ((otmp = uarmf) != 0) {
951 if (donning(otmp))
952 cancel_don();
953 if (is_whirly(youmonst.data))
954 Your("boots fall away!");
955 else
956 Your("boots %s off your feet!",
957 verysmall(youmonst.data) ? "slide" : "are pushed");
958 (void) Boots_off();
959 dropx(otmp);
964 STATIC_OVL void
965 drop_weapon(alone)
966 int alone;
968 struct obj *otmp;
969 const char *what, *which, *whichtoo;
970 boolean candropwep, candropswapwep;
972 if (uwep) {
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, "");
980 if (alone) {
981 what = (candropwep && candropswapwep) ? "drop" : "release";
982 which = is_sword(uwep) ? "sword" : weapon_descr(uwep);
983 if (u.twoweap) {
984 whichtoo =
985 is_sword(uswapwep) ? "sword" : weapon_descr(uswapwep);
986 if (strcmp(which, whichtoo))
987 which = "weapon";
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);
995 if (u.twoweap) {
996 otmp = uswapwep;
997 uswapwepgone();
998 if (candropswapwep)
999 dropx(otmp);
1001 otmp = uwep;
1002 uwepgone();
1003 if (candropwep)
1004 dropx(otmp);
1005 update_inventory();
1006 } else if (!could_twoweap(youmonst.data)) {
1007 untwoweapon();
1012 void
1013 rehumanize()
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");
1019 done(DIED);
1022 if (emits_light(youmonst.data))
1023 del_light_source(LS_MONSTER, monst_to_any(&youmonst));
1024 polyman("return to %s form!", urace.adj);
1026 if (u.uhp < 1) {
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;
1032 done(DIED);
1034 nomul(0);
1036 context.botl = 1;
1037 vision_full_recalc = 1;
1038 (void) encumber_msg();
1040 retouch_equipment(2);
1041 if (!uarmg)
1042 selftouch(no_longer_petrify_resistant);
1046 dobreathe()
1048 struct attack *mattk;
1050 if (Strangled) {
1051 You_cant("breathe. Sorry.");
1052 return 0;
1054 if (u.uen < 15) {
1055 You("don't have enough energy to breathe!");
1056 return 0;
1058 u.uen -= 15;
1059 context.botl = 1;
1061 if (!getdir((char *) 0))
1062 return 0;
1064 mattk = attacktype_fordmg(youmonst.data, AT_BREA, AD_ANY);
1065 if (!mattk)
1066 impossible("bad breath attack?"); /* mouthwash needed... */
1067 else if (!u.dx && !u.dy && !u.dz)
1068 ubreatheu(mattk);
1069 else
1070 buzz((int) (20 + mattk->adtyp - 1), (int) mattk->damn, u.ux, u.uy,
1071 u.dx, u.dy);
1072 return 1;
1076 dospit()
1078 struct obj *otmp;
1079 struct attack *mattk;
1081 if (!getdir((char *) 0))
1082 return 0;
1083 mattk = attacktype_fordmg(youmonst.data, AT_SPIT, AD_ANY);
1084 if (!mattk) {
1085 impossible("bad spit attack?");
1086 } else {
1087 switch (mattk->adtyp) {
1088 case AD_BLND:
1089 case AD_DRST:
1090 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
1091 break;
1092 default:
1093 impossible("bad attack type in dospit");
1094 /* fall through */
1095 case AD_ACID:
1096 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
1097 break;
1099 otmp->spe = 1; /* to indicate it's yours */
1100 throwit(otmp, 0L, FALSE);
1102 return 1;
1106 doremove()
1108 if (!Punished) {
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));
1112 return 0;
1114 You("are not chained to anything!");
1115 return 0;
1117 unpunish();
1118 return 1;
1122 dospinweb()
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.");
1129 return 0;
1131 if (u.uswallow) {
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);
1135 return 0;
1137 if (is_whirly(u.ustuck->data)) {
1138 int i;
1140 for (i = 0; i < NATTK; i++)
1141 if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
1142 break;
1143 if (i == NATTK)
1144 impossible("Swallower has no engulfing attack?");
1145 else {
1146 char sweep[30];
1148 sweep[0] = '\0';
1149 switch (u.ustuck->data->mattk[i].adtyp) {
1150 case AD_FIRE:
1151 Strcpy(sweep, "ignites and ");
1152 break;
1153 case AD_ELEC:
1154 Strcpy(sweep, "fries and ");
1155 break;
1156 case AD_COLD:
1157 Strcpy(sweep, "freezes, shatters and ");
1158 break;
1160 pline_The("web %sis swept away!", sweep);
1162 return 0;
1163 } /* default: a nasty jelly-like creature */
1164 pline_The("web dissolves into %s.", mon_nam(u.ustuck));
1165 return 0;
1167 if (u.utrap) {
1168 You("cannot spin webs while stuck in a trap.");
1169 return 0;
1171 exercise(A_DEX, TRUE);
1172 if (ttmp) {
1173 switch (ttmp->ttyp) {
1174 case PIT:
1175 case SPIKED_PIT:
1176 You("spin a web, covering up the pit.");
1177 deltrap(ttmp);
1178 bury_objs(u.ux, u.uy);
1179 newsym(u.ux, u.uy);
1180 return 1;
1181 case SQKY_BOARD:
1182 pline_The("squeaky board is muffled.");
1183 deltrap(ttmp);
1184 newsym(u.ux, u.uy);
1185 return 1;
1186 case TELEP_TRAP:
1187 case LEVEL_TELEP:
1188 case MAGIC_PORTAL:
1189 case VIBRATING_SQUARE:
1190 Your("webbing vanishes!");
1191 return 0;
1192 case WEB:
1193 You("make the web thicker.");
1194 return 1;
1195 case HOLE:
1196 case TRAPDOOR:
1197 You("web over the %s.",
1198 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
1199 deltrap(ttmp);
1200 newsym(u.ux, u.uy);
1201 return 1;
1202 case ROLLING_BOULDER_TRAP:
1203 You("spin a web, jamming the trigger.");
1204 deltrap(ttmp);
1205 newsym(u.ux, u.uy);
1206 return 1;
1207 case ARROW_TRAP:
1208 case DART_TRAP:
1209 case BEAR_TRAP:
1210 case ROCKTRAP:
1211 case FIRE_TRAP:
1212 case LANDMINE:
1213 case SLP_GAS_TRAP:
1214 case RUST_TRAP:
1215 case MAGIC_TRAP:
1216 case ANTI_MAGIC:
1217 case POLY_TRAP:
1218 You("have triggered a trap!");
1219 dotrap(ttmp, 0);
1220 return 1;
1221 default:
1222 impossible("Webbing over trap type %d?", ttmp->ttyp);
1223 return 0;
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");
1229 return 1;
1231 ttmp = maketrap(u.ux, u.uy, WEB);
1232 if (ttmp) {
1233 ttmp->madeby_u = 1;
1234 feeltrap(ttmp);
1236 return 1;
1240 dosummon()
1242 int placeholder;
1243 if (u.uen < 10) {
1244 You("lack the energy to send forth a call for help!");
1245 return 0;
1247 u.uen -= 10;
1248 context.botl = 1;
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.");
1254 return 1;
1258 dogaze()
1260 register struct monst *mtmp;
1261 int looked = 0;
1262 char qbuf[QBUFSZ];
1263 int i;
1264 uchar adtyp = 0;
1266 for (i = 0; i < NATTK; i++) {
1267 if (youmonst.data->mattk[i].aatyp == AT_GAZE) {
1268 adtyp = youmonst.data->mattk[i].adtyp;
1269 break;
1272 if (adtyp != AD_CONF && adtyp != AD_FIRE) {
1273 impossible("gaze attack %d?", adtyp);
1274 return 0;
1277 if (Blind) {
1278 You_cant("see anything to gaze at.");
1279 return 0;
1280 } else if (Hallucination) {
1281 You_cant("gaze at anything you can see.");
1282 return 0;
1284 if (u.uen < 15) {
1285 You("lack the energy to use your special gaze!");
1286 return 0;
1288 u.uen -= 15;
1289 context.botl = 1;
1291 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1292 if (DEADMONSTER(mtmp))
1293 continue;
1294 if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
1295 looked++;
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) {
1302 looked--;
1303 continue;
1304 } else if (flags.safe_dog && mtmp->mtame && !Confusion) {
1305 You("avoid gazing at %s.", y_monnam(mtmp));
1306 } else {
1307 if (flags.confirm && mtmp->mpeaceful && !Confusion) {
1308 Sprintf(qbuf, "Really %s %s?",
1309 (adtyp == AD_CONF) ? "confuse" : "attack",
1310 mon_nam(mtmp));
1311 if (yn(qbuf) != 'y')
1312 continue;
1313 setmangry(mtmp);
1315 if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping
1316 || !mtmp->mcansee || !haseyes(mtmp->data)) {
1317 looked--;
1318 continue;
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) {
1324 if (!mtmp->mconf)
1325 Your("gaze confuses %s!", mon_nam(mtmp));
1326 else
1327 pline("%s is getting more and more confused.",
1328 Monnam(mtmp));
1329 mtmp->mconf = 1;
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));
1336 dmg = 0;
1338 if (lev > rn2(20))
1339 (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
1340 if (lev > rn2(20))
1341 (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
1342 if (lev > rn2(25))
1343 (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
1344 if (dmg)
1345 mtmp->mhp -= dmg;
1346 if (mtmp->mhp <= 0)
1347 killed(mtmp);
1349 /* For consistency with passive() in uhitm.c, this only
1350 * affects you if the monster is still alive.
1352 if (DEADMONSTER(mtmp))
1353 continue;
1355 if (mtmp->data == &mons[PM_FLOATING_EYE] && !mtmp->mcan) {
1356 if (!Free_action) {
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)
1362 : -200);
1363 multi_reason = "frozen by a monster's gaze";
1364 nomovemsg = 0;
1365 return 1;
1366 } else
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.",
1377 l_monnam(mtmp));
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");
1382 done(STONING);
1387 if (!looked)
1388 You("gaze at no place in particular.");
1389 return 1;
1393 dohide()
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)
1403 ? "being held"
1404 : humanoid(u.ustuck->data)
1405 ? "holding someone"
1406 : "holding that creature");
1407 if (u.uundetected
1408 || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1409 u.uundetected = 0;
1410 youmonst.m_ap_type = M_AP_NOTHING;
1411 newsym(u.ux, u.uy);
1413 return 0;
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.");
1420 else
1421 There("is no water to hide in here.");
1422 u.uundetected = 0;
1423 return 0;
1425 if (hides_under(youmonst.data) && !level.objects[u.ux][u.uy]) {
1426 There("is nothing to hide under here.");
1427 u.uundetected = 0;
1428 return 0;
1430 /* Planes of Air and Water */
1431 if (on_ceiling && !has_ceiling(&u.uz)) {
1432 There("is nowhere to hide above you.");
1433 u.uundetected = 0;
1434 return 0;
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.");
1439 u.uundetected = 0;
1440 return 0;
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" */
1448 return 0;
1451 if (ismimic) {
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;
1455 } else
1456 u.uundetected = 1;
1457 newsym(u.ux, u.uy);
1458 youhiding(FALSE, 0); /* "you are now hiding" */
1459 return 1;
1463 dopoly()
1465 struct permonst *savedat = youmonst.data;
1467 if (is_vampire(youmonst.data)) {
1468 polyself(2);
1469 if (savedat != youmonst.data) {
1470 You("transform into %s.", an(youmonst.data->mname));
1471 newsym(u.ux, u.uy);
1474 return 1;
1478 domindblast()
1480 struct monst *mtmp, *nmon;
1482 if (u.uen < 10) {
1483 You("concentrate but lack the energy to maintain doing so.");
1484 return 0;
1486 u.uen -= 10;
1487 context.botl = 1;
1489 You("concentrate.");
1490 pline("A wave of psychic energy pours out.");
1491 for (mtmp = fmon; mtmp; mtmp = nmon) {
1492 int u_sen;
1494 nmon = mtmp->nmon;
1495 if (DEADMONSTER(mtmp))
1496 continue;
1497 if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
1498 continue;
1499 if (mtmp->mpeaceful)
1500 continue;
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)),
1504 u_sen ? "telepathy"
1505 : telepathic(mtmp->data) ? "latent telepathy" : "mind");
1506 mtmp->mhp -= rnd(15);
1507 if (mtmp->mhp <= 0)
1508 killed(mtmp);
1511 return 1;
1514 STATIC_OVL void
1515 uunstick()
1517 pline("%s is no longer in your clutches.", Monnam(u.ustuck));
1518 u.ustuck = 0;
1521 void
1522 skinback(silently)
1523 boolean silently;
1525 if (uskin) {
1526 if (!silently)
1527 Your("skin returns to its original form.");
1528 uarm = uskin;
1529 uskin = (struct obj *) 0;
1530 /* undo save/restore hack */
1531 uarm->owornmask &= ~I_SPECIAL;
1535 const char *
1536 mbodypart(mon, part)
1537 struct monst *mon;
1538 int 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",
1552 "stomach" },
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",
1559 "stomach" },
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",
1571 "stomach" },
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",
1584 "interior" },
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",
1591 "interior" },
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",
1597 "stomach" },
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]) {
1621 switch (part) {
1622 case HAND:
1623 return "paw";
1624 case HANDED:
1625 return "pawed";
1626 case FOOT:
1627 return "rear paw";
1628 case ARM:
1629 case LEG:
1630 return horse_parts[part]; /* "foreleg", "rear leg" */
1631 default:
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])
1644 && part == NOSE)
1645 return "trunk";
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
1650 || part == TOE))
1651 return "tentacle";
1652 if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
1653 return "cornea";
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) {
1663 if (part == HANDED)
1664 return "rayed";
1665 else if (part == ARM || part == FINGER || part == FINGERTIP
1666 || part == HAND)
1667 return "ray";
1668 else
1669 return "beam";
1671 if (mptr == &mons[PM_STALKER] && part == HEAD)
1672 return "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];
1688 if (humanoid(mptr))
1689 return humanoid_parts[part];
1690 return animal_parts[part];
1693 const char *
1694 body_part(part)
1695 int part;
1697 return mbodypart(&youmonst, part);
1701 poly_gender()
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))
1707 return 2;
1708 return flags.female;
1711 void
1712 ugolemeffects(damtype, dam)
1713 int damtype, dam;
1715 int heal = 0;
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)
1722 return;
1723 switch (damtype) {
1724 case AD_ELEC:
1725 if (u.umonnum == PM_FLESH_GOLEM)
1726 heal = (dam + 5) / 6; /* Approx 1 per die */
1727 break;
1728 case AD_FIRE:
1729 if (u.umonnum == PM_IRON_GOLEM)
1730 heal = dam;
1731 break;
1733 if (heal && (u.mh < u.mhmax)) {
1734 u.mh += heal;
1735 if (u.mh > u.mhmax)
1736 u.mh = u.mhmax;
1737 context.botl = 1;
1738 pline("Strangely, you feel better than before.");
1739 exercise(A_STR, TRUE);
1743 STATIC_OVL int
1744 armor_to_dragon(atyp)
1745 int atyp;
1747 switch (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;
1758 #endif
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;
1780 default:
1781 return -1;
1785 /* some species have awareness of other species */
1786 static void
1787 polysense()
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;
1799 break;
1800 case PM_VAMPIRE:
1801 case PM_VAMPIRE_LORD:
1802 context.warntype.polyd = M2_HUMAN | M2_ELF;
1803 HWarn_of_mon |= FROMRACE;
1804 return;
1806 if (warnidx >= LOW_PM) {
1807 context.warntype.speciesidx = warnidx;
1808 context.warntype.species = &mons[warnidx];
1809 HWarn_of_mon |= FROMRACE;
1813 /*polyself.c*/