1 | /* SCCS Id: @(#)polyself.c 3.3 2000/07/14 */
2 | /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | /*
6 | * Polymorph self routine.
7 | *
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.
11 | */
12 |
13 | #include "hack.h"
14 |
15 | #ifdef OVLB
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(skinback);
20 | STATIC_DCL void NDECL(uunstick);
21 | STATIC_DCL int FDECL(armor_to_dragon,(int));
22 | STATIC_DCL void NDECL(newman);
23 |
24 | /* update the youmonst.data structure pointer */
25 | void
26 | set_uasmon()
27 | {
28 | set_mon_data(&youmonst, &mons[u.umonnum], 0);
29 | }
30 |
31 | /* make a (new) human out of the player */
32 | STATIC_OVL void
33 | polyman(fmt, arg)
34 | const char *fmt, *arg;
35 | {
36 | boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
37 | was_mimicking_gold = (youmonst.m_ap_type == M_AP_OBJECT
38 | && youmonst.mappearance == GOLD_PIECE);
39 | boolean was_blind = !!Blind;
40 |
41 | if (Upolyd) {
42 | u.acurr = u.macurr; /* restore old attribs */
43 | u.amax = u.mamax;
44 | u.umonnum = u.umonster;
45 | flags.female = u.mfemale;
46 | }
47 | set_uasmon();
48 |
49 | u.mh = u.mhmax = 0;
50 | u.mtimedone = 0;
51 | skinback();
52 | u.uundetected = 0;
53 |
54 | if (sticky) uunstick();
55 | find_ac();
56 | if (was_mimicking_gold) {
57 | if (multi < 0) unmul("");
58 | } else {
59 | /*
60 | * Clear any in-progress imitations -- the case where not a
61 | * mimic is handled above.
62 | *
63 | * Except, this is not complete if the hero ever gets the
64 | * chance to imitate anything, then s/he may be mimicing
65 | * gold, but not the way its done for eating a mimic.
66 | */
67 | youmonst.m_ap_type = M_AP_NOTHING;
68 | }
69 | newsym(u.ux,u.uy);
70 |
71 | You(fmt, arg);
72 | /* check whether player foolishly genocided self while poly'd */
73 | if ((mvitals[urole.malenum].mvflags & G_GENOD) ||
74 | (urole.femalenum != NON_PM &&
75 | (mvitals[urole.femalenum].mvflags & G_GENOD)) ||
76 | (mvitals[urace.malenum].mvflags & G_GENOD) ||
77 | (urace.femalenum != NON_PM &&
78 | (mvitals[urace.femalenum].mvflags & G_GENOD))) {
79 | /* intervening activity might have clobbered genocide info */
80 | killer = delayed_killer;
81 | if (!killer || !strstri(killer, "genocid")) {
82 | killer_format = KILLED_BY;
83 | killer = "self-genocide";
84 | }
85 | done(GENOCIDED);
86 | }
87 | if (was_blind && !Blind) { /* reverting from eyeless */
88 | Blinded = 1L;
89 | make_blinded(0L, TRUE); /* remove blindness */
90 | }
91 |
92 | if(!Levitation && !u.ustuck &&
93 | (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy)))
94 | spoteffects(TRUE);
95 |
96 | see_monsters();
97 | }
98 |
99 | void
100 | change_sex()
101 | {
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;
105 |
106 | /* Some monsters are always of one sex and their sex can't be changed */
107 | /* succubi/incubi are handled below */
108 | if (u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS && !is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data))
109 | flags.female = !flags.female;
110 | if (already_polyd) /* poly'd: also change saved sex */
111 | u.mfemale = !u.mfemale;
112 | max_rank_sz(); /* [this appears to be superfluous] */
113 | if (flags.female && urole.name.f)
114 | Strcpy(pl_character, urole.name.f);
115 | else
116 | Strcpy(pl_character, urole.name.m);
117 | u.umonster = ((already_polyd ? u.mfemale : flags.female) && urole.femalenum != NON_PM) ?
118 | urole.femalenum : urole.malenum;
119 | if (!already_polyd) {
120 | u.umonnum = u.umonster;
121 | } else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) {
122 | /* change monster type to match new sex */
123 | u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS;
124 | set_uasmon();
125 | }
126 | }
127 |
128 | STATIC_OVL void
129 | newman()
130 | {
131 | int tmp, tmp2;
132 |
133 | if (!rn2(10)) change_sex();
134 |
135 | tmp = u.uhpmax;
136 | tmp2 = u.ulevel;
137 | u.ulevel = u.ulevel + rn1(5, -2);
138 | if (u.ulevel > 127 || u.ulevel < 1) u.ulevel = 1;
139 | if (u.ulevel > MAXULEV) u.ulevel = MAXULEV;
140 | if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel;
141 |
142 | adjabil(tmp2, (int)u.ulevel);
143 | reset_rndmonst(NON_PM); /* new monster generation criteria */
144 |
145 | /* random experience points for the new experience level */
146 | u.uexp = rndexp();
147 |
148 | /* u.uhpmax * u.ulevel / tmp2: proportionate hit points to new level
149 | * -10 and +10: don't apply proportionate HP to 10 of a starting
150 | * character's hit points (since a starting character's hit points
151 | * are not on the same scale with hit points obtained through level
152 | * gain)
153 | * 9 - rn2(19): random change of -9 to +9 hit points
154 | */
155 | #ifndef LINT
156 | u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / tmp2 + 10) +
157 | (9 - rn2(19));
158 | #endif
159 |
160 | #ifdef LINT
161 | u.uhp = u.uhp + tmp;
162 | #else
163 | u.uhp = u.uhp * (long)u.uhpmax/tmp;
164 | #endif
165 |
166 | tmp = u.uenmax;
167 | #ifndef LINT
168 | u.uenmax = u.uenmax * (long)u.ulevel / tmp2 + 9 - rn2(19);
169 | #endif
170 | if (u.uenmax < 0) u.uenmax = 0;
171 | #ifndef LINT
172 | u.uen = (tmp ? u.uen * (long)u.uenmax / tmp : u.uenmax);
173 | #endif
174 |
175 | redist_attr();
176 | u.uhunger = rn1(500,500);
177 | newuhs(FALSE);
178 | if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL);
179 | Stoned = 0;
180 | delayed_killer = 0;
181 | if (u.uhp <= 0 || u.uhpmax <= 0) {
182 | if (Polymorph_control) {
183 | if (u.uhp <= 0) u.uhp = 1;
184 | if (u.uhpmax <= 0) u.uhpmax = 1;
185 | } else {
186 | Your("new form doesn't seem healthy enough to survive.");
187 | killer_format = KILLED_BY_AN;
188 | killer="unsuccessful polymorph";
189 | done(DIED);
190 | }
191 | }
192 | polyman("feel like a new %s!",
193 | (flags.female && urace.individual.f) ? urace.individual.f :
194 | (urace.individual.m) ? urace.individual.m : urace.noun);
195 | if (Slimed) {
196 | Your("body transforms, but there is still slime on you.");
197 | Slimed = 10L;
198 | }
199 | flags.botl = 1;
200 | see_monsters();
201 | (void) encumber_msg();
202 | }
203 |
204 | void
205 | polyself()
206 | {
207 | char buf[BUFSZ];
208 | int old_light, new_light;
209 | int mntmp = NON_PM;
210 | int tries=0;
211 | boolean draconian = (uarm &&
212 | uarm->otyp >= GRAY_DRAGON_SCALE_MAIL &&
213 | uarm->otyp <= YELLOW_DRAGON_SCALES);
214 | boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data));
215 | boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT);
216 |
217 | if(!Polymorph_control && !draconian && !iswere && !isvamp) {
218 | if (rn2(20) > ACURR(A_CON)) {
219 | You(shudder_for_moment);
220 | losehp(rnd(30), "system shock", KILLED_BY_AN);
221 | exercise(A_CON, FALSE);
222 | return;
223 | }
224 | }
225 | old_light = Upolyd ? emits_light(youmonst.data) : 0;
226 |
227 | if (Polymorph_control) {
228 | do {
229 | getlin("Become what kind of monster? [type the name]",
230 | buf);
231 | mntmp = name_to_mon(buf);
232 | if (mntmp < LOW_PM)
233 | pline("I've never heard of such monsters.");
234 | /* Note: humans are illegal as monsters, but an
235 | * illegal monster forces newman(), which is what we
236 | * want if they specified a human.... */
237 | else if (!polyok(&mons[mntmp]) && !your_race(&mons[mntmp]))
238 | You("cannot polymorph into that.");
239 | else break;
240 | } while(++tries < 5);
241 | if (tries==5) pline(thats_enough_tries);
242 | /* allow skin merging, even when polymorph is controlled */
243 | if (draconian &&
244 | (mntmp == armor_to_dragon(uarm->otyp) || tries == 5))
245 | goto do_merge;
246 | } else if (draconian || iswere || isvamp) {
247 | /* special changes that don't require polyok() */
248 | if (draconian) {
249 | do_merge:
250 | mntmp = armor_to_dragon(uarm->otyp);
251 | if (!(mvitals[mntmp].mvflags & G_GENOD)) {
252 | /* allow G_EXTINCT */
253 | You("merge with your scaly armor.");
254 | uskin = uarm;
255 | uarm = (struct obj *)0;
256 | /* save/restore hack */
257 | uskin->owornmask |= I_SPECIAL;
258 | }
259 | } else if (iswere) {
260 | if (is_were(youmonst.data))
261 | mntmp = PM_HUMAN; /* Illegal; force newman() */
262 | else
263 | mntmp = u.ulycn;
264 | } else {
265 | if (youmonst.data->mlet == S_VAMPIRE)
266 | mntmp = PM_VAMPIRE_BAT;
267 | else
268 | mntmp = PM_VAMPIRE;
269 | }
270 | /* if polymon fails, "you feel" message has been given
271 | so don't follow up with another polymon or newman */
272 | if (mntmp == PM_HUMAN) newman(); /* werecritter */
273 | else (void) polymon(mntmp);
274 | goto made_change; /* maybe not, but this is right anyway */
275 | }
276 |
277 | if (mntmp < LOW_PM) {
278 | tries = 0;
279 | do {
280 | /* randomly pick an "ordinary" monster */
281 | mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
282 | } while((!polyok(&mons[mntmp]) || is_placeholder(&mons[mntmp]))
283 | && tries++ < 200);
284 | }
285 |
286 | /* The below polyok() fails either if everything is genocided, or if
287 | * we deliberately chose something illegal to force newman().
288 | */
289 | if (!polyok(&mons[mntmp]) || !rn2(5))
290 | newman();
291 | else if(!polymon(mntmp)) return;
292 |
293 | if (!uarmg) selftouch("No longer petrify-resistant, you");
294 |
295 | made_change:
296 | new_light = Upolyd ? emits_light(youmonst.data) : 0;
297 | if (old_light != new_light) {
298 | if (old_light)
299 | del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
300 | if (new_light == 1) ++new_light; /* otherwise it's undetectable */
301 | if (new_light)
302 | new_light_source(u.ux, u.uy, new_light,
303 | LS_MONSTER, (genericptr_t)&youmonst);
304 | }
305 | }
306 |
307 | /* (try to) make a mntmp monster out of the player */
308 | int
309 | polymon(mntmp) /* returns 1 if polymorph successful */
310 | int mntmp;
311 | {
312 | boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
313 | was_blind = !!Blind, dochange = FALSE;
314 | int mlvl;
315 |
316 | if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
317 | You_feel("rather %s-ish.",mons[mntmp].mname);
318 | exercise(A_WIS, TRUE);
319 | return(0);
320 | }
321 |
322 | /* KMH, conduct */
323 | u.uconduct.polyselfs++;
324 |
325 | if (!Upolyd) {
326 | /* Human to monster; save human stats */
327 | u.macurr = u.acurr;
328 | u.mamax = u.amax;
329 | u.mfemale = flags.female;
330 | } else {
331 | /* Monster to monster; restore human stats, to be
332 | * immediately changed to provide stats for the new monster
333 | */
334 | u.acurr = u.macurr;
335 | u.amax = u.mamax;
336 | flags.female = u.mfemale;
337 | }
338 |
339 | if (youmonst.m_ap_type == M_AP_OBJECT &&
340 | youmonst.mappearance == GOLD_PIECE) {
341 | /* stop mimicking gold immediately */
342 | if (multi < 0) unmul("");
343 | }
344 | if (is_male(&mons[mntmp])) {
345 | if(flags.female) dochange = TRUE;
346 | } else if (is_female(&mons[mntmp])) {
347 | if(!flags.female) dochange = TRUE;
348 | } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
349 | if(!rn2(10)) dochange = TRUE;
350 | }
351 | if (dochange) {
352 | flags.female = !flags.female;
353 | You("%s %s%s!",
354 | (u.umonnum != mntmp) ? "turn into a" : "feel like a new",
355 | (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" :
356 | flags.female ? "female " : "male ",
357 | mons[mntmp].mname);
358 | } else {
359 | if (u.umonnum != mntmp)
360 | You("turn into %s!", an(mons[mntmp].mname));
361 | else
362 | You_feel("like a new %s!", mons[mntmp].mname);
363 | }
364 | if (Stoned && poly_when_stoned(&mons[mntmp])) {
365 | /* poly_when_stoned already checked stone golem genocide */
366 | You("turn to stone!");
367 | mntmp = PM_STONE_GOLEM;
368 | Stoned = 0;
369 | delayed_killer = 0;
370 | }
371 |
372 | u.mtimedone = rn1(500, 500);
373 | u.umonnum = mntmp;
374 | set_uasmon();
375 |
376 | /* New stats for monster, to last only as long as polymorphed.
377 | * Currently only strength gets changed.
378 | */
379 | if(strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100);
380 |
381 | if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */
382 | Stoned = 0;
383 | delayed_killer = 0;
384 | You("no longer seem to be petrifying.");
385 | }
386 | if (Sick_resistance && Sick) {
387 | make_sick(0L, (char *) 0, FALSE, SICK_ALL);
388 | You("no longer feel sick.");
389 | }
390 | if (Slimed) {
391 | if (mntmp == PM_FIRE_VORTEX || mntmp == PM_FIRE_ELEMENTAL) {
392 | pline_The("slime burns away!");
393 | Slimed = 0;
394 | } else if (mntmp == PM_GREEN_SLIME) {
395 | /* do it silently */
396 | Slimed = 0;
397 | }
398 | }
399 | if (nohands(youmonst.data)) Glib = 0;
400 |
401 | /*
402 | mlvl = adj_lev(&mons[mntmp]);
403 | * We can't do the above, since there's no such thing as an
404 | * "experience level of you as a monster" for a polymorphed character.
405 | */
406 | mlvl = (int)mons[mntmp].mlevel;
407 | if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) {
408 | u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + d(mlvl,4));
409 | } else if (is_golem(youmonst.data)) {
410 | u.mhmax = golemhp(mntmp);
411 | } else {
412 | if (!mlvl) u.mhmax = rnd(4);
413 | else u.mhmax = d(mlvl, 8);
414 | if (is_home_elemental(&mons[mntmp])) u.mhmax *= 3;
415 | }
416 | u.mh = u.mhmax;
417 |
418 | if (u.ulevel < mlvl) {
419 | /* Low level characters can't become high level monsters for long */
420 | #ifdef DUMB
421 | /* DRS/NS 2.2.6 messes up -- Peter Kendell */
422 | int mtd = u.mtimedone, ulv = u.ulevel;
423 |
424 | u.mtimedone = mtd * ulv / mlvl;
425 | #else
426 | u.mtimedone = u.mtimedone * u.ulevel / mlvl;
427 | #endif
428 | }
429 |
430 | if (uskin && mntmp != armor_to_dragon(uskin->otyp))
431 | skinback();
432 | break_armor();
433 | drop_weapon(1);
434 | if (hides_under(youmonst.data))
435 | u.uundetected = OBJ_AT(u.ux, u.uy);
436 | else if (youmonst.data->mlet == S_EEL)
437 | u.uundetected = is_pool(u.ux, u.uy);
438 | else
439 | u.uundetected = 0;
440 |
441 | if (was_blind && !Blind) { /* previous form was eyeless */
442 | Blinded = 1L;
443 | make_blinded(0L, TRUE); /* remove blindness */
444 | }
445 | newsym(u.ux,u.uy); /* Change symbol */
446 |
447 | if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0;
448 | else if (sticky && !sticks(youmonst.data)) uunstick();
449 | #ifdef STEED
450 | if (u.usteed) {
451 | if (touch_petrifies(u.usteed->data) &&
452 | !Stone_resistance && rnl(3)) {
453 | char buf[BUFSZ];
454 |
455 | pline("No longer petrifying-resistant, you touch %s.",
456 | mon_nam(u.usteed));
457 | Sprintf(buf, "riding %s", an(u.usteed->data->mname));
458 | instapetrify(buf);
459 | }
460 | if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY);
461 | }
462 | #endif
463 |
464 | if (flags.verbose) {
465 | static const char use_thec[] = "Use the command #%s to %s.";
466 | static const char monsterc[] = "monster";
467 | if (can_breathe(youmonst.data))
468 | pline(use_thec,monsterc,"use your breath weapon");
469 | if (attacktype(youmonst.data, AT_SPIT))
470 | pline(use_thec,monsterc,"spit venom");
471 | if (youmonst.data->mlet == S_NYMPH)
472 | pline(use_thec,monsterc,"remove an iron ball");
473 | if (youmonst.data->mlet == S_UMBER)
474 | pline(use_thec,monsterc,"confuse monsters");
475 | if (is_hider(youmonst.data))
476 | pline(use_thec,monsterc,"hide");
477 | if (is_were(youmonst.data))
478 | pline(use_thec,monsterc,"summon help");
479 | if (webmaker(youmonst.data))
480 | pline(use_thec,monsterc,"spin a web");
481 | if (u.umonnum == PM_GREMLIN)
482 | pline(use_thec,monsterc,"multiply in a fountain");
483 | if (is_unicorn(youmonst.data))
484 | pline(use_thec,monsterc,"use your horn");
485 | if (is_mind_flayer(youmonst.data))
486 | pline(use_thec,monsterc,"emit a mental blast");
487 | if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
488 | pline(use_thec,monsterc,"shriek");
489 | if (lays_eggs(youmonst.data) && flags.female)
490 | pline(use_thec,"sit","lay an egg");
491 | }
492 | /* you now know what an egg of your type looks like */
493 | if (lays_eggs(youmonst.data)) {
494 | learn_egg_type(u.umonnum);
495 | /* make queen bees recognize killer bee eggs */
496 | learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
497 | }
498 | find_ac();
499 | if((!Levitation && !u.ustuck && !Flying &&
500 | (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) ||
501 | (Underwater && !Swimming))
502 | spoteffects(TRUE);
503 | if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) {
504 | u.utrap = 0;
505 | pline_The("rock seems to no longer trap you.");
506 | } else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) {
507 | u.utrap = 0;
508 | pline_The("lava now feels soothing.");
509 | }
510 | if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) {
511 | if (Punished) {
512 | You("slip out of the iron chain.");
513 | unpunish();
514 | }
515 | }
516 | if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) &&
517 | (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) ||
518 | (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) {
519 | You("are no longer stuck in the %s.",
520 | u.utraptype == TT_WEB ? "web" : "bear trap");
521 | /* probably should burn webs too if PM_FIRE_ELEMENTAL */
522 | u.utrap = 0;
523 | }
524 | if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
525 | You("orient yourself on the web.");
526 | u.utrap = 0;
527 | }
528 | flags.botl = 1;
529 | vision_full_recalc = 1;
530 | see_monsters();
531 | exercise(A_CON, FALSE);
532 | exercise(A_WIS, TRUE);
533 | (void) encumber_msg();
534 | return(1);
535 | }
536 |
537 | STATIC_OVL void
538 | break_armor()
539 | {
540 | register struct obj *otmp;
541 |
542 | if (breakarm(youmonst.data)) {
543 | if ((otmp = uarm) != 0) {
544 | if (donning(otmp)) cancel_don();
545 | You("break out of your armor!");
546 | exercise(A_STR, FALSE);
547 | (void) Armor_gone();
548 | useup(otmp);
549 | }
550 | if ((otmp = uarmc) != 0) {
551 | if(otmp->oartifact) {
552 | Your("cloak falls off!");
553 | (void) Cloak_off();
554 | dropx(otmp);
555 | } else {
556 | Your("cloak tears apart!");
557 | (void) Cloak_off();
558 | useup(otmp);
559 | }
560 | }
561 | #ifdef TOURIST
562 | if (uarmu) {
563 | Your("shirt rips to shreds!");
564 | useup(uarmu);
565 | }
566 | #endif
567 | } else if (sliparm(youmonst.data)) {
568 | if ((otmp = uarm) != 0) {
569 | if (donning(otmp)) cancel_don();
570 | Your("armor falls around you!");
571 | (void) Armor_gone();
572 | dropx(otmp);
573 | }
574 | if ((otmp = uarmc) != 0) {
575 | if (is_whirly(youmonst.data))
576 | Your("cloak falls, unsupported!");
577 | else You("shrink out of your cloak!");
578 | (void) Cloak_off();
579 | dropx(otmp);
580 | }
581 | #ifdef TOURIST
582 | if ((otmp = uarmu) != 0) {
583 | if (is_whirly(youmonst.data))
584 | You("seep right through your shirt!");
585 | else You("become much too small for your shirt!");
586 | setworn((struct obj *)0, otmp->owornmask & W_ARMU);
587 | dropx(otmp);
588 | }
589 | #endif
590 | }
591 | if (nohands(youmonst.data) || verysmall(youmonst.data)) {
592 | if ((otmp = uarmg) != 0) {
593 | if (donning(otmp)) cancel_don();
594 | /* Drop weapon along with gloves */
595 | You("drop your gloves%s!", uwep ? " and weapon" : "");
596 | drop_weapon(0);
597 | (void) Gloves_off();
598 | dropx(otmp);
599 | }
600 | if ((otmp = uarms) != 0) {
601 | You("can no longer hold your shield!");
602 | (void) Shield_off();
603 | dropx(otmp);
604 | }
605 | if ((otmp = uarmh) != 0) {
606 | if (donning(otmp)) cancel_don();
607 | Your("helmet falls to the %s!", surface(u.ux, u.uy));
608 | (void) Helmet_off();
609 | dropx(otmp);
610 | }
611 | }
612 | if (nohands(youmonst.data) || verysmall(youmonst.data) ||
613 | slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) {
614 | if ((otmp = uarmf) != 0) {
615 | if (donning(otmp)) cancel_don();
616 | if (is_whirly(youmonst.data))
617 | Your("boots fall away!");
618 | else Your("boots %s off your feet!",
619 | verysmall(youmonst.data) ? "slide" : "are pushed");
620 | (void) Boots_off();
621 | dropx(otmp);
622 | }
623 | }
624 | }
625 |
626 | STATIC_OVL void
627 | drop_weapon(alone)
628 | int alone;
629 | {
630 | struct obj *otmp;
631 | if ((otmp = uwep) != 0) {
632 | /* !alone check below is currently superfluous but in the
633 | * future it might not be so if there are monsters which cannot
634 | * wear gloves but can wield weapons
635 | */
636 | if (!alone || cantwield(youmonst.data)) {
637 | struct obj *wep = uwep;
638 |
639 | if (alone) You("find you must drop your weapon%s!",
640 | u.twoweap ? "s" : "");
641 | uwepgone();
642 | if (!wep->cursed || wep->otyp != LOADSTONE)
643 | dropx(otmp);
644 | untwoweapon();
645 | }
646 | }
647 | }
648 |
649 | void
650 | rehumanize()
651 | {
652 | /* You can't revert back while unchanging */
653 | if (Unchanging && (u.mh < 1)) {
654 | killer_format = NO_KILLER_PREFIX;
655 | killer = "killed while stuck in creature form";
656 | done(DIED);
657 | }
658 |
659 | if (emits_light(youmonst.data))
660 | del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
661 | polyman("return to %s form!", urace.adj);
662 |
663 | if (u.uhp < 1) {
664 | char kbuf[256];
665 |
666 | Sprintf(kbuf, "reverting to unhealthy %s form", urace.adj);
667 | killer_format = KILLED_BY;
668 | killer = kbuf;
669 | done(DIED);
670 | }
671 | if (!uarmg) selftouch("No longer petrify-resistant, you");
672 | nomul(0);
673 |
674 | flags.botl = 1;
675 | vision_full_recalc = 1;
676 | (void) encumber_msg();
677 | }
678 |
679 | int
680 | dobreathe()
681 | {
682 | if (Strangled) {
683 | You_cant("breathe. Sorry.");
684 | return(0);
685 | }
686 | if (u.uen < 15) {
687 | You("don't have enough energy to breathe!");
688 | return(0);
689 | }
690 | u.uen -= 15;
691 | flags.botl = 1;
692 |
693 | if (!getdir((char *)0)) return(0);
694 | else {
695 | register struct attack *mattk;
696 | register int i;
697 |
698 | for(i = 0; i < NATTK; i++) {
699 | mattk = &(youmonst.data->mattk[i]);
700 | if(mattk->aatyp == AT_BREA) break;
701 | }
702 | buzz((int) (20 + mattk->adtyp-1), (int)mattk->damn,
703 | u.ux, u.uy, u.dx, u.dy);
704 | }
705 | return(1);
706 | }
707 |
708 | int
709 | dospit()
710 | {
711 | struct obj *otmp;
712 |
713 | if (!getdir((char *)0)) return(0);
714 | otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM,
715 | TRUE, FALSE);
716 | otmp->spe = 1; /* to indicate it's yours */
717 | throwit(otmp, 0L);
718 | return(1);
719 | }
720 |
721 | int
722 | doremove()
723 | {
724 | if (!Punished) {
725 | You("are not chained to anything!");
726 | return(0);
727 | }
728 | unpunish();
729 | return(1);
730 | }
731 |
732 | int
733 | dospinweb()
734 | {
735 | register struct trap *ttmp = t_at(u.ux,u.uy);
736 |
737 | if (Levitation || Is_airlevel(&u.uz)
738 | || Underwater || Is_waterlevel(&u.uz)) {
739 | You("must be on the ground to spin a web.");
740 | return(0);
741 | }
742 | if (u.uswallow) {
743 | You("release web fluid inside %s.", mon_nam(u.ustuck));
744 | if (is_animal(u.ustuck->data)) {
745 | expels(u.ustuck, u.ustuck->data, TRUE);
746 | return(0);
747 | }
748 | if (is_whirly(u.ustuck->data)) {
749 | int i;
750 |
751 | for (i = 0; i < NATTK; i++)
752 | if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
753 | break;
754 | if (i == NATTK)
755 | impossible("Swallower has no engulfing attack?");
756 | else {
757 | char sweep[30];
758 |
759 | sweep[0] = '\0';
760 | switch(u.ustuck->data->mattk[i].adtyp) {
761 | case AD_FIRE:
762 | Strcpy(sweep, "ignites and ");
763 | break;
764 | case AD_ELEC:
765 | Strcpy(sweep, "fries and ");
766 | break;
767 | case AD_COLD:
768 | Strcpy(sweep,
769 | "freezes, shatters and ");
770 | break;
771 | }
772 | pline_The("web %sis swept away!", sweep);
773 | }
774 | return(0);
775 | } /* default: a nasty jelly-like creature */
776 | pline_The("web dissolves into %s.", mon_nam(u.ustuck));
777 | return(0);
778 | }
779 | if (u.utrap) {
780 | You("cannot spin webs while stuck in a trap.");
781 | return(0);
782 | }
783 | exercise(A_DEX, TRUE);
784 | if (ttmp) switch (ttmp->ttyp) {
785 | case PIT:
786 | case SPIKED_PIT: You("spin a web, covering up the pit.");
787 | deltrap(ttmp);
788 | bury_objs(u.ux, u.uy);
789 | if (Invisible) newsym(u.ux, u.uy);
790 | return(1);
791 | case SQKY_BOARD: pline_The("squeaky board is muffled.");
792 | deltrap(ttmp);
793 | if (Invisible) newsym(u.ux, u.uy);
794 | return(1);
795 | case TELEP_TRAP:
796 | case LEVEL_TELEP:
797 | case MAGIC_PORTAL:
798 | Your("webbing vanishes!");
799 | return(0);
800 | case WEB: You("make the web thicker.");
801 | return(1);
802 | case HOLE:
803 | case TRAPDOOR:
804 | You("web over the %s.",
805 | (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
806 | deltrap(ttmp);
807 | if (Invisible) newsym(u.ux, u.uy);
808 | return 1;
809 | case ARROW_TRAP:
810 | case DART_TRAP:
811 | case BEAR_TRAP:
812 | case LANDMINE:
813 | case SLP_GAS_TRAP:
814 | case RUST_TRAP:
815 | case MAGIC_TRAP:
816 | case ANTI_MAGIC:
817 | case POLY_TRAP:
818 | You("have triggered a trap!");
819 | dotrap(ttmp);
820 | return(1);
821 | default:
822 | impossible("Webbing over trap type %d?", ttmp->ttyp);
823 | return(0);
824 | }
825 | ttmp = maketrap(u.ux, u.uy, WEB);
826 | if (ttmp) {
827 | ttmp->tseen = 1;
828 | ttmp->madeby_u = 1;
829 | }
830 | if (Invisible) newsym(u.ux, u.uy);
831 | return(1);
832 | }
833 |
834 | int
835 | dosummon()
836 | {
837 | if (u.uen < 10) {
838 | You("lack the energy to send forth a call for help!");
839 | return(0);
840 | }
841 | u.uen -= 10;
842 | flags.botl = 1;
843 |
844 | You("call upon your brethren for help!");
845 | exercise(A_WIS, TRUE);
846 | if (!were_summon(youmonst.data,TRUE))
847 | pline("But none arrive.");
848 | return(1);
849 | }
850 |
851 | int
852 | doconfuse()
853 | {
854 | register struct monst *mtmp;
855 | int looked = 0;
856 | char qbuf[QBUFSZ];
857 |
858 | if (Blind) {
859 | You_cant("see anything to gaze at.");
860 | return 0;
861 | }
862 | if (u.uen < 15) {
863 | You("lack the energy to use your special gaze!");
864 | return(0);
865 | }
866 | u.uen -= 15;
867 | flags.botl = 1;
868 |
869 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
870 | if (DEADMONSTER(mtmp)) continue;
871 | if (canseemon(mtmp)) {
872 | looked++;
873 | if (Invis && !perceives(mtmp->data))
874 | pline("%s seems not to notice your gaze.", Monnam(mtmp));
875 | else if (mtmp->minvis && !See_invisible)
876 | You_cant("see where to gaze at %s.", Monnam(mtmp));
877 | else if (mtmp->m_ap_type == M_AP_FURNITURE
878 | || mtmp->m_ap_type == M_AP_OBJECT) {
879 | looked--;
880 | continue;
881 | } else if (flags.safe_dog && !Confusion && !Hallucination
882 | && mtmp->mtame) {
883 | You("avoid gazing at %s.", y_monnam(mtmp));
884 | } else {
885 | if (flags.confirm && mtmp->mpeaceful && !Confusion
886 | && !Hallucination) {
887 | Sprintf(qbuf, "Really confuse %s?", mon_nam(mtmp));
888 | if (yn(qbuf) != 'y') continue;
889 | setmangry(mtmp);
890 | }
891 | if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping ||
892 | !mtmp->mcansee || !haseyes(mtmp->data)) {
893 | looked--;
894 | continue;
895 | }
896 | if (!mon_reflects(mtmp,"Your gaze is reflected by %s %s.")){
897 | if (!mtmp->mconf)
898 | Your("gaze confuses %s!", mon_nam(mtmp));
899 | else
900 | pline("%s is getting more and more confused.",
901 | Monnam(mtmp));
902 | mtmp->mconf = 1;
903 | }
904 | if ((mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) {
905 | if (!Free_action) {
906 | You("are frozen by %s gaze!",
907 | s_suffix(mon_nam(mtmp)));
908 | nomul((u.ulevel > 6 || rn2(4)) ?
909 | -d((int)mtmp->m_lev+1,
910 | (int)mtmp->data->mattk[0].damd)
911 | : -200);
912 | return 1;
913 | } else
914 | You("stiffen momentarily under %s gaze.",
915 | s_suffix(mon_nam(mtmp)));
916 | }
917 | if ((mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) {
918 | pline(
919 | "Gazing at the awake %s is not a very good idea.",
920 | l_monnam(mtmp));
921 | /* as if gazing at a sleeping anything is fruitful... */
922 | You("turn to stone...");
923 | killer_format = KILLED_BY;
924 | killer =
925 | "deliberately gazing at Medusa's hideous countenance";
926 | done(STONING);
927 | }
928 | }
929 | }
930 | }
931 | if (!looked) You("gaze at no place in particular.");
932 | return 1;
933 | }
934 |
935 | int
936 | dohide()
937 | {
938 | boolean ismimic = youmonst.data->mlet == S_MIMIC;
939 |
940 | if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) {
941 | You("are already hiding.");
942 | return(0);
943 | }
944 | if (ismimic) {
945 | /* should bring up a dialog "what would you like to imitate?" */
946 | youmonst.m_ap_type = M_AP_OBJECT;
947 | youmonst.mappearance = STRANGE_OBJECT;
948 | } else
949 | u.uundetected = 1;
950 | newsym(u.ux,u.uy);
951 | return(1);
952 | }
953 |
954 | int
955 | domindblast()
956 | {
957 | struct monst *mtmp, *nmon;
958 |
959 | if (u.uen < 10) {
960 | You("concentrate but lack the energy to maintain doing so.");
961 | return(0);
962 | }
963 | u.uen -= 10;
964 | flags.botl = 1;
965 |
966 | You("concentrate.");
967 | pline("A wave of psychic energy pours out.");
968 | for(mtmp=fmon; mtmp; mtmp = nmon) {
969 | int u_sen;
970 |
971 | nmon = mtmp->nmon;
972 | if (DEADMONSTER(mtmp))
973 | continue;
974 | if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
975 | continue;
976 | if(mtmp->mpeaceful)
977 | continue;
978 | u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
979 | if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) {
980 | You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
981 | u_sen ? "telepathy" :
982 | telepathic(mtmp->data) ? "latent telepathy" :
983 | "mind");
984 | mtmp->mhp -= rnd(15);
985 | if (mtmp->mhp <= 0)
986 | killed(mtmp);
987 | }
988 | }
989 | return 1;
990 | }
991 |
992 | STATIC_OVL void
993 | uunstick()
994 | {
995 | pline("%s is no longer in your clutches.", Monnam(u.ustuck));
996 | u.ustuck = 0;
997 | }
998 |
999 | STATIC_OVL void
1000 | skinback()
1001 | {
1002 | if (uskin) {
1003 | Your("skin returns to its original form.");
1004 | uarm = uskin;
1005 | uskin = (struct obj *)0;
1006 | /* undo save/restore hack */
1007 | uarm->owornmask &= ~I_SPECIAL;
1008 | }
1009 | }
1010 |
1011 | #endif /* OVLB */
1012 | #ifdef OVL1
1013 |
1014 | const char *
1015 | mbodypart(mon, part)
1016 | struct monst *mon;
1017 | int part;
1018 | {
1019 | static NEARDATA const char
1020 | *humanoid_parts[] = { "arm", "eye", "face", "finger",
1021 | "fingertip", "foot", "hand", "handed", "head", "leg",
1022 | "light headed", "neck", "spine", "toe", "hair", "blood", "lung"},
1023 | *jelly_parts[] = { "pseudopod", "dark spot", "front",
1024 | "pseudopod extension", "pseudopod extremity",
1025 | "pseudopod root", "grasp", "grasped", "cerebral area",
1026 | "lower pseudopod", "viscous", "middle", "surface",
1027 | "pseudopod extremity", "ripples", "juices", "surface" },
1028 | *animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip",
1029 | "rear claw", "foreclaw", "clawed", "head", "rear limb",
1030 | "light headed", "neck", "spine", "rear claw tip",
1031 | "fur", "blood", "lung" },
1032 | *horse_parts[] = { "foreleg", "eye", "face", "forehoof", "hoof tip",
1033 | "rear hoof", "foreclaw", "hooved", "head", "rear leg",
1034 | "light headed", "neck", "backbone", "rear hoof tip",
1035 | "mane", "blood", "lung" },
1036 | *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
1037 | "tentacle tip", "lower appendage", "tentacle", "tentacled",
1038 | "body", "lower tentacle", "rotational", "equator", "body",
1039 | "lower tentacle tip", "cilia", "life force", "retina" },
1040 | *fungus_parts[] = { "mycelium", "visual area", "front", "hypha",
1041 | "hypha", "root", "strand", "stranded", "cap area",
1042 | "rhizome", "sporulated", "stalk", "root", "rhizome tip",
1043 | "spores", "juices", "gill" },
1044 | *vortex_parts[] = { "region", "eye", "front", "minor current",
1045 | "minor current", "lower current", "swirl", "swirled",
1046 | "central core", "lower current", "addled", "center",
1047 | "currents", "edge", "currents", "life force", "center" },
1048 | *snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
1049 | "large scale tip", "rear region", "scale gap", "scale gapped",
1050 | "head", "rear region", "light headed", "neck", "length",
1051 | "rear scale", "scales", "blood", "lung" },
1052 | *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
1053 | "pelvic fin", "anal fin", "pectoral fin", "finned", "head", "peduncle",
1054 | "played out", "gills", "dorsal fin", "caudal fin",
1055 | "scales", "blood", "gill" };
1056 | /* claw attacks are overloaded in mons[]; most humanoids with
1057 | such attacks should still reference hands rather than claws */
1058 | static const char not_claws[] = {
1059 | S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL,
1060 | S_NYMPH, S_LEPRECHAUN, S_QUANTMECH, S_VAMPIRE,
1061 | S_ORC, S_GIANT, /* quest nemeses */
1062 | '\0' /* string terminator; assert( S_xxx != 0 ); */
1063 | };
1064 | struct permonst *mptr = mon->data;
1065 |
1066 | if (part == HAND || part == HANDED) { /* some special cases */
1067 | if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE ||
1068 | mptr->mlet == S_YETI)
1069 | return part == HAND ? "paw" : "pawed";
1070 | if (humanoid(mptr) && attacktype(mptr, AT_CLAW) &&
1071 | !index(not_claws, mptr->mlet) &&
1072 | mptr != &mons[PM_STONE_GOLEM] &&
1073 | mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS])
1074 | return part == HAND ? "claw" : "clawed";
1075 | }
1076 | if (mptr == &mons[PM_SHARK] && part == HAIR)
1077 | return "skin"; /* sharks don't have scales */
1078 | if (humanoid(mptr) &&
1079 | (part == ARM || part == FINGER || part == FINGERTIP ||
1080 | part == HAND || part == HANDED))
1081 | return humanoid_parts[part];
1082 | if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN ||
1083 | (mptr == &mons[PM_ROTHE] && part != HAIR))
1084 | return horse_parts[part];
1085 | if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
1086 | return fish_parts[part];
1087 | if (slithy(mptr))
1088 | return snake_parts[part];
1089 | if (mptr->mlet == S_EYE)
1090 | return sphere_parts[part];
1091 | if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING ||
1092 | mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH])
1093 | return jelly_parts[part];
1094 | if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL)
1095 | return vortex_parts[part];
1096 | if (mptr->mlet == S_FUNGUS)
1097 | return fungus_parts[part];
1098 | if (humanoid(mptr))
1099 | return humanoid_parts[part];
1100 | return animal_parts[part];
1101 | }
1102 |
1103 | const char *
1104 | body_part(part)
1105 | int part;
1106 | {
1107 | return mbodypart(&youmonst, part);
1108 | }
1109 |
1110 | #endif /* OVL1 */
1111 | #ifdef OVL0
1112 |
1113 | int
1114 | poly_gender()
1115 | {
1116 | /* Returns gender of polymorphed player; 0/1=same meaning as flags.female,
1117 | * 2=none.
1118 | */
1119 | if (is_neuter(youmonst.data) || !humanoid(youmonst.data)) return 2;
1120 | return flags.female;
1121 | }
1122 |
1123 | #endif /* OVL0 */
1124 | #ifdef OVLB
1125 |
1126 | void
1127 | ugolemeffects(damtype, dam)
1128 | int damtype, dam;
1129 | {
1130 | int heal = 0;
1131 | /* We won't bother with "slow"/"haste" since players do not
1132 | * have a monster-specific slow/haste so there is no way to
1133 | * restore the old velocity once they are back to human.
1134 | */
1135 | if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
1136 | return;
1137 | switch (damtype) {
1138 | case AD_ELEC: if (u.umonnum == PM_IRON_GOLEM)
1139 | heal = dam / 6; /* Approx 1 per die */
1140 | break;
1141 | case AD_FIRE: if (u.umonnum == PM_IRON_GOLEM)
1142 | heal = dam;
1143 | break;
1144 | }
1145 | if (heal && (u.mh < u.mhmax)) {
1146 | u.mh += heal;
1147 | if (u.mh > u.mhmax) u.mh = u.mhmax;
1148 | flags.botl = 1;
1149 | pline("Strangely, you feel better than before.");
1150 | exercise(A_STR, TRUE);
1151 | }
1152 | }
1153 |
1154 | STATIC_OVL int
1155 | armor_to_dragon(atyp)
1156 | int atyp;
1157 | {
1158 | switch(atyp) {
1159 | case GRAY_DRAGON_SCALE_MAIL:
1160 | case GRAY_DRAGON_SCALES:
1161 | return PM_GRAY_DRAGON;
1162 | case SILVER_DRAGON_SCALE_MAIL:
1163 | case SILVER_DRAGON_SCALES:
1164 | return PM_SILVER_DRAGON;
1165 | #if 0 /* DEFERRED */
1166 | case SHIMMERING_DRAGON_SCALE_MAIL:
1167 | case SHIMMERING_DRAGON_SCALES:
1168 | return PM_SHIMMERING_DRAGON;
1169 | #endif
1170 | case RED_DRAGON_SCALE_MAIL:
1171 | case RED_DRAGON_SCALES:
1172 | return PM_RED_DRAGON;
1173 | case ORANGE_DRAGON_SCALE_MAIL:
1174 | case ORANGE_DRAGON_SCALES:
1175 | return PM_ORANGE_DRAGON;
1176 | case WHITE_DRAGON_SCALE_MAIL:
1177 | case WHITE_DRAGON_SCALES:
1178 | return PM_WHITE_DRAGON;
1179 | case BLACK_DRAGON_SCALE_MAIL:
1180 | case BLACK_DRAGON_SCALES:
1181 | return PM_BLACK_DRAGON;
1182 | case BLUE_DRAGON_SCALE_MAIL:
1183 | case BLUE_DRAGON_SCALES:
1184 | return PM_BLUE_DRAGON;
1185 | case GREEN_DRAGON_SCALE_MAIL:
1186 | case GREEN_DRAGON_SCALES:
1187 | return PM_GREEN_DRAGON;
1188 | case YELLOW_DRAGON_SCALE_MAIL:
1189 | case YELLOW_DRAGON_SCALES:
1190 | return PM_YELLOW_DRAGON;
1191 | default:
1192 | return -1;
1193 | }
1194 | }
1195 |
1196 | #endif /* OVLB */
1197 |
1198 | /*polyself.c*/