NHDT->ANH, in most cases
[aNetHack.git] / src / polyself.c
blob720272907fdd6c93035f22c25f2b26d01e479b09
1 /* NetHack 3.6 polyself.c $ANH-Date: 1457572516 2016/03/10 01:15:16 $ $ANH-Branch: master $:$ANH-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("%s now feels soothing.", hliquid("lava"));
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;
1314 setmangry(mtmp, TRUE);
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"
1403 : u.uswallow ? (is_animal(u.ustuck->data) ? "swallowed"
1404 : "engulfed")
1405 : !sticks(youmonst.data) ? "being held"
1406 : (humanoid(u.ustuck->data) ? "holding someone"
1407 : "holding that creature"));
1408 if (u.uundetected
1409 || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1410 u.uundetected = 0;
1411 youmonst.m_ap_type = M_AP_NOTHING;
1412 newsym(u.ux, u.uy);
1414 return 0;
1416 /* note: the eel and hides_under cases are hypothetical;
1417 such critters aren't offered the option of hiding via #monster */
1418 if (youmonst.data->mlet == S_EEL && !is_pool(u.ux, u.uy)) {
1419 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ))
1420 The("fountain is not deep enough to hide in.");
1421 else
1422 There("is no %s to hide in here.", hliquid("water"));
1423 u.uundetected = 0;
1424 return 0;
1426 if (hides_under(youmonst.data) && !level.objects[u.ux][u.uy]) {
1427 There("is nothing to hide under here.");
1428 u.uundetected = 0;
1429 return 0;
1431 /* Planes of Air and Water */
1432 if (on_ceiling && !has_ceiling(&u.uz)) {
1433 There("is nowhere to hide above you.");
1434 u.uundetected = 0;
1435 return 0;
1437 if ((is_hider(youmonst.data) && !Flying) /* floor hider */
1438 && (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))) {
1439 There("is nowhere to hide beneath you.");
1440 u.uundetected = 0;
1441 return 0;
1443 /* TODO? inhibit floor hiding at furniture locations, or
1444 * else make youhiding() give smarter messages at such spots.
1447 if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1448 youhiding(FALSE, 1); /* "you are already hiding" */
1449 return 0;
1452 if (ismimic) {
1453 /* should bring up a dialog "what would you like to imitate?" */
1454 youmonst.m_ap_type = M_AP_OBJECT;
1455 youmonst.mappearance = STRANGE_OBJECT;
1456 } else
1457 u.uundetected = 1;
1458 newsym(u.ux, u.uy);
1459 youhiding(FALSE, 0); /* "you are now hiding" */
1460 return 1;
1464 dopoly()
1466 struct permonst *savedat = youmonst.data;
1468 if (is_vampire(youmonst.data)) {
1469 polyself(2);
1470 if (savedat != youmonst.data) {
1471 You("transform into %s.", an(youmonst.data->mname));
1472 newsym(u.ux, u.uy);
1475 return 1;
1479 domindblast()
1481 struct monst *mtmp, *nmon;
1483 if (u.uen < 10) {
1484 You("concentrate but lack the energy to maintain doing so.");
1485 return 0;
1487 u.uen -= 10;
1488 context.botl = 1;
1490 You("concentrate.");
1491 pline("A wave of psychic energy pours out.");
1492 for (mtmp = fmon; mtmp; mtmp = nmon) {
1493 int u_sen;
1495 nmon = mtmp->nmon;
1496 if (DEADMONSTER(mtmp))
1497 continue;
1498 if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
1499 continue;
1500 if (mtmp->mpeaceful)
1501 continue;
1502 u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
1503 if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) {
1504 You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
1505 u_sen ? "telepathy"
1506 : telepathic(mtmp->data) ? "latent telepathy" : "mind");
1507 mtmp->mhp -= rnd(15);
1508 if (mtmp->mhp <= 0)
1509 killed(mtmp);
1512 return 1;
1515 STATIC_OVL void
1516 uunstick()
1518 pline("%s is no longer in your clutches.", Monnam(u.ustuck));
1519 u.ustuck = 0;
1522 void
1523 skinback(silently)
1524 boolean silently;
1526 if (uskin) {
1527 if (!silently)
1528 Your("skin returns to its original form.");
1529 uarm = uskin;
1530 uskin = (struct obj *) 0;
1531 /* undo save/restore hack */
1532 uarm->owornmask &= ~I_SPECIAL;
1536 const char *
1537 mbodypart(mon, part)
1538 struct monst *mon;
1539 int part;
1541 static NEARDATA const char
1542 *humanoid_parts[] = { "arm", "eye", "face", "finger",
1543 "fingertip", "foot", "hand", "handed",
1544 "head", "leg", "light headed", "neck",
1545 "spine", "toe", "hair", "blood",
1546 "lung", "nose", "stomach" },
1547 *jelly_parts[] = { "pseudopod", "dark spot", "front",
1548 "pseudopod extension", "pseudopod extremity",
1549 "pseudopod root", "grasp", "grasped",
1550 "cerebral area", "lower pseudopod", "viscous",
1551 "middle", "surface", "pseudopod extremity",
1552 "ripples", "juices", "surface", "sensor",
1553 "stomach" },
1554 *animal_parts[] = { "forelimb", "eye", "face",
1555 "foreclaw", "claw tip", "rear claw",
1556 "foreclaw", "clawed", "head",
1557 "rear limb", "light headed", "neck",
1558 "spine", "rear claw tip", "fur",
1559 "blood", "lung", "nose",
1560 "stomach" },
1561 *bird_parts[] = { "wing", "eye", "face", "wing",
1562 "wing tip", "foot", "wing", "winged",
1563 "head", "leg", "light headed", "neck",
1564 "spine", "toe", "feathers", "blood",
1565 "lung", "bill", "stomach" },
1566 *horse_parts[] = { "foreleg", "eye", "face",
1567 "forehoof", "hoof tip", "rear hoof",
1568 "forehoof", "hooved", "head",
1569 "rear leg", "light headed", "neck",
1570 "backbone", "rear hoof tip", "mane",
1571 "blood", "lung", "nose",
1572 "stomach" },
1573 *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
1574 "tentacle tip", "lower appendage", "tentacle",
1575 "tentacled", "body", "lower tentacle",
1576 "rotational", "equator", "body",
1577 "lower tentacle tip", "cilia", "life force",
1578 "retina", "olfactory nerve", "interior" },
1579 *fungus_parts[] = { "mycelium", "visual area", "front",
1580 "hypha", "hypha", "root",
1581 "strand", "stranded", "cap area",
1582 "rhizome", "sporulated", "stalk",
1583 "root", "rhizome tip", "spores",
1584 "juices", "gill", "gill",
1585 "interior" },
1586 *vortex_parts[] = { "region", "eye", "front",
1587 "minor current", "minor current", "lower current",
1588 "swirl", "swirled", "central core",
1589 "lower current", "addled", "center",
1590 "currents", "edge", "currents",
1591 "life force", "center", "leading edge",
1592 "interior" },
1593 *snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
1594 "large scale tip", "rear region", "scale gap",
1595 "scale gapped", "head", "rear region",
1596 "light headed", "neck", "length", "rear scale",
1597 "scales", "blood", "lung", "forked tongue",
1598 "stomach" },
1599 *worm_parts[] = { "anterior segment", "light sensitive cell",
1600 "clitellum", "setae", "setae", "posterior segment",
1601 "segment", "segmented", "anterior segment",
1602 "posterior", "over stretched", "clitellum",
1603 "length", "posterior setae", "setae", "blood",
1604 "skin", "prostomium", "stomach" },
1605 *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
1606 "pelvic fin", "anal fin", "pectoral fin", "finned",
1607 "head", "peduncle", "played out", "gills",
1608 "dorsal fin", "caudal fin", "scales", "blood",
1609 "gill", "nostril", "stomach" };
1610 /* claw attacks are overloaded in mons[]; most humanoids with
1611 such attacks should still reference hands rather than claws */
1612 static const char not_claws[] = {
1613 S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL, S_NYMPH, S_LEPRECHAUN,
1614 S_QUANTMECH, S_VAMPIRE, S_ORC, S_GIANT, /* quest nemeses */
1615 '\0' /* string terminator; assert( S_xxx != 0 ); */
1617 struct permonst *mptr = mon->data;
1619 /* some special cases */
1620 if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE
1621 || mptr->mlet == S_RODENT || mptr == &mons[PM_OWLBEAR]) {
1622 switch (part) {
1623 case HAND:
1624 return "paw";
1625 case HANDED:
1626 return "pawed";
1627 case FOOT:
1628 return "rear paw";
1629 case ARM:
1630 case LEG:
1631 return horse_parts[part]; /* "foreleg", "rear leg" */
1632 default:
1633 break; /* for other parts, use animal_parts[] below */
1635 } else if (mptr->mlet == S_YETI) { /* excl. owlbear due to 'if' above */
1636 /* opposable thumbs, hence "hands", "arms", "legs", &c */
1637 return humanoid_parts[part]; /* yeti/sasquatch, monkey/ape */
1639 if ((part == HAND || part == HANDED)
1640 && (humanoid(mptr) && attacktype(mptr, AT_CLAW)
1641 && !index(not_claws, mptr->mlet) && mptr != &mons[PM_STONE_GOLEM]
1642 && mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS]))
1643 return (part == HAND) ? "claw" : "clawed";
1644 if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON])
1645 && part == NOSE)
1646 return "trunk";
1647 if (mptr == &mons[PM_SHARK] && part == HAIR)
1648 return "skin"; /* sharks don't have scales */
1649 if ((mptr == &mons[PM_JELLYFISH] || mptr == &mons[PM_KRAKEN])
1650 && (part == ARM || part == FINGER || part == HAND || part == FOOT
1651 || part == TOE))
1652 return "tentacle";
1653 if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
1654 return "cornea";
1655 if (humanoid(mptr) && (part == ARM || part == FINGER || part == FINGERTIP
1656 || part == HAND || part == HANDED))
1657 return humanoid_parts[part];
1658 if (mptr == &mons[PM_RAVEN])
1659 return bird_parts[part];
1660 if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN
1661 || (mptr == &mons[PM_ROTHE] && part != HAIR))
1662 return horse_parts[part];
1663 if (mptr->mlet == S_LIGHT) {
1664 if (part == HANDED)
1665 return "rayed";
1666 else if (part == ARM || part == FINGER || part == FINGERTIP
1667 || part == HAND)
1668 return "ray";
1669 else
1670 return "beam";
1672 if (mptr == &mons[PM_STALKER] && part == HEAD)
1673 return "head";
1674 if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
1675 return fish_parts[part];
1676 if (mptr->mlet == S_WORM)
1677 return worm_parts[part];
1678 if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR))
1679 return snake_parts[part];
1680 if (mptr->mlet == S_EYE)
1681 return sphere_parts[part];
1682 if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING
1683 || mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH])
1684 return jelly_parts[part];
1685 if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL)
1686 return vortex_parts[part];
1687 if (mptr->mlet == S_FUNGUS)
1688 return fungus_parts[part];
1689 if (humanoid(mptr))
1690 return humanoid_parts[part];
1691 return animal_parts[part];
1694 const char *
1695 body_part(part)
1696 int part;
1698 return mbodypart(&youmonst, part);
1702 poly_gender()
1704 /* Returns gender of polymorphed player;
1705 * 0/1=same meaning as flags.female, 2=none.
1707 if (is_neuter(youmonst.data) || !humanoid(youmonst.data))
1708 return 2;
1709 return flags.female;
1712 void
1713 ugolemeffects(damtype, dam)
1714 int damtype, dam;
1716 int heal = 0;
1718 /* We won't bother with "slow"/"haste" since players do not
1719 * have a monster-specific slow/haste so there is no way to
1720 * restore the old velocity once they are back to human.
1722 if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
1723 return;
1724 switch (damtype) {
1725 case AD_ELEC:
1726 if (u.umonnum == PM_FLESH_GOLEM)
1727 heal = (dam + 5) / 6; /* Approx 1 per die */
1728 break;
1729 case AD_FIRE:
1730 if (u.umonnum == PM_IRON_GOLEM)
1731 heal = dam;
1732 break;
1734 if (heal && (u.mh < u.mhmax)) {
1735 u.mh += heal;
1736 if (u.mh > u.mhmax)
1737 u.mh = u.mhmax;
1738 context.botl = 1;
1739 pline("Strangely, you feel better than before.");
1740 exercise(A_STR, TRUE);
1744 STATIC_OVL int
1745 armor_to_dragon(atyp)
1746 int atyp;
1748 switch (atyp) {
1749 case GRAY_DRAGON_SCALE_MAIL:
1750 case GRAY_DRAGON_SCALES:
1751 return PM_GRAY_DRAGON;
1752 case SILVER_DRAGON_SCALE_MAIL:
1753 case SILVER_DRAGON_SCALES:
1754 return PM_SILVER_DRAGON;
1755 #if 0 /* DEFERRED */
1756 case SHIMMERING_DRAGON_SCALE_MAIL:
1757 case SHIMMERING_DRAGON_SCALES:
1758 return PM_SHIMMERING_DRAGON;
1759 #endif
1760 case RED_DRAGON_SCALE_MAIL:
1761 case RED_DRAGON_SCALES:
1762 return PM_RED_DRAGON;
1763 case ORANGE_DRAGON_SCALE_MAIL:
1764 case ORANGE_DRAGON_SCALES:
1765 return PM_ORANGE_DRAGON;
1766 case WHITE_DRAGON_SCALE_MAIL:
1767 case WHITE_DRAGON_SCALES:
1768 return PM_WHITE_DRAGON;
1769 case BLACK_DRAGON_SCALE_MAIL:
1770 case BLACK_DRAGON_SCALES:
1771 return PM_BLACK_DRAGON;
1772 case BLUE_DRAGON_SCALE_MAIL:
1773 case BLUE_DRAGON_SCALES:
1774 return PM_BLUE_DRAGON;
1775 case GREEN_DRAGON_SCALE_MAIL:
1776 case GREEN_DRAGON_SCALES:
1777 return PM_GREEN_DRAGON;
1778 case YELLOW_DRAGON_SCALE_MAIL:
1779 case YELLOW_DRAGON_SCALES:
1780 return PM_YELLOW_DRAGON;
1781 default:
1782 return -1;
1786 /* some species have awareness of other species */
1787 static void
1788 polysense()
1790 short warnidx = NON_PM;
1792 context.warntype.speciesidx = NON_PM;
1793 context.warntype.species = 0;
1794 context.warntype.polyd = 0;
1795 HWarn_of_mon &= ~FROMRACE;
1797 switch (u.umonnum) {
1798 case PM_PURPLE_WORM:
1799 warnidx = PM_SHRIEKER;
1800 break;
1801 case PM_VAMPIRE:
1802 case PM_VAMPIRE_LORD:
1803 context.warntype.polyd = M2_HUMAN | M2_ELF;
1804 HWarn_of_mon |= FROMRACE;
1805 return;
1807 if (warnidx >= LOW_PM) {
1808 context.warntype.speciesidx = warnidx;
1809 context.warntype.species = &mons[warnidx];
1810 HWarn_of_mon |= FROMRACE;
1814 /*polyself.c*/