/* SCCS Id: @(#)polyself.c 3.3 2000/07/14 */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ /* * Polymorph self routine. * * Note: the light source handling code assumes that both youmonst.m_id * and youmonst.mx will always remain 0 when it handles the case of the * player polymorphed into a light-emitting monster. */ #include "hack.h" #ifdef OVLB STATIC_DCL void FDECL(polyman, (const char *,const char *)); STATIC_DCL void NDECL(break_armor); STATIC_DCL void FDECL(drop_weapon,(int)); STATIC_DCL void NDECL(skinback); STATIC_DCL void NDECL(uunstick); STATIC_DCL int FDECL(armor_to_dragon,(int)); STATIC_DCL void NDECL(newman); /* update the youmonst.data structure pointer */ void set_uasmon() { set_mon_data(&youmonst, &mons[u.umonnum], 0); } /* make a (new) human out of the player */ STATIC_OVL void polyman(fmt, arg) const char *fmt, *arg; { boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow, was_mimicking_gold = (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == GOLD_PIECE); boolean was_blind = !!Blind; if (Upolyd) { u.acurr = u.macurr; /* restore old attribs */ u.amax = u.mamax; u.umonnum = u.umonster; flags.female = u.mfemale; } set_uasmon(); u.mh = u.mhmax = 0; u.mtimedone = 0; skinback(); u.uundetected = 0; if (sticky) uunstick(); find_ac(); if (was_mimicking_gold) { if (multi < 0) unmul(""); } else { /* * Clear any in-progress imitations -- the case where not a * mimic is handled above. * * Except, this is not complete if the hero ever gets the * chance to imitate anything, then s/he may be mimicing * gold, but not the way its done for eating a mimic. */ youmonst.m_ap_type = M_AP_NOTHING; } newsym(u.ux,u.uy); You(fmt, arg); /* check whether player foolishly genocided self while poly'd */ if ((mvitals[urole.malenum].mvflags & G_GENOD) || (urole.femalenum != NON_PM && (mvitals[urole.femalenum].mvflags & G_GENOD)) || (mvitals[urace.malenum].mvflags & G_GENOD) || (urace.femalenum != NON_PM && (mvitals[urace.femalenum].mvflags & G_GENOD))) { /* intervening activity might have clobbered genocide info */ killer = delayed_killer; if (!killer || !strstri(killer, "genocid")) { killer_format = KILLED_BY; killer = "self-genocide"; } done(GENOCIDED); } if (was_blind && !Blind) { /* reverting from eyeless */ Blinded = 1L; make_blinded(0L, TRUE); /* remove blindness */ } if(!Levitation && !u.ustuck && (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) spoteffects(TRUE); see_monsters(); } void change_sex() { /* setting u.umonster for caveman/cavewoman or priest/priestess swap unintentionally makes `Upolyd' appear to be true */ boolean already_polyd = (boolean) Upolyd; /* Some monsters are always of one sex and their sex can't be changed */ /* succubi/incubi are handled below */ if (u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS && !is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data)) flags.female = !flags.female; if (already_polyd) /* poly'd: also change saved sex */ u.mfemale = !u.mfemale; max_rank_sz(); /* [this appears to be superfluous] */ if (flags.female && urole.name.f) Strcpy(pl_character, urole.name.f); else Strcpy(pl_character, urole.name.m); u.umonster = ((already_polyd ? u.mfemale : flags.female) && urole.femalenum != NON_PM) ? urole.femalenum : urole.malenum; if (!already_polyd) { u.umonnum = u.umonster; } else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) { /* change monster type to match new sex */ u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS; set_uasmon(); } } STATIC_OVL void newman() { int tmp, tmp2; if (!rn2(10)) change_sex(); tmp = u.uhpmax; tmp2 = u.ulevel; u.ulevel = u.ulevel + rn1(5, -2); if (u.ulevel > 127 || u.ulevel < 1) u.ulevel = 1; if (u.ulevel > MAXULEV) u.ulevel = MAXULEV; if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel; adjabil(tmp2, (int)u.ulevel); reset_rndmonst(NON_PM); /* new monster generation criteria */ /* random experience points for the new experience level */ u.uexp = rndexp(); /* u.uhpmax * u.ulevel / tmp2: proportionate hit points to new level * -10 and +10: don't apply proportionate HP to 10 of a starting * character's hit points (since a starting character's hit points * are not on the same scale with hit points obtained through level * gain) * 9 - rn2(19): random change of -9 to +9 hit points */ #ifndef LINT u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / tmp2 + 10) + (9 - rn2(19)); #endif #ifdef LINT u.uhp = u.uhp + tmp; #else u.uhp = u.uhp * (long)u.uhpmax/tmp; #endif tmp = u.uenmax; #ifndef LINT u.uenmax = u.uenmax * (long)u.ulevel / tmp2 + 9 - rn2(19); #endif if (u.uenmax < 0) u.uenmax = 0; #ifndef LINT u.uen = (tmp ? u.uen * (long)u.uenmax / tmp : u.uenmax); #endif redist_attr(); u.uhunger = rn1(500,500); newuhs(FALSE); if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL); Stoned = 0; delayed_killer = 0; if (u.uhp <= 0 || u.uhpmax <= 0) { if (Polymorph_control) { if (u.uhp <= 0) u.uhp = 1; if (u.uhpmax <= 0) u.uhpmax = 1; } else { Your("new form doesn't seem healthy enough to survive."); killer_format = KILLED_BY_AN; killer="unsuccessful polymorph"; done(DIED); } } polyman("feel like a new %s!", (flags.female && urace.individual.f) ? urace.individual.f : (urace.individual.m) ? urace.individual.m : urace.noun); if (Slimed) { Your("body transforms, but there is still slime on you."); Slimed = 10L; } flags.botl = 1; see_monsters(); (void) encumber_msg(); } void polyself() { char buf[BUFSZ]; int old_light, new_light; int mntmp = NON_PM; int tries=0; boolean draconian = (uarm && uarm->otyp >= GRAY_DRAGON_SCALE_MAIL && uarm->otyp <= YELLOW_DRAGON_SCALES); boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data)); boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT); if(!Polymorph_control && !draconian && !iswere && !isvamp) { if (rn2(20) > ACURR(A_CON)) { You(shudder_for_moment); losehp(rnd(30), "system shock", KILLED_BY_AN); exercise(A_CON, FALSE); return; } } old_light = Upolyd ? emits_light(youmonst.data) : 0; if (Polymorph_control) { do { getlin("Become what kind of monster? [type the name]", buf); mntmp = name_to_mon(buf); if (mntmp < LOW_PM) pline("I've never heard of such monsters."); /* Note: humans are illegal as monsters, but an * illegal monster forces newman(), which is what we * want if they specified a human.... */ else if (!polyok(&mons[mntmp]) && !your_race(&mons[mntmp])) You("cannot polymorph into that."); else break; } while(++tries < 5); if (tries==5) pline(thats_enough_tries); /* allow skin merging, even when polymorph is controlled */ if (draconian && (mntmp == armor_to_dragon(uarm->otyp) || tries == 5)) goto do_merge; } else if (draconian || iswere || isvamp) { /* special changes that don't require polyok() */ if (draconian) { do_merge: mntmp = armor_to_dragon(uarm->otyp); if (!(mvitals[mntmp].mvflags & G_GENOD)) { /* allow G_EXTINCT */ You("merge with your scaly armor."); uskin = uarm; uarm = (struct obj *)0; /* save/restore hack */ uskin->owornmask |= I_SPECIAL; } } else if (iswere) { if (is_were(youmonst.data)) mntmp = PM_HUMAN; /* Illegal; force newman() */ else mntmp = u.ulycn; } else { if (youmonst.data->mlet == S_VAMPIRE) mntmp = PM_VAMPIRE_BAT; else mntmp = PM_VAMPIRE; } /* if polymon fails, "you feel" message has been given so don't follow up with another polymon or newman */ if (mntmp == PM_HUMAN) newman(); /* werecritter */ else (void) polymon(mntmp); goto made_change; /* maybe not, but this is right anyway */ } if (mntmp < LOW_PM) { tries = 0; do { /* randomly pick an "ordinary" monster */ mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM); } while((!polyok(&mons[mntmp]) || is_placeholder(&mons[mntmp])) && tries++ < 200); } /* The below polyok() fails either if everything is genocided, or if * we deliberately chose something illegal to force newman(). */ if (!polyok(&mons[mntmp]) || !rn2(5)) newman(); else if(!polymon(mntmp)) return; if (!uarmg) selftouch("No longer petrify-resistant, you"); made_change: new_light = Upolyd ? emits_light(youmonst.data) : 0; if (old_light != new_light) { if (old_light) del_light_source(LS_MONSTER, (genericptr_t)&youmonst); if (new_light == 1) ++new_light; /* otherwise it's undetectable */ if (new_light) new_light_source(u.ux, u.uy, new_light, LS_MONSTER, (genericptr_t)&youmonst); } } /* (try to) make a mntmp monster out of the player */ int polymon(mntmp) /* returns 1 if polymorph successful */ int mntmp; { boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow, was_blind = !!Blind, dochange = FALSE; int mlvl; if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */ You_feel("rather %s-ish.",mons[mntmp].mname); exercise(A_WIS, TRUE); return(0); } /* KMH, conduct */ u.uconduct.polyselfs++; if (!Upolyd) { /* Human to monster; save human stats */ u.macurr = u.acurr; u.mamax = u.amax; u.mfemale = flags.female; } else { /* Monster to monster; restore human stats, to be * immediately changed to provide stats for the new monster */ u.acurr = u.macurr; u.amax = u.mamax; flags.female = u.mfemale; } if (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == GOLD_PIECE) { /* stop mimicking gold immediately */ if (multi < 0) unmul(""); } if (is_male(&mons[mntmp])) { if(flags.female) dochange = TRUE; } else if (is_female(&mons[mntmp])) { if(!flags.female) dochange = TRUE; } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) { if(!rn2(10)) dochange = TRUE; } if (dochange) { flags.female = !flags.female; You("%s %s%s!", (u.umonnum != mntmp) ? "turn into a" : "feel like a new", (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" : flags.female ? "female " : "male ", mons[mntmp].mname); } else { if (u.umonnum != mntmp) You("turn into %s!", an(mons[mntmp].mname)); else You_feel("like a new %s!", mons[mntmp].mname); } if (Stoned && poly_when_stoned(&mons[mntmp])) { /* poly_when_stoned already checked stone golem genocide */ You("turn to stone!"); mntmp = PM_STONE_GOLEM; Stoned = 0; delayed_killer = 0; } u.mtimedone = rn1(500, 500); u.umonnum = mntmp; set_uasmon(); /* New stats for monster, to last only as long as polymorphed. * Currently only strength gets changed. */ if(strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100); if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */ Stoned = 0; delayed_killer = 0; You("no longer seem to be petrifying."); } if (Sick_resistance && Sick) { make_sick(0L, (char *) 0, FALSE, SICK_ALL); You("no longer feel sick."); } if (Slimed) { if (mntmp == PM_FIRE_VORTEX || mntmp == PM_FIRE_ELEMENTAL) { pline_The("slime burns away!"); Slimed = 0; } else if (mntmp == PM_GREEN_SLIME) { /* do it silently */ Slimed = 0; } } if (nohands(youmonst.data)) Glib = 0; /* mlvl = adj_lev(&mons[mntmp]); * We can't do the above, since there's no such thing as an * "experience level of you as a monster" for a polymorphed character. */ mlvl = (int)mons[mntmp].mlevel; if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) { u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + d(mlvl,4)); } else if (is_golem(youmonst.data)) { u.mhmax = golemhp(mntmp); } else { if (!mlvl) u.mhmax = rnd(4); else u.mhmax = d(mlvl, 8); if (is_home_elemental(&mons[mntmp])) u.mhmax *= 3; } u.mh = u.mhmax; if (u.ulevel < mlvl) { /* Low level characters can't become high level monsters for long */ #ifdef DUMB /* DRS/NS 2.2.6 messes up -- Peter Kendell */ int mtd = u.mtimedone, ulv = u.ulevel; u.mtimedone = mtd * ulv / mlvl; #else u.mtimedone = u.mtimedone * u.ulevel / mlvl; #endif } if (uskin && mntmp != armor_to_dragon(uskin->otyp)) skinback(); break_armor(); drop_weapon(1); if (hides_under(youmonst.data)) u.uundetected = OBJ_AT(u.ux, u.uy); else if (youmonst.data->mlet == S_EEL) u.uundetected = is_pool(u.ux, u.uy); else u.uundetected = 0; if (was_blind && !Blind) { /* previous form was eyeless */ Blinded = 1L; make_blinded(0L, TRUE); /* remove blindness */ } newsym(u.ux,u.uy); /* Change symbol */ if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0; else if (sticky && !sticks(youmonst.data)) uunstick(); #ifdef STEED if (u.usteed) { if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) { char buf[BUFSZ]; pline("No longer petrifying-resistant, you touch %s.", mon_nam(u.usteed)); Sprintf(buf, "riding %s", an(u.usteed->data->mname)); instapetrify(buf); } if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY); } #endif if (flags.verbose) { static const char use_thec[] = "Use the command #%s to %s."; static const char monsterc[] = "monster"; if (can_breathe(youmonst.data)) pline(use_thec,monsterc,"use your breath weapon"); if (attacktype(youmonst.data, AT_SPIT)) pline(use_thec,monsterc,"spit venom"); if (youmonst.data->mlet == S_NYMPH) pline(use_thec,monsterc,"remove an iron ball"); if (youmonst.data->mlet == S_UMBER) pline(use_thec,monsterc,"confuse monsters"); if (is_hider(youmonst.data)) pline(use_thec,monsterc,"hide"); if (is_were(youmonst.data)) pline(use_thec,monsterc,"summon help"); if (webmaker(youmonst.data)) pline(use_thec,monsterc,"spin a web"); if (u.umonnum == PM_GREMLIN) pline(use_thec,monsterc,"multiply in a fountain"); if (is_unicorn(youmonst.data)) pline(use_thec,monsterc,"use your horn"); if (is_mind_flayer(youmonst.data)) pline(use_thec,monsterc,"emit a mental blast"); if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */ pline(use_thec,monsterc,"shriek"); if (lays_eggs(youmonst.data) && flags.female) pline(use_thec,"sit","lay an egg"); } /* you now know what an egg of your type looks like */ if (lays_eggs(youmonst.data)) { learn_egg_type(u.umonnum); /* make queen bees recognize killer bee eggs */ learn_egg_type(egg_type_from_parent(u.umonnum, TRUE)); } find_ac(); if((!Levitation && !u.ustuck && !Flying && (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) || (Underwater && !Swimming)) spoteffects(TRUE); if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) { u.utrap = 0; pline_The("rock seems to no longer trap you."); } else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) { u.utrap = 0; pline_The("lava now feels soothing."); } if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) { if (Punished) { You("slip out of the iron chain."); unpunish(); } } if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) && (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) || (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) { You("are no longer stuck in the %s.", u.utraptype == TT_WEB ? "web" : "bear trap"); /* probably should burn webs too if PM_FIRE_ELEMENTAL */ u.utrap = 0; } if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) { You("orient yourself on the web."); u.utrap = 0; } flags.botl = 1; vision_full_recalc = 1; see_monsters(); exercise(A_CON, FALSE); exercise(A_WIS, TRUE); (void) encumber_msg(); return(1); } STATIC_OVL void break_armor() { register struct obj *otmp; if (breakarm(youmonst.data)) { if ((otmp = uarm) != 0) { if (donning(otmp)) cancel_don(); You("break out of your armor!"); exercise(A_STR, FALSE); (void) Armor_gone(); useup(otmp); } if ((otmp = uarmc) != 0) { if(otmp->oartifact) { Your("cloak falls off!"); (void) Cloak_off(); dropx(otmp); } else { Your("cloak tears apart!"); (void) Cloak_off(); useup(otmp); } } #ifdef TOURIST if (uarmu) { Your("shirt rips to shreds!"); useup(uarmu); } #endif } else if (sliparm(youmonst.data)) { if ((otmp = uarm) != 0) { if (donning(otmp)) cancel_don(); Your("armor falls around you!"); (void) Armor_gone(); dropx(otmp); } if ((otmp = uarmc) != 0) { if (is_whirly(youmonst.data)) Your("cloak falls, unsupported!"); else You("shrink out of your cloak!"); (void) Cloak_off(); dropx(otmp); } #ifdef TOURIST if ((otmp = uarmu) != 0) { if (is_whirly(youmonst.data)) You("seep right through your shirt!"); else You("become much too small for your shirt!"); setworn((struct obj *)0, otmp->owornmask & W_ARMU); dropx(otmp); } #endif } if (nohands(youmonst.data) || verysmall(youmonst.data)) { if ((otmp = uarmg) != 0) { if (donning(otmp)) cancel_don(); /* Drop weapon along with gloves */ You("drop your gloves%s!", uwep ? " and weapon" : ""); drop_weapon(0); (void) Gloves_off(); dropx(otmp); } if ((otmp = uarms) != 0) { You("can no longer hold your shield!"); (void) Shield_off(); dropx(otmp); } if ((otmp = uarmh) != 0) { if (donning(otmp)) cancel_don(); Your("helmet falls to the %s!", surface(u.ux, u.uy)); (void) Helmet_off(); dropx(otmp); } } if (nohands(youmonst.data) || verysmall(youmonst.data) || slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) { if ((otmp = uarmf) != 0) { if (donning(otmp)) cancel_don(); if (is_whirly(youmonst.data)) Your("boots fall away!"); else Your("boots %s off your feet!", verysmall(youmonst.data) ? "slide" : "are pushed"); (void) Boots_off(); dropx(otmp); } } } STATIC_OVL void drop_weapon(alone) int alone; { struct obj *otmp; if ((otmp = uwep) != 0) { /* !alone check below is currently superfluous but in the * future it might not be so if there are monsters which cannot * wear gloves but can wield weapons */ if (!alone || cantwield(youmonst.data)) { struct obj *wep = uwep; if (alone) You("find you must drop your weapon%s!", u.twoweap ? "s" : ""); uwepgone(); if (!wep->cursed || wep->otyp != LOADSTONE) dropx(otmp); untwoweapon(); } } } void rehumanize() { /* You can't revert back while unchanging */ if (Unchanging && (u.mh < 1)) { killer_format = NO_KILLER_PREFIX; killer = "killed while stuck in creature form"; done(DIED); } if (emits_light(youmonst.data)) del_light_source(LS_MONSTER, (genericptr_t)&youmonst); polyman("return to %s form!", urace.adj); if (u.uhp < 1) { char kbuf[256]; Sprintf(kbuf, "reverting to unhealthy %s form", urace.adj); killer_format = KILLED_BY; killer = kbuf; done(DIED); } if (!uarmg) selftouch("No longer petrify-resistant, you"); nomul(0); flags.botl = 1; vision_full_recalc = 1; (void) encumber_msg(); } int dobreathe() { if (Strangled) { You_cant("breathe. Sorry."); return(0); } if (u.uen < 15) { You("don't have enough energy to breathe!"); return(0); } u.uen -= 15; flags.botl = 1; if (!getdir((char *)0)) return(0); else { register struct attack *mattk; register int i; for(i = 0; i < NATTK; i++) { mattk = &(youmonst.data->mattk[i]); if(mattk->aatyp == AT_BREA) break; } buzz((int) (20 + mattk->adtyp-1), (int)mattk->damn, u.ux, u.uy, u.dx, u.dy); } return(1); } int dospit() { struct obj *otmp; if (!getdir((char *)0)) return(0); otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM, TRUE, FALSE); otmp->spe = 1; /* to indicate it's yours */ throwit(otmp, 0L); return(1); } int doremove() { if (!Punished) { You("are not chained to anything!"); return(0); } unpunish(); return(1); } int dospinweb() { register struct trap *ttmp = t_at(u.ux,u.uy); if (Levitation || Is_airlevel(&u.uz) || Underwater || Is_waterlevel(&u.uz)) { You("must be on the ground to spin a web."); return(0); } if (u.uswallow) { You("release web fluid inside %s.", mon_nam(u.ustuck)); if (is_animal(u.ustuck->data)) { expels(u.ustuck, u.ustuck->data, TRUE); return(0); } if (is_whirly(u.ustuck->data)) { int i; for (i = 0; i < NATTK; i++) if (u.ustuck->data->mattk[i].aatyp == AT_ENGL) break; if (i == NATTK) impossible("Swallower has no engulfing attack?"); else { char sweep[30]; sweep[0] = '\0'; switch(u.ustuck->data->mattk[i].adtyp) { case AD_FIRE: Strcpy(sweep, "ignites and "); break; case AD_ELEC: Strcpy(sweep, "fries and "); break; case AD_COLD: Strcpy(sweep, "freezes, shatters and "); break; } pline_The("web %sis swept away!", sweep); } return(0); } /* default: a nasty jelly-like creature */ pline_The("web dissolves into %s.", mon_nam(u.ustuck)); return(0); } if (u.utrap) { You("cannot spin webs while stuck in a trap."); return(0); } exercise(A_DEX, TRUE); if (ttmp) switch (ttmp->ttyp) { case PIT: case SPIKED_PIT: You("spin a web, covering up the pit."); deltrap(ttmp); bury_objs(u.ux, u.uy); if (Invisible) newsym(u.ux, u.uy); return(1); case SQKY_BOARD: pline_The("squeaky board is muffled."); deltrap(ttmp); if (Invisible) newsym(u.ux, u.uy); return(1); case TELEP_TRAP: case LEVEL_TELEP: case MAGIC_PORTAL: Your("webbing vanishes!"); return(0); case WEB: You("make the web thicker."); return(1); case HOLE: case TRAPDOOR: You("web over the %s.", (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole"); deltrap(ttmp); if (Invisible) newsym(u.ux, u.uy); return 1; case ARROW_TRAP: case DART_TRAP: case BEAR_TRAP: case LANDMINE: case SLP_GAS_TRAP: case RUST_TRAP: case MAGIC_TRAP: case ANTI_MAGIC: case POLY_TRAP: You("have triggered a trap!"); dotrap(ttmp); return(1); default: impossible("Webbing over trap type %d?", ttmp->ttyp); return(0); } ttmp = maketrap(u.ux, u.uy, WEB); if (ttmp) { ttmp->tseen = 1; ttmp->madeby_u = 1; } if (Invisible) newsym(u.ux, u.uy); return(1); } int dosummon() { if (u.uen < 10) { You("lack the energy to send forth a call for help!"); return(0); } u.uen -= 10; flags.botl = 1; You("call upon your brethren for help!"); exercise(A_WIS, TRUE); if (!were_summon(youmonst.data,TRUE)) pline("But none arrive."); return(1); } int doconfuse() { register struct monst *mtmp; int looked = 0; char qbuf[QBUFSZ]; if (Blind) { You_cant("see anything to gaze at."); return 0; } if (u.uen < 15) { You("lack the energy to use your special gaze!"); return(0); } u.uen -= 15; flags.botl = 1; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (canseemon(mtmp)) { looked++; if (Invis && !perceives(mtmp->data)) pline("%s seems not to notice your gaze.", Monnam(mtmp)); else if (mtmp->minvis && !See_invisible) You_cant("see where to gaze at %s.", Monnam(mtmp)); else if (mtmp->m_ap_type == M_AP_FURNITURE || mtmp->m_ap_type == M_AP_OBJECT) { looked--; continue; } else if (flags.safe_dog && !Confusion && !Hallucination && mtmp->mtame) { You("avoid gazing at %s.", y_monnam(mtmp)); } else { if (flags.confirm && mtmp->mpeaceful && !Confusion && !Hallucination) { Sprintf(qbuf, "Really confuse %s?", mon_nam(mtmp)); if (yn(qbuf) != 'y') continue; setmangry(mtmp); } if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping || !mtmp->mcansee || !haseyes(mtmp->data)) { looked--; continue; } if (!mon_reflects(mtmp,"Your gaze is reflected by %s %s.")){ if (!mtmp->mconf) Your("gaze confuses %s!", mon_nam(mtmp)); else pline("%s is getting more and more confused.", Monnam(mtmp)); mtmp->mconf = 1; } if ((mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) { if (!Free_action) { You("are frozen by %s gaze!", s_suffix(mon_nam(mtmp))); nomul((u.ulevel > 6 || rn2(4)) ? -d((int)mtmp->m_lev+1, (int)mtmp->data->mattk[0].damd) : -200); return 1; } else You("stiffen momentarily under %s gaze.", s_suffix(mon_nam(mtmp))); } if ((mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) { pline( "Gazing at the awake %s is not a very good idea.", l_monnam(mtmp)); /* as if gazing at a sleeping anything is fruitful... */ You("turn to stone..."); killer_format = KILLED_BY; killer = "deliberately gazing at Medusa's hideous countenance"; done(STONING); } } } } if (!looked) You("gaze at no place in particular."); return 1; } int dohide() { boolean ismimic = youmonst.data->mlet == S_MIMIC; if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) { You("are already hiding."); return(0); } if (ismimic) { /* should bring up a dialog "what would you like to imitate?" */ youmonst.m_ap_type = M_AP_OBJECT; youmonst.mappearance = STRANGE_OBJECT; } else u.uundetected = 1; newsym(u.ux,u.uy); return(1); } int domindblast() { struct monst *mtmp, *nmon; if (u.uen < 10) { You("concentrate but lack the energy to maintain doing so."); return(0); } u.uen -= 10; flags.botl = 1; You("concentrate."); pline("A wave of psychic energy pours out."); for(mtmp=fmon; mtmp; mtmp = nmon) { int u_sen; nmon = mtmp->nmon; if (DEADMONSTER(mtmp)) continue; if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM) continue; if(mtmp->mpeaceful) continue; u_sen = telepathic(mtmp->data) && !mtmp->mcansee; if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) { You("lock in on %s %s.", s_suffix(mon_nam(mtmp)), u_sen ? "telepathy" : telepathic(mtmp->data) ? "latent telepathy" : "mind"); mtmp->mhp -= rnd(15); if (mtmp->mhp <= 0) killed(mtmp); } } return 1; } STATIC_OVL void uunstick() { pline("%s is no longer in your clutches.", Monnam(u.ustuck)); u.ustuck = 0; } STATIC_OVL void skinback() { if (uskin) { Your("skin returns to its original form."); uarm = uskin; uskin = (struct obj *)0; /* undo save/restore hack */ uarm->owornmask &= ~I_SPECIAL; } } #endif /* OVLB */ #ifdef OVL1 const char * mbodypart(mon, part) struct monst *mon; int part; { static NEARDATA const char *humanoid_parts[] = { "arm", "eye", "face", "finger", "fingertip", "foot", "hand", "handed", "head", "leg", "light headed", "neck", "spine", "toe", "hair", "blood", "lung"}, *jelly_parts[] = { "pseudopod", "dark spot", "front", "pseudopod extension", "pseudopod extremity", "pseudopod root", "grasp", "grasped", "cerebral area", "lower pseudopod", "viscous", "middle", "surface", "pseudopod extremity", "ripples", "juices", "surface" }, *animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip", "rear claw", "foreclaw", "clawed", "head", "rear limb", "light headed", "neck", "spine", "rear claw tip", "fur", "blood", "lung" }, *horse_parts[] = { "foreleg", "eye", "face", "forehoof", "hoof tip", "rear hoof", "foreclaw", "hooved", "head", "rear leg", "light headed", "neck", "backbone", "rear hoof tip", "mane", "blood", "lung" }, *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle", "tentacle tip", "lower appendage", "tentacle", "tentacled", "body", "lower tentacle", "rotational", "equator", "body", "lower tentacle tip", "cilia", "life force", "retina" }, *fungus_parts[] = { "mycelium", "visual area", "front", "hypha", "hypha", "root", "strand", "stranded", "cap area", "rhizome", "sporulated", "stalk", "root", "rhizome tip", "spores", "juices", "gill" }, *vortex_parts[] = { "region", "eye", "front", "minor current", "minor current", "lower current", "swirl", "swirled", "central core", "lower current", "addled", "center", "currents", "edge", "currents", "life force", "center" }, *snake_parts[] = { "vestigial limb", "eye", "face", "large scale", "large scale tip", "rear region", "scale gap", "scale gapped", "head", "rear region", "light headed", "neck", "length", "rear scale", "scales", "blood", "lung" }, *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary", "pelvic fin", "anal fin", "pectoral fin", "finned", "head", "peduncle", "played out", "gills", "dorsal fin", "caudal fin", "scales", "blood", "gill" }; /* claw attacks are overloaded in mons[]; most humanoids with such attacks should still reference hands rather than claws */ static const char not_claws[] = { S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL, S_NYMPH, S_LEPRECHAUN, S_QUANTMECH, S_VAMPIRE, S_ORC, S_GIANT, /* quest nemeses */ '\0' /* string terminator; assert( S_xxx != 0 ); */ }; struct permonst *mptr = mon->data; if (part == HAND || part == HANDED) { /* some special cases */ if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE || mptr->mlet == S_YETI) return part == HAND ? "paw" : "pawed"; if (humanoid(mptr) && attacktype(mptr, AT_CLAW) && !index(not_claws, mptr->mlet) && mptr != &mons[PM_STONE_GOLEM] && mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS]) return part == HAND ? "claw" : "clawed"; } if (mptr == &mons[PM_SHARK] && part == HAIR) return "skin"; /* sharks don't have scales */ if (humanoid(mptr) && (part == ARM || part == FINGER || part == FINGERTIP || part == HAND || part == HANDED)) return humanoid_parts[part]; if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN || (mptr == &mons[PM_ROTHE] && part != HAIR)) return horse_parts[part]; if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH]) return fish_parts[part]; if (slithy(mptr)) return snake_parts[part]; if (mptr->mlet == S_EYE) return sphere_parts[part]; if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING || mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH]) return jelly_parts[part]; if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL) return vortex_parts[part]; if (mptr->mlet == S_FUNGUS) return fungus_parts[part]; if (humanoid(mptr)) return humanoid_parts[part]; return animal_parts[part]; } const char * body_part(part) int part; { return mbodypart(&youmonst, part); } #endif /* OVL1 */ #ifdef OVL0 int poly_gender() { /* Returns gender of polymorphed player; 0/1=same meaning as flags.female, * 2=none. */ if (is_neuter(youmonst.data) || !humanoid(youmonst.data)) return 2; return flags.female; } #endif /* OVL0 */ #ifdef OVLB void ugolemeffects(damtype, dam) int damtype, dam; { int heal = 0; /* We won't bother with "slow"/"haste" since players do not * have a monster-specific slow/haste so there is no way to * restore the old velocity once they are back to human. */ if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM) return; switch (damtype) { case AD_ELEC: if (u.umonnum == PM_IRON_GOLEM) heal = dam / 6; /* Approx 1 per die */ break; case AD_FIRE: if (u.umonnum == PM_IRON_GOLEM) heal = dam; break; } if (heal && (u.mh < u.mhmax)) { u.mh += heal; if (u.mh > u.mhmax) u.mh = u.mhmax; flags.botl = 1; pline("Strangely, you feel better than before."); exercise(A_STR, TRUE); } } STATIC_OVL int armor_to_dragon(atyp) int atyp; { switch(atyp) { case GRAY_DRAGON_SCALE_MAIL: case GRAY_DRAGON_SCALES: return PM_GRAY_DRAGON; case SILVER_DRAGON_SCALE_MAIL: case SILVER_DRAGON_SCALES: return PM_SILVER_DRAGON; #if 0 /* DEFERRED */ case SHIMMERING_DRAGON_SCALE_MAIL: case SHIMMERING_DRAGON_SCALES: return PM_SHIMMERING_DRAGON; #endif case RED_DRAGON_SCALE_MAIL: case RED_DRAGON_SCALES: return PM_RED_DRAGON; case ORANGE_DRAGON_SCALE_MAIL: case ORANGE_DRAGON_SCALES: return PM_ORANGE_DRAGON; case WHITE_DRAGON_SCALE_MAIL: case WHITE_DRAGON_SCALES: return PM_WHITE_DRAGON; case BLACK_DRAGON_SCALE_MAIL: case BLACK_DRAGON_SCALES: return PM_BLACK_DRAGON; case BLUE_DRAGON_SCALE_MAIL: case BLUE_DRAGON_SCALES: return PM_BLUE_DRAGON; case GREEN_DRAGON_SCALE_MAIL: case GREEN_DRAGON_SCALES: return PM_GREEN_DRAGON; case YELLOW_DRAGON_SCALE_MAIL: case YELLOW_DRAGON_SCALES: return PM_YELLOW_DRAGON; default: return -1; } } #endif /* OVLB */ /*polyself.c*/