make rank() static again
[NetHack.git] / src / polyself.c
blob625d0072042861a35dfd3b32fddb95633143d18a
1 /* NetHack 3.7 polyself.c $NHDT-Date: 1703845752 2023/12/29 10:29:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */
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 gy.youmonst.m_id
9 * always remains 1 and gy.youmonst.mx will always remain 0 when it handles
10 * the case of the 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 staticfn void check_strangling(boolean);
25 staticfn void polyman(const char *, const char *);
26 staticfn void dropp(struct obj *);
27 staticfn void break_armor(void);
28 staticfn void drop_weapon(int);
29 staticfn int armor_to_dragon(int);
30 staticfn void newman(void);
31 staticfn void polysense(void);
33 static const char no_longer_petrify_resistant[] =
34 "No longer petrify-resistant, you";
36 /* update the gy.youmonst.data structure pointer and intrinsics */
37 void
38 set_uasmon(void)
40 struct permonst *mdat = &mons[u.umonnum];
41 boolean was_vampshifter = valid_vampshiftform(gy.youmonst.cham, u.umonnum);
43 set_mon_data(&gy.youmonst, mdat);
44 gy.youmonst.m_id = 1;
46 if (Protection_from_shape_changers)
47 gy.youmonst.cham = NON_PM;
48 else if (is_vampire(gy.youmonst.data))
49 gy.youmonst.cham = gy.youmonst.mnum;
50 /* assume hero-as-chameleon/doppelganger/sandestin doesn't change shape */
51 else if (!was_vampshifter)
52 gy.youmonst.cham = NON_PM;
53 u.mcham = gy.youmonst.cham; /* for save/restore since youmonst isn't */
55 #define PROPSET(PropIndx, ON) \
56 do { \
57 if (ON) \
58 u.uprops[PropIndx].intrinsic |= FROMFORM; \
59 else \
60 u.uprops[PropIndx].intrinsic &= ~FROMFORM; \
61 } while (0)
63 PROPSET(FIRE_RES, resists_fire(&gy.youmonst));
64 PROPSET(COLD_RES, resists_cold(&gy.youmonst));
65 PROPSET(SLEEP_RES, resists_sleep(&gy.youmonst));
66 PROPSET(DISINT_RES, resists_disint(&gy.youmonst));
67 PROPSET(SHOCK_RES, resists_elec(&gy.youmonst));
68 PROPSET(POISON_RES, resists_poison(&gy.youmonst));
69 PROPSET(ACID_RES, resists_acid(&gy.youmonst));
70 PROPSET(STONE_RES, resists_ston(&gy.youmonst));
72 /* resists_drli() takes wielded weapon into account; suppress it */
73 struct obj *save_uwep = uwep;
75 uwep = 0;
76 PROPSET(DRAIN_RES, resists_drli(&gy.youmonst));
77 uwep = save_uwep;
79 /* resists_magm() takes wielded, worn, and carried equipment into
80 into account; cheat and duplicate its monster-specific part */
81 PROPSET(ANTIMAGIC, (dmgtype(mdat, AD_MAGM)
82 || mdat == &mons[PM_BABY_GRAY_DRAGON]
83 || dmgtype(mdat, AD_RBRE)));
84 PROPSET(SICK_RES, (mdat->mlet == S_FUNGUS || mdat == &mons[PM_GHOUL]));
86 PROPSET(STUNNED, (mdat == &mons[PM_STALKER] || is_bat(mdat)));
87 PROPSET(HALLUC_RES, dmgtype(mdat, AD_HALU));
88 PROPSET(SEE_INVIS, perceives(mdat));
89 PROPSET(TELEPAT, telepathic(mdat));
90 /* note that Infravision uses mons[race] rather than usual mons[role] */
91 PROPSET(INFRAVISION, infravision(Upolyd ? mdat : &mons[gu.urace.mnum]));
92 PROPSET(INVIS, pm_invisible(mdat));
93 PROPSET(TELEPORT, can_teleport(mdat));
94 PROPSET(TELEPORT_CONTROL, control_teleport(mdat));
95 PROPSET(LEVITATION, is_floater(mdat));
96 /* floating eye is the only 'floater'; it is also flagged as a 'flyer';
97 suppress flying for it so that enlightenment doesn't confusingly
98 show latent flight capability always blocked by levitation */
99 PROPSET(FLYING, (is_flyer(mdat) && !is_floater(mdat)));
100 PROPSET(SWIMMING, is_swimmer(mdat));
101 /* [don't touch MAGICAL_BREATHING here; both Amphibious and Breathless
102 key off of it but include different monster forms...] */
103 PROPSET(PASSES_WALLS, passes_walls(mdat));
104 PROPSET(REGENERATION, regenerates(mdat));
105 PROPSET(REFLECTING, (mdat == &mons[PM_SILVER_DRAGON]));
106 PROPSET(BLINDED, !haseyes(mdat));
107 PROPSET(BLND_RES, (dmgtype_fromattack(mdat, AD_BLND, AT_EXPL)
108 || dmgtype_fromattack(mdat, AD_BLND, AT_GAZE)));
109 #undef PROPSET
111 /* whether the player is flying/floating depends on their steed,
112 which won't be known during the restore process: but BFlying
113 and BStealth should be set correctly already in that case, so
114 there's nothing to do */
115 if (!program_state.restoring)
116 float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
117 polysense();
119 #ifdef STATUS_HILITES
120 if (VIA_WINDOWPORT())
121 status_initialize(REASSESS_ONLY);
122 #endif
123 /* we can reset this now, having just done what it is meant to trigger */
124 gw.were_changes = 0L;
127 /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */
128 void
129 float_vs_flight(void)
131 boolean stuck_in_floor = (u.utrap && u.utraptype != TT_PIT);
133 /* floating overrides flight; so does being trapped in the floor */
134 if ((HLevitation || ELevitation)
135 || ((HFlying || EFlying) && stuck_in_floor))
136 BFlying |= I_SPECIAL;
137 else
138 BFlying &= ~I_SPECIAL;
139 /* being trapped on the ground (bear trap, web, molten lava survived
140 with fire resistance, former lava solidified via cold, tethered
141 to a buried iron ball) overrides floating--the floor is reachable */
142 if ((HLevitation || ELevitation) && stuck_in_floor)
143 BLevitation |= I_SPECIAL;
144 else
145 BLevitation &= ~I_SPECIAL;
147 /* riding blocks stealth unless hero+steed fly, so a change in flying
148 might cause a change in stealth */
149 steed_vs_stealth();
151 disp.botl = TRUE;
154 /* riding blocks stealth unless hero+steed fly */
155 void
156 steed_vs_stealth(void)
158 if (u.usteed && !Flying && !Levitation)
159 BStealth |= FROMOUTSIDE;
160 else
161 BStealth &= ~FROMOUTSIDE;
164 /* for changing into form that's immune to strangulation */
165 staticfn void
166 check_strangling(boolean on)
168 /* on -- maybe resume strangling */
169 if (on) {
170 boolean was_strangled = (Strangled != 0L);
172 /* when Strangled is already set, polymorphing from one
173 vulnerable form into another causes the counter to be reset */
174 if (uamul && uamul->otyp == AMULET_OF_STRANGULATION
175 && can_be_strangled(&gy.youmonst)) {
176 Strangled = 6L;
177 disp.botl = TRUE;
178 Your("%s %s your %s!", simpleonames(uamul),
179 was_strangled ? "still constricts" : "begins constricting",
180 body_part(NECK)); /* "throat" */
181 makeknown(AMULET_OF_STRANGULATION);
184 /* off -- maybe block strangling */
185 } else {
186 if (Strangled && !can_be_strangled(&gy.youmonst)) {
187 Strangled = 0L;
188 disp.botl = TRUE;
189 You("are no longer being strangled.");
194 DISABLE_WARNING_FORMAT_NONLITERAL
196 /* make a (new) human out of the player */
197 staticfn void
198 polyman(const char *fmt, const char *arg)
200 boolean sticking = (sticks(gy.youmonst.data) && u.ustuck && !u.uswallow),
201 was_mimicking = (U_AP_TYPE != M_AP_NOTHING);
202 boolean was_blind = !!Blind;
204 if (Upolyd) {
205 u.acurr = u.macurr; /* restore old attribs */
206 u.amax = u.mamax;
207 u.umonnum = u.umonster;
208 flags.female = u.mfemale;
210 set_uasmon();
212 u.mh = u.mhmax = 0;
213 u.mtimedone = 0;
214 skinback(FALSE);
215 u.uundetected = 0;
217 if (sticking)
218 uunstick();
219 find_ac();
220 if (was_mimicking) {
221 if (gm.multi < 0)
222 unmul("");
223 gy.youmonst.m_ap_type = M_AP_NOTHING;
224 gy.youmonst.mappearance = 0;
227 newsym(u.ux, u.uy);
229 urgent_pline(fmt, arg);
230 /* check whether player foolishly genocided self while poly'd */
231 if (ugenocided()) {
232 /* intervening activity might have clobbered genocide info */
233 struct kinfo *kptr = find_delayed_killer(POLYMORPH);
235 if (kptr != (struct kinfo *) 0 && kptr->name[0]) {
236 svk.killer.format = kptr->format;
237 Strcpy(svk.killer.name, kptr->name);
238 } else {
239 svk.killer.format = KILLED_BY;
240 Strcpy(svk.killer.name, "self-genocide");
242 dealloc_killer(kptr);
243 done(GENOCIDED);
246 if (u.twoweap && !could_twoweap(gy.youmonst.data))
247 untwoweapon();
249 if (u.utrap && u.utraptype == TT_PIT) {
250 set_utrap(rn1(6, 2), TT_PIT); /* time to escape resets */
252 if (was_blind && !Blind) { /* reverting from eyeless */
253 set_itimeout(&HBlinded, 1L);
254 make_blinded(0L, TRUE); /* remove blindness */
256 check_strangling(TRUE);
258 if (!Levitation && !u.ustuck && is_pool_or_lava(u.ux, u.uy))
259 spoteffects(TRUE);
261 see_monsters();
264 RESTORE_WARNING_FORMAT_NONLITERAL
266 void
267 change_sex(void)
269 /* Some monsters are always of one sex and their sex can't be changed;
270 * Succubi/incubi can change, but are handled below.
272 * !Upolyd check necessary because is_male() and is_female()
273 * may be true for certain roles
275 if (!Upolyd
276 || (!is_male(gy.youmonst.data) && !is_female(gy.youmonst.data)
277 && !is_neuter(gy.youmonst.data)))
278 flags.female = !flags.female;
279 if (Upolyd) /* poly'd: also change saved sex */
280 u.mfemale = !u.mfemale;
281 max_rank_sz(); /* [this appears to be superfluous] */
282 if ((Upolyd ? u.mfemale : flags.female) && gu.urole.name.f)
283 Strcpy(svp.pl_character, gu.urole.name.f);
284 else
285 Strcpy(svp.pl_character, gu.urole.name.m);
286 if (!Upolyd) {
287 u.umonnum = u.umonster;
288 } else if (u.umonnum == PM_AMOROUS_DEMON) {
289 flags.female = !flags.female;
290 #if 0
291 /* change monster type to match new sex; disabled with
292 PM_AMOROUS_DEMON */
293 u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS;
294 #endif
295 set_uasmon();
299 /* log a message if non-poly'd hero's gender has changed */
300 void
301 livelog_newform(boolean viapoly, int oldgend, int newgend)
303 char buf[BUFSZ];
304 const char *oldrole, *oldrank, *newrole, *newrank;
307 * TODO?
308 * Give other logging feedback here instead of in newman().
311 if (!Upolyd) {
312 if (newgend != oldgend) {
313 oldrole = (oldgend && gu.urole.name.f) ? gu.urole.name.f
314 : gu.urole.name.m;
315 newrole = (newgend && gu.urole.name.f) ? gu.urole.name.f
316 : gu.urole.name.m;
317 oldrank = rank_of(u.ulevel, Role_switch, oldgend);
318 newrank = rank_of(u.ulevel, Role_switch, newgend);
319 Sprintf(buf, "%.10s %.30s", genders[flags.female].adj, newrank);
320 livelog_printf(LL_MINORAC, "%s into %s",
321 viapoly ? "polymorphed" : "transformed",
322 an(strcmp(newrole, oldrole) ? newrole
323 : strcmp(newrank, oldrank) ? newrank
324 : buf));
329 staticfn void
330 newman(void)
332 const char *newform;
333 int i, oldlvl, newlvl, oldgend, newgend, hpmax, enmax;
335 oldlvl = u.ulevel;
336 newlvl = oldlvl + rn1(5, -2); /* new = old + {-2,-1,0,+1,+2} */
337 if (newlvl > 127 || newlvl < 1) { /* level went below 0? */
338 goto dead; /* old level is still intact (in case of lifesaving) */
340 if (newlvl > MAXULEV)
341 newlvl = MAXULEV;
342 /* If your level goes down, your peak level goes down by
343 the same amount so that you can't simply use blessed
344 full healing to undo the decrease. But if your level
345 goes up, your peak level does *not* undergo the same
346 adjustment; you might end up losing out on the chance
347 to regain some levels previously lost to other causes. */
348 if (newlvl < oldlvl)
349 u.ulevelmax -= (oldlvl - newlvl);
350 if (u.ulevelmax < newlvl)
351 u.ulevelmax = newlvl;
352 u.ulevel = newlvl;
354 oldgend = poly_gender();
355 if (gs.sex_change_ok && !rn2(10))
356 change_sex();
358 adjabil(oldlvl, (int) u.ulevel);
360 /* random experience points for the new experience level */
361 u.uexp = rndexp(FALSE);
363 /* set up new attribute points (particularly Con) */
364 redist_attr();
367 * New hit points:
368 * remove "level gain"-based HP from any extra HP accumulated
369 * (the "extra" might actually be negative);
370 * modify the extra, retaining {80%, 90%, 100%, or 110%};
371 * add in newly generated set of level-gain HP.
373 * (This used to calculate new HP in direct proportion to old HP,
374 * but that was subject to abuse: accumulate a large amount of
375 * extra HP, drain level down to 1, then polyself to level 2 or 3
376 * [lifesaving capability needed to handle level 0 and -1 cases]
377 * and the extra got multiplied by 2 or 3. Repeat the level
378 * drain and polyself steps until out of lifesaving capability.)
380 hpmax = u.uhpmax;
381 for (i = 0; i < oldlvl; i++)
382 hpmax -= (int) u.uhpinc[i];
383 /* hpmax * rn1(4,8) / 10; 0.95*hpmax on average */
384 hpmax = rounddiv((long) hpmax * (long) rn1(4, 8), 10);
385 for (i = 0; (u.ulevel = i) < newlvl; i++)
386 hpmax += newhp();
387 if (hpmax < u.ulevel)
388 hpmax = u.ulevel; /* min of 1 HP per level */
389 /* retain same proportion for current HP; u.uhp * hpmax / u.uhpmax */
390 u.uhp = rounddiv((long) u.uhp * (long) hpmax, u.uhpmax);
391 setuhpmax(hpmax, TRUE); /* might reduce u.uhp */
393 * Do the same for spell power.
395 enmax = u.uenmax;
396 for (i = 0; i < oldlvl; i++)
397 enmax -= (int) u.ueninc[i];
398 enmax = rounddiv((long) enmax * (long) rn1(4, 8), 10);
399 for (i = 0; (u.ulevel = i) < newlvl; i++)
400 enmax += newpw();
401 if (enmax < u.ulevel)
402 enmax = u.ulevel;
403 u.uen = rounddiv((long) u.uen * (long) enmax,
404 ((u.uenmax < 1) ? 1 : u.uenmax));
405 u.uenmax = enmax;
406 /* [should alignment record be tweaked too?] */
408 u.uhunger = rn1(500, 500);
409 if (Sick)
410 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
411 if (Stoned)
412 make_stoned(0L, (char *) 0, 0, (char *) 0);
413 if (u.uhp <= 0) {
414 if (Polymorph_control) { /* even when Stunned || Unaware */
415 if (u.uhp <= 0)
416 u.uhp = 1;
417 } else {
418 dead: /* we come directly here if experience level went to 0 or less */
419 urgent_pline(
420 "Your new form doesn't seem healthy enough to survive.");
421 svk.killer.format = KILLED_BY_AN;
422 Strcpy(svk.killer.name, "unsuccessful polymorph");
423 done(DIED);
424 /* must have been life-saved to get here */
425 newuhs(FALSE);
426 (void) encumber_msg(); /* used to be done by redist_attr() */
427 return; /* lifesaved */
430 newuhs(FALSE);
431 /* use saved gender we're about to revert to, not current */
432 newform = ((Upolyd ? u.mfemale : flags.female) && gu.urace.individual.f)
433 ? gu.urace.individual.f
434 : (gu.urace.individual.m)
435 ? gu.urace.individual.m
436 : gu.urace.noun;
437 polyman("You feel like a new %s!", newform);
439 newgend = poly_gender();
440 /* note: newman() bypasses achievements for new ranks attained and
441 doesn't log "new <form>" when that isn't accompanied by level change */
442 if (newlvl != oldlvl)
443 livelog_printf(LL_MINORAC, "became experience level %d as a new %s",
444 newlvl, newform);
445 else
446 livelog_newform(TRUE, oldgend, newgend);
448 if (Slimed) {
449 Your("body transforms, but there is still slime on you.");
450 make_slimed(10L, (const char *) 0);
453 disp.botl = TRUE;
454 see_monsters();
455 (void) encumber_msg();
457 retouch_equipment(2);
458 if (!uarmg)
459 selftouch(no_longer_petrify_resistant);
462 void
463 polyself(int psflags)
465 char buf[BUFSZ];
466 int old_light, new_light, mntmp, class, tryct, gvariant = NEUTRAL;
467 boolean forcecontrol = ((psflags & POLY_CONTROLLED) != 0),
468 low_control = ((psflags & POLY_LOW_CTRL) != 0),
469 monsterpoly = ((psflags & POLY_MONSTER) != 0),
470 formrevert = ((psflags & POLY_REVERT) != 0),
471 draconian = (uarm && Is_dragon_armor(uarm)),
472 iswere = (ismnum(u.ulycn)),
473 isvamp = (is_vampire(gy.youmonst.data)
474 || is_vampshifter(&gy.youmonst)),
475 controllable_poly = Polymorph_control && !(Stunned || Unaware);
477 if (Unchanging) {
478 You("fail to transform!");
479 return;
481 /* being Stunned|Unaware doesn't negate this aspect of Poly_control */
482 if (!Polymorph_control && !forcecontrol && !draconian && !iswere
483 && !isvamp) {
484 if (rn2(20) > ACURR(A_CON)) {
485 You1(shudder_for_moment);
486 losehp(rnd(30), "system shock", KILLED_BY_AN);
487 exercise(A_CON, FALSE);
488 return;
491 old_light = emits_light(gy.youmonst.data);
492 mntmp = NON_PM;
494 if (formrevert) {
495 mntmp = gy.youmonst.cham;
496 monsterpoly = TRUE;
497 controllable_poly = FALSE;
500 if (forcecontrol && low_control
501 && (draconian || monsterpoly || isvamp || iswere))
502 forcecontrol = FALSE;
504 if (monsterpoly && isvamp)
505 goto do_vampyr;
507 if (controllable_poly || forcecontrol) {
508 buf[0] = '\0';
509 tryct = 5;
511 do {
512 mntmp = NON_PM;
513 getlin("Become what kind of monster? [type the name]", buf);
514 (void) mungspaces(buf);
515 if (*buf == '\033') {
516 /* user is cancelling controlled poly */
517 if (forcecontrol) { /* wizard mode #polyself */
518 pline1(Never_mind);
519 return;
521 Strcpy(buf, "*"); /* resort to random */
523 if (!strcmp(buf, "*") || !strcmp(buf, "random")) {
524 /* explicitly requesting random result */
525 tryct = 0; /* will skip thats_enough_tries */
526 continue; /* end do-while(--tryct > 0) loop */
528 class = 0;
529 mntmp = name_to_mon(buf, &gvariant);
530 if (mntmp < LOW_PM) {
531 by_class:
532 class = name_to_monclass(buf, &mntmp);
533 if (class && mntmp == NON_PM)
534 mntmp = (draconian && class == S_DRAGON)
535 ? armor_to_dragon(uarm->otyp)
536 : mkclass_poly(class);
538 /* placeholder monsters are for corpses and all flagged
539 M2_NOPOLY but they are reasonable polymorph targets;
540 pick a suitable substitute (which might be geno'd) */
541 } else if (is_placeholder(&mons[mntmp])
542 /* when your own race, fall to !polyok() case */
543 && !your_race(&mons[mntmp])
544 /* same for generic human, even if hero isn't human */
545 && mntmp != PM_HUMAN) {
546 /* far less general than mkclass() */
547 if (mntmp == PM_ORC)
548 mntmp = rn2(3) ? PM_HILL_ORC : PM_MORDOR_ORC;
549 else if (mntmp == PM_ELF)
550 mntmp = rn2(3) ? PM_GREEN_ELF : PM_GREY_ELF;
551 else if (mntmp == PM_GIANT)
552 mntmp = rn2(3) ? PM_STONE_GIANT : PM_HILL_GIANT;
553 /* note: PM_DWARF and PM_GNOME are ordinary monsters and
554 no longer flagged no-poly so have no need for placeholder
555 handling; PM_HUMAN is a placeholder without a suitable
556 substitute so gets handled differently below */
559 if (mntmp < LOW_PM) {
560 if (!class)
561 pline("I've never heard of such monsters.");
562 else
563 You_cant("polymorph into any of those.");
564 } else if (wizard && Upolyd
565 && (mntmp == u.umonster
566 /* "priest" and "priestess" match the monster
567 rather than the role; override that unless
568 the text explicitly contains "aligned" */
569 || (u.umonster == PM_CLERIC
570 && mntmp == PM_ALIGNED_CLERIC
571 && !strstri(buf, "aligned")))) {
572 /* in wizard mode, picking own role while poly'd reverts to
573 normal without newman()'s chance of level or sex change */
574 rehumanize();
575 old_light = 0; /* rehumanize() extinguishes u-as-mon light */
576 goto made_change;
577 } else if (iswere && (were_beastie(mntmp) == u.ulycn
578 || mntmp == counter_were(u.ulycn)
579 || (Upolyd && mntmp == PM_HUMAN))) {
580 goto do_shift;
581 } else if (!polyok(&mons[mntmp])
582 /* Note: humans are illegal as monsters, but an
583 illegal monster forces newman(), which is what
584 we want if they specified a human.... (unless
585 they specified a unique monster) */
586 && !(mntmp == PM_HUMAN
587 || (your_race(&mons[mntmp])
588 && (mons[mntmp].geno & G_UNIQ) == 0)
589 || mntmp == gu.urole.mnum)) {
590 const char *pm_name;
592 /* mkclass_poly() can pick a !polyok()
593 candidate; if so, usually try again */
594 if (class) {
595 if (rn2(3) || --tryct > 0)
596 goto by_class;
597 /* no retries left; put one back on counter
598 so that end of loop decrement will yield
599 0 and trigger thats_enough_tries message */
600 ++tryct;
602 pm_name = pmname(&mons[mntmp], flags.female ? FEMALE : MALE);
603 if (the_unique_pm(&mons[mntmp]))
604 pm_name = the(pm_name);
605 else if (!type_is_pname(&mons[mntmp]))
606 pm_name = an(pm_name);
607 You_cant("polymorph into %s.", pm_name);
608 } else
609 break;
610 } while (--tryct > 0);
612 if (!tryct)
613 pline1(thats_enough_tries);
614 /* allow skin merging, even when polymorph is controlled */
615 if (draconian && (tryct <= 0 || mntmp == armor_to_dragon(uarm->otyp)))
616 goto do_merge;
617 if (isvamp && (tryct <= 0 || mntmp == PM_WOLF || mntmp == PM_FOG_CLOUD
618 || is_bat(&mons[mntmp])))
619 goto do_vampyr;
620 } else if (draconian || iswere || isvamp) {
621 /* special changes that don't require polyok() */
622 if (draconian) {
623 do_merge:
624 mntmp = armor_to_dragon(uarm->otyp);
625 if (!(svm.mvitals[mntmp].mvflags & G_GENOD)) {
626 unsigned was_lit = uarm->lamplit;
627 int arm_light = artifact_light(uarm) ? arti_light_radius(uarm)
628 : 0;
630 /* allow G_EXTINCT */
631 if (Is_dragon_scales(uarm)) {
632 /* dragon scales remain intact as uskin */
633 You("merge with your scaly armor.");
634 } else { /* dragon scale mail reverts to scales */
635 /* similar to noarmor(invent.c),
636 shorten to "<color> scale mail" */
637 Strcpy(buf, simpleonames(uarm));
638 strsubst(buf, " dragon ", " ");
639 /* tricky phrasing; dragon scale mail is singular, dragon
640 scales are plural (note: we don't use "set of scales",
641 which usually overrides the distinction, here) */
642 Your("%s reverts to scales as you merge with them.", buf);
643 /* uarm->spe enchantment remains unchanged;
644 re-converting scales to mail poses risk
645 of evaporation due to over enchanting */
646 uarm->otyp += GRAY_DRAGON_SCALES - GRAY_DRAGON_SCALE_MAIL;
647 uarm->dknown = 1;
648 disp.botl = TRUE; /* AC is changing */
650 uskin = uarm;
651 uarm = (struct obj *) 0;
652 /* save/restore hack */
653 uskin->owornmask |= I_SPECIAL;
654 if (was_lit)
655 maybe_adjust_light(uskin, arm_light);
656 update_inventory();
658 } else if (iswere) {
659 do_shift:
660 if (Upolyd && were_beastie(mntmp) != u.ulycn)
661 mntmp = PM_HUMAN; /* Illegal; force newman() */
662 else
663 mntmp = u.ulycn;
664 } else if (isvamp) {
665 do_vampyr:
666 if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ)) {
667 mntmp = (gy.youmonst.data == &mons[PM_VAMPIRE_LEADER]
668 && !rn2(10)) ? PM_WOLF
669 : !rn2(4) ? PM_FOG_CLOUD
670 : PM_VAMPIRE_BAT;
671 if (ismnum(gy.youmonst.cham)
672 && !is_vampire(gy.youmonst.data) && !rn2(2))
673 mntmp = gy.youmonst.cham;
675 if (controllable_poly) {
676 Sprintf(buf, "Become %s?",
677 an(pmname(&mons[mntmp], gvariant)));
678 if (y_n(buf) != 'y')
679 return;
682 /* if polymon fails, "you feel" message has been given
683 so don't follow up with another polymon or newman;
684 sex_change_ok left disabled here */
685 if (mntmp == PM_HUMAN)
686 newman(); /* werecritter */
687 else
688 (void) polymon(mntmp);
689 goto made_change; /* maybe not, but this is right anyway */
692 if (mntmp < LOW_PM) {
693 tryct = 200;
694 do {
695 /* randomly pick an "ordinary" monster */
696 mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
697 if (polyok(&mons[mntmp]) && !is_placeholder(&mons[mntmp]))
698 break;
699 } while (--tryct > 0);
702 /* The below polyok() fails either if everything is genocided, or if
703 * we deliberately chose something illegal to force newman().
705 gs.sex_change_ok++;
706 if (!polyok(&mons[mntmp]) || (!forcecontrol && !rn2(5))
707 || your_race(&mons[mntmp])) {
708 newman();
709 } else {
710 (void) polymon(mntmp);
712 gs.sex_change_ok--; /* reset */
714 made_change:
715 new_light = emits_light(gy.youmonst.data);
716 if (old_light != new_light) {
717 if (old_light)
718 del_light_source(LS_MONSTER, monst_to_any(&gy.youmonst));
719 if (new_light == 1)
720 ++new_light; /* otherwise it's undetectable */
721 if (new_light)
722 new_light_source(u.ux, u.uy, new_light, LS_MONSTER,
723 monst_to_any(&gy.youmonst));
727 /* (try to) make a mntmp monster out of the player; return 1 if successful */
729 polymon(int mntmp)
731 char buf[BUFSZ], ustuckNam[BUFSZ];
732 boolean sticking = sticks(gy.youmonst.data) && u.ustuck && !u.uswallow,
733 was_blind = !!Blind, dochange = FALSE, was_expelled = FALSE,
734 was_hiding_under = u.uundetected && hides_under(gy.youmonst.data);
735 int mlvl, newMaxStr;
737 if (svm.mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
738 You_feel("rather %s-ish.",
739 pmname(&mons[mntmp], flags.female ? FEMALE : MALE));
740 exercise(A_WIS, TRUE);
741 return 0;
744 /* KMH, conduct */
745 if (!u.uconduct.polyselfs++)
746 livelog_printf(LL_CONDUCT,
747 "changed form for the first time, becoming %s",
748 an(pmname(&mons[mntmp], flags.female ? FEMALE : MALE)));
750 /* exercise used to be at the very end but only Wis was affected
751 there since the polymorph was always in effect by then */
752 exercise(A_CON, FALSE);
753 exercise(A_WIS, TRUE);
755 if (!Upolyd) {
756 /* Human to monster; save human stats */
757 u.macurr = u.acurr;
758 u.mamax = u.amax;
759 u.mfemale = flags.female;
760 } else {
761 /* Monster to monster; restore human stats, to be
762 * immediately changed to provide stats for the new monster
764 u.acurr = u.macurr;
765 u.amax = u.mamax;
766 flags.female = u.mfemale;
769 /* if stuck mimicking gold, stop immediately */
770 if (gm.multi < 0 && U_AP_TYPE == M_AP_OBJECT
771 && gy.youmonst.data->mlet != S_MIMIC)
772 unmul("");
773 /* if becoming a non-mimic, stop mimicking anything */
774 if (mons[mntmp].mlet != S_MIMIC) {
775 /* as in polyman() */
776 gy.youmonst.m_ap_type = M_AP_NOTHING;
777 gy.youmonst.mappearance = 0;
779 if (is_male(&mons[mntmp])) {
780 if (flags.female)
781 dochange = TRUE;
782 } else if (is_female(&mons[mntmp])) {
783 if (!flags.female)
784 dochange = TRUE;
785 } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
786 if (gs.sex_change_ok && !rn2(10))
787 dochange = TRUE;
790 Strcpy(ustuckNam, u.ustuck ? Some_Monnam(u.ustuck) : "");
792 Strcpy(buf, (u.umonnum != mntmp) ? "" : "new ");
793 if (dochange) {
794 flags.female = !flags.female;
795 Strcat(buf, (is_male(&mons[mntmp]) || is_female(&mons[mntmp]))
796 ? "" : flags.female ? "female " : "male ");
798 Strcat(buf, pmname(&mons[mntmp], flags.female ? FEMALE : MALE));
799 You("%s %s!", (u.umonnum != mntmp) ? "turn into" : "feel like", an(buf));
801 if (Stoned && poly_when_stoned(&mons[mntmp])) {
802 /* poly_when_stoned already checked stone golem genocide */
803 mntmp = PM_STONE_GOLEM;
804 make_stoned(0L, "You turn to stone!", 0, (char *) 0);
807 u.mtimedone = rn1(500, 500);
808 u.umonnum = mntmp;
809 set_uasmon();
811 /* New stats for monster, to last only as long as polymorphed.
812 * Currently only strength gets changed.
814 newMaxStr = uasmon_maxStr();
815 if (strongmonst(&mons[mntmp])) {
816 ABASE(A_STR) = AMAX(A_STR) = (schar) newMaxStr;
817 } else {
818 /* not a strongmonst(); if hero has exceptional strength, remove it
819 (note: removal is temporary until returning to original form);
820 we don't attempt to enforce lower maximum for wimpy forms;
821 unlike for strongmonst, current strength does not get set to max */
822 AMAX(A_STR) = (schar) newMaxStr;
823 /* make sure current is not higher than max (strip exceptional Str) */
824 if (ABASE(A_STR) > AMAX(A_STR))
825 ABASE(A_STR) = AMAX(A_STR);
828 if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */
829 make_stoned(0L, "You no longer seem to be petrifying.", 0,
830 (char *) 0);
832 if (Sick_resistance && Sick) {
833 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
834 You("no longer feel sick.");
836 if (Slimed) {
837 if (flaming(gy.youmonst.data)) {
838 make_slimed(0L, "The slime burns away!");
839 } else if (mntmp == PM_GREEN_SLIME) {
840 /* do it silently */
841 make_slimed(0L, (char *) 0);
844 check_strangling(FALSE); /* maybe stop strangling */
845 if (nohands(gy.youmonst.data))
846 make_glib(0);
849 mlvl = adj_lev(&mons[mntmp]);
850 * We can't do the above, since there's no such thing as an
851 * "experience level of you as a monster" for a polymorphed character.
853 mlvl = (int) mons[mntmp].mlevel;
854 if (gy.youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) {
855 u.mhmax = In_endgame(&u.uz) ? (8 * mlvl) : (4 * mlvl + d(mlvl, 4));
856 } else if (is_golem(gy.youmonst.data)) {
857 u.mhmax = golemhp(mntmp);
858 } else {
859 if (!mlvl)
860 u.mhmax = rnd(4);
861 else
862 u.mhmax = d(mlvl, 8);
863 if (is_home_elemental(&mons[mntmp]))
864 u.mhmax *= 3;
866 u.mh = u.mhmax;
868 if (u.ulevel < mlvl) {
869 /* Low level characters can't become high level monsters for long */
870 #ifdef DUMB
871 /* DRS/NS 2.2.6 messes up -- Peter Kendell */
872 int mtd = u.mtimedone, ulv = u.ulevel;
874 u.mtimedone = mtd * ulv / mlvl;
875 #else
876 u.mtimedone = u.mtimedone * u.ulevel / mlvl;
877 #endif
880 if (uskin && mntmp != armor_to_dragon(uskin->otyp))
881 skinback(FALSE);
882 break_armor();
883 drop_weapon(1);
884 find_ac(); /* (repeated below) */
885 /* if hiding under something and can't hide anymore, unhide now;
886 but don't auto-hide when not already hiding-under */
887 if (was_hiding_under)
888 (void) hideunder(&gy.youmonst);
890 if (u.utrap && u.utraptype == TT_PIT) {
891 set_utrap(rn1(6, 2), TT_PIT); /* time to escape resets */
893 if (was_blind && !Blind) { /* previous form was eyeless */
894 set_itimeout(&HBlinded, 1L);
895 make_blinded(0L, TRUE); /* remove blindness */
897 newsym(u.ux, u.uy); /* Change symbol */
899 /* you now know what an egg of your type looks like; [moved from
900 below in case expels() -> spoteffects() drops hero onto any eggs] */
901 if (lays_eggs(gy.youmonst.data)) {
902 learn_egg_type(u.umonnum);
903 /* make queen bees recognize killer bee eggs */
904 learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
907 if (u.uswallow) {
908 uchar usiz;
910 /* if new form can't be swallowed, make engulfer expel hero */
911 if (unsolid(gy.youmonst.data)
912 /* subset of engulf_target() */
913 || (usiz = gy.youmonst.data->msize) >= MZ_HUGE
914 || (u.ustuck->data->msize < usiz && !is_whirly(u.ustuck->data))) {
915 boolean expels_mesg = TRUE;
917 if (unsolid(gy.youmonst.data)) {
918 if (canspotmon(u.ustuck)) /* [see below for explanation] */
919 Strcpy(ustuckNam, Monnam(u.ustuck));
920 pline("%s can no longer contain you.", ustuckNam);
921 expels_mesg = FALSE;
923 expels(u.ustuck, u.ustuck->data, expels_mesg);
924 was_expelled = TRUE;
925 /* FIXME? if expels() triggered rehumanize then we should
926 return early */
929 /* [note: this 'sticking' handling is only sufficient for changing from
930 grabber to engulfer or vice versa because engulfing by poly'd hero
931 always ends immediately so won't be in effect during a polymorph] */
932 } else if (u.ustuck && !sticking /* && !u.uswallow */
933 /* being held; if now capable of holding, make holder
934 release so that hero doesn't automagically start holding
935 it; or, release if no longer capable of being held */
936 && (sticks(gy.youmonst.data) || unsolid(gy.youmonst.data))) {
937 /* u.ustuck name was saved above in case we're changing from can-see
938 to can't-see; but might have changed from can't-see to can-see so
939 override here if hero knows who u.ustuck is */
940 if (canspotmon(u.ustuck))
941 Strcpy(ustuckNam, Monnam(u.ustuck));
942 set_ustuck((struct monst *) 0);
943 pline("%s loses its grip on you.", ustuckNam);
944 } else if (sticking && !sticks(gy.youmonst.data)) {
945 /* was holding onto u.ustuck but no longer capable of that */
946 uunstick();
949 if (u.usteed) {
950 if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) {
951 pline("%s touch %s.", no_longer_petrify_resistant,
952 mon_nam(u.usteed));
953 Sprintf(buf, "riding %s",
954 an(pmname(u.usteed->data, Mgender(u.usteed))));
955 instapetrify(buf);
957 if (!can_ride(u.usteed))
958 dismount_steed(DISMOUNT_POLY);
961 find_ac();
962 if (((!Levitation && !u.ustuck && !Flying && is_pool_or_lava(u.ux, u.uy))
963 || (Underwater && !Swimming))
964 /* if expelled above, expels() already called spoteffects() */
965 && !was_expelled) {
966 spoteffects(TRUE);
967 /* FIXME? if spoteffects() triggered rehumanize then we should
968 return early */
970 if (Passes_walls && u.utrap
971 && (u.utraptype == TT_INFLOOR || u.utraptype == TT_BURIEDBALL)) {
972 if (u.utraptype == TT_INFLOOR) {
973 pline_The("rock seems to no longer trap you.");
974 } else {
975 pline_The("buried ball is no longer bound to you.");
976 buried_ball_to_freedom();
978 reset_utrap(TRUE);
979 } else if (likes_lava(gy.youmonst.data) && u.utrap
980 && u.utraptype == TT_LAVA) {
981 pline_The("%s now feels soothing.", hliquid("lava"));
982 reset_utrap(TRUE);
984 if (amorphous(gy.youmonst.data) || is_whirly(gy.youmonst.data)
985 || unsolid(gy.youmonst.data)) {
986 if (Punished) {
987 You("slip out of the iron chain.");
988 unpunish();
989 } else if (u.utrap && u.utraptype == TT_BURIEDBALL) {
990 You("slip free of the buried ball and chain.");
991 buried_ball_to_freedom();
994 if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP)
995 && (amorphous(gy.youmonst.data) || is_whirly(gy.youmonst.data)
996 || unsolid(gy.youmonst.data)
997 || (gy.youmonst.data->msize <= MZ_SMALL
998 && u.utraptype == TT_BEARTRAP))) {
999 You("are no longer stuck in the %s.",
1000 u.utraptype == TT_WEB ? "web" : "bear trap");
1001 /* probably should burn webs too if PM_FIRE_ELEMENTAL */
1002 reset_utrap(TRUE);
1004 if (webmaker(gy.youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
1005 You("orient yourself on the web.");
1006 reset_utrap(TRUE);
1008 check_strangling(TRUE); /* maybe start strangling */
1010 disp.botl = TRUE;
1011 gv.vision_full_recalc = 1;
1012 see_monsters();
1013 (void) encumber_msg();
1015 retouch_equipment(2);
1016 /* this might trigger a recursive call to polymon() [stone golem
1017 wielding cockatrice corpse and hit by stone-to-flesh, becomes
1018 flesh golem above, now gets transformed back into stone golem;
1019 fortunately neither form uses #monster] */
1020 if (!uarmg)
1021 selftouch(no_longer_petrify_resistant);
1023 /* the explanation of '#monster' used to be shown sooner, but there are
1024 possible fatalities above and it isn't useful unless hero survives */
1025 if (flags.verbose) {
1026 static const char use_thec[] = "Use the command #%s to %s.";
1027 static const char monsterc[] = "monster";
1028 struct permonst *uptr = gy.youmonst.data;
1029 boolean might_hide = (is_hider(uptr) || hides_under(uptr));
1031 if (can_breathe(uptr))
1032 pline(use_thec, monsterc, "use your breath weapon");
1033 if (attacktype(uptr, AT_SPIT))
1034 pline(use_thec, monsterc, "spit venom");
1035 if (uptr->mlet == S_NYMPH)
1036 pline(use_thec, monsterc, "remove an iron ball");
1037 if (attacktype(uptr, AT_GAZE))
1038 pline(use_thec, monsterc, "gaze at monsters");
1039 if (might_hide && webmaker(uptr))
1040 pline(use_thec, monsterc, "hide or to spin a web");
1041 else if (might_hide)
1042 pline(use_thec, monsterc, "hide");
1043 else if (webmaker(uptr))
1044 pline(use_thec, monsterc, "spin a web");
1045 if (is_were(uptr))
1046 pline(use_thec, monsterc, "summon help");
1047 if (u.umonnum == PM_GREMLIN)
1048 pline(use_thec, monsterc, "multiply in a fountain");
1049 if (is_unicorn(uptr))
1050 pline(use_thec, monsterc, "use your horn");
1051 if (is_mind_flayer(uptr))
1052 pline(use_thec, monsterc, "emit a mental blast");
1053 if (uptr->msound == MS_SHRIEK) /* worthless, actually */
1054 pline(use_thec, monsterc, "shriek");
1055 if (is_vampire(uptr) || is_vampshifter(&gy.youmonst))
1056 pline(use_thec, monsterc, "change shape");
1058 if (lays_eggs(uptr) && flags.female
1059 && !(uptr == &mons[PM_GIANT_EEL]
1060 || uptr == &mons[PM_ELECTRIC_EEL]))
1061 pline(use_thec, "sit",
1062 eggs_in_water(uptr) ? "spawn in the water" : "lay an egg");
1064 return 1;
1067 /* determine hero's temporary strength value used while polymorphed;
1068 hero poly'd into M2_STRONG monster usually gets 18/100 strength but
1069 there are exceptions; non-M2_STRONG get maximum strength set to 18 */
1070 schar
1071 uasmon_maxStr(void)
1073 const struct Race *R;
1074 int newMaxStr;
1075 int mndx = u.umonnum;
1076 struct permonst *ptr = &mons[mndx];
1078 if (is_orc(ptr)) {
1079 if (mndx != PM_URUK_HAI && mndx != PM_ORC_CAPTAIN)
1080 mndx = PM_ORC;
1081 } else if (is_elf(ptr)) {
1082 mndx = PM_ELF;
1083 } else if (is_dwarf(ptr)) {
1084 mndx = PM_DWARF;
1085 } else if (is_gnome(ptr)) {
1086 mndx = PM_GNOME;
1087 #if 0 /* use the mons[] value for humans */
1088 } else if (is_human(ptr)) {
1089 mndx = PM_HUMAN;
1090 #endif
1092 R = character_race(mndx);
1094 if (strongmonst(ptr)) {
1095 /* ettins, titans and minotaurs don't pass the is_giant() test;
1096 giant mummies and giant zombies do but we throttle those */
1097 boolean live_H = is_giant(ptr) && !is_undead(ptr);
1099 /* hero orcs are limited to 18/50 for maximum strength, so treat
1100 hero poly'd into an orc the same; goblins, orc shamans, and orc
1101 zombies don't have strongmonst() attribute so won't get here;
1102 hobgoblins and orc mummies do get here and are limited to 18/50
1103 like normal orcs; however, orc captains and Uruk-hai retain 18/100
1104 strength; hero gnomes are also limited to 18/50; hero elves are
1105 limited to 18/00 regardless of whether they're strongmonst, but
1106 the two strongmonst types (monarchs and nobles) have current
1107 strength set to 18 [by polymon()], the others don't */
1108 newMaxStr = R ? R->attrmax[A_STR] : live_H ? STR19(19) : STR18(100);
1109 } else {
1110 newMaxStr = R ? R->attrmax[A_STR] : 18; /* 18 is same as STR18(0) */
1112 return (schar) newMaxStr;
1115 /* dropx() jacket for break_armor() */
1116 staticfn void
1117 dropp(struct obj *obj)
1119 struct obj *otmp;
1122 * Dropping worn armor while polymorphing might put hero into water
1123 * (loss of levitation boots or water walking boots that the new
1124 * form can't wear), where emergency_disrobe() could remove it from
1125 * inventory. Without this, dropx() could trigger an 'object lost'
1126 * panic. Right now, boots are the only armor which might encounter
1127 * this situation, but handle it for all armor.
1129 * Hypothetically, 'obj' could have merged with something (not
1130 * applicable for armor) and no longer be a valid pointer, so scan
1131 * inventory for it instead of trusting obj->where.
1133 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
1134 if (otmp == obj) {
1135 dropx(obj);
1136 /* Note that otmp->nobj is pointing at fobj now,
1137 * as a result of:
1138 * dropx() -> dropy() -> dropz() -> place_object(),
1139 * and no longer pointing at the next obj in inventory.
1140 * That would be an issue if this loop were allowed
1141 * to continue, but the break statement that
1142 * follows prevents the loop from continuing on with
1143 * objects on the floor.
1145 break;
1150 staticfn void
1151 break_armor(void)
1153 struct obj *otmp;
1154 struct permonst *uptr = gy.youmonst.data;
1156 if (breakarm(uptr)) {
1157 if ((otmp = uarm) != 0) {
1158 if (donning(otmp))
1159 cancel_don();
1160 /* for gold DSM, we don't want Armor_gone() to report that it
1161 stops shining _after_ we've been told that it is destroyed */
1162 if (otmp->lamplit)
1163 end_burn(otmp, FALSE);
1165 You("break out of your armor!");
1166 exercise(A_STR, FALSE);
1167 (void) Armor_gone();
1168 useup(otmp);
1170 if ((otmp = uarmc) != 0
1171 /* mummy wrapping adapts to small and very big sizes */
1172 && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(uptr))) {
1173 if (otmp->oartifact) {
1174 Your("%s falls off!", cloak_simple_name(otmp));
1175 (void) Cloak_off();
1176 dropp(otmp);
1177 } else {
1178 Your("%s tears apart!", cloak_simple_name(otmp));
1179 (void) Cloak_off();
1180 useup(otmp);
1183 if (uarmu) {
1184 Your("shirt rips to shreds!");
1185 useup(uarmu);
1187 } else if (sliparm(uptr)) {
1188 if ((otmp = uarm) != 0 && racial_exception(&gy.youmonst, otmp) < 1) {
1189 if (donning(otmp))
1190 cancel_don();
1191 Your("armor falls around you!");
1192 /* [note: _gone() instead of _off() dates to when life-saving
1193 could force fire resisting armor back on if hero burned in
1194 hell (3.0, predating Gehennom); the armor isn't actually
1195 gone here but also isn't available to be put back on] */
1196 (void) Armor_gone();
1197 dropp(otmp);
1199 if ((otmp = uarmc) != 0
1200 /* mummy wrapping adapts to small and very big sizes */
1201 && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(uptr))) {
1202 if (is_whirly(uptr))
1203 Your("%s falls, unsupported!", cloak_simple_name(otmp));
1204 else
1205 You("shrink out of your %s!", cloak_simple_name(otmp));
1206 (void) Cloak_off();
1207 dropp(otmp);
1209 if ((otmp = uarmu) != 0) {
1210 if (is_whirly(uptr))
1211 You("seep right through your shirt!");
1212 else
1213 You("become much too small for your shirt!");
1214 setworn((struct obj *) 0, otmp->owornmask & W_ARMU);
1215 dropp(otmp);
1218 if (has_horns(uptr)) {
1219 if ((otmp = uarmh) != 0) {
1220 if (is_flimsy(otmp) && !donning(otmp)) {
1221 char hornbuf[BUFSZ];
1223 /* Future possibilities: This could damage/destroy helmet */
1224 Sprintf(hornbuf, "horn%s", plur(num_horns(uptr)));
1225 Your("%s %s through %s.", hornbuf, vtense(hornbuf, "pierce"),
1226 yname(otmp));
1227 } else {
1228 if (donning(otmp))
1229 cancel_don();
1230 Your("%s falls to the %s!", helm_simple_name(otmp),
1231 surface(u.ux, u.uy));
1232 (void) Helmet_off();
1233 dropp(otmp);
1237 if (nohands(uptr) || verysmall(uptr)) {
1238 if ((otmp = uarmg) != 0) {
1239 if (donning(otmp))
1240 cancel_don();
1241 /* Drop weapon along with gloves */
1242 You("drop your gloves%s!", uwep ? " and weapon" : "");
1243 drop_weapon(0);
1244 (void) Gloves_off();
1245 /* Glib manipulation (ends immediately) handled by Gloves_off */
1246 dropp(otmp);
1248 if ((otmp = uarms) != 0) {
1249 You("can no longer hold your shield!");
1250 (void) Shield_off();
1251 dropp(otmp);
1253 if ((otmp = uarmh) != 0) {
1254 if (donning(otmp))
1255 cancel_don();
1256 Your("%s falls to the %s!", helm_simple_name(otmp),
1257 surface(u.ux, u.uy));
1258 (void) Helmet_off();
1259 dropp(otmp);
1262 if (nohands(uptr) || verysmall(uptr)
1263 || slithy(uptr) || uptr->mlet == S_CENTAUR) {
1264 if ((otmp = uarmf) != 0) {
1265 if (donning(otmp))
1266 cancel_don();
1267 if (is_whirly(uptr))
1268 Your("boots fall away!");
1269 else
1270 Your("boots %s off your feet!",
1271 verysmall(uptr) ? "slide" : "are pushed");
1272 (void) Boots_off();
1273 dropp(otmp);
1276 /* not armor, but eyewear shouldn't stay worn without a head to wear
1277 it/them on (should also come off if head is too tiny or too huge,
1278 but putting accessories on doesn't reject those cases [yet?]);
1279 amulet stays worn */
1280 if ((otmp = ublindf) != 0 && !has_head(uptr)) {
1281 int l;
1282 const char *eyewear = simpleonames(otmp); /* blindfold|towel|lenses */
1284 if (!strncmp(eyewear, "pair of ", l = 8)) /* lenses */
1285 eyewear += l;
1286 Your("%s %s off!", eyewear, vtense(eyewear, "fall"));
1287 (void) Blindf_off((struct obj *) 0); /* Null: skip usual off mesg */
1288 dropp(otmp);
1290 /* rings stay worn even when no hands */
1293 staticfn void
1294 drop_weapon(int alone)
1296 struct obj *otmp;
1297 const char *what, *which, *whichtoo;
1298 boolean candropwep, candropswapwep, updateinv = TRUE;
1300 if (uwep) {
1301 /* !alone check below is currently superfluous but in the
1302 * future it might not be so if there are monsters which cannot
1303 * wear gloves but can wield weapons
1305 if (!alone || cantwield(gy.youmonst.data)) {
1306 candropwep = canletgo(uwep, "");
1307 candropswapwep = !u.twoweap || canletgo(uswapwep, "");
1308 if (alone) {
1309 what = (candropwep && candropswapwep) ? "drop" : "release";
1310 which = is_sword(uwep) ? "sword" : weapon_descr(uwep);
1311 if (u.twoweap) {
1312 whichtoo =
1313 is_sword(uswapwep) ? "sword" : weapon_descr(uswapwep);
1314 if (strcmp(which, whichtoo))
1315 which = "weapon";
1317 if (uwep->quan != 1L || u.twoweap)
1318 which = makeplural(which);
1320 You("find you must %s %s %s!", what,
1321 the_your[!!strncmp(which, "corpse", 6)], which);
1323 /* if either uwep or wielded uswapwep is flagged as 'in_use'
1324 then don't drop it or explicitly update inventory; leave
1325 those actions to caller (or caller's caller, &c) */
1326 if (u.twoweap) {
1327 otmp = uswapwep;
1328 uswapwepgone();
1329 if (otmp->in_use)
1330 updateinv = FALSE;
1331 else if (candropswapwep)
1332 dropx(otmp);
1334 otmp = uwep;
1335 uwepgone();
1336 if (otmp->in_use)
1337 updateinv = FALSE;
1338 else if (candropwep)
1339 dropx(otmp);
1340 /* [note: dropp vs dropx -- if heart of ahriman is wielded, we
1341 might be losing levitation by dropping it; but that won't
1342 happen until the drop, unlike Boots_off() dumping hero into
1343 water and triggering emergency_disrobe() before dropx()] */
1345 if (updateinv)
1346 update_inventory();
1347 } else if (!could_twoweap(gy.youmonst.data)) {
1348 untwoweapon();
1353 /* return to original form, usually either due to polymorph timing out
1354 or dying from loss of hit points while being polymorphed */
1355 void
1356 rehumanize(void)
1358 boolean was_flying = (Flying != 0);
1360 /* You can't revert back while unchanging */
1361 if (Unchanging) {
1362 if (u.mh < 1) {
1363 svk.killer.format = NO_KILLER_PREFIX;
1364 Strcpy(svk.killer.name, "killed while stuck in creature form");
1365 done(DIED);
1366 /* can get to here if declining to die in explore or wizard
1367 mode; since we're wearing an amulet of unchanging we can't
1368 be wearing an amulet of life-saving */
1369 return; /* don't rehumanize after all */
1370 } else if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) {
1371 Your("%s %s!", simpleonames(uamul), otense(uamul, "fail"));
1372 uamul->dknown = 1;
1373 makeknown(AMULET_OF_UNCHANGING);
1378 * Right now, dying while being a shifted vampire (bat, cloud, wolf)
1379 * reverts to human rather than to vampire.
1382 if (emits_light(gy.youmonst.data))
1383 del_light_source(LS_MONSTER, monst_to_any(&gy.youmonst));
1384 polyman("You return to %s form!", gu.urace.adj);
1386 if (u.uhp < 1) {
1387 /* can only happen if some bit of code reduces u.uhp
1388 instead of u.mh while poly'd */
1389 Your("old form was not healthy enough to survive.");
1390 Sprintf(svk.killer.name, "reverting to unhealthy %s form",
1391 gu.urace.adj);
1392 svk.killer.format = KILLED_BY;
1393 done(DIED);
1395 nomul(0);
1397 disp.botl = TRUE;
1398 gv.vision_full_recalc = 1;
1399 (void) encumber_msg();
1400 if (was_flying && !Flying && u.usteed)
1401 You("and %s return gently to the %s.",
1402 mon_nam(u.usteed), surface(u.ux, u.uy));
1403 retouch_equipment(2);
1404 if (!uarmg)
1405 selftouch(no_longer_petrify_resistant);
1409 dobreathe(void)
1411 struct attack *mattk;
1413 if (Strangled) {
1414 You_cant("breathe. Sorry.");
1415 return ECMD_OK;
1417 if (u.uen < 15) {
1418 You("don't have enough energy to breathe!");
1419 return ECMD_OK;
1421 u.uen -= 15;
1422 disp.botl = TRUE;
1424 if (!getdir((char *) 0))
1425 return ECMD_CANCEL;
1427 mattk = attacktype_fordmg(gy.youmonst.data, AT_BREA, AD_ANY);
1428 if (!mattk)
1429 impossible("bad breath attack?"); /* mouthwash needed... */
1430 else if (!u.dx && !u.dy && !u.dz)
1431 ubreatheu(mattk);
1432 else
1433 ubuzz(BZ_U_BREATH(BZ_OFS_AD(mattk->adtyp)), (int) mattk->damn);
1434 return ECMD_TIME;
1438 dospit(void)
1440 struct obj *otmp;
1441 struct attack *mattk;
1443 if (!getdir((char *) 0))
1444 return ECMD_CANCEL;
1445 mattk = attacktype_fordmg(gy.youmonst.data, AT_SPIT, AD_ANY);
1446 if (!mattk) {
1447 impossible("bad spit attack?");
1448 } else {
1449 switch (mattk->adtyp) {
1450 case AD_BLND:
1451 case AD_DRST:
1452 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
1453 break;
1454 default:
1455 impossible("bad attack type in dospit");
1456 FALLTHROUGH;
1457 /*FALLTHRU*/
1458 case AD_ACID:
1459 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
1460 break;
1462 otmp->spe = 1; /* to indicate it's yours */
1463 throwit(otmp, 0L, FALSE, (struct obj *) 0);
1465 return ECMD_TIME;
1469 doremove(void)
1471 if (!Punished) {
1472 if (u.utrap && u.utraptype == TT_BURIEDBALL) {
1473 pline_The("ball and chain are buried firmly in the %s.",
1474 surface(u.ux, u.uy));
1475 return ECMD_OK;
1477 You("are not chained to anything!");
1478 return ECMD_OK;
1480 unpunish();
1481 return ECMD_TIME;
1485 dospinweb(void)
1487 coordxy x = u.ux, y = u.uy;
1488 struct trap *ttmp = t_at(x, y);
1489 /* disallow webs on water, lava, air & cloud */
1490 boolean reject_terrain = is_pool_or_lava(x, y) || IS_AIR(levl[x][y].typ);
1492 /* [at the time this was written, it was not possible to be both a
1493 webmaker and a flyer, but with the advent of amulet of flying that
1494 became a possibility; at present hero can spin a web while flying] */
1495 if (Levitation || reject_terrain) {
1496 You("must be on %s ground to spin a web.",
1497 reject_terrain ? "solid" : "the");
1498 return ECMD_OK;
1500 if (u.uswallow) {
1501 You("release web fluid inside %s.", mon_nam(u.ustuck));
1502 if (is_animal(u.ustuck->data)) {
1503 expels(u.ustuck, u.ustuck->data, TRUE);
1504 return ECMD_OK;
1506 if (is_whirly(u.ustuck->data)) {
1507 int i;
1509 for (i = 0; i < NATTK; i++)
1510 if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
1511 break;
1512 if (i == NATTK)
1513 impossible("Swallower has no engulfing attack?");
1514 else {
1515 char sweep[30];
1517 sweep[0] = '\0';
1518 switch (u.ustuck->data->mattk[i].adtyp) {
1519 case AD_FIRE:
1520 Strcpy(sweep, "ignites and ");
1521 break;
1522 case AD_ELEC:
1523 Strcpy(sweep, "fries and ");
1524 break;
1525 case AD_COLD:
1526 Strcpy(sweep, "freezes, shatters and ");
1527 break;
1529 pline_The("web %sis swept away!", sweep);
1531 return ECMD_OK;
1532 } /* default: a nasty jelly-like creature */
1533 pline_The("web dissolves into %s.", mon_nam(u.ustuck));
1534 return ECMD_OK;
1536 if (u.utrap) {
1537 You("cannot spin webs while stuck in a trap.");
1538 return ECMD_OK;
1540 exercise(A_DEX, TRUE);
1541 if (ttmp) {
1542 switch (ttmp->ttyp) {
1543 case PIT:
1544 case SPIKED_PIT:
1545 You("spin a web, covering up the pit.");
1546 deltrap(ttmp);
1547 bury_objs(x, y);
1548 newsym(x, y);
1549 return ECMD_TIME;
1550 case SQKY_BOARD:
1551 pline_The("squeaky board is muffled.");
1552 deltrap(ttmp);
1553 newsym(x, y);
1554 return ECMD_TIME;
1555 case TELEP_TRAP:
1556 case LEVEL_TELEP:
1557 case MAGIC_PORTAL:
1558 case VIBRATING_SQUARE:
1559 Your("webbing vanishes!");
1560 return ECMD_OK;
1561 case WEB:
1562 You("make the web thicker.");
1563 return ECMD_TIME;
1564 case HOLE:
1565 case TRAPDOOR:
1566 You("web over the %s.",
1567 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
1568 deltrap(ttmp);
1569 newsym(x, y);
1570 return ECMD_TIME;
1571 case ROLLING_BOULDER_TRAP:
1572 You("spin a web, jamming the trigger.");
1573 deltrap(ttmp);
1574 newsym(x, y);
1575 return ECMD_TIME;
1576 case ARROW_TRAP:
1577 case DART_TRAP:
1578 case BEAR_TRAP:
1579 case ROCKTRAP:
1580 case FIRE_TRAP:
1581 case LANDMINE:
1582 case SLP_GAS_TRAP:
1583 case RUST_TRAP:
1584 case MAGIC_TRAP:
1585 case ANTI_MAGIC:
1586 case POLY_TRAP:
1587 You("have triggered a trap!");
1588 dotrap(ttmp, NO_TRAP_FLAGS);
1589 return ECMD_TIME;
1590 default:
1591 impossible("Webbing over trap type %d?", ttmp->ttyp);
1592 return ECMD_OK;
1594 } else if (On_stairs(x, y)) {
1595 /* cop out: don't let them hide the stairs */
1596 Your("web fails to impede access to the %s.",
1597 (levl[x][y].typ == STAIRS) ? "stairs" : "ladder");
1598 return ECMD_TIME;
1600 ttmp = maketrap(x, y, WEB);
1601 if (ttmp) {
1602 You("spin a web.");
1603 ttmp->madeby_u = 1;
1604 feeltrap(ttmp);
1605 if (*in_rooms(x, y, SHOPBASE))
1606 add_damage(x, y, SHOP_WEB_COST);
1608 return ECMD_TIME;
1612 dosummon(void)
1614 int placeholder;
1615 if (u.uen < 10) {
1616 You("lack the energy to send forth a call for help!");
1617 return ECMD_OK;
1619 u.uen -= 10;
1620 disp.botl = TRUE;
1622 You("call upon your brethren for help!");
1623 exercise(A_WIS, TRUE);
1624 if (!were_summon(gy.youmonst.data, TRUE, &placeholder, (char *) 0))
1625 pline("But none arrive.");
1626 return ECMD_TIME;
1630 dogaze(void)
1632 struct monst *mtmp;
1633 int looked = 0;
1634 char qbuf[QBUFSZ];
1635 int i;
1636 uchar adtyp = 0;
1638 for (i = 0; i < NATTK; i++) {
1639 if (gy.youmonst.data->mattk[i].aatyp == AT_GAZE) {
1640 adtyp = gy.youmonst.data->mattk[i].adtyp;
1641 break;
1644 if (adtyp != AD_CONF && adtyp != AD_FIRE) {
1645 impossible("gaze attack %d?", adtyp);
1646 return ECMD_OK;
1649 if (Blind) {
1650 You_cant("see anything to gaze at.");
1651 return ECMD_OK;
1652 } else if (Hallucination) {
1653 You_cant("gaze at anything you can see.");
1654 return ECMD_OK;
1656 if (u.uen < 15) {
1657 You("lack the energy to use your special gaze!");
1658 return ECMD_OK;
1660 u.uen -= 15;
1661 disp.botl = TRUE;
1663 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1664 if (DEADMONSTER(mtmp))
1665 continue;
1666 if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
1667 looked++;
1668 if (Invis && !perceives(mtmp->data)) {
1669 pline("%s seems not to notice your gaze.", Monnam(mtmp));
1670 } else if (mtmp->minvis && !See_invisible) {
1671 You_cant("see where to gaze at %s.", Monnam(mtmp));
1672 } else if (M_AP_TYPE(mtmp) == M_AP_FURNITURE
1673 || M_AP_TYPE(mtmp) == M_AP_OBJECT) {
1674 looked--;
1675 continue;
1676 } else if (flags.safe_dog && mtmp->mtame && !Confusion) {
1677 You("avoid gazing at %s.", y_monnam(mtmp));
1678 } else {
1679 if (flags.confirm && mtmp->mpeaceful && !Confusion) {
1680 Sprintf(qbuf, "Really %s %s?",
1681 (adtyp == AD_CONF) ? "confuse" : "attack",
1682 mon_nam(mtmp));
1683 if (y_n(qbuf) != 'y')
1684 continue;
1686 setmangry(mtmp, TRUE);
1687 if (helpless(mtmp) || mtmp->mstun
1688 || !mtmp->mcansee || !haseyes(mtmp->data)) {
1689 looked--;
1690 continue;
1692 /* No reflection check for consistency with when a monster
1693 * gazes at *you*--only medusa gaze gets reflected then.
1695 if (adtyp == AD_CONF) {
1696 if (!mtmp->mconf)
1697 Your("gaze confuses %s!", mon_nam(mtmp));
1698 else
1699 pline("%s is getting more and more confused.",
1700 Monnam(mtmp));
1701 mtmp->mconf = 1;
1702 } else if (adtyp == AD_FIRE) {
1703 int dmg = d(2, 6), orig_dmg = dmg, lev = (int) u.ulevel;
1705 You("attack %s with a fiery gaze!", mon_nam(mtmp));
1706 if (resists_fire(mtmp)) {
1707 pline_The("fire doesn't burn %s!", mon_nam(mtmp));
1708 dmg = 0;
1710 if (lev > rn2(20)) {
1711 dmg += destroy_items(mtmp, AD_FIRE, orig_dmg);
1712 ignite_items(mtmp->minvent);
1714 if (dmg)
1715 mtmp->mhp -= dmg;
1716 if (DEADMONSTER(mtmp))
1717 killed(mtmp);
1719 /* For consistency with passive() in uhitm.c, this only
1720 * affects you if the monster is still alive.
1722 if (DEADMONSTER(mtmp))
1723 continue;
1725 if (mtmp->data == &mons[PM_FLOATING_EYE] && !mtmp->mcan) {
1726 if (!Free_action) {
1727 You("are frozen by %s gaze!",
1728 s_suffix(mon_nam(mtmp)));
1729 nomul((u.ulevel > 6 || rn2(4))
1730 ? -d((int) mtmp->m_lev + 1,
1731 (int) mtmp->data->mattk[0].damd)
1732 : -200);
1733 gm.multi_reason = "frozen by a monster's gaze";
1734 gn.nomovemsg = 0;
1735 return ECMD_TIME;
1736 } else
1737 You("stiffen momentarily under %s gaze.",
1738 s_suffix(mon_nam(mtmp)));
1740 /* Technically this one shouldn't affect you at all because
1741 * the Medusa gaze is an active monster attack that only
1742 * works on the monster's turn, but for it to *not* have an
1743 * effect would be too weird.
1745 if (mtmp->data == &mons[PM_MEDUSA] && !mtmp->mcan) {
1746 pline("Gazing at the awake %s is not a very good idea.",
1747 l_monnam(mtmp));
1748 /* as if gazing at a sleeping anything is fruitful... */
1749 urgent_pline("You turn to stone...");
1750 svk.killer.format = KILLED_BY;
1751 Strcpy(svk.killer.name,
1752 "deliberately meeting Medusa's gaze");
1753 done(STONING);
1758 if (!looked)
1759 You("gaze at no place in particular.");
1760 return ECMD_TIME;
1763 /* called by domonability() for #monster */
1765 dohide(void)
1767 boolean ismimic = gy.youmonst.data->mlet == S_MIMIC,
1768 on_ceiling = is_clinger(gy.youmonst.data) || Flying;
1770 /* can't hide while being held (or holding) or while trapped
1771 (except for floor hiders [trapper or mimic] in pits) */
1772 if (u.ustuck || (u.utrap && (u.utraptype != TT_PIT || on_ceiling))) {
1773 You_cant("hide while you're %s.",
1774 !u.ustuck ? "trapped"
1775 : u.uswallow ? (digests(u.ustuck->data) ? "swallowed"
1776 : "engulfed")
1777 : !sticks(gy.youmonst.data) ? "being held"
1778 : (humanoid(u.ustuck->data) ? "holding someone"
1779 : "holding that creature"));
1780 if (u.uundetected || (ismimic && U_AP_TYPE != M_AP_NOTHING)) {
1781 u.uundetected = 0;
1782 gy.youmonst.m_ap_type = M_AP_NOTHING;
1783 newsym(u.ux, u.uy);
1785 return ECMD_OK;
1787 /* note: hero-as-eel handling is incomplete but unnecessary;
1788 such critters aren't offered the option of hiding via #monster */
1789 if (gy.youmonst.data->mlet == S_EEL && !is_pool(u.ux, u.uy)) {
1790 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ))
1791 pline_The("fountain is not deep enough to hide in.");
1792 else
1793 There("is no %s to hide in here.", hliquid("water"));
1794 u.uundetected = 0;
1795 return ECMD_OK;
1797 if (hides_under(gy.youmonst.data)) {
1798 long ct = 0L;
1799 struct obj *otmp, *otop = svl.level.objects[u.ux][u.uy];
1801 if (!otop) {
1802 There("is nothing to hide under here.");
1803 u.uundetected = 0;
1804 return ECMD_OK;
1806 for (otmp = otop;
1807 otmp && otmp->otyp == CORPSE
1808 && touch_petrifies(&mons[otmp->corpsenm]);
1809 otmp = otmp->nexthere)
1810 ct += otmp->quan;
1811 /* otmp will be Null iff the entire pile consists of 'trice corpses */
1812 if (!otmp && !Stone_resistance) {
1813 char kbuf[BUFSZ];
1814 const char *corpse_name = cxname(otop);
1816 /* for the plural case, we'll say "cockatrice corpses" or
1817 "chickatrice corpses" depending on the top of the pile
1818 even if both types are present */
1819 if (ct == 1)
1820 corpse_name = an(corpse_name);
1821 /* no need to check poly_when_stoned(); no hide-underers can
1822 turn into stone golems instead of becoming petrified */
1823 pline("Hiding under %s%s is a fatal mistake...",
1824 corpse_name, plur(ct));
1825 Sprintf(kbuf, "hiding under %s%s", corpse_name, plur(ct));
1826 instapetrify(kbuf);
1827 /* only reach here if life-saved */
1828 u.uundetected = 0;
1829 return ECMD_TIME;
1832 /* Planes of Air and Water */
1833 if (on_ceiling && !has_ceiling(&u.uz)) {
1834 There("is nowhere to hide above you.");
1835 u.uundetected = 0;
1836 return ECMD_OK;
1838 if ((is_hider(gy.youmonst.data) && !Flying) /* floor hider */
1839 && (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))) {
1840 There("is nowhere to hide beneath you.");
1841 u.uundetected = 0;
1842 return ECMD_OK;
1844 /* TODO? inhibit floor hiding at furniture locations, or
1845 * else make youhiding() give smarter messages at such spots.
1848 if (u.uundetected || (ismimic && U_AP_TYPE != M_AP_NOTHING)) {
1849 youhiding(FALSE, 1); /* "you are already hiding" */
1850 return ECMD_OK;
1853 if (ismimic) {
1854 /* should bring up a dialog "what would you like to imitate?" */
1855 gy.youmonst.m_ap_type = M_AP_OBJECT;
1856 gy.youmonst.mappearance = STRANGE_OBJECT;
1857 } else
1858 u.uundetected = 1;
1859 newsym(u.ux, u.uy);
1860 youhiding(FALSE, 0); /* "you are now hiding" */
1861 return ECMD_TIME;
1865 dopoly(void)
1867 struct permonst *savedat = gy.youmonst.data;
1869 if (is_vampire(gy.youmonst.data) || is_vampshifter(&gy.youmonst)) {
1870 polyself(POLY_MONSTER);
1871 if (savedat != gy.youmonst.data) {
1872 You("transform into %s.",
1873 an(pmname(gy.youmonst.data, Ugender)));
1874 newsym(u.ux, u.uy);
1877 return ECMD_TIME;
1880 /* #monster for hero-as-mind_flayer giving psychic blast */
1882 domindblast(void)
1884 struct monst *mtmp, *nmon;
1885 int dmg;
1887 if (u.uen < 10) {
1888 You("concentrate but lack the energy to maintain doing so.");
1889 return ECMD_OK;
1891 u.uen -= 10;
1892 disp.botl = TRUE;
1894 You("concentrate.");
1895 pline("A wave of psychic energy pours out.");
1896 for (mtmp = fmon; mtmp; mtmp = nmon) {
1897 int u_sen;
1899 nmon = mtmp->nmon;
1900 if (DEADMONSTER(mtmp))
1901 continue;
1902 if (mdistu(mtmp) > BOLT_LIM * BOLT_LIM)
1903 continue;
1904 if (mtmp->mpeaceful)
1905 continue;
1906 if (mindless(mtmp->data))
1907 continue;
1908 u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
1909 if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) {
1910 dmg = rnd(15);
1911 /* wake it up first, to bring hidden monster out of hiding;
1912 but in case it is currently peaceful, don't make it hostile
1913 unless it will survive the psychic blast, otherwise hero
1914 would avoid the penalty for killing it while peaceful */
1915 wakeup(mtmp, (dmg > mtmp->mhp) ? TRUE : FALSE);
1916 You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
1917 u_sen ? "telepathy"
1918 : telepathic(mtmp->data) ? "latent telepathy"
1919 : "mind");
1920 mtmp->mhp -= dmg;
1921 if (DEADMONSTER(mtmp))
1922 killed(mtmp);
1925 return ECMD_TIME;
1928 void
1929 uunstick(void)
1931 struct monst *mtmp = u.ustuck;
1933 if (!mtmp) {
1934 impossible("uunstick: no ustuck?");
1935 return;
1937 set_ustuck((struct monst *) 0); /* before pline() */
1938 pline("%s is no longer in your clutches.", Monnam(mtmp));
1941 void
1942 skinback(boolean silently)
1944 if (uskin) {
1945 int old_light = arti_light_radius(uskin);
1947 if (!silently)
1948 Your("skin returns to its original form.");
1949 uarm = uskin;
1950 uskin = (struct obj *) 0;
1951 /* undo save/restore hack */
1952 uarm->owornmask &= ~I_SPECIAL;
1954 if (artifact_light(uarm))
1955 maybe_adjust_light(uarm, old_light);
1959 const char *
1960 mbodypart(struct monst *mon, int part)
1962 static NEARDATA const char
1963 *humanoid_parts[] = { "arm", "eye", "face", "finger",
1964 "fingertip", "foot", "hand", "handed",
1965 "head", "leg", "light headed", "neck",
1966 "spine", "toe", "hair", "blood",
1967 "lung", "nose", "stomach" },
1968 *jelly_parts[] = { "pseudopod", "dark spot", "front",
1969 "pseudopod extension", "pseudopod extremity",
1970 "pseudopod root", "grasp", "grasped",
1971 "cerebral area", "lower pseudopod", "viscous",
1972 "middle", "surface", "pseudopod extremity",
1973 "ripples", "juices", "surface", "sensor",
1974 "stomach" },
1975 *animal_parts[] = { "forelimb", "eye", "face",
1976 "foreclaw", "claw tip", "rear claw",
1977 "foreclaw", "clawed", "head",
1978 "rear limb", "light headed", "neck",
1979 "spine", "rear claw tip", "fur",
1980 "blood", "lung", "nose",
1981 "stomach" },
1982 *bird_parts[] = { "wing", "eye", "face", "wing",
1983 "wing tip", "foot", "wing", "winged",
1984 "head", "leg", "light headed", "neck",
1985 "spine", "toe", "feathers", "blood",
1986 "lung", "bill", "stomach" },
1987 *horse_parts[] = { "foreleg", "eye", "face",
1988 "forehoof", "hoof tip", "rear hoof",
1989 "forehoof", "hooved", "head",
1990 "rear leg", "light headed", "neck",
1991 "backbone", "rear hoof tip", "mane",
1992 "blood", "lung", "nose",
1993 "stomach" },
1994 *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
1995 "tentacle tip", "lower appendage", "tentacle",
1996 "tentacled", "body", "lower tentacle",
1997 "rotational", "equator", "body",
1998 "lower tentacle tip", "cilia", "life force",
1999 "retina", "olfactory nerve", "interior" },
2000 *fungus_parts[] = { "mycelium", "visual area", "front",
2001 "hypha", "hypha", "root",
2002 "strand", "stranded", "cap area",
2003 "rhizome", "sporulated", "stalk",
2004 "root", "rhizome tip", "spores",
2005 "juices", "gill", "gill",
2006 "interior" },
2007 *vortex_parts[] = { "region", "eye", "front",
2008 "minor current", "minor current", "lower current",
2009 "swirl", "swirled", "central core",
2010 "lower current", "addled", "center",
2011 "currents", "edge", "currents",
2012 "life force", "center", "leading edge",
2013 "interior" },
2014 *snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
2015 "large scale tip", "rear region", "scale gap",
2016 "scale gapped", "head", "rear region",
2017 "light headed", "neck", "length", "rear scale",
2018 "scales", "blood", "lung", "forked tongue",
2019 "stomach" },
2020 *worm_parts[] = { "anterior segment", "light sensitive cell",
2021 "clitellum", "setae", "setae", "posterior segment",
2022 "segment", "segmented", "anterior segment",
2023 "posterior", "over stretched", "clitellum",
2024 "length", "posterior setae", "setae", "blood",
2025 "skin", "prostomium", "stomach" },
2026 *spider_parts[] = { "pedipalp", "eye", "face", "pedipalp", "tarsus",
2027 "claw", "pedipalp", "palped", "cephalothorax",
2028 "leg", "spun out", "cephalothorax", "abdomen",
2029 "claw", "hair", "hemolymph", "book lung",
2030 "labrum", "digestive tract" },
2031 *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
2032 "pelvic fin", "anal fin", "pectoral fin", "finned",
2033 "head", "peduncle", "played out", "gills",
2034 "dorsal fin", "caudal fin", "scales", "blood",
2035 "gill", "nostril", "stomach" };
2036 /* claw attacks are overloaded in mons[]; most humanoids with
2037 such attacks should still reference hands rather than claws */
2038 static const char not_claws[] = {
2039 S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL, S_NYMPH, S_LEPRECHAUN,
2040 S_QUANTMECH, S_VAMPIRE, S_ORC, S_GIANT, /* quest nemeses */
2041 '\0' /* string terminator; assert( S_xxx != 0 ); */
2043 struct permonst *mptr = mon->data;
2045 if (part <= NO_PART) {
2046 impossible("mbodypart: bad part %d", part);
2047 return "mystery part";
2050 /* some special cases */
2051 if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE
2052 || mptr->mlet == S_RODENT || mptr == &mons[PM_OWLBEAR]) {
2053 switch (part) {
2054 case HAND:
2055 return "paw";
2056 case HANDED:
2057 return "pawed";
2058 case FOOT:
2059 return "rear paw";
2060 case ARM:
2061 case LEG:
2062 return horse_parts[part]; /* "foreleg", "rear leg" */
2063 default:
2064 break; /* for other parts, use animal_parts[] below */
2066 } else if (mptr->mlet == S_YETI) { /* excl. owlbear due to 'if' above */
2067 /* opposable thumbs, hence "hands", "arms", "legs", &c */
2068 return humanoid_parts[part]; /* yeti/sasquatch, monkey/ape */
2070 if ((part == HAND || part == HANDED)
2071 && (humanoid(mptr) && attacktype(mptr, AT_CLAW)
2072 && !strchr(not_claws, mptr->mlet) && mptr != &mons[PM_STONE_GOLEM]
2073 && mptr != &mons[PM_AMOROUS_DEMON]))
2074 return (part == HAND) ? "claw" : "clawed";
2075 if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON])
2076 && part == NOSE)
2077 return "trunk";
2078 if (mptr == &mons[PM_SHARK] && part == HAIR)
2079 return "skin"; /* sharks don't have scales */
2080 if ((mptr == &mons[PM_JELLYFISH] || mptr == &mons[PM_KRAKEN])
2081 && (part == ARM || part == FINGER || part == HAND || part == FOOT
2082 || part == TOE))
2083 return "tentacle";
2084 if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
2085 return "cornea";
2086 if (humanoid(mptr) && (part == ARM || part == FINGER || part == FINGERTIP
2087 || part == HAND || part == HANDED))
2088 return humanoid_parts[part];
2089 if (mptr->mlet == S_COCKATRICE)
2090 return (part == HAIR) ? snake_parts[part] : bird_parts[part];
2091 if (mptr == &mons[PM_RAVEN])
2092 return bird_parts[part];
2093 if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN
2094 || mptr == &mons[PM_KI_RIN]
2095 || (mptr == &mons[PM_ROTHE] && part != HAIR))
2096 return horse_parts[part];
2097 if (mptr->mlet == S_LIGHT) {
2098 if (part == HANDED)
2099 return "rayed";
2100 else if (part == ARM || part == FINGER || part == FINGERTIP
2101 || part == HAND)
2102 return "ray";
2103 else
2104 return "beam";
2106 if (mptr == &mons[PM_STALKER] && part == HEAD)
2107 return "head";
2108 if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
2109 return fish_parts[part];
2110 if (mptr->mlet == S_WORM)
2111 return worm_parts[part];
2112 if (mptr->mlet == S_SPIDER)
2113 return spider_parts[part];
2114 if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR))
2115 return snake_parts[part];
2116 if (mptr->mlet == S_EYE)
2117 return sphere_parts[part];
2118 if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING
2119 || mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH])
2120 return jelly_parts[part];
2121 if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL)
2122 return vortex_parts[part];
2123 if (mptr->mlet == S_FUNGUS)
2124 return fungus_parts[part];
2125 if (humanoid(mptr))
2126 return humanoid_parts[part];
2127 return animal_parts[part];
2130 const char *
2131 body_part(int part)
2133 return mbodypart(&gy.youmonst, part);
2137 poly_gender(void)
2139 /* Returns gender of polymorphed player;
2140 * 0/1=same meaning as flags.female, 2=none.
2142 if (is_neuter(gy.youmonst.data) || !humanoid(gy.youmonst.data))
2143 return 2;
2144 return flags.female;
2147 void
2148 ugolemeffects(int damtype, int dam)
2150 int heal = 0;
2152 /* We won't bother with "slow"/"haste" since players do not
2153 * have a monster-specific slow/haste so there is no way to
2154 * restore the old velocity once they are back to human.
2156 if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
2157 return;
2158 switch (damtype) {
2159 case AD_ELEC:
2160 if (u.umonnum == PM_FLESH_GOLEM)
2161 heal = (dam + 5) / 6; /* Approx 1 per die */
2162 break;
2163 case AD_FIRE:
2164 if (u.umonnum == PM_IRON_GOLEM)
2165 heal = dam;
2166 break;
2168 if (heal && (u.mh < u.mhmax)) {
2169 u.mh += heal;
2170 if (u.mh > u.mhmax)
2171 u.mh = u.mhmax;
2172 disp.botl = TRUE;
2173 pline("Strangely, you feel better than before.");
2174 exercise(A_STR, TRUE);
2178 staticfn int
2179 armor_to_dragon(int atyp)
2181 switch (atyp) {
2182 case GRAY_DRAGON_SCALE_MAIL:
2183 case GRAY_DRAGON_SCALES:
2184 return PM_GRAY_DRAGON;
2185 case SILVER_DRAGON_SCALE_MAIL:
2186 case SILVER_DRAGON_SCALES:
2187 return PM_SILVER_DRAGON;
2188 case GOLD_DRAGON_SCALE_MAIL:
2189 case GOLD_DRAGON_SCALES:
2190 return PM_GOLD_DRAGON;
2191 #if 0 /* DEFERRED */
2192 case SHIMMERING_DRAGON_SCALE_MAIL:
2193 case SHIMMERING_DRAGON_SCALES:
2194 return PM_SHIMMERING_DRAGON;
2195 #endif
2196 case RED_DRAGON_SCALE_MAIL:
2197 case RED_DRAGON_SCALES:
2198 return PM_RED_DRAGON;
2199 case ORANGE_DRAGON_SCALE_MAIL:
2200 case ORANGE_DRAGON_SCALES:
2201 return PM_ORANGE_DRAGON;
2202 case WHITE_DRAGON_SCALE_MAIL:
2203 case WHITE_DRAGON_SCALES:
2204 return PM_WHITE_DRAGON;
2205 case BLACK_DRAGON_SCALE_MAIL:
2206 case BLACK_DRAGON_SCALES:
2207 return PM_BLACK_DRAGON;
2208 case BLUE_DRAGON_SCALE_MAIL:
2209 case BLUE_DRAGON_SCALES:
2210 return PM_BLUE_DRAGON;
2211 case GREEN_DRAGON_SCALE_MAIL:
2212 case GREEN_DRAGON_SCALES:
2213 return PM_GREEN_DRAGON;
2214 case YELLOW_DRAGON_SCALE_MAIL:
2215 case YELLOW_DRAGON_SCALES:
2216 return PM_YELLOW_DRAGON;
2217 default:
2218 return NON_PM;
2222 /* some species have awareness of other species */
2223 staticfn void
2224 polysense(void)
2226 short warnidx = NON_PM;
2228 svc.context.warntype.speciesidx = NON_PM;
2229 svc.context.warntype.species = 0;
2230 svc.context.warntype.polyd = 0;
2231 HWarn_of_mon &= ~FROMRACE;
2233 switch (u.umonnum) {
2234 case PM_PURPLE_WORM:
2235 case PM_BABY_PURPLE_WORM:
2236 warnidx = PM_SHRIEKER;
2237 break;
2238 case PM_VAMPIRE:
2239 case PM_VAMPIRE_LEADER:
2240 svc.context.warntype.polyd = M2_HUMAN | M2_ELF;
2241 HWarn_of_mon |= FROMRACE;
2242 return;
2244 if (ismnum(warnidx)) {
2245 svc.context.warntype.speciesidx = warnidx;
2246 svc.context.warntype.species = &mons[warnidx];
2247 HWarn_of_mon |= FROMRACE;
2251 /* True iff hero's role or race has been genocided */
2252 boolean
2253 ugenocided(void)
2255 return ((svm.mvitals[gu.urole.mnum].mvflags & G_GENOD)
2256 || (svm.mvitals[gu.urace.mnum].mvflags & G_GENOD));
2259 /* how hero feels "inside" after self-genocide of role or race */
2260 const char *
2261 udeadinside(void)
2263 /* self-genocide used to always say "you feel dead inside" but that
2264 seems silly when you're polymorphed into something undead;
2265 monkilled() distinguishes between living (killed) and non (destroyed)
2266 for monster death message; we refine the nonliving aspect a bit */
2267 return !nonliving(gy.youmonst.data)
2268 ? "dead" /* living, including demons */
2269 : !weirdnonliving(gy.youmonst.data)
2270 ? "condemned" /* undead plus manes */
2271 : "empty"; /* golems plus vortices */
2274 /*polyself.c*/