1 /* SCCS Id: @(#)polyself.c 3.4 2003/01/08 */
2 /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
3 /* NetHack may be freely redistributed. See license for details. */
6 * Polymorph self routine.
8 * Note: the light source handling code assumes that both youmonst.m_id
9 * and youmonst.mx will always remain 0 when it handles the case of the
10 * player polymorphed into a light-emitting monster.
16 STATIC_DCL void FDECL(polyman, (const char *,const char *));
17 STATIC_DCL void NDECL(break_armor);
18 STATIC_DCL void FDECL(drop_weapon,(int));
19 STATIC_DCL void NDECL(uunstick);
20 STATIC_DCL int FDECL(armor_to_dragon,(int));
21 STATIC_DCL void NDECL(newman);
23 /* update the youmonst.data structure pointer */
27 set_mon_data(&youmonst, &mons[u.umonnum], 0);
30 /* make a (new) human out of the player */
33 const char *fmt, *arg;
35 boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
36 was_mimicking = (youmonst.m_ap_type == M_AP_OBJECT);
37 boolean could_pass_walls = Passes_walls;
38 boolean was_blind = !!Blind;
41 u.acurr = u.macurr; /* restore old attribs */
43 u.umonnum = u.umonster;
44 flags.female = u.mfemale;
53 if (sticky) uunstick();
56 if (multi < 0) unmul("");
57 youmonst.m_ap_type = M_AP_NOTHING;
63 /* check whether player foolishly genocided self while poly'd */
64 if ((mvitals[urole.malenum].mvflags & G_GENOD) ||
65 (urole.femalenum != NON_PM &&
66 (mvitals[urole.femalenum].mvflags & G_GENOD)) ||
67 (mvitals[urace.malenum].mvflags & G_GENOD) ||
68 (urace.femalenum != NON_PM &&
69 (mvitals[urace.femalenum].mvflags & G_GENOD))) {
70 /* intervening activity might have clobbered genocide info */
71 killer = delayed_killer;
72 if (!killer || !strstri(killer, "genocid")) {
73 killer_format = KILLED_BY;
74 killer = "self-genocide";
79 if (u.twoweap && !could_twoweap(youmonst.data))
82 if (u.utraptype == TT_PIT) {
83 if (could_pass_walls) { /* player forms cannot pass walls */
87 if (was_blind && !Blind) { /* reverting from eyeless */
89 make_blinded(0L, TRUE); /* remove blindness */
92 if(!Levitation && !u.ustuck &&
93 (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy)))
102 /* setting u.umonster for caveman/cavewoman or priest/priestess
103 swap unintentionally makes `Upolyd' appear to be true */
104 boolean already_polyd = (boolean) Upolyd;
106 /* Some monsters are always of one sex and their sex can't be changed */
107 /* succubi/incubi can change, but are handled below */
108 /* !already_polyd check necessary because is_male() and is_female()
109 are true if the player is a priest/priestess */
110 if (!already_polyd || (!is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data)))
111 flags.female = !flags.female;
112 if (already_polyd) /* poly'd: also change saved sex */
113 u.mfemale = !u.mfemale;
114 max_rank_sz(); /* [this appears to be superfluous] */
115 if ((already_polyd ? u.mfemale : flags.female) && urole.name.f)
116 Strcpy(pl_character, urole.name.f);
118 Strcpy(pl_character, urole.name.m);
119 u.umonster = ((already_polyd ? u.mfemale : flags.female) && urole.femalenum != NON_PM) ?
120 urole.femalenum : urole.malenum;
121 if (!already_polyd) {
122 u.umonnum = u.umonster;
123 } else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) {
124 flags.female = !flags.female;
125 /* change monster type to match new sex */
126 u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS;
138 u.ulevel = u.ulevel + rn1(5, -2);
139 if (u.ulevel > 127 || u.ulevel < 1) { /* level went below 0? */
140 u.ulevel = oldlvl; /* restore old level in case they lifesave */
143 if (u.ulevel > MAXULEV) u.ulevel = MAXULEV;
144 /* If your level goes down, your peak level goes down by
145 the same amount so that you can't simply use blessed
146 full healing to undo the decrease. But if your level
147 goes up, your peak level does *not* undergo the same
148 adjustment; you might end up losing out on the chance
149 to regain some levels previously lost to other causes. */
150 if (u.ulevel < oldlvl) u.ulevelmax -= (oldlvl - u.ulevel);
151 if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel;
153 if (!rn2(10)) change_sex();
155 adjabil(oldlvl, (int)u.ulevel);
156 reset_rndmonst(NON_PM); /* new monster generation criteria */
158 /* random experience points for the new experience level */
159 u.uexp = rndexp(FALSE);
161 /* u.uhpmax * u.ulevel / oldlvl: proportionate hit points to new level
162 * -10 and +10: don't apply proportionate HP to 10 of a starting
163 * character's hit points (since a starting character's hit points
164 * are not on the same scale with hit points obtained through level
166 * 9 - rn2(19): random change of -9 to +9 hit points
169 u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / oldlvl + 10) +
176 u.uhp = u.uhp * (long)u.uhpmax/tmp;
181 u.uenmax = u.uenmax * (long)u.ulevel / oldlvl + 9 - rn2(19);
183 if (u.uenmax < 0) u.uenmax = 0;
185 u.uen = (tmp ? u.uen * (long)u.uenmax / tmp : u.uenmax);
189 u.uhunger = rn1(500,500);
190 if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL);
193 if (u.uhp <= 0 || u.uhpmax <= 0) {
194 if (Polymorph_control) {
195 if (u.uhp <= 0) u.uhp = 1;
196 if (u.uhpmax <= 0) u.uhpmax = 1;
198 dead: /* we come directly here if their experience level went to 0 or less */
199 Your("new form doesn't seem healthy enough to survive.");
200 killer_format = KILLED_BY_AN;
201 killer="unsuccessful polymorph";
204 return; /* lifesaved */
208 polyman("feel like a new %s!",
209 (flags.female && urace.individual.f) ? urace.individual.f :
210 (urace.individual.m) ? urace.individual.m : urace.noun);
212 Your("body transforms, but there is still slime on you.");
217 (void) encumber_msg();
221 polyself(forcecontrol)
222 boolean forcecontrol;
225 int old_light, new_light;
228 boolean draconian = (uarm &&
229 uarm->otyp >= GRAY_DRAGON_SCALE_MAIL &&
230 uarm->otyp <= YELLOW_DRAGON_SCALES);
231 boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data));
232 boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT);
233 boolean was_floating = (Levitation || Flying);
235 if(!Polymorph_control && !forcecontrol && !draconian && !iswere && !isvamp) {
236 if (rn2(20) > ACURR(A_CON)) {
237 You(shudder_for_moment);
238 losehp(rnd(30), "system shock", KILLED_BY_AN);
239 exercise(A_CON, FALSE);
243 old_light = Upolyd ? emits_light(youmonst.data) : 0;
245 if (Polymorph_control || forcecontrol) {
247 getlin("Become what kind of monster? [type the name]",
249 mntmp = name_to_mon(buf);
251 pline("I've never heard of such monsters.");
252 /* Note: humans are illegal as monsters, but an
253 * illegal monster forces newman(), which is what we
254 * want if they specified a human.... */
255 else if (!polyok(&mons[mntmp]) && !your_race(&mons[mntmp]))
256 You("cannot polymorph into that.");
258 } while(++tries < 5);
259 if (tries==5) pline(thats_enough_tries);
260 /* allow skin merging, even when polymorph is controlled */
262 (mntmp == armor_to_dragon(uarm->otyp) || tries == 5))
264 } else if (draconian || iswere || isvamp) {
265 /* special changes that don't require polyok() */
268 mntmp = armor_to_dragon(uarm->otyp);
269 if (!(mvitals[mntmp].mvflags & G_GENOD)) {
270 /* allow G_EXTINCT */
271 You("merge with your scaly armor.");
273 uarm = (struct obj *)0;
274 /* save/restore hack */
275 uskin->owornmask |= I_SPECIAL;
278 if (is_were(youmonst.data))
279 mntmp = PM_HUMAN; /* Illegal; force newman() */
283 if (youmonst.data->mlet == S_VAMPIRE)
284 mntmp = PM_VAMPIRE_BAT;
288 /* if polymon fails, "you feel" message has been given
289 so don't follow up with another polymon or newman */
290 if (mntmp == PM_HUMAN) newman(); /* werecritter */
291 else (void) polymon(mntmp);
292 goto made_change; /* maybe not, but this is right anyway */
295 if (mntmp < LOW_PM) {
298 /* randomly pick an "ordinary" monster */
299 mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
300 } while((!polyok(&mons[mntmp]) || is_placeholder(&mons[mntmp]))
304 /* The below polyok() fails either if everything is genocided, or if
305 * we deliberately chose something illegal to force newman().
307 if (!polyok(&mons[mntmp]) || !rn2(5) || your_race(&mons[mntmp]))
309 else if(!polymon(mntmp)) return;
311 if (!uarmg) selftouch("No longer petrify-resistant, you");
314 new_light = Upolyd ? emits_light(youmonst.data) : 0;
315 if (old_light != new_light) {
317 del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
318 if (new_light == 1) ++new_light; /* otherwise it's undetectable */
320 new_light_source(u.ux, u.uy, new_light,
321 LS_MONSTER, (genericptr_t)&youmonst);
323 if (is_pool(u.ux,u.uy) && was_floating && !(Levitation || Flying) &&
324 !breathless(youmonst.data) && !amphibious(youmonst.data) &&
328 /* (try to) make a mntmp monster out of the player */
330 polymon(mntmp) /* returns 1 if polymorph successful */
333 boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
334 was_blind = !!Blind, dochange = FALSE;
335 boolean could_pass_walls = Passes_walls;
338 if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
339 You_feel("rather %s-ish.",mons[mntmp].mname);
340 exercise(A_WIS, TRUE);
345 u.uconduct.polyselfs++;
348 /* Human to monster; save human stats */
351 u.mfemale = flags.female;
353 /* Monster to monster; restore human stats, to be
354 * immediately changed to provide stats for the new monster
358 flags.female = u.mfemale;
361 if (youmonst.m_ap_type) {
362 /* stop mimicking immediately */
363 if (multi < 0) unmul("");
364 } else if (mons[mntmp].mlet != S_MIMIC) {
365 /* as in polyman() */
366 youmonst.m_ap_type = M_AP_NOTHING;
368 if (is_male(&mons[mntmp])) {
369 if(flags.female) dochange = TRUE;
370 } else if (is_female(&mons[mntmp])) {
371 if(!flags.female) dochange = TRUE;
372 } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
373 if(!rn2(10)) dochange = TRUE;
376 flags.female = !flags.female;
378 (u.umonnum != mntmp) ? "turn into a" : "feel like a new",
379 (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" :
380 flags.female ? "female " : "male ",
383 if (u.umonnum != mntmp)
384 You("turn into %s!", an(mons[mntmp].mname));
386 You_feel("like a new %s!", mons[mntmp].mname);
388 if (Stoned && poly_when_stoned(&mons[mntmp])) {
389 /* poly_when_stoned already checked stone golem genocide */
390 You("turn to stone!");
391 mntmp = PM_STONE_GOLEM;
396 u.mtimedone = rn1(500, 500);
400 /* New stats for monster, to last only as long as polymorphed.
401 * Currently only strength gets changed.
403 if(strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100);
405 if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */
408 You("no longer seem to be petrifying.");
410 if (Sick_resistance && Sick) {
411 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
412 You("no longer feel sick.");
415 if (flaming(youmonst.data)) {
416 pline_The("slime burns away!");
419 } else if (mntmp == PM_GREEN_SLIME) {
425 if (nohands(youmonst.data)) Glib = 0;
428 mlvl = adj_lev(&mons[mntmp]);
429 * We can't do the above, since there's no such thing as an
430 * "experience level of you as a monster" for a polymorphed character.
432 mlvl = (int)mons[mntmp].mlevel;
433 if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) {
434 u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + d(mlvl,4));
435 } else if (is_golem(youmonst.data)) {
436 u.mhmax = golemhp(mntmp);
438 if (!mlvl) u.mhmax = rnd(4);
439 else u.mhmax = d(mlvl, 8);
440 if (is_home_elemental(&mons[mntmp])) u.mhmax *= 3;
444 if (u.ulevel < mlvl) {
445 /* Low level characters can't become high level monsters for long */
447 /* DRS/NS 2.2.6 messes up -- Peter Kendell */
448 int mtd = u.mtimedone, ulv = u.ulevel;
450 u.mtimedone = mtd * ulv / mlvl;
452 u.mtimedone = u.mtimedone * u.ulevel / mlvl;
456 if (uskin && mntmp != armor_to_dragon(uskin->otyp))
460 if (hides_under(youmonst.data))
461 u.uundetected = OBJ_AT(u.ux, u.uy);
462 else if (youmonst.data->mlet == S_EEL)
463 u.uundetected = is_pool(u.ux, u.uy);
467 if (u.utraptype == TT_PIT) {
468 if (could_pass_walls && !Passes_walls) {
470 } else if (!could_pass_walls && Passes_walls) {
474 if (was_blind && !Blind) { /* previous form was eyeless */
476 make_blinded(0L, TRUE); /* remove blindness */
478 newsym(u.ux,u.uy); /* Change symbol */
480 if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0;
481 else if (sticky && !sticks(youmonst.data)) uunstick();
484 if (touch_petrifies(u.usteed->data) &&
485 !Stone_resistance && rnl(3)) {
488 pline("No longer petrifying-resistant, you touch %s.",
490 Sprintf(buf, "riding %s", an(u.usteed->data->mname));
493 if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY);
498 static const char use_thec[] = "Use the command #%s to %s.";
499 static const char monsterc[] = "monster";
500 if (can_breathe(youmonst.data))
501 pline(use_thec,monsterc,"use your breath weapon");
502 if (attacktype(youmonst.data, AT_SPIT))
503 pline(use_thec,monsterc,"spit venom");
504 if (youmonst.data->mlet == S_NYMPH)
505 pline(use_thec,monsterc,"remove an iron ball");
506 if (attacktype(youmonst.data, AT_GAZE))
507 pline(use_thec,monsterc,"gaze at monsters");
508 if (is_hider(youmonst.data))
509 pline(use_thec,monsterc,"hide");
510 if (is_were(youmonst.data))
511 pline(use_thec,monsterc,"summon help");
512 if (webmaker(youmonst.data))
513 pline(use_thec,monsterc,"spin a web");
514 if (u.umonnum == PM_GREMLIN)
515 pline(use_thec,monsterc,"multiply in a fountain");
516 if (is_unicorn(youmonst.data))
517 pline(use_thec,monsterc,"use your horn");
518 if (is_mind_flayer(youmonst.data))
519 pline(use_thec,monsterc,"emit a mental blast");
520 if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
521 pline(use_thec,monsterc,"shriek");
522 if (lays_eggs(youmonst.data) && flags.female)
523 pline(use_thec,"sit","lay an egg");
525 /* you now know what an egg of your type looks like */
526 if (lays_eggs(youmonst.data)) {
527 learn_egg_type(u.umonnum);
528 /* make queen bees recognize killer bee eggs */
529 learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
532 if((!Levitation && !u.ustuck && !Flying &&
533 (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) ||
534 (Underwater && !Swimming))
536 if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) {
538 pline_The("rock seems to no longer trap you.");
539 } else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) {
541 pline_The("lava now feels soothing.");
543 if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) {
545 You("slip out of the iron chain.");
549 if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) &&
550 (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) ||
551 (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) {
552 You("are no longer stuck in the %s.",
553 u.utraptype == TT_WEB ? "web" : "bear trap");
554 /* probably should burn webs too if PM_FIRE_ELEMENTAL */
557 if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
558 You("orient yourself on the web.");
562 vision_full_recalc = 1;
564 exercise(A_CON, FALSE);
565 exercise(A_WIS, TRUE);
566 (void) encumber_msg();
573 register struct obj *otmp;
575 if (breakarm(youmonst.data)) {
576 if ((otmp = uarm) != 0) {
577 if (donning(otmp)) cancel_don();
578 You("break out of your armor!");
579 exercise(A_STR, FALSE);
583 if ((otmp = uarmc) != 0) {
584 if(otmp->oartifact) {
585 Your("%s falls off!", cloak_simple_name(otmp));
589 Your("%s tears apart!", cloak_simple_name(otmp));
596 Your("shirt rips to shreds!");
600 } else if (sliparm(youmonst.data)) {
601 if (((otmp = uarm) != 0) && (racial_exception(&youmonst, otmp) < 1)) {
602 if (donning(otmp)) cancel_don();
603 Your("armor falls around you!");
607 if ((otmp = uarmc) != 0) {
608 if (is_whirly(youmonst.data))
609 Your("%s falls, unsupported!", cloak_simple_name(otmp));
610 else You("shrink out of your %s!", cloak_simple_name(otmp));
615 if ((otmp = uarmu) != 0) {
616 if (is_whirly(youmonst.data))
617 You("seep right through your shirt!");
618 else You("become much too small for your shirt!");
619 setworn((struct obj *)0, otmp->owornmask & W_ARMU);
624 if (has_horns(youmonst.data)) {
625 if ((otmp = uarmh) != 0) {
626 if (is_flimsy(otmp) && !donning(otmp)) {
627 char hornbuf[BUFSZ], yourbuf[BUFSZ];
629 /* Future possiblities: This could damage/destroy helmet */
630 Sprintf(hornbuf, "horn%s", plur(num_horns(youmonst.data)));
631 Your("%s %s through %s %s.", hornbuf, vtense(hornbuf, "pierce"),
632 shk_your(yourbuf, otmp), xname(otmp));
634 if (donning(otmp)) cancel_don();
635 Your("helmet falls to the %s!", surface(u.ux, u.uy));
641 if (nohands(youmonst.data) || verysmall(youmonst.data)) {
642 if ((otmp = uarmg) != 0) {
643 if (donning(otmp)) cancel_don();
644 /* Drop weapon along with gloves */
645 You("drop your gloves%s!", uwep ? " and weapon" : "");
650 if ((otmp = uarms) != 0) {
651 You("can no longer hold your shield!");
655 if ((otmp = uarmh) != 0) {
656 if (donning(otmp)) cancel_don();
657 Your("helmet falls to the %s!", surface(u.ux, u.uy));
662 if (nohands(youmonst.data) || verysmall(youmonst.data) ||
663 slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) {
664 if ((otmp = uarmf) != 0) {
665 if (donning(otmp)) cancel_don();
666 if (is_whirly(youmonst.data))
667 Your("boots fall away!");
668 else Your("boots %s off your feet!",
669 verysmall(youmonst.data) ? "slide" : "are pushed");
683 if ((otmp = uwep) != 0) {
684 /* !alone check below is currently superfluous but in the
685 * future it might not be so if there are monsters which cannot
686 * wear gloves but can wield weapons
688 if (!alone || cantwield(youmonst.data)) {
689 struct obj *wep = uwep;
691 if (alone) You("find you must drop your weapon%s!",
692 u.twoweap ? "s" : "");
693 otmp2 = u.twoweap ? uswapwep : 0;
695 if (!wep->cursed || wep->otyp != LOADSTONE)
699 if (!otmp2->cursed || otmp2->otyp != LOADSTONE)
703 } else if (!could_twoweap(youmonst.data)) {
712 /* You can't revert back while unchanging */
713 if (Unchanging && (u.mh < 1)) {
714 killer_format = NO_KILLER_PREFIX;
715 killer = "killed while stuck in creature form";
719 if (emits_light(youmonst.data))
720 del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
721 polyman("return to %s form!", urace.adj);
726 Sprintf(kbuf, "reverting to unhealthy %s form", urace.adj);
727 killer_format = KILLED_BY;
731 if (!uarmg) selftouch("No longer petrify-resistant, you");
735 vision_full_recalc = 1;
736 (void) encumber_msg();
742 struct attack *mattk;
745 You_cant("breathe. Sorry.");
749 You("don't have enough energy to breathe!");
755 if (!getdir((char *)0)) return(0);
757 mattk = attacktype_fordmg(youmonst.data, AT_BREA, AD_ANY);
759 impossible("bad breath attack?"); /* mouthwash needed... */
761 buzz((int) (20 + mattk->adtyp-1), (int)mattk->damn,
762 u.ux, u.uy, u.dx, u.dy);
771 if (!getdir((char *)0)) return(0);
772 otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM,
774 otmp->spe = 1; /* to indicate it's yours */
775 throwit(otmp, 0L, FALSE);
783 You("are not chained to anything!");
793 register struct trap *ttmp = t_at(u.ux,u.uy);
795 if (Levitation || Is_airlevel(&u.uz)
796 || Underwater || Is_waterlevel(&u.uz)) {
797 You("must be on the ground to spin a web.");
801 You("release web fluid inside %s.", mon_nam(u.ustuck));
802 if (is_animal(u.ustuck->data)) {
803 expels(u.ustuck, u.ustuck->data, TRUE);
806 if (is_whirly(u.ustuck->data)) {
809 for (i = 0; i < NATTK; i++)
810 if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
813 impossible("Swallower has no engulfing attack?");
818 switch(u.ustuck->data->mattk[i].adtyp) {
820 Strcpy(sweep, "ignites and ");
823 Strcpy(sweep, "fries and ");
827 "freezes, shatters and ");
830 pline_The("web %sis swept away!", sweep);
833 } /* default: a nasty jelly-like creature */
834 pline_The("web dissolves into %s.", mon_nam(u.ustuck));
838 You("cannot spin webs while stuck in a trap.");
841 exercise(A_DEX, TRUE);
842 if (ttmp) switch (ttmp->ttyp) {
844 case SPIKED_PIT: You("spin a web, covering up the pit.");
846 bury_objs(u.ux, u.uy);
849 case SQKY_BOARD: pline_The("squeaky board is muffled.");
856 Your("webbing vanishes!");
858 case WEB: You("make the web thicker.");
862 You("web over the %s.",
863 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
867 case ROLLING_BOULDER_TRAP:
868 You("spin a web, jamming the trigger.");
883 You("have triggered a trap!");
887 impossible("Webbing over trap type %d?", ttmp->ttyp);
890 else if (On_stairs(u.ux, u.uy)) {
891 /* cop out: don't let them hide the stairs */
892 Your("web fails to impede access to the %s.",
893 (levl[u.ux][u.uy].typ == STAIRS) ? "stairs" : "ladder");
897 ttmp = maketrap(u.ux, u.uy, WEB);
911 You("lack the energy to send forth a call for help!");
917 You("call upon your brethren for help!");
918 exercise(A_WIS, TRUE);
919 if (!were_summon(youmonst.data, TRUE, &placeholder, (char *)0))
920 pline("But none arrive.");
927 register struct monst *mtmp;
933 for (i = 0; i < NATTK; i++) {
934 if(youmonst.data->mattk[i].aatyp == AT_GAZE) {
935 adtyp = youmonst.data->mattk[i].adtyp;
939 if (adtyp != AD_CONF && adtyp != AD_FIRE) {
940 impossible("gaze attack %d?", adtyp);
946 You_cant("see anything to gaze at.");
950 You("lack the energy to use your special gaze!");
956 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
957 if (DEADMONSTER(mtmp)) continue;
958 if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
960 if (Invis && !perceives(mtmp->data))
961 pline("%s seems not to notice your gaze.", Monnam(mtmp));
962 else if (mtmp->minvis && !See_invisible)
963 You_cant("see where to gaze at %s.", Monnam(mtmp));
964 else if (mtmp->m_ap_type == M_AP_FURNITURE
965 || mtmp->m_ap_type == M_AP_OBJECT) {
968 } else if (flags.safe_dog && !Confusion && !Hallucination
970 You("avoid gazing at %s.", y_monnam(mtmp));
972 if (flags.confirm && mtmp->mpeaceful && !Confusion
974 Sprintf(qbuf, "Really %s %s?",
975 (adtyp == AD_CONF) ? "confuse" : "attack",
977 if (yn(qbuf) != 'y') continue;
980 if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping ||
981 !mtmp->mcansee || !haseyes(mtmp->data)) {
985 /* No reflection check for consistency with when a monster
986 * gazes at *you*--only medusa gaze gets reflected then.
988 if (adtyp == AD_CONF) {
990 Your("gaze confuses %s!", mon_nam(mtmp));
992 pline("%s is getting more and more confused.",
995 } else if (adtyp == AD_FIRE) {
997 You("attack %s with a fiery gaze!", mon_nam(mtmp));
998 if (resists_fire(mtmp)) {
999 pline_The("fire doesn't burn %s!", mon_nam(mtmp));
1002 if((int) u.ulevel > rn2(20))
1003 (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
1004 if((int) u.ulevel > rn2(20))
1005 (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
1006 if((int) u.ulevel > rn2(25))
1007 (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
1008 if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg;
1009 if (mtmp->mhp <= 0) killed(mtmp);
1011 /* For consistency with passive() in uhitm.c, this only
1012 * affects you if the monster is still alive.
1014 if (!DEADMONSTER(mtmp) &&
1015 (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) {
1017 You("are frozen by %s gaze!",
1018 s_suffix(mon_nam(mtmp)));
1019 nomul((u.ulevel > 6 || rn2(4)) ?
1020 -d((int)mtmp->m_lev+1,
1021 (int)mtmp->data->mattk[0].damd)
1025 You("stiffen momentarily under %s gaze.",
1026 s_suffix(mon_nam(mtmp)));
1028 /* Technically this one shouldn't affect you at all because
1029 * the Medusa gaze is an active monster attack that only
1030 * works on the monster's turn, but for it to *not* have an
1031 * effect would be too weird.
1033 if (!DEADMONSTER(mtmp) &&
1034 (mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) {
1036 "Gazing at the awake %s is not a very good idea.",
1038 /* as if gazing at a sleeping anything is fruitful... */
1039 You("turn to stone...");
1040 killer_format = KILLED_BY;
1041 killer = "deliberately meeting Medusa's gaze";
1047 if (!looked) You("gaze at no place in particular.");
1054 boolean ismimic = youmonst.data->mlet == S_MIMIC;
1056 if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
1057 You("are already hiding.");
1061 /* should bring up a dialog "what would you like to imitate?" */
1062 youmonst.m_ap_type = M_AP_OBJECT;
1063 youmonst.mappearance = STRANGE_OBJECT;
1073 struct monst *mtmp, *nmon;
1076 You("concentrate but lack the energy to maintain doing so.");
1082 You("concentrate.");
1083 pline("A wave of psychic energy pours out.");
1084 for(mtmp=fmon; mtmp; mtmp = nmon) {
1088 if (DEADMONSTER(mtmp))
1090 if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
1094 u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
1095 if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) {
1096 You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
1097 u_sen ? "telepathy" :
1098 telepathic(mtmp->data) ? "latent telepathy" :
1100 mtmp->mhp -= rnd(15);
1111 pline("%s is no longer in your clutches.", Monnam(u.ustuck));
1120 if (!silently) Your("skin returns to its original form.");
1122 uskin = (struct obj *)0;
1123 /* undo save/restore hack */
1124 uarm->owornmask &= ~I_SPECIAL;
1132 mbodypart(mon, part)
1136 static NEARDATA const char
1137 *humanoid_parts[] = { "arm", "eye", "face", "finger",
1138 "fingertip", "foot", "hand", "handed", "head", "leg",
1139 "light headed", "neck", "spine", "toe", "hair",
1140 "blood", "lung", "nose", "stomach"},
1141 *jelly_parts[] = { "pseudopod", "dark spot", "front",
1142 "pseudopod extension", "pseudopod extremity",
1143 "pseudopod root", "grasp", "grasped", "cerebral area",
1144 "lower pseudopod", "viscous", "middle", "surface",
1145 "pseudopod extremity", "ripples", "juices",
1146 "surface", "sensor", "stomach" },
1147 *animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip",
1148 "rear claw", "foreclaw", "clawed", "head", "rear limb",
1149 "light headed", "neck", "spine", "rear claw tip",
1150 "fur", "blood", "lung", "nose", "stomach" },
1151 *bird_parts[] = { "wing", "eye", "face", "wing", "wing tip",
1152 "foot", "wing", "winged", "head", "leg",
1153 "light headed", "neck", "spine", "toe",
1154 "feathers", "blood", "lung", "bill", "stomach" },
1155 *horse_parts[] = { "foreleg", "eye", "face", "forehoof", "hoof tip",
1156 "rear hoof", "foreclaw", "hooved", "head", "rear leg",
1157 "light headed", "neck", "backbone", "rear hoof tip",
1158 "mane", "blood", "lung", "nose", "stomach"},
1159 *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
1160 "tentacle tip", "lower appendage", "tentacle", "tentacled",
1161 "body", "lower tentacle", "rotational", "equator", "body",
1162 "lower tentacle tip", "cilia", "life force", "retina",
1163 "olfactory nerve", "interior" },
1164 *fungus_parts[] = { "mycelium", "visual area", "front", "hypha",
1165 "hypha", "root", "strand", "stranded", "cap area",
1166 "rhizome", "sporulated", "stalk", "root", "rhizome tip",
1167 "spores", "juices", "gill", "gill", "interior" },
1168 *vortex_parts[] = { "region", "eye", "front", "minor current",
1169 "minor current", "lower current", "swirl", "swirled",
1170 "central core", "lower current", "addled", "center",
1171 "currents", "edge", "currents", "life force",
1172 "center", "leading edge", "interior" },
1173 *snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
1174 "large scale tip", "rear region", "scale gap", "scale gapped",
1175 "head", "rear region", "light headed", "neck", "length",
1176 "rear scale", "scales", "blood", "lung", "forked tongue", "stomach" },
1177 *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
1178 "pelvic fin", "anal fin", "pectoral fin", "finned", "head", "peduncle",
1179 "played out", "gills", "dorsal fin", "caudal fin",
1180 "scales", "blood", "gill", "nostril", "stomach" };
1181 /* claw attacks are overloaded in mons[]; most humanoids with
1182 such attacks should still reference hands rather than claws */
1183 static const char not_claws[] = {
1184 S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL,
1185 S_NYMPH, S_LEPRECHAUN, S_QUANTMECH, S_VAMPIRE,
1186 S_ORC, S_GIANT, /* quest nemeses */
1187 '\0' /* string terminator; assert( S_xxx != 0 ); */
1189 struct permonst *mptr = mon->data;
1191 if (part == HAND || part == HANDED) { /* some special cases */
1192 if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE ||
1193 mptr->mlet == S_YETI)
1194 return part == HAND ? "paw" : "pawed";
1195 if (humanoid(mptr) && attacktype(mptr, AT_CLAW) &&
1196 !index(not_claws, mptr->mlet) &&
1197 mptr != &mons[PM_STONE_GOLEM] &&
1198 mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS])
1199 return part == HAND ? "claw" : "clawed";
1201 if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON]) &&
1204 if (mptr == &mons[PM_SHARK] && part == HAIR)
1205 return "skin"; /* sharks don't have scales */
1206 if (mptr == &mons[PM_JELLYFISH] && (part == ARM || part == FINGER ||
1207 part == HAND || part == FOOT || part == TOE))
1209 if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
1211 if (humanoid(mptr) &&
1212 (part == ARM || part == FINGER || part == FINGERTIP ||
1213 part == HAND || part == HANDED))
1214 return humanoid_parts[part];
1215 if (mptr == &mons[PM_RAVEN])
1216 return bird_parts[part];
1217 if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN ||
1218 (mptr == &mons[PM_ROTHE] && part != HAIR))
1219 return horse_parts[part];
1220 if (mptr->mlet == S_LIGHT) {
1221 if (part == HANDED) return "rayed";
1222 else if (part == ARM || part == FINGER ||
1223 part == FINGERTIP || part == HAND) return "ray";
1226 if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
1227 return fish_parts[part];
1228 if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR))
1229 return snake_parts[part];
1230 if (mptr->mlet == S_EYE)
1231 return sphere_parts[part];
1232 if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING ||
1233 mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH])
1234 return jelly_parts[part];
1235 if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL)
1236 return vortex_parts[part];
1237 if (mptr->mlet == S_FUNGUS)
1238 return fungus_parts[part];
1240 return humanoid_parts[part];
1241 return animal_parts[part];
1248 return mbodypart(&youmonst, part);
1257 /* Returns gender of polymorphed player; 0/1=same meaning as flags.female,
1260 if (is_neuter(youmonst.data) || !humanoid(youmonst.data)) return 2;
1261 return flags.female;
1268 ugolemeffects(damtype, dam)
1272 /* We won't bother with "slow"/"haste" since players do not
1273 * have a monster-specific slow/haste so there is no way to
1274 * restore the old velocity once they are back to human.
1276 if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
1279 case AD_ELEC: if (u.umonnum == PM_FLESH_GOLEM)
1280 heal = dam / 6; /* Approx 1 per die */
1282 case AD_FIRE: if (u.umonnum == PM_IRON_GOLEM)
1286 if (heal && (u.mh < u.mhmax)) {
1288 if (u.mh > u.mhmax) u.mh = u.mhmax;
1290 pline("Strangely, you feel better than before.");
1291 exercise(A_STR, TRUE);
1296 armor_to_dragon(atyp)
1300 case GRAY_DRAGON_SCALE_MAIL:
1301 case GRAY_DRAGON_SCALES:
1302 return PM_GRAY_DRAGON;
1303 case SILVER_DRAGON_SCALE_MAIL:
1304 case SILVER_DRAGON_SCALES:
1305 return PM_SILVER_DRAGON;
1306 #if 0 /* DEFERRED */
1307 case SHIMMERING_DRAGON_SCALE_MAIL:
1308 case SHIMMERING_DRAGON_SCALES:
1309 return PM_SHIMMERING_DRAGON;
1311 case RED_DRAGON_SCALE_MAIL:
1312 case RED_DRAGON_SCALES:
1313 return PM_RED_DRAGON;
1314 case ORANGE_DRAGON_SCALE_MAIL:
1315 case ORANGE_DRAGON_SCALES:
1316 return PM_ORANGE_DRAGON;
1317 case WHITE_DRAGON_SCALE_MAIL:
1318 case WHITE_DRAGON_SCALES:
1319 return PM_WHITE_DRAGON;
1320 case BLACK_DRAGON_SCALE_MAIL:
1321 case BLACK_DRAGON_SCALES:
1322 return PM_BLACK_DRAGON;
1323 case BLUE_DRAGON_SCALE_MAIL:
1324 case BLUE_DRAGON_SCALES:
1325 return PM_BLUE_DRAGON;
1326 case GREEN_DRAGON_SCALE_MAIL:
1327 case GREEN_DRAGON_SCALES:
1328 return PM_GREEN_DRAGON;
1329 case YELLOW_DRAGON_SCALE_MAIL:
1330 case YELLOW_DRAGON_SCALES:
1331 return PM_YELLOW_DRAGON;