Fix findtravelpath buffer overflow
[aNetHack.git] / src / polyself.c
blob4cd01d0d6461e75df12511d4eaae8cacfc06b70f
1 /* NetHack 3.6 polyself.c $NHDT-Date: 1448496566 2015/11/26 00:09:26 $ $NHDT-Branch: master $:$NHDT-Revision: 1.104 $ */
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 boolean FDECL(polysense, (struct permonst *));
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];
46 set_mon_data(&youmonst, mdat, 0);
48 #define PROPSET(PropIndx, ON) \
49 do { \
50 if (ON) \
51 u.uprops[PropIndx].intrinsic |= FROMFORM; \
52 else \
53 u.uprops[PropIndx].intrinsic &= ~FROMFORM; \
54 } while (0)
56 PROPSET(FIRE_RES, resists_fire(&youmonst));
57 PROPSET(COLD_RES, resists_cold(&youmonst));
58 PROPSET(SLEEP_RES, resists_sleep(&youmonst));
59 PROPSET(DISINT_RES, resists_disint(&youmonst));
60 PROPSET(SHOCK_RES, resists_elec(&youmonst));
61 PROPSET(POISON_RES, resists_poison(&youmonst));
62 PROPSET(ACID_RES, resists_acid(&youmonst));
63 PROPSET(STONE_RES, resists_ston(&youmonst));
65 /* resists_drli() takes wielded weapon into account; suppress it */
66 struct obj *save_uwep = uwep;
68 uwep = 0;
69 PROPSET(DRAIN_RES, resists_drli(&youmonst));
70 uwep = save_uwep;
72 /* resists_magm() takes wielded, worn, and carried equipment into
73 into account; cheat and duplicate its monster-specific part */
74 PROPSET(ANTIMAGIC, (dmgtype(mdat, AD_MAGM)
75 || mdat == &mons[PM_BABY_GRAY_DRAGON]
76 || dmgtype(mdat, AD_RBRE)));
77 PROPSET(SICK_RES, (mdat->mlet == S_FUNGUS || mdat == &mons[PM_GHOUL]));
79 PROPSET(STUNNED, (mdat == &mons[PM_STALKER] || is_bat(mdat)));
80 PROPSET(HALLUC_RES, dmgtype(mdat, AD_HALU));
81 PROPSET(SEE_INVIS, perceives(mdat));
82 PROPSET(TELEPAT, telepathic(mdat));
83 PROPSET(INFRAVISION, infravision(mdat));
84 PROPSET(INVIS, pm_invisible(mdat));
85 PROPSET(TELEPORT, can_teleport(mdat));
86 PROPSET(TELEPORT_CONTROL, control_teleport(mdat));
87 PROPSET(LEVITATION, is_floater(mdat));
88 PROPSET(FLYING, is_flyer(mdat));
89 PROPSET(SWIMMING, is_swimmer(mdat));
90 /* [don't touch MAGICAL_BREATHING here; both Amphibious and Breathless
91 key off of it but include different monster forms...] */
92 PROPSET(PASSES_WALLS, passes_walls(mdat));
93 PROPSET(REGENERATION, regenerates(mdat));
94 PROPSET(REFLECTING, (mdat == &mons[PM_SILVER_DRAGON]));
96 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
98 #undef PROPSET
100 #ifdef STATUS_VIA_WINDOWPORT
101 status_initialize(REASSESS_ONLY);
102 #endif
105 /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */
106 void
107 float_vs_flight()
109 /* floating overrides flight; normally float_up() and float_down()
110 handle this, but sometimes they're skipped */
111 if (HLevitation || ELevitation)
112 BFlying |= I_SPECIAL;
113 else
114 BFlying &= ~I_SPECIAL;
117 /* for changing into form that's immune to strangulation */
118 STATIC_OVL void
119 check_strangling(on)
120 boolean on;
122 /* on -- maybe resume strangling */
123 if (on) {
124 /* when Strangled is already set, polymorphing from one
125 vulnerable form into another causes the counter to be reset */
126 if (uamul && uamul->otyp == AMULET_OF_STRANGULATION
127 && can_be_strangled(&youmonst)) {
128 Your("%s %s your %s!", simpleonames(uamul),
129 Strangled ? "still constricts" : "begins constricting",
130 body_part(NECK)); /* "throat" */
131 Strangled = 6L;
132 makeknown(AMULET_OF_STRANGULATION);
135 /* off -- maybe block strangling */
136 } else {
137 if (Strangled && !can_be_strangled(&youmonst)) {
138 Strangled = 0L;
139 You("are no longer being strangled.");
144 /* make a (new) human out of the player */
145 STATIC_OVL void
146 polyman(fmt, arg)
147 const char *fmt, *arg;
149 boolean sticky = (sticks(youmonst.data) && u.ustuck && !u.uswallow),
150 was_mimicking = (youmonst.m_ap_type == M_AP_OBJECT);
151 boolean was_blind = !!Blind;
153 if (Upolyd) {
154 u.acurr = u.macurr; /* restore old attribs */
155 u.amax = u.mamax;
156 u.umonnum = u.umonster;
157 flags.female = u.mfemale;
159 set_uasmon();
161 u.mh = u.mhmax = 0;
162 u.mtimedone = 0;
163 skinback(FALSE);
164 u.uundetected = 0;
166 if (sticky)
167 uunstick();
168 find_ac();
169 if (was_mimicking) {
170 if (multi < 0)
171 unmul("");
172 youmonst.m_ap_type = M_AP_NOTHING;
175 newsym(u.ux, u.uy);
177 You(fmt, arg);
178 /* check whether player foolishly genocided self while poly'd */
179 if ((mvitals[urole.malenum].mvflags & G_GENOD)
180 || (urole.femalenum != NON_PM
181 && (mvitals[urole.femalenum].mvflags & G_GENOD))
182 || (mvitals[urace.malenum].mvflags & G_GENOD)
183 || (urace.femalenum != NON_PM
184 && (mvitals[urace.femalenum].mvflags & G_GENOD))) {
185 /* intervening activity might have clobbered genocide info */
186 struct kinfo *kptr = find_delayed_killer(POLYMORPH);
188 if (kptr != (struct kinfo *) 0 && kptr->name[0]) {
189 killer.format = kptr->format;
190 Strcpy(killer.name, kptr->name);
191 } else {
192 killer.format = KILLED_BY;
193 Strcpy(killer.name, "self-genocide");
195 dealloc_killer(kptr);
196 done(GENOCIDED);
199 if (u.twoweap && !could_twoweap(youmonst.data))
200 untwoweapon();
202 if (u.utraptype == TT_PIT && u.utrap) {
203 u.utrap = rn1(6, 2); /* time to escape resets */
205 if (was_blind && !Blind) { /* reverting from eyeless */
206 Blinded = 1L;
207 make_blinded(0L, TRUE); /* remove blindness */
209 check_strangling(TRUE);
211 if (!Levitation && !u.ustuck && is_pool_or_lava(u.ux, u.uy))
212 spoteffects(TRUE);
214 see_monsters();
217 void
218 change_sex()
220 /* setting u.umonster for caveman/cavewoman or priest/priestess
221 swap unintentionally makes `Upolyd' appear to be true */
222 boolean already_polyd = (boolean) Upolyd;
224 /* Some monsters are always of one sex and their sex can't be changed;
225 * Succubi/incubi can change, but are handled below.
227 * !already_polyd check necessary because is_male() and is_female()
228 * are true if the player is a priest/priestess.
230 if (!already_polyd
231 || (!is_male(youmonst.data) && !is_female(youmonst.data)
232 && !is_neuter(youmonst.data)))
233 flags.female = !flags.female;
234 if (already_polyd) /* poly'd: also change saved sex */
235 u.mfemale = !u.mfemale;
236 max_rank_sz(); /* [this appears to be superfluous] */
237 if ((already_polyd ? u.mfemale : flags.female) && urole.name.f)
238 Strcpy(pl_character, urole.name.f);
239 else
240 Strcpy(pl_character, urole.name.m);
241 u.umonster = ((already_polyd ? u.mfemale : flags.female)
242 && urole.femalenum != NON_PM)
243 ? urole.femalenum
244 : urole.malenum;
245 if (!already_polyd) {
246 u.umonnum = u.umonster;
247 } else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) {
248 flags.female = !flags.female;
249 /* change monster type to match new sex */
250 u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS;
251 set_uasmon();
255 STATIC_OVL void
256 newman()
258 int i, oldlvl, newlvl, hpmax, enmax;
260 oldlvl = u.ulevel;
261 newlvl = oldlvl + rn1(5, -2); /* new = old + {-2,-1,0,+1,+2} */
262 if (newlvl > 127 || newlvl < 1) { /* level went below 0? */
263 goto dead; /* old level is still intact (in case of lifesaving) */
265 if (newlvl > MAXULEV)
266 newlvl = MAXULEV;
267 /* If your level goes down, your peak level goes down by
268 the same amount so that you can't simply use blessed
269 full healing to undo the decrease. But if your level
270 goes up, your peak level does *not* undergo the same
271 adjustment; you might end up losing out on the chance
272 to regain some levels previously lost to other causes. */
273 if (newlvl < oldlvl)
274 u.ulevelmax -= (oldlvl - newlvl);
275 if (u.ulevelmax < newlvl)
276 u.ulevelmax = newlvl;
277 u.ulevel = newlvl;
279 if (sex_change_ok && !rn2(10))
280 change_sex();
282 adjabil(oldlvl, (int) u.ulevel);
283 reset_rndmonst(NON_PM); /* new monster generation criteria */
285 /* random experience points for the new experience level */
286 u.uexp = rndexp(FALSE);
288 /* set up new attribute points (particularly Con) */
289 redist_attr();
292 * New hit points:
293 * remove level-gain based HP from any extra HP accumulated
294 * (the "extra" might actually be negative);
295 * modify the extra, retaining {80%, 90%, 100%, or 110%};
296 * add in newly generated set of level-gain HP.
298 * (This used to calculate new HP in direct proportion to old HP,
299 * but that was subject to abuse: accumulate a large amount of
300 * extra HP, drain level down to 1, then polyself to level 2 or 3
301 * [lifesaving capability needed to handle level 0 and -1 cases]
302 * and the extra got multiplied by 2 or 3. Repeat the level
303 * drain and polyself steps until out of lifesaving capability.)
305 hpmax = u.uhpmax;
306 for (i = 0; i < oldlvl; i++)
307 hpmax -= (int) u.uhpinc[i];
308 /* hpmax * rn1(4,8) / 10; 0.95*hpmax on average */
309 hpmax = rounddiv((long) hpmax * (long) rn1(4, 8), 10);
310 for (i = 0; (u.ulevel = i) < newlvl; i++)
311 hpmax += newhp();
312 if (hpmax < u.ulevel)
313 hpmax = u.ulevel; /* min of 1 HP per level */
314 /* retain same proportion for current HP; u.uhp * hpmax / u.uhpmax */
315 u.uhp = rounddiv((long) u.uhp * (long) hpmax, u.uhpmax);
316 u.uhpmax = hpmax;
318 * Do the same for spell power.
320 enmax = u.uenmax;
321 for (i = 0; i < oldlvl; i++)
322 enmax -= (int) u.ueninc[i];
323 enmax = rounddiv((long) enmax * (long) rn1(4, 8), 10);
324 for (i = 0; (u.ulevel = i) < newlvl; i++)
325 enmax += newpw();
326 if (enmax < u.ulevel)
327 enmax = u.ulevel;
328 u.uen = rounddiv((long) u.uen * (long) enmax,
329 ((u.uenmax < 1) ? 1 : u.uenmax));
330 u.uenmax = enmax;
331 /* [should alignment record be tweaked too?] */
333 u.uhunger = rn1(500, 500);
334 if (Sick)
335 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
336 if (Stoned)
337 make_stoned(0L, (char *) 0, 0, (char *) 0);
338 if (u.uhp <= 0) {
339 if (Polymorph_control) { /* even when Stunned || Unaware */
340 if (u.uhp <= 0)
341 u.uhp = 1;
342 } else {
343 dead: /* we come directly here if their experience level went to 0 or
344 less */
345 Your("new form doesn't seem healthy enough to survive.");
346 killer.format = KILLED_BY_AN;
347 Strcpy(killer.name, "unsuccessful polymorph");
348 done(DIED);
349 newuhs(FALSE);
350 (void) polysense(youmonst.data);
351 return; /* lifesaved */
354 newuhs(FALSE);
355 polyman("feel like a new %s!",
356 /* use saved gender we're about to revert to, not current */
357 (u.mfemale && urace.individual.f)
358 ? urace.individual.f
359 : (urace.individual.m) ? urace.individual.m : urace.noun);
360 if (Slimed) {
361 Your("body transforms, but there is still slime on you.");
362 make_slimed(10L, (const char *) 0);
365 (void) polysense(youmonst.data);
366 context.botl = 1;
367 see_monsters();
368 (void) encumber_msg();
370 retouch_equipment(2);
371 if (!uarmg)
372 selftouch(no_longer_petrify_resistant);
375 void
376 polyself(psflags)
377 int psflags;
379 char buf[BUFSZ];
380 int old_light, new_light, mntmp, class, tryct;
381 boolean forcecontrol = (psflags == 1), monsterpoly = (psflags == 2),
382 draconian = (uarm && Is_dragon_armor(uarm)),
383 iswere = (u.ulycn >= LOW_PM), isvamp = is_vampire(youmonst.data),
384 controllable_poly = Polymorph_control && !(Stunned || Unaware);
386 if (Unchanging) {
387 pline("You fail to transform!");
388 return;
390 /* being Stunned|Unaware doesn't negate this aspect of Poly_control */
391 if (!Polymorph_control && !forcecontrol && !draconian && !iswere
392 && !isvamp) {
393 if (rn2(20) > ACURR(A_CON)) {
394 You1(shudder_for_moment);
395 losehp(rnd(30), "system shock", KILLED_BY_AN);
396 exercise(A_CON, FALSE);
397 return;
400 old_light = emits_light(youmonst.data);
401 mntmp = NON_PM;
403 if (monsterpoly && isvamp)
404 goto do_vampyr;
406 if (controllable_poly || forcecontrol) {
407 tryct = 5;
408 do {
409 mntmp = NON_PM;
410 getlin("Become what kind of monster? [type the name]", buf);
411 (void) mungspaces(buf);
412 if (*buf == '\033') {
413 /* user is cancelling controlled poly */
414 if (forcecontrol) { /* wizard mode #polyself */
415 pline1(Never_mind);
416 return;
418 Strcpy(buf, "*"); /* resort to random */
420 if (!strcmp(buf, "*") || !strcmp(buf, "random")) {
421 /* explicitly requesting random result */
422 tryct = 0; /* will skip thats_enough_tries */
423 continue; /* end do-while(--tryct > 0) loop */
425 class = 0;
426 mntmp = name_to_mon(buf);
427 if (mntmp < LOW_PM) {
428 by_class:
429 class = name_to_monclass(buf, &mntmp);
430 if (class && mntmp == NON_PM)
431 mntmp = mkclass_poly(class);
433 if (mntmp < LOW_PM) {
434 if (!class)
435 pline("I've never heard of such monsters.");
436 else
437 You_cant("polymorph into any of those.");
438 } else if (iswere && (were_beastie(mntmp) == u.ulycn
439 || mntmp == counter_were(u.ulycn)
440 || (Upolyd && mntmp == PM_HUMAN))) {
441 goto do_shift;
442 /* Note: humans are illegal as monsters, but an
443 * illegal monster forces newman(), which is what we
444 * want if they specified a human.... */
445 } else if (!polyok(&mons[mntmp])
446 && !(mntmp == PM_HUMAN || your_race(&mons[mntmp])
447 || mntmp == urole.malenum
448 || mntmp == urole.femalenum)) {
449 const char *pm_name;
451 /* mkclass_poly() can pick a !polyok()
452 candidate; if so, usually try again */
453 if (class) {
454 if (rn2(3) || --tryct > 0)
455 goto by_class;
456 /* no retries left; put one back on counter
457 so that end of loop decrement will yield
458 0 and trigger thats_enough_tries message */
459 ++tryct;
461 pm_name = mons[mntmp].mname;
462 if (the_unique_pm(&mons[mntmp]))
463 pm_name = the(pm_name);
464 else if (!type_is_pname(&mons[mntmp]))
465 pm_name = an(pm_name);
466 You_cant("polymorph into %s.", pm_name);
467 } else
468 break;
469 } while (--tryct > 0);
470 if (!tryct)
471 pline1(thats_enough_tries);
472 /* allow skin merging, even when polymorph is controlled */
473 if (draconian && (tryct <= 0 || mntmp == armor_to_dragon(uarm->otyp)))
474 goto do_merge;
475 if (isvamp && (tryct <= 0 || mntmp == PM_WOLF || mntmp == PM_FOG_CLOUD
476 || is_bat(&mons[mntmp])))
477 goto do_vampyr;
478 } else if (draconian || iswere || isvamp) {
479 /* special changes that don't require polyok() */
480 if (draconian) {
481 do_merge:
482 mntmp = armor_to_dragon(uarm->otyp);
483 if (!(mvitals[mntmp].mvflags & G_GENOD)) {
484 /* allow G_EXTINCT */
485 if (Is_dragon_scales(uarm)) {
486 /* dragon scales remain intact as uskin */
487 You("merge with your scaly armor.");
488 } else { /* dragon scale mail */
489 /* d.scale mail first reverts to scales */
490 char *p, *dsmail;
492 /* similar to noarmor(invent.c),
493 shorten to "<color> scale mail" */
494 dsmail = strcpy(buf, simpleonames(uarm));
495 if ((p = strstri(dsmail, " dragon ")) != 0)
496 while ((p[1] = p[8]) != '\0')
497 ++p;
498 /* tricky phrasing; dragon scale mail
499 is singular, dragon scales are plural */
500 Your("%s reverts to scales as you merge with them.",
501 dsmail);
502 /* uarm->spe enchantment remains unchanged;
503 re-converting scales to mail poses risk
504 of evaporation due to over enchanting */
505 uarm->otyp += GRAY_DRAGON_SCALES - GRAY_DRAGON_SCALE_MAIL;
506 uarm->dknown = 1;
507 context.botl = 1; /* AC is changing */
509 uskin = uarm;
510 uarm = (struct obj *) 0;
511 /* save/restore hack */
512 uskin->owornmask |= I_SPECIAL;
513 update_inventory();
515 } else if (iswere) {
516 do_shift:
517 if (Upolyd && were_beastie(mntmp) != u.ulycn)
518 mntmp = PM_HUMAN; /* Illegal; force newman() */
519 else
520 mntmp = u.ulycn;
521 } else if (isvamp) {
522 do_vampyr:
523 if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ))
524 mntmp = (youmonst.data != &mons[PM_VAMPIRE] && !rn2(10))
525 ? PM_WOLF
526 : !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT;
527 if (controllable_poly) {
528 Sprintf(buf, "Become %s?", an(mons[mntmp].mname));
529 if (yn(buf) != 'y')
530 return;
533 /* if polymon fails, "you feel" message has been given
534 so don't follow up with another polymon or newman;
535 sex_change_ok left disabled here */
536 if (mntmp == PM_HUMAN)
537 newman(); /* werecritter */
538 else
539 (void) polymon(mntmp);
540 goto made_change; /* maybe not, but this is right anyway */
543 if (mntmp < LOW_PM) {
544 tryct = 200;
545 do {
546 /* randomly pick an "ordinary" monster */
547 mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
548 if (polyok(&mons[mntmp]) && !is_placeholder(&mons[mntmp]))
549 break;
550 } while (--tryct > 0);
553 /* The below polyok() fails either if everything is genocided, or if
554 * we deliberately chose something illegal to force newman().
556 sex_change_ok++;
557 if (!polyok(&mons[mntmp]) || (!forcecontrol && !rn2(5))
558 || your_race(&mons[mntmp])) {
559 newman();
560 } else {
561 (void) polymon(mntmp);
563 sex_change_ok--; /* reset */
565 made_change:
566 new_light = emits_light(youmonst.data);
567 if (old_light != new_light) {
568 if (old_light)
569 del_light_source(LS_MONSTER, monst_to_any(&youmonst));
570 if (new_light == 1)
571 ++new_light; /* otherwise it's undetectable */
572 if (new_light)
573 new_light_source(u.ux, u.uy, new_light, LS_MONSTER,
574 monst_to_any(&youmonst));
578 /* (try to) make a mntmp monster out of the player;
579 returns 1 if polymorph successful */
581 polymon(mntmp)
582 int mntmp;
584 boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
585 was_blind = !!Blind, dochange = FALSE;
586 int mlvl;
588 if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
589 You_feel("rather %s-ish.", mons[mntmp].mname);
590 exercise(A_WIS, TRUE);
591 return 0;
594 /* KMH, conduct */
595 u.uconduct.polyselfs++;
597 /* exercise used to be at the very end but only Wis was affected
598 there since the polymorph was always in effect by then */
599 exercise(A_CON, FALSE);
600 exercise(A_WIS, TRUE);
602 if (!Upolyd) {
603 /* Human to monster; save human stats */
604 u.macurr = u.acurr;
605 u.mamax = u.amax;
606 u.mfemale = flags.female;
607 } else {
608 /* Monster to monster; restore human stats, to be
609 * immediately changed to provide stats for the new monster
611 u.acurr = u.macurr;
612 u.amax = u.mamax;
613 flags.female = u.mfemale;
616 /* if stuck mimicking gold, stop immediately */
617 if (multi < 0 && youmonst.m_ap_type == M_AP_OBJECT
618 && youmonst.data->mlet != S_MIMIC)
619 unmul("");
620 /* if becoming a non-mimic, stop mimicking anything */
621 if (mons[mntmp].mlet != S_MIMIC) {
622 /* as in polyman() */
623 youmonst.m_ap_type = M_AP_NOTHING;
625 if (is_male(&mons[mntmp])) {
626 if (flags.female)
627 dochange = TRUE;
628 } else if (is_female(&mons[mntmp])) {
629 if (!flags.female)
630 dochange = TRUE;
631 } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
632 if (sex_change_ok && !rn2(10))
633 dochange = TRUE;
635 if (dochange) {
636 flags.female = !flags.female;
637 You("%s %s%s!",
638 (u.umonnum != mntmp) ? "turn into a" : "feel like a new",
639 (is_male(&mons[mntmp]) || is_female(&mons[mntmp]))
640 ? ""
641 : flags.female ? "female " : "male ",
642 mons[mntmp].mname);
643 } else {
644 if (u.umonnum != mntmp)
645 You("turn into %s!", an(mons[mntmp].mname));
646 else
647 You_feel("like a new %s!", mons[mntmp].mname);
649 if (Stoned && poly_when_stoned(&mons[mntmp])) {
650 /* poly_when_stoned already checked stone golem genocide */
651 mntmp = PM_STONE_GOLEM;
652 make_stoned(0L, "You turn to stone!", 0, (char *) 0);
655 u.mtimedone = rn1(500, 500);
656 u.umonnum = mntmp;
657 set_uasmon();
659 /* New stats for monster, to last only as long as polymorphed.
660 * Currently only strength gets changed.
662 if (strongmonst(&mons[mntmp]))
663 ABASE(A_STR) = AMAX(A_STR) = STR18(100);
665 if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */
666 make_stoned(0L, "You no longer seem to be petrifying.", 0,
667 (char *) 0);
669 if (Sick_resistance && Sick) {
670 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
671 You("no longer feel sick.");
673 if (Slimed) {
674 if (flaming(youmonst.data)) {
675 make_slimed(0L, "The slime burns away!");
676 } else if (mntmp == PM_GREEN_SLIME) {
677 /* do it silently */
678 make_slimed(0L, (char *) 0);
681 check_strangling(FALSE); /* maybe stop strangling */
682 if (nohands(youmonst.data))
683 Glib = 0;
686 mlvl = adj_lev(&mons[mntmp]);
687 * We can't do the above, since there's no such thing as an
688 * "experience level of you as a monster" for a polymorphed character.
690 mlvl = (int) mons[mntmp].mlevel;
691 if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) {
692 u.mhmax = In_endgame(&u.uz) ? (8 * mlvl) : (4 * mlvl + d(mlvl, 4));
693 } else if (is_golem(youmonst.data)) {
694 u.mhmax = golemhp(mntmp);
695 } else {
696 if (!mlvl)
697 u.mhmax = rnd(4);
698 else
699 u.mhmax = d(mlvl, 8);
700 if (is_home_elemental(&mons[mntmp]))
701 u.mhmax *= 3;
703 u.mh = u.mhmax;
705 if (u.ulevel < mlvl) {
706 /* Low level characters can't become high level monsters for long */
707 #ifdef DUMB
708 /* DRS/NS 2.2.6 messes up -- Peter Kendell */
709 int mtd = u.mtimedone, ulv = u.ulevel;
711 u.mtimedone = mtd * ulv / mlvl;
712 #else
713 u.mtimedone = u.mtimedone * u.ulevel / mlvl;
714 #endif
717 if (uskin && mntmp != armor_to_dragon(uskin->otyp))
718 skinback(FALSE);
719 break_armor();
720 drop_weapon(1);
721 (void) hideunder(&youmonst);
723 if (u.utraptype == TT_PIT && u.utrap) {
724 u.utrap = rn1(6, 2); /* time to escape resets */
726 if (was_blind && !Blind) { /* previous form was eyeless */
727 Blinded = 1L;
728 make_blinded(0L, TRUE); /* remove blindness */
730 newsym(u.ux, u.uy); /* Change symbol */
732 if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data))
733 u.ustuck = 0;
734 else if (sticky && !sticks(youmonst.data))
735 uunstick();
736 if (u.usteed) {
737 if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) {
738 char buf[BUFSZ];
740 pline("%s touch %s.", no_longer_petrify_resistant,
741 mon_nam(u.usteed));
742 Sprintf(buf, "riding %s", an(u.usteed->data->mname));
743 instapetrify(buf);
745 if (!can_ride(u.usteed))
746 dismount_steed(DISMOUNT_POLY);
749 if (flags.verbose) {
750 static const char use_thec[] = "Use the command #%s to %s.";
751 static const char monsterc[] = "monster";
753 if (can_breathe(youmonst.data))
754 pline(use_thec, monsterc, "use your breath weapon");
755 if (attacktype(youmonst.data, AT_SPIT))
756 pline(use_thec, monsterc, "spit venom");
757 if (youmonst.data->mlet == S_NYMPH)
758 pline(use_thec, monsterc, "remove an iron ball");
759 if (attacktype(youmonst.data, AT_GAZE))
760 pline(use_thec, monsterc, "gaze at monsters");
761 if (is_hider(youmonst.data))
762 pline(use_thec, monsterc, "hide");
763 if (is_were(youmonst.data))
764 pline(use_thec, monsterc, "summon help");
765 if (webmaker(youmonst.data))
766 pline(use_thec, monsterc, "spin a web");
767 if (u.umonnum == PM_GREMLIN)
768 pline(use_thec, monsterc, "multiply in a fountain");
769 if (is_unicorn(youmonst.data))
770 pline(use_thec, monsterc, "use your horn");
771 if (is_mind_flayer(youmonst.data))
772 pline(use_thec, monsterc, "emit a mental blast");
773 if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
774 pline(use_thec, monsterc, "shriek");
775 if (is_vampire(youmonst.data))
776 pline(use_thec, monsterc, "change shape");
778 if (lays_eggs(youmonst.data) && flags.female)
779 pline(use_thec, "sit", "lay an egg");
782 /* you now know what an egg of your type looks like */
783 if (lays_eggs(youmonst.data)) {
784 learn_egg_type(u.umonnum);
785 /* make queen bees recognize killer bee eggs */
786 learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
788 find_ac();
789 if ((!Levitation && !u.ustuck && !Flying && is_pool_or_lava(u.ux, u.uy))
790 || (Underwater && !Swimming))
791 spoteffects(TRUE);
792 if (Passes_walls && u.utrap
793 && (u.utraptype == TT_INFLOOR || u.utraptype == TT_BURIEDBALL)) {
794 u.utrap = 0;
795 if (u.utraptype == TT_INFLOOR)
796 pline_The("rock seems to no longer trap you.");
797 else {
798 pline_The("buried ball is no longer bound to you.");
799 buried_ball_to_freedom();
801 } else if (likes_lava(youmonst.data) && u.utrap
802 && u.utraptype == TT_LAVA) {
803 u.utrap = 0;
804 pline_The("lava now feels soothing.");
806 if (amorphous(youmonst.data) || is_whirly(youmonst.data)
807 || unsolid(youmonst.data)) {
808 if (Punished) {
809 You("slip out of the iron chain.");
810 unpunish();
811 } else if (u.utrap && u.utraptype == TT_BURIEDBALL) {
812 You("slip free of the buried ball and chain.");
813 buried_ball_to_freedom();
816 if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP)
817 && (amorphous(youmonst.data) || is_whirly(youmonst.data)
818 || unsolid(youmonst.data) || (youmonst.data->msize <= MZ_SMALL
819 && u.utraptype == TT_BEARTRAP))) {
820 You("are no longer stuck in the %s.",
821 u.utraptype == TT_WEB ? "web" : "bear trap");
822 /* probably should burn webs too if PM_FIRE_ELEMENTAL */
823 u.utrap = 0;
825 if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
826 You("orient yourself on the web.");
827 u.utrap = 0;
829 check_strangling(TRUE); /* maybe start strangling */
830 (void) polysense(youmonst.data);
832 context.botl = 1;
833 vision_full_recalc = 1;
834 see_monsters();
835 (void) encumber_msg();
837 retouch_equipment(2);
838 /* this might trigger a recursive call to polymon() [stone golem
839 wielding cockatrice corpse and hit by stone-to-flesh, becomes
840 flesh golem above, now gets transformed back into stone golem] */
841 if (!uarmg)
842 selftouch(no_longer_petrify_resistant);
843 return 1;
846 STATIC_OVL void
847 break_armor()
849 register struct obj *otmp;
851 if (breakarm(youmonst.data)) {
852 if ((otmp = uarm) != 0) {
853 if (donning(otmp))
854 cancel_don();
855 You("break out of your armor!");
856 exercise(A_STR, FALSE);
857 (void) Armor_gone();
858 useup(otmp);
860 if ((otmp = uarmc) != 0) {
861 if (otmp->oartifact) {
862 Your("%s falls off!", cloak_simple_name(otmp));
863 (void) Cloak_off();
864 dropx(otmp);
865 } else {
866 Your("%s tears apart!", cloak_simple_name(otmp));
867 (void) Cloak_off();
868 useup(otmp);
871 if (uarmu) {
872 Your("shirt rips to shreds!");
873 useup(uarmu);
875 } else if (sliparm(youmonst.data)) {
876 if (((otmp = uarm) != 0) && (racial_exception(&youmonst, otmp) < 1)) {
877 if (donning(otmp))
878 cancel_don();
879 Your("armor falls around you!");
880 (void) Armor_gone();
881 dropx(otmp);
883 if ((otmp = uarmc) != 0) {
884 if (is_whirly(youmonst.data))
885 Your("%s falls, unsupported!", cloak_simple_name(otmp));
886 else
887 You("shrink out of your %s!", cloak_simple_name(otmp));
888 (void) Cloak_off();
889 dropx(otmp);
891 if ((otmp = uarmu) != 0) {
892 if (is_whirly(youmonst.data))
893 You("seep right through your shirt!");
894 else
895 You("become much too small for your shirt!");
896 setworn((struct obj *) 0, otmp->owornmask & W_ARMU);
897 dropx(otmp);
900 if (has_horns(youmonst.data)) {
901 if ((otmp = uarmh) != 0) {
902 if (is_flimsy(otmp) && !donning(otmp)) {
903 char hornbuf[BUFSZ];
905 /* Future possibilities: This could damage/destroy helmet */
906 Sprintf(hornbuf, "horn%s", plur(num_horns(youmonst.data)));
907 Your("%s %s through %s.", hornbuf, vtense(hornbuf, "pierce"),
908 yname(otmp));
909 } else {
910 if (donning(otmp))
911 cancel_don();
912 Your("%s falls to the %s!", helm_simple_name(otmp),
913 surface(u.ux, u.uy));
914 (void) Helmet_off();
915 dropx(otmp);
919 if (nohands(youmonst.data) || verysmall(youmonst.data)) {
920 if ((otmp = uarmg) != 0) {
921 if (donning(otmp))
922 cancel_don();
923 /* Drop weapon along with gloves */
924 You("drop your gloves%s!", uwep ? " and weapon" : "");
925 drop_weapon(0);
926 (void) Gloves_off();
927 dropx(otmp);
929 if ((otmp = uarms) != 0) {
930 You("can no longer hold your shield!");
931 (void) Shield_off();
932 dropx(otmp);
934 if ((otmp = uarmh) != 0) {
935 if (donning(otmp))
936 cancel_don();
937 Your("%s falls to the %s!", helm_simple_name(otmp),
938 surface(u.ux, u.uy));
939 (void) Helmet_off();
940 dropx(otmp);
943 if (nohands(youmonst.data) || verysmall(youmonst.data)
944 || slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) {
945 if ((otmp = uarmf) != 0) {
946 if (donning(otmp))
947 cancel_don();
948 if (is_whirly(youmonst.data))
949 Your("boots fall away!");
950 else
951 Your("boots %s off your feet!",
952 verysmall(youmonst.data) ? "slide" : "are pushed");
953 (void) Boots_off();
954 dropx(otmp);
959 STATIC_OVL void
960 drop_weapon(alone)
961 int alone;
963 struct obj *otmp;
964 const char *what, *which, *whichtoo;
965 boolean candropwep, candropswapwep;
967 if (uwep) {
968 /* !alone check below is currently superfluous but in the
969 * future it might not be so if there are monsters which cannot
970 * wear gloves but can wield weapons
972 if (!alone || cantwield(youmonst.data)) {
973 candropwep = canletgo(uwep, "");
974 candropswapwep = !u.twoweap || canletgo(uswapwep, "");
975 if (alone) {
976 what = (candropwep && candropswapwep) ? "drop" : "release";
977 which = is_sword(uwep) ? "sword" : weapon_descr(uwep);
978 if (u.twoweap) {
979 whichtoo =
980 is_sword(uswapwep) ? "sword" : weapon_descr(uswapwep);
981 if (strcmp(which, whichtoo))
982 which = "weapon";
984 if (uwep->quan != 1L || u.twoweap)
985 which = makeplural(which);
987 You("find you must %s %s %s!", what,
988 the_your[!!strncmp(which, "corpse", 6)], which);
990 if (u.twoweap) {
991 otmp = uswapwep;
992 uswapwepgone();
993 if (candropswapwep)
994 dropx(otmp);
996 otmp = uwep;
997 uwepgone();
998 if (candropwep)
999 dropx(otmp);
1000 update_inventory();
1001 } else if (!could_twoweap(youmonst.data)) {
1002 untwoweapon();
1007 void
1008 rehumanize()
1010 /* You can't revert back while unchanging */
1011 if (Unchanging && (u.mh < 1)) {
1012 killer.format = NO_KILLER_PREFIX;
1013 Strcpy(killer.name, "killed while stuck in creature form");
1014 done(DIED);
1017 if (emits_light(youmonst.data))
1018 del_light_source(LS_MONSTER, monst_to_any(&youmonst));
1019 polyman("return to %s form!", urace.adj);
1021 if (u.uhp < 1) {
1022 /* can only happen if some bit of code reduces u.uhp
1023 instead of u.mh while poly'd */
1024 Your("old form was not healthy enough to survive.");
1025 Sprintf(killer.name, "reverting to unhealthy %s form", urace.adj);
1026 killer.format = KILLED_BY;
1027 done(DIED);
1029 nomul(0);
1031 context.botl = 1;
1032 vision_full_recalc = 1;
1033 (void) encumber_msg();
1035 retouch_equipment(2);
1036 if (!uarmg)
1037 selftouch(no_longer_petrify_resistant);
1041 dobreathe()
1043 struct attack *mattk;
1045 if (Strangled) {
1046 You_cant("breathe. Sorry.");
1047 return 0;
1049 if (u.uen < 15) {
1050 You("don't have enough energy to breathe!");
1051 return 0;
1053 u.uen -= 15;
1054 context.botl = 1;
1056 if (!getdir((char *) 0))
1057 return 0;
1059 mattk = attacktype_fordmg(youmonst.data, AT_BREA, AD_ANY);
1060 if (!mattk)
1061 impossible("bad breath attack?"); /* mouthwash needed... */
1062 else if (!u.dx && !u.dy && !u.dz)
1063 ubreatheu(mattk);
1064 else
1065 buzz((int) (20 + mattk->adtyp - 1), (int) mattk->damn, u.ux, u.uy,
1066 u.dx, u.dy);
1067 return 1;
1071 dospit()
1073 struct obj *otmp;
1074 struct attack *mattk;
1076 if (!getdir((char *) 0))
1077 return 0;
1078 mattk = attacktype_fordmg(youmonst.data, AT_SPIT, AD_ANY);
1079 if (!mattk) {
1080 impossible("bad spit attack?");
1081 } else {
1082 switch (mattk->adtyp) {
1083 case AD_BLND:
1084 case AD_DRST:
1085 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
1086 break;
1087 default:
1088 impossible("bad attack type in dospit");
1089 /* fall through */
1090 case AD_ACID:
1091 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
1092 break;
1094 otmp->spe = 1; /* to indicate it's yours */
1095 throwit(otmp, 0L, FALSE);
1097 return 1;
1101 doremove()
1103 if (!Punished) {
1104 if (u.utrap && u.utraptype == TT_BURIEDBALL) {
1105 pline_The("ball and chain are buried firmly in the %s.",
1106 surface(u.ux, u.uy));
1107 return 0;
1109 You("are not chained to anything!");
1110 return 0;
1112 unpunish();
1113 return 1;
1117 dospinweb()
1119 register struct trap *ttmp = t_at(u.ux, u.uy);
1121 if (Levitation || Is_airlevel(&u.uz) || Underwater
1122 || Is_waterlevel(&u.uz)) {
1123 You("must be on the ground to spin a web.");
1124 return 0;
1126 if (u.uswallow) {
1127 You("release web fluid inside %s.", mon_nam(u.ustuck));
1128 if (is_animal(u.ustuck->data)) {
1129 expels(u.ustuck, u.ustuck->data, TRUE);
1130 return 0;
1132 if (is_whirly(u.ustuck->data)) {
1133 int i;
1135 for (i = 0; i < NATTK; i++)
1136 if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
1137 break;
1138 if (i == NATTK)
1139 impossible("Swallower has no engulfing attack?");
1140 else {
1141 char sweep[30];
1143 sweep[0] = '\0';
1144 switch (u.ustuck->data->mattk[i].adtyp) {
1145 case AD_FIRE:
1146 Strcpy(sweep, "ignites and ");
1147 break;
1148 case AD_ELEC:
1149 Strcpy(sweep, "fries and ");
1150 break;
1151 case AD_COLD:
1152 Strcpy(sweep, "freezes, shatters and ");
1153 break;
1155 pline_The("web %sis swept away!", sweep);
1157 return 0;
1158 } /* default: a nasty jelly-like creature */
1159 pline_The("web dissolves into %s.", mon_nam(u.ustuck));
1160 return 0;
1162 if (u.utrap) {
1163 You("cannot spin webs while stuck in a trap.");
1164 return 0;
1166 exercise(A_DEX, TRUE);
1167 if (ttmp) {
1168 switch (ttmp->ttyp) {
1169 case PIT:
1170 case SPIKED_PIT:
1171 You("spin a web, covering up the pit.");
1172 deltrap(ttmp);
1173 bury_objs(u.ux, u.uy);
1174 newsym(u.ux, u.uy);
1175 return 1;
1176 case SQKY_BOARD:
1177 pline_The("squeaky board is muffled.");
1178 deltrap(ttmp);
1179 newsym(u.ux, u.uy);
1180 return 1;
1181 case TELEP_TRAP:
1182 case LEVEL_TELEP:
1183 case MAGIC_PORTAL:
1184 case VIBRATING_SQUARE:
1185 Your("webbing vanishes!");
1186 return 0;
1187 case WEB:
1188 You("make the web thicker.");
1189 return 1;
1190 case HOLE:
1191 case TRAPDOOR:
1192 You("web over the %s.",
1193 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
1194 deltrap(ttmp);
1195 newsym(u.ux, u.uy);
1196 return 1;
1197 case ROLLING_BOULDER_TRAP:
1198 You("spin a web, jamming the trigger.");
1199 deltrap(ttmp);
1200 newsym(u.ux, u.uy);
1201 return 1;
1202 case ARROW_TRAP:
1203 case DART_TRAP:
1204 case BEAR_TRAP:
1205 case ROCKTRAP:
1206 case FIRE_TRAP:
1207 case LANDMINE:
1208 case SLP_GAS_TRAP:
1209 case RUST_TRAP:
1210 case MAGIC_TRAP:
1211 case ANTI_MAGIC:
1212 case POLY_TRAP:
1213 You("have triggered a trap!");
1214 dotrap(ttmp, 0);
1215 return 1;
1216 default:
1217 impossible("Webbing over trap type %d?", ttmp->ttyp);
1218 return 0;
1220 } else if (On_stairs(u.ux, u.uy)) {
1221 /* cop out: don't let them hide the stairs */
1222 Your("web fails to impede access to the %s.",
1223 (levl[u.ux][u.uy].typ == STAIRS) ? "stairs" : "ladder");
1224 return 1;
1226 ttmp = maketrap(u.ux, u.uy, WEB);
1227 if (ttmp) {
1228 ttmp->madeby_u = 1;
1229 feeltrap(ttmp);
1231 return 1;
1235 dosummon()
1237 int placeholder;
1238 if (u.uen < 10) {
1239 You("lack the energy to send forth a call for help!");
1240 return 0;
1242 u.uen -= 10;
1243 context.botl = 1;
1245 You("call upon your brethren for help!");
1246 exercise(A_WIS, TRUE);
1247 if (!were_summon(youmonst.data, TRUE, &placeholder, (char *) 0))
1248 pline("But none arrive.");
1249 return 1;
1253 dogaze()
1255 register struct monst *mtmp;
1256 int looked = 0;
1257 char qbuf[QBUFSZ];
1258 int i;
1259 uchar adtyp = 0;
1261 for (i = 0; i < NATTK; i++) {
1262 if (youmonst.data->mattk[i].aatyp == AT_GAZE) {
1263 adtyp = youmonst.data->mattk[i].adtyp;
1264 break;
1267 if (adtyp != AD_CONF && adtyp != AD_FIRE) {
1268 impossible("gaze attack %d?", adtyp);
1269 return 0;
1272 if (Blind) {
1273 You_cant("see anything to gaze at.");
1274 return 0;
1275 } else if (Hallucination) {
1276 You_cant("gaze at anything you can see.");
1277 return 0;
1279 if (u.uen < 15) {
1280 You("lack the energy to use your special gaze!");
1281 return 0;
1283 u.uen -= 15;
1284 context.botl = 1;
1286 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1287 if (DEADMONSTER(mtmp))
1288 continue;
1289 if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
1290 looked++;
1291 if (Invis && !perceives(mtmp->data)) {
1292 pline("%s seems not to notice your gaze.", Monnam(mtmp));
1293 } else if (mtmp->minvis && !See_invisible) {
1294 You_cant("see where to gaze at %s.", Monnam(mtmp));
1295 } else if (mtmp->m_ap_type == M_AP_FURNITURE
1296 || mtmp->m_ap_type == M_AP_OBJECT) {
1297 looked--;
1298 continue;
1299 } else if (flags.safe_dog && mtmp->mtame && !Confusion) {
1300 You("avoid gazing at %s.", y_monnam(mtmp));
1301 } else {
1302 if (flags.confirm && mtmp->mpeaceful && !Confusion) {
1303 Sprintf(qbuf, "Really %s %s?",
1304 (adtyp == AD_CONF) ? "confuse" : "attack",
1305 mon_nam(mtmp));
1306 if (yn(qbuf) != 'y')
1307 continue;
1308 setmangry(mtmp);
1310 if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping
1311 || !mtmp->mcansee || !haseyes(mtmp->data)) {
1312 looked--;
1313 continue;
1315 /* No reflection check for consistency with when a monster
1316 * gazes at *you*--only medusa gaze gets reflected then.
1318 if (adtyp == AD_CONF) {
1319 if (!mtmp->mconf)
1320 Your("gaze confuses %s!", mon_nam(mtmp));
1321 else
1322 pline("%s is getting more and more confused.",
1323 Monnam(mtmp));
1324 mtmp->mconf = 1;
1325 } else if (adtyp == AD_FIRE) {
1326 int dmg = d(2, 6), lev = (int) u.ulevel;
1328 You("attack %s with a fiery gaze!", mon_nam(mtmp));
1329 if (resists_fire(mtmp)) {
1330 pline_The("fire doesn't burn %s!", mon_nam(mtmp));
1331 dmg = 0;
1333 if (lev > rn2(20))
1334 (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
1335 if (lev > rn2(20))
1336 (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
1337 if (lev > rn2(25))
1338 (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
1339 if (dmg)
1340 mtmp->mhp -= dmg;
1341 if (mtmp->mhp <= 0)
1342 killed(mtmp);
1344 /* For consistency with passive() in uhitm.c, this only
1345 * affects you if the monster is still alive.
1347 if (DEADMONSTER(mtmp))
1348 continue;
1350 if (mtmp->data == &mons[PM_FLOATING_EYE] && !mtmp->mcan) {
1351 if (!Free_action) {
1352 You("are frozen by %s gaze!",
1353 s_suffix(mon_nam(mtmp)));
1354 nomul((u.ulevel > 6 || rn2(4))
1355 ? -d((int) mtmp->m_lev + 1,
1356 (int) mtmp->data->mattk[0].damd)
1357 : -200);
1358 multi_reason = "frozen by a monster's gaze";
1359 nomovemsg = 0;
1360 return 1;
1361 } else
1362 You("stiffen momentarily under %s gaze.",
1363 s_suffix(mon_nam(mtmp)));
1365 /* Technically this one shouldn't affect you at all because
1366 * the Medusa gaze is an active monster attack that only
1367 * works on the monster's turn, but for it to *not* have an
1368 * effect would be too weird.
1370 if (mtmp->data == &mons[PM_MEDUSA] && !mtmp->mcan) {
1371 pline("Gazing at the awake %s is not a very good idea.",
1372 l_monnam(mtmp));
1373 /* as if gazing at a sleeping anything is fruitful... */
1374 You("turn to stone...");
1375 killer.format = KILLED_BY;
1376 Strcpy(killer.name, "deliberately meeting Medusa's gaze");
1377 done(STONING);
1382 if (!looked)
1383 You("gaze at no place in particular.");
1384 return 1;
1388 dohide()
1390 boolean ismimic = youmonst.data->mlet == S_MIMIC,
1391 on_ceiling = is_clinger(youmonst.data) || Flying;
1393 /* can't hide while being held (or holding) or while trapped
1394 (except for floor hiders [trapper or mimic] in pits) */
1395 if (u.ustuck || (u.utrap && (u.utraptype != TT_PIT || on_ceiling))) {
1396 You_cant("hide while you're %s.",
1397 !u.ustuck ? "trapped" : !sticks(youmonst.data)
1398 ? "being held"
1399 : humanoid(u.ustuck->data)
1400 ? "holding someone"
1401 : "holding that creature");
1402 if (u.uundetected
1403 || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1404 u.uundetected = 0;
1405 youmonst.m_ap_type = M_AP_NOTHING;
1406 newsym(u.ux, u.uy);
1408 return 0;
1410 /* note: the eel and hides_under cases are hypothetical;
1411 such critters aren't offered the option of hiding via #monster */
1412 if (youmonst.data->mlet == S_EEL && !is_pool(u.ux, u.uy)) {
1413 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ))
1414 The("fountain is not deep enough to hide in.");
1415 else
1416 There("is no water to hide in here.");
1417 u.uundetected = 0;
1418 return 0;
1420 if (hides_under(youmonst.data) && !level.objects[u.ux][u.uy]) {
1421 There("is nothing to hide under here.");
1422 u.uundetected = 0;
1423 return 0;
1425 /* Planes of Air and Water */
1426 if (on_ceiling && !has_ceiling(&u.uz)) {
1427 There("is nowhere to hide above you.");
1428 u.uundetected = 0;
1429 return 0;
1431 if ((is_hider(youmonst.data) && !Flying) /* floor hider */
1432 && (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))) {
1433 There("is nowhere to hide beneath you.");
1434 u.uundetected = 0;
1435 return 0;
1437 /* TODO? inhibit floor hiding at furniture locations, or
1438 * else make youhiding() give smarter messages at such spots.
1441 if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1442 youhiding(FALSE, 1); /* "you are already hiding" */
1443 return 0;
1446 if (ismimic) {
1447 /* should bring up a dialog "what would you like to imitate?" */
1448 youmonst.m_ap_type = M_AP_OBJECT;
1449 youmonst.mappearance = STRANGE_OBJECT;
1450 } else
1451 u.uundetected = 1;
1452 newsym(u.ux, u.uy);
1453 youhiding(FALSE, 0); /* "you are now hiding" */
1454 return 1;
1458 dopoly()
1460 struct permonst *savedat = youmonst.data;
1462 if (is_vampire(youmonst.data)) {
1463 polyself(2);
1464 if (savedat != youmonst.data) {
1465 You("transform into %s.", an(youmonst.data->mname));
1466 newsym(u.ux, u.uy);
1469 return 1;
1473 domindblast()
1475 struct monst *mtmp, *nmon;
1477 if (u.uen < 10) {
1478 You("concentrate but lack the energy to maintain doing so.");
1479 return 0;
1481 u.uen -= 10;
1482 context.botl = 1;
1484 You("concentrate.");
1485 pline("A wave of psychic energy pours out.");
1486 for (mtmp = fmon; mtmp; mtmp = nmon) {
1487 int u_sen;
1489 nmon = mtmp->nmon;
1490 if (DEADMONSTER(mtmp))
1491 continue;
1492 if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
1493 continue;
1494 if (mtmp->mpeaceful)
1495 continue;
1496 u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
1497 if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) {
1498 You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
1499 u_sen ? "telepathy"
1500 : telepathic(mtmp->data) ? "latent telepathy" : "mind");
1501 mtmp->mhp -= rnd(15);
1502 if (mtmp->mhp <= 0)
1503 killed(mtmp);
1506 return 1;
1509 STATIC_OVL void
1510 uunstick()
1512 pline("%s is no longer in your clutches.", Monnam(u.ustuck));
1513 u.ustuck = 0;
1516 void
1517 skinback(silently)
1518 boolean silently;
1520 if (uskin) {
1521 if (!silently)
1522 Your("skin returns to its original form.");
1523 uarm = uskin;
1524 uskin = (struct obj *) 0;
1525 /* undo save/restore hack */
1526 uarm->owornmask &= ~I_SPECIAL;
1530 const char *
1531 mbodypart(mon, part)
1532 struct monst *mon;
1533 int part;
1535 static NEARDATA const char
1536 *humanoid_parts[] = { "arm", "eye", "face", "finger",
1537 "fingertip", "foot", "hand", "handed",
1538 "head", "leg", "light headed", "neck",
1539 "spine", "toe", "hair", "blood",
1540 "lung", "nose", "stomach" },
1541 *jelly_parts[] = { "pseudopod", "dark spot", "front",
1542 "pseudopod extension", "pseudopod extremity",
1543 "pseudopod root", "grasp", "grasped",
1544 "cerebral area", "lower pseudopod", "viscous",
1545 "middle", "surface", "pseudopod extremity",
1546 "ripples", "juices", "surface", "sensor",
1547 "stomach" },
1548 *animal_parts[] = { "forelimb", "eye", "face",
1549 "foreclaw", "claw tip", "rear claw",
1550 "foreclaw", "clawed", "head",
1551 "rear limb", "light headed", "neck",
1552 "spine", "rear claw tip", "fur",
1553 "blood", "lung", "nose",
1554 "stomach" },
1555 *bird_parts[] = { "wing", "eye", "face", "wing",
1556 "wing tip", "foot", "wing", "winged",
1557 "head", "leg", "light headed", "neck",
1558 "spine", "toe", "feathers", "blood",
1559 "lung", "bill", "stomach" },
1560 *horse_parts[] = { "foreleg", "eye", "face",
1561 "forehoof", "hoof tip", "rear hoof",
1562 "forehoof", "hooved", "head",
1563 "rear leg", "light headed", "neck",
1564 "backbone", "rear hoof tip", "mane",
1565 "blood", "lung", "nose",
1566 "stomach" },
1567 *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
1568 "tentacle tip", "lower appendage", "tentacle",
1569 "tentacled", "body", "lower tentacle",
1570 "rotational", "equator", "body",
1571 "lower tentacle tip", "cilia", "life force",
1572 "retina", "olfactory nerve", "interior" },
1573 *fungus_parts[] = { "mycelium", "visual area", "front",
1574 "hypha", "hypha", "root",
1575 "strand", "stranded", "cap area",
1576 "rhizome", "sporulated", "stalk",
1577 "root", "rhizome tip", "spores",
1578 "juices", "gill", "gill",
1579 "interior" },
1580 *vortex_parts[] = { "region", "eye", "front",
1581 "minor current", "minor current", "lower current",
1582 "swirl", "swirled", "central core",
1583 "lower current", "addled", "center",
1584 "currents", "edge", "currents",
1585 "life force", "center", "leading edge",
1586 "interior" },
1587 *snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
1588 "large scale tip", "rear region", "scale gap",
1589 "scale gapped", "head", "rear region",
1590 "light headed", "neck", "length", "rear scale",
1591 "scales", "blood", "lung", "forked tongue",
1592 "stomach" },
1593 *worm_parts[] = { "anterior segment", "light sensitive cell",
1594 "clitellum", "setae", "setae", "posterior segment",
1595 "segment", "segmented", "anterior segment",
1596 "posterior", "over stretched", "clitellum",
1597 "length", "posterior setae", "setae", "blood",
1598 "skin", "prostomium", "stomach" },
1599 *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
1600 "pelvic fin", "anal fin", "pectoral fin", "finned",
1601 "head", "peduncle", "played out", "gills",
1602 "dorsal fin", "caudal fin", "scales", "blood",
1603 "gill", "nostril", "stomach" };
1604 /* claw attacks are overloaded in mons[]; most humanoids with
1605 such attacks should still reference hands rather than claws */
1606 static const char not_claws[] = {
1607 S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL, S_NYMPH, S_LEPRECHAUN,
1608 S_QUANTMECH, S_VAMPIRE, S_ORC, S_GIANT, /* quest nemeses */
1609 '\0' /* string terminator; assert( S_xxx != 0 ); */
1611 struct permonst *mptr = mon->data;
1613 /* some special cases */
1614 if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE
1615 || mptr->mlet == S_RODENT || mptr == &mons[PM_OWLBEAR]) {
1616 switch (part) {
1617 case HAND:
1618 return "paw";
1619 case HANDED:
1620 return "pawed";
1621 case FOOT:
1622 return "rear paw";
1623 case ARM:
1624 case LEG:
1625 return horse_parts[part]; /* "foreleg", "rear leg" */
1626 default:
1627 break; /* for other parts, use animal_parts[] below */
1629 } else if (mptr->mlet == S_YETI) { /* excl. owlbear due to 'if' above */
1630 /* opposable thumbs, hence "hands", "arms", "legs", &c */
1631 return humanoid_parts[part]; /* yeti/sasquatch, monkey/ape */
1633 if ((part == HAND || part == HANDED)
1634 && (humanoid(mptr) && attacktype(mptr, AT_CLAW)
1635 && !index(not_claws, mptr->mlet) && mptr != &mons[PM_STONE_GOLEM]
1636 && mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS]))
1637 return (part == HAND) ? "claw" : "clawed";
1638 if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON])
1639 && part == NOSE)
1640 return "trunk";
1641 if (mptr == &mons[PM_SHARK] && part == HAIR)
1642 return "skin"; /* sharks don't have scales */
1643 if ((mptr == &mons[PM_JELLYFISH] || mptr == &mons[PM_KRAKEN])
1644 && (part == ARM || part == FINGER || part == HAND || part == FOOT
1645 || part == TOE))
1646 return "tentacle";
1647 if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
1648 return "cornea";
1649 if (humanoid(mptr) && (part == ARM || part == FINGER || part == FINGERTIP
1650 || part == HAND || part == HANDED))
1651 return humanoid_parts[part];
1652 if (mptr == &mons[PM_RAVEN])
1653 return bird_parts[part];
1654 if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN
1655 || (mptr == &mons[PM_ROTHE] && part != HAIR))
1656 return horse_parts[part];
1657 if (mptr->mlet == S_LIGHT) {
1658 if (part == HANDED)
1659 return "rayed";
1660 else if (part == ARM || part == FINGER || part == FINGERTIP
1661 || part == HAND)
1662 return "ray";
1663 else
1664 return "beam";
1666 if (mptr == &mons[PM_STALKER] && part == HEAD)
1667 return "head";
1668 if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
1669 return fish_parts[part];
1670 if (mptr->mlet == S_WORM)
1671 return worm_parts[part];
1672 if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR))
1673 return snake_parts[part];
1674 if (mptr->mlet == S_EYE)
1675 return sphere_parts[part];
1676 if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING
1677 || mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH])
1678 return jelly_parts[part];
1679 if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL)
1680 return vortex_parts[part];
1681 if (mptr->mlet == S_FUNGUS)
1682 return fungus_parts[part];
1683 if (humanoid(mptr))
1684 return humanoid_parts[part];
1685 return animal_parts[part];
1688 const char *
1689 body_part(part)
1690 int part;
1692 return mbodypart(&youmonst, part);
1696 poly_gender()
1698 /* Returns gender of polymorphed player;
1699 * 0/1=same meaning as flags.female, 2=none.
1701 if (is_neuter(youmonst.data) || !humanoid(youmonst.data))
1702 return 2;
1703 return flags.female;
1706 void
1707 ugolemeffects(damtype, dam)
1708 int damtype, dam;
1710 int heal = 0;
1712 /* We won't bother with "slow"/"haste" since players do not
1713 * have a monster-specific slow/haste so there is no way to
1714 * restore the old velocity once they are back to human.
1716 if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
1717 return;
1718 switch (damtype) {
1719 case AD_ELEC:
1720 if (u.umonnum == PM_FLESH_GOLEM)
1721 heal = (dam + 5) / 6; /* Approx 1 per die */
1722 break;
1723 case AD_FIRE:
1724 if (u.umonnum == PM_IRON_GOLEM)
1725 heal = dam;
1726 break;
1728 if (heal && (u.mh < u.mhmax)) {
1729 u.mh += heal;
1730 if (u.mh > u.mhmax)
1731 u.mh = u.mhmax;
1732 context.botl = 1;
1733 pline("Strangely, you feel better than before.");
1734 exercise(A_STR, TRUE);
1738 STATIC_OVL int
1739 armor_to_dragon(atyp)
1740 int atyp;
1742 switch (atyp) {
1743 case GRAY_DRAGON_SCALE_MAIL:
1744 case GRAY_DRAGON_SCALES:
1745 return PM_GRAY_DRAGON;
1746 case SILVER_DRAGON_SCALE_MAIL:
1747 case SILVER_DRAGON_SCALES:
1748 return PM_SILVER_DRAGON;
1749 #if 0 /* DEFERRED */
1750 case SHIMMERING_DRAGON_SCALE_MAIL:
1751 case SHIMMERING_DRAGON_SCALES:
1752 return PM_SHIMMERING_DRAGON;
1753 #endif
1754 case RED_DRAGON_SCALE_MAIL:
1755 case RED_DRAGON_SCALES:
1756 return PM_RED_DRAGON;
1757 case ORANGE_DRAGON_SCALE_MAIL:
1758 case ORANGE_DRAGON_SCALES:
1759 return PM_ORANGE_DRAGON;
1760 case WHITE_DRAGON_SCALE_MAIL:
1761 case WHITE_DRAGON_SCALES:
1762 return PM_WHITE_DRAGON;
1763 case BLACK_DRAGON_SCALE_MAIL:
1764 case BLACK_DRAGON_SCALES:
1765 return PM_BLACK_DRAGON;
1766 case BLUE_DRAGON_SCALE_MAIL:
1767 case BLUE_DRAGON_SCALES:
1768 return PM_BLUE_DRAGON;
1769 case GREEN_DRAGON_SCALE_MAIL:
1770 case GREEN_DRAGON_SCALES:
1771 return PM_GREEN_DRAGON;
1772 case YELLOW_DRAGON_SCALE_MAIL:
1773 case YELLOW_DRAGON_SCALES:
1774 return PM_YELLOW_DRAGON;
1775 default:
1776 return -1;
1781 * Some species have awareness of other species
1783 static boolean
1784 polysense(mptr)
1785 struct permonst *mptr;
1787 short warnidx = 0;
1789 context.warntype.speciesidx = 0;
1790 context.warntype.species = 0;
1791 context.warntype.polyd = 0;
1793 switch (monsndx(mptr)) {
1794 case PM_PURPLE_WORM:
1795 warnidx = PM_SHRIEKER;
1796 break;
1797 case PM_VAMPIRE:
1798 case PM_VAMPIRE_LORD:
1799 context.warntype.polyd = M2_HUMAN | M2_ELF;
1800 HWarn_of_mon |= FROMRACE;
1801 return TRUE;
1803 if (warnidx) {
1804 context.warntype.speciesidx = warnidx;
1805 context.warntype.species = &mons[warnidx];
1806 HWarn_of_mon |= FROMRACE;
1807 return TRUE;
1809 context.warntype.speciesidx = 0;
1810 context.warntype.species = 0;
1811 HWarn_of_mon &= ~FROMRACE;
1812 return FALSE;
1815 /*polyself.c*/