/* Returns "mortal", "creature" (if allow_creature), or a more specific term. This is mostly used by immortal/unalive entities when talking to other entities. If they're talking /down/, "creature" is normally inappropriate (e.g. "laughing at cowardly creatures"). If they're merely addressing the permonst in question, "creature" is typically fine (e.g. "Hark, creature!"). Undead can be addressed as "creature"; constructs and demons won't be. */ const char * mortal_or_creature(const struct permonst *data, boolean allow_creature) { if (data->mlet == S_HUMAN) return "mortal"; else if (allow_creature && (!nonliving(data) || is_undead(data)) && !is_demon(data)) return "creature"; else if (!nonliving(data) && !is_demon(data)) return "mortal"; else if (is_demon(data) || monsndx(data) == PM_MANES) return "demon"; else if (nonliving(data) && !is_undead(data)) return "construct"; /* golems and vortices */ else if (data->mlet == S_LICH) return "lich"; else if (data->mlet == S_MUMMY) return "mummy"; else if (data->mlet == S_VAMPIRE) return "vampire"; else if (data->mlet == S_ZOMBIE && monsndx(data) != PM_GHOUL && monsndx(data) != PM_SKELETON) return "zombie"; else return data->mname; }
int rndmonnum(void) /* select a random, common monster type */ { register struct permonst *ptr; register int i; /* Plan A: get a level-appropriate common monster */ ptr = rndmonst(); if (ptr) return(monsndx(ptr)); /* Plan B: get any common proper monster */ int count = 0; do { i = rn1(SPECIAL_PM - LOW_PM, LOW_PM); ptr = &mons[i]; count++; } while (((ptr->geno & G_NOGEN) || prohibited_by_generation_flags(ptr)) && count < 10000); /* Plan C: get any common monster */ do { i = rn1(SPECIAL_PM - LOW_PM, LOW_PM); ptr = &mons[i]; count++; } while(ptr->geno & G_NOGEN); return(i); }
/* return a phrase describing the effect of fire attack on a type of monster */ const char *on_fire(const struct permonst *mptr, const struct attack *mattk) { const char *what; switch (monsndx(mptr)) { case PM_FLAMING_SPHERE: case PM_FIRE_VORTEX: case PM_FIRE_ELEMENTAL: case PM_SALAMANDER: what = "already on fire"; break; case PM_WATER_ELEMENTAL: case PM_FOG_CLOUD: case PM_STEAM_VORTEX: what = "boiling"; break; case PM_ICE_VORTEX: case PM_GLASS_GOLEM: what = "melting"; break; case PM_STONE_GOLEM: case PM_CLAY_GOLEM: case PM_GOLD_GOLEM: case PM_AIR_ELEMENTAL: case PM_EARTH_ELEMENTAL: case PM_DUST_VORTEX: case PM_ENERGY_VORTEX: what = "heating up"; break; default: what = (mattk->aatyp == AT_HUGS) ? "being roasted" : "on fire"; break; } return what; }
void new_were(struct monst *mon) { int pm; pm = counter_were(monsndx(mon->data)); if (!pm) { impossible("unknown lycanthrope %s.", mon->data->mname); return; } if (canseemon(mon) && !Hallucination) pline("%s changes into a %s.", Monnam(mon), is_human(&mons[pm]) ? "human" : mons[pm].mname+4); set_mon_data(mon, &mons[pm], 0); if (mon->msleeping || !mon->mcanmove) { /* transformation wakens and/or revitalizes */ mon->msleeping = 0; mon->mfrozen = 0; /* not asleep or paralyzed */ mon->mcanmove = 1; } /* regenerate by 1/4 of the lost hit points */ mon->mhp += (mon->mhpmax - mon->mhp) / 4; newsym(mon->mx,mon->my); mon_break_armor(mon, FALSE); possibly_unwield(mon, FALSE); }
int lminion(void) { int tryct; const struct permonst *ptr; for (tryct = 0; tryct < 20; tryct++) { ptr = mkclass(&u.uz, S_ANGEL, 0); if (ptr && !is_lord(ptr)) return monsndx(ptr); } return NON_PM; }
int ndemon(const d_level * dlev, aligntyp atyp) { int tryct; const struct permonst *ptr; for (tryct = 0; tryct < 20; tryct++) { ptr = mkclass(dlev, S_DEMON, 0); if (ptr && is_ndemon(ptr) && (atyp == A_NONE || sgn(ptr->maligntyp) == sgn(atyp))) return monsndx(ptr); } return NON_PM; }
/* select a random, common monster type */ int rndmonnum(const d_level *dlev) { const struct permonst *ptr; int i; /* Plan A: get a level-appropriate common monster */ ptr = rndmonst(dlev); if (ptr) return monsndx(ptr); /* Plan B: get any common monster */ do { i = rn1(SPECIAL_PM - LOW_PM, LOW_PM); ptr = &mons[i]; } while ((ptr->geno & G_NOGEN) || (!In_hell(dlev) && (ptr->geno & G_HELL))); return i; }
/* number of horns this type of monster has on its head */ int num_horns(const struct permonst *ptr) { switch (monsndx(ptr)) { case PM_HORNED_DEVIL: /* ? "more than one" */ case PM_MINOTAUR: case PM_ASMODEUS: case PM_BALROG: return 2; case PM_WHITE_UNICORN: case PM_GRAY_UNICORN: case PM_BLACK_UNICORN: case PM_KI_RIN: return 1; default: break; } return 0; }
/* * OEXTRA note: Passing mtmp causes mtraits to be saved * even if ptr passed as well, but ptr is always used for * the corpse type (corpsenm). That allows the corpse type * to be different from the original monster, * i.e. vampire -> human corpse * yet still allow restoration of the original monster upon * resurrection. */ struct obj * mkcorpstat(int objtype, struct monst *mtmp, struct permonst *ptr, int x, int y, boolean init) /* CORPSE or STATUE */ { register struct obj *otmp; if (objtype != CORPSE && objtype != STATUE) warning("making corpstat type %d", objtype); if (x == 0 && y == 0) { /* special case - random placement */ otmp = mksobj(objtype, init, FALSE); if (otmp) rloco(otmp); } else otmp = mksobj_at(objtype, x, y, init, FALSE); if (otmp) { if (mtmp) { struct obj *otmp2; if (!ptr) ptr = mtmp->data; /* save_mtraits frees original data pointed to by otmp */ otmp2 = save_mtraits(otmp, mtmp); if (otmp2) otmp = otmp2; } /* use the corpse or statue produced by mksobj() as-is unless `ptr' is non-null */ if (ptr) { int old_corpsenm = otmp->corpsenm; otmp->corpsenm = monsndx(ptr); otmp->owt = weight(otmp); if (otmp->otyp == CORPSE && (special_corpse(old_corpsenm) || special_corpse(otmp->corpsenm))) { obj_stop_timers(otmp); start_corpse_timeout(otmp); } } } return(otmp); }
/* were-creature (even you) summons a horde */ int were_summon(const struct permonst *ptr, boolean yours, int *visible, /* number of visible helpers created */ char *genbuf) { int i, typ, pm = monsndx(ptr); struct monst *mtmp; int total = 0; *visible = 0; if (Protection_from_shape_changers && !yours) return 0; for (i = rnd(5); i > 0; i--) { switch(pm) { case PM_WERERAT: case PM_HUMAN_WERERAT: typ = rn2(3) ? PM_SEWER_RAT : rn2(3) ? PM_GIANT_RAT : PM_RABID_RAT ; if (genbuf) strcpy(genbuf, "rat"); break; case PM_WEREJACKAL: case PM_HUMAN_WEREJACKAL: typ = PM_JACKAL; if (genbuf) strcpy(genbuf, "jackal"); break; case PM_WEREWOLF: case PM_HUMAN_WEREWOLF: typ = rn2(5) ? PM_WOLF : PM_WINTER_WOLF ; if (genbuf) strcpy(genbuf, "wolf"); break; default: continue; } mtmp = makemon(&mons[typ], level, u.ux, u.uy, NO_MM_FLAGS); if (mtmp) { total++; if (canseemon(mtmp)) *visible += 1; } if (yours && mtmp) tamedog(mtmp, NULL); } return total; }
static struct obj *save_mtraits(struct obj *obj, struct monst *mtmp) { struct obj *otmp; int lth, namelth; lth = sizeof(struct monst) + mtmp->mxlth + mtmp->mnamelth; namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0; otmp = realloc_obj(obj, lth, mtmp, namelth, ONAME(obj)); if (otmp && otmp->oxlth) { struct monst *mtmp2 = (struct monst *)otmp->oextra; if (mtmp->data) mtmp2->mnum = monsndx(mtmp->data); /* invalidate pointers */ /* m_id is needed to know if this is a revived quest leader */ /* but m_id must be cleared when loading bones */ mtmp2->nmon = NULL; mtmp2->data = NULL; mtmp2->minvent = NULL; otmp->oattached = OATTACHED_MONST; /* mark it */ } return otmp; }
int dosit() { static const char *sit_message = "sit on the %s."; register struct trap *trap; register int typ = levl[u.ux][u.uy].typ; if(Levitation) { pline("You're sitting on air."); return 0; } if(OBJ_AT(u.ux, u.uy)) { register struct obj *obj; obj = level.objects[u.ux][u.uy]; You("sit on %s.", the(xname(obj))); if(!Is_box(obj)) pline("It's not very comfortable..."); } else if ((trap = t_at(u.ux, u.uy)) != 0) { if (u.utrap) { exercise(A_WIS, FALSE); /* you're getting stuck longer */ if(u.utraptype == TT_BEARTRAP) { You("can't sit down with your %s in the bear trap.", body_part(FOOT)); u.utrap++; } else if(u.utraptype == TT_PIT) { if(trap->ttyp == SPIKED_PIT) { You("sit down on a spike. Ouch!"); losehp(1, "sitting on an iron spike", KILLED_BY); exercise(A_STR, FALSE); } else You("sit down in the pit."); u.utrap += rn2(5); } else if(u.utraptype == TT_WEB) { You("sit in the spider web and get entangled further!"); u.utrap += rn1(10, 5); } else if(u.utraptype == TT_LAVA) { /* Must have fire resistance or they'd be dead already */ You("sit in the lava!"); u.utrap += rnd(4); losehp(d(2,10), "sitting in lava", KILLED_BY); } else if(u.utraptype == TT_INFLOOR) { You("can't maneuver to sit!"); u.utrap++; } } else { You("sit down."); dotrap(trap); } } else if(Underwater || Is_waterlevel(&u.uz)) { if (Is_waterlevel(&u.uz)) pline("There are no cushions floating nearby."); else You("sit down in the muddy bottom."); } else if(is_pool(u.ux, u.uy)) { You("sit in the water."); if (!rn2(10) && uarm) (void) rust_dmg(uarm, "armor", 1, TRUE); #ifdef POLYSELF /* Note: without POLYSELF, this can't _happen_ without */ /* water walking boots.... */ if (!rn2(10) && uarmf && uarmf->otyp != WATER_WALKING_BOOTS) (void) rust_dmg(uarm, "armor", 1, TRUE); #endif #ifdef SINKS } else if(IS_SINK(typ)) { You(sit_message, defsyms[S_sink].explanation); Your("%s gets wet.", humanoid(uasmon) ? "rump" : "underside"); #endif } else if(IS_ALTAR(typ)) { You(sit_message, defsyms[S_altar].explanation); altar_wrath(u.ux, u.uy); } else if(typ == STAIRS) { You(sit_message, "stairs"); } else if(typ == LADDER) { You(sit_message, "ladder"); } else if (is_lava(u.ux, u.uy)) { /* must be WWalking */ You(sit_message, "lava"); pline("The lava burns you!"); losehp(d((Fire_resistance ? 2 : 10), 10), "sitting on lava", KILLED_BY); } else if (is_ice(u.ux, u.uy)) { You(sit_message, defsyms[S_ice].explanation); if (!Cold_resistance) pline("The ice feels cold."); } else if (typ == DRAWBRIDGE_DOWN) { You(sit_message, "drawbridge"); } else if(IS_THRONE(typ)) { You(sit_message, defsyms[S_throne].explanation); if (rnd(6) > 4) { switch (rnd(13)) { case 1: (void) adjattrib(rn2(A_MAX), -rn1(4,3), FALSE); losehp(rnd(10), "cursed throne", KILLED_BY_AN); break; case 2: (void) adjattrib(rn2(A_MAX), 1, FALSE); break; case 3: pline("A%s electric shock shoots through your body!", (Shock_resistance) ? "" : " massive"); losehp(Shock_resistance ? rnd(6) : rnd(30), "electric chair", KILLED_BY_AN); exercise(A_CON, FALSE); break; case 4: You("feel much, much better!"); if(u.uhp >= (u.uhpmax - 5)) u.uhpmax += 4; u.uhp = u.uhpmax; make_blinded(0L,TRUE); make_sick(0L,FALSE); heal_legs(); flags.botl = 1; break; case 5: take_gold(); break; case 6: if(u.uluck + rn2(5) < 0) { You("feel your luck is changing."); change_luck(1); } else makewish(); break; case 7: { register int cnt = rnd(10); pline("A voice echoes:"); verbalize("Thy audience hath been summoned, %s!", flags.female ? "Dame" : "Sire"); while(cnt--) (void) makemon(courtmon(), u.ux, u.uy); break; } case 8: pline("A voice echoes:"); verbalize("By thy Imperious order, %s...", flags.female ? "Dame" : "Sire"); do_genocide(1); break; case 9: pline("A voice echoes:"); verbalize("A curse upon thee for sitting upon this most holy throne!"); if (Luck > 0) { make_blinded(Blinded + rn1(100,250),TRUE); } else rndcurse(); break; case 10: if (Luck < 0 || (HSee_invisible & INTRINSIC)) { if (level.flags.nommap) { pline( "A terrible drone fills your head!"); make_confused(HConfusion + rnd(30), FALSE); } else { pline("An image forms in your mind."); do_mapping(); } } else { Your("vision becomes clear."); HSee_invisible |= FROMOUTSIDE; newsym(u.ux, u.uy); } break; case 11: if (Luck < 0) { You("feel threatened."); aggravate(); } else { You("feel a wrenching sensation."); tele(); /* teleport him */ } break; case 12: You("are granted an insight!"); if (invent) { int ret, cval = rn2(5); /* agrees w/seffects() */ /* use up `cval' "charges"; 0 is special case */ do { ret = ggetobj("identify", identify, cval); if (ret < 0) break; /* quit */ } while (ret == 0 || (cval -= ret) > 0); } break; case 13: Your("mind turns into a pretzel!"); make_confused(HConfusion + rn1(7,16),FALSE); break; default: impossible("throne effect"); break; } } else You("feel somehow out of place..."); if (!rn2(3) && IS_THRONE(levl[u.ux][u.uy].typ)) { /* may have teleported */ pline("The throne vanishes in a puff of logic."); levl[u.ux][u.uy].typ = ROOM; if(Invisible) newsym(u.ux,u.uy); } #ifdef POLYSELF } else if (lays_eggs(uasmon) || u.umonnum == PM_QUEEN_BEE) { struct obj *uegg; if (!flags.female) { pline("Males can't lay eggs!"); return 0; } if (u.uhunger < (int)objects[EGG].oc_nutrition) { You("don't have enough energy to lay an egg."); return 0; } uegg = mksobj(EGG, FALSE, FALSE); uegg->spe = 1; uegg->quan = 1; uegg->owt = weight(uegg); uegg->corpsenm = (u.umonnum==PM_QUEEN_BEE ? PM_KILLER_BEE : monsndx(uasmon)); uegg->known = uegg->dknown = 1; You("lay an egg."); dropy(uegg); stackobj(uegg); morehungry((int)objects[EGG].oc_nutrition); #endif } else if (u.uswallow) pline("There are no seats in here!"); else pline("Having fun sitting on the %s?", surface(u.ux,u.uy)); return(1); }
static int domonnoise(struct monst *mtmp) { const char *pline_msg = 0, /* Monnam(mtmp) will be prepended */ *verbl_msg = 0; /* verbalize() */ const struct permonst *ptr = mtmp->data; /* presumably nearness checks have already been made */ if (!canhear()) return 0; if (is_silent(ptr)) return 0; /* Make sure its your role's quest quardian; adjust if not */ if (ptr->msound == MS_GUARDIAN && ptr != &pm_guardian) { int mndx = monsndx(ptr); ptr = &mons[genus(mndx, 1)]; } /* be sure to do this before talking; the monster might teleport away, in which case we want to check its pre-teleport position */ if (!canspotmon(mtmp)) map_invisible(mtmp->mx, mtmp->my); switch (ptr->msound) { case MS_ORACLE: return doconsult(mtmp); case MS_PRIEST: priest_talk(mtmp); break; case MS_LEADER: case MS_NEMESIS: case MS_GUARDIAN: quest_chat(mtmp); break; case MS_SELL: /* pitch, pay, total */ shk_chat(mtmp); break; case MS_VAMPIRE: { /* vampire messages are varied by tameness, peacefulness, and time of night */ boolean isnight = night(); boolean kindred = (Upolyd && (u.umonnum == PM_VAMPIRE || u.umonnum == PM_VAMPIRE_LORD)); boolean nightchild = (Upolyd && (u.umonnum == PM_WOLF || u.umonnum == PM_WINTER_WOLF || u.umonnum == PM_WINTER_WOLF_CUB)); const char *racenoun = (u.ufemale && urace.individual.f) ? urace. individual.f : (urace.individual.m) ? urace.individual. m : urace.noun; if (mtmp->mtame) { if (kindred) verbl_msg = msgprintf("Good %s to you Master%s", isnight ? "evening" : "day", isnight ? "!" : ". Why do we not rest?"); else verbl_msg = msgcat( nightchild ? "Child of the night, " : "", midnight()? "I can stand this craving no longer!" : isnight ? "I beg you, help me satisfy this growing craving!" : "I find myself growing a little weary."); } else if (mtmp->mpeaceful) { if (kindred && isnight) verbl_msg = msgprintf("Good feeding %s!", u.ufemale ? "sister" : "brother"); else if (nightchild && isnight) verbl_msg = "How nice to hear you, child of the night!"; else verbl_msg = "I only drink... potions."; } else { int vampindex; static const char *const vampmsg[] = { /* These first two (0 and 1) are specially handled below */ "I vant to suck your %s!", "I vill come after %s without regret!", /* other famous vampire quotes can follow here if desired */ }; if (kindred) verbl_msg = "This is my hunting ground that you dare to prowl!"; else if (youmonst.data == &mons[PM_SILVER_DRAGON] || youmonst.data == &mons[PM_BABY_SILVER_DRAGON]) { /* Silver dragons are silver in color, not made of silver */ verbl_msg = msgprintf( "%s! Your silver sheen does not frighten me!", youmonst.data == &mons[PM_SILVER_DRAGON] ? "Fool" : "Young Fool"); } else { vampindex = rn2(SIZE(vampmsg)); if (vampindex == 0) { verbl_msg = msgprintf( vampmsg[vampindex], body_part(BLOOD)); } else if (vampindex == 1) { verbl_msg = msgprintf( vampmsg[vampindex], Upolyd ? an(mons[u.umonnum].mname) : an(racenoun)); } else verbl_msg = vampmsg[vampindex]; } } } break; case MS_WERE: if (flags.moonphase == FULL_MOON && (night() ^ !rn2(13))) { pline("%s throws back %s head and lets out a blood curdling %s!", Monnam(mtmp), mhis(mtmp), ptr == &mons[PM_HUMAN_WERERAT] ? "shriek" : "howl"); wake_nearto(mtmp->mx, mtmp->my, 11 * 11); } else pline_msg = "whispers inaudibly. All you can make out is \"moon\"."; break; case MS_BARK: if (flags.moonphase == FULL_MOON && night()) { pline_msg = "howls."; } else if (mtmp->mpeaceful) { if (mtmp->mtame && (mtmp->mconf || mtmp->mflee || mtmp->mtrapped || moves > EDOG(mtmp)->hungrytime || mtmp->mtame < 5)) pline_msg = "whines."; else if (mtmp->mtame && EDOG(mtmp)->hungrytime > moves + 1000) pline_msg = "yips."; else { if (mtmp->data == &mons[PM_FOX]) pline_msg = whatthefoxsays(); else if (mtmp->data != &mons[PM_DINGO]) /* dingos do not actually bark */ pline_msg = "barks."; } } else { if (mtmp->data == &mons[PM_FOX]) pline_msg = whatthefoxsays(); else pline_msg = "growls."; } break; case MS_MEW: if (mtmp->mtame) { if (mtmp->mconf || mtmp->mflee || mtmp->mtrapped || mtmp->mtame < 5) pline_msg = "yowls."; else if (moves > EDOG(mtmp)->hungrytime) pline_msg = "meows."; else if (EDOG(mtmp)->hungrytime > moves + 1000) pline_msg = "purrs."; else pline_msg = "mews."; break; } /* else FALLTHRU */ case MS_GROWL: pline_msg = mtmp->mpeaceful ? "snarls." : "growls!"; break; case MS_ROAR: pline_msg = mtmp->mpeaceful ? "snarls." : "roars!"; break; case MS_SQEEK: pline_msg = "squeaks."; break; case MS_SQAWK: if (ptr == &mons[PM_RAVEN] && !mtmp->mpeaceful) verbl_msg = "Nevermore!"; else pline_msg = "squawks."; break; case MS_HISS: if (!mtmp->mpeaceful) pline_msg = "hisses!"; else return 0; /* no sound */ break; case MS_BUZZ: pline_msg = mtmp->mpeaceful ? "drones." : "buzzes angrily."; break; case MS_GRUNT: pline_msg = "grunts."; break; case MS_NEIGH: if (mtmp->mtame < 5) pline_msg = "neighs."; else if (moves > EDOG(mtmp)->hungrytime) pline_msg = "whinnies."; else pline_msg = "whickers."; break; case MS_WAIL: pline_msg = "wails mournfully."; break; case MS_GURGLE: pline_msg = "gurgles."; break; case MS_BURBLE: pline_msg = "burbles."; break; case MS_SHRIEK: pline_msg = "shrieks."; aggravate(); break; case MS_IMITATE: pline_msg = "imitates you."; break; case MS_BONES: pline("%s rattles noisily.", Monnam(mtmp)); pline("You freeze for a moment."); helpless(2, hr_afraid, "scared by rattling", NULL); break; case MS_LAUGH: { static const char *const laugh_msg[4] = { "giggles.", "chuckles.", "snickers.", "laughs.", }; pline_msg = laugh_msg[rn2(4)]; } break; case MS_MUMBLE: pline_msg = "mumbles incomprehensibly."; break; case MS_WISHGIVER: if (mtmp->mtame) { verbl_msg = "Sorry, I'm all out of wishes."; } else if (mtmp->mpeaceful) { if (ptr == &mons[PM_WATER_DEMON]) pline_msg = "gurgles."; else verbl_msg = "I'm free!"; } else verbl_msg = "This will teach you not to disturb me!"; break; case MS_BOAST: /* giants */ if (!mtmp->mpeaceful) { switch (rn2(4)) { case 0: pline("%s boasts about %s gem collection.", Monnam(mtmp), mhis(mtmp)); break; case 1: pline_msg = "complains about a diet of mutton."; break; default: pline_msg = "shouts \"Fee Fie Foe Foo!\" and guffaws."; wake_nearto(mtmp->mx, mtmp->my, 7 * 7); break; } break; } /* else FALLTHRU */ case MS_HUMANOID: if (!mtmp->mpeaceful) { if (In_endgame(&u.uz) && is_mplayer(ptr)) { mplayer_talk(mtmp); break; } else return 0; /* no sound */ } /* Generic peaceful humanoid behaviour. */ if (mtmp->mflee) pline_msg = "wants nothing to do with you."; else if (mtmp->mhp < mtmp->mhpmax / 4) pline_msg = "moans."; else if (mtmp->mconf || mtmp->mstun) verbl_msg = !rn2(3) ? "Huh?" : rn2(2) ? "What?" : "Eh?"; else if (!mtmp->mcansee) verbl_msg = "I can't see!"; else if (mtmp->mtrapped) { struct trap *t = t_at(level, mtmp->mx, mtmp->my); if (t) t->tseen = 1; verbl_msg = "I'm trapped!"; } else if (mtmp->mhp < mtmp->mhpmax / 2) pline_msg = "asks for a potion of healing."; else if (mtmp->mtame && !mtmp->isminion && moves > EDOG(mtmp)->hungrytime) verbl_msg = "I'm hungry."; /* Specific monsters' interests */ else if (is_elf(ptr)) pline_msg = "curses orcs."; else if (is_dwarf(ptr)) pline_msg = "talks about mining."; else if (likes_magic(ptr)) pline_msg = "talks about spellcraft."; else if (ptr->mlet == S_CENTAUR) pline_msg = "discusses hunting."; else switch (monsndx(ptr)) { case PM_HOBBIT: pline_msg = (mtmp->mhpmax - mtmp->mhp >= 10) ? "complains about unpleasant dungeon conditions." : "asks you about the One Ring."; break; case PM_ARCHEOLOGIST: pline_msg = "describes a recent article in \"Spelunker Today\" " "magazine."; break; case PM_TOURIST: verbl_msg = "Aloha."; break; case PM_PRISONER: verbl_msg = "Thank you for freeing me!"; break; default: pline_msg = "discusses dungeon exploration."; break; } break; case MS_SEDUCE: if (ptr->mlet != S_NYMPH && flags.seduce_enabled && could_seduce(mtmp, &youmonst, NULL) == 1) { doseduce(mtmp); break; } switch ((poly_gender() != (int)mtmp->female) ? rn2(3) : 0) { case 2: verbl_msg = "Hello, sailor."; break; case 1: pline_msg = "comes on to you."; break; default: pline_msg = "cajoles you."; } break; case MS_ARREST: if (mtmp->mpeaceful) verbalize("Just the facts, %s.", u.ufemale ? "Ma'am" : "Sir"); else { static const char *const arrest_msg[3] = { "Anything you say can be used against you.", "You're under arrest!", "Stop in the name of the Law!", }; verbl_msg = arrest_msg[rn2(3)]; } break; case MS_BRIBE: if (mtmp->mpeaceful && !mtmp->mtame) { demon_talk(mtmp); break; } /* fall through */ case MS_CUSS: if (!mtmp->mpeaceful) cuss(mtmp); break; case MS_SPELL: /* deliberately vague, since it's not actually casting any spell */ pline_msg = "seems to mutter a cantrip."; break; case MS_NURSE: if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) verbl_msg = "Put that weapon away before you hurt someone!"; else if (uarmc || (uarm && !uskin()) || uarmh || uarms || uarmg || uarmf) verbl_msg = Role_if(PM_HEALER) ? "Doc, I can't help you unless you cooperate." : "Please undress so I can examine you."; else if (uarmu) verbl_msg = "Take off your shirt, please."; else verbl_msg = "Relax, this won't hurt a bit."; break; case MS_GUARD: if (money_cnt(invent)) verbl_msg = "Please drop that gold and follow me."; else verbl_msg = "Please follow me."; break; case MS_SOLDIER: { static const char *const soldier_foe_msg[3] = { "Resistance is useless!", "You're dog meat!", "Surrender!", }, *const soldier_pax_msg[3] = { "What lousy pay we're getting here!", "The food's not fit for Orcs!", "My feet hurt, I've been on them all day!", }; verbl_msg = mtmp->mpeaceful ? soldier_pax_msg[rn2(3)] : soldier_foe_msg[rn2(3)]; } break; case MS_RIDER: if (ptr == &mons[PM_DEATH] && !rn2(10)) pline_msg = "is busy reading a copy of Sandman #8."; else verbl_msg = "Who do you think you are, War?"; break; } if (pline_msg) pline("%s %s", Monnam(mtmp), pline_msg); else if (verbl_msg) verbalize("%s", verbl_msg); return 1; }
int dosit() { #ifdef THRONES register int cnt; #endif if(Levitation) { pline("There's nothing to sit on up here."); #ifdef THRONES } else if(IS_THRONE(levl[u.ux][u.uy].typ)) { if (rnd(6) > 4) { switch (rnd(13)) { case 1: adjattrib(rn2(A_MAX), -rn1(4,3), FALSE); losehp(rnd(10), "cursed throne", KILLED_BY_AN); break; case 2: adjattrib(rn2(A_MAX), 1, FALSE); break; case 3: pline("A%s charge of electricity shoots through your body!", (Shock_resistance) ? "" : " massive"); if(Shock_resistance) losehp(rnd(6), "electric chair", KILLED_BY_AN); else losehp(rnd(30), "electric chair", KILLED_BY_AN); break; case 4: You("feel much, much better!"); if(u.uhp >= (u.uhpmax - 5)) u.uhpmax += 4; u.uhp = u.uhpmax; make_blinded(0L,TRUE); make_sick(0L,FALSE); heal_legs(); flags.botl = 1; break; case 5: take_gold(); break; case 6: if(u.uluck + rn2(5) < 0) { You("feel your luck is changing."); change_luck(1); } else makewish(); break; case 7: cnt = rnd(10); You("hear a voice echo:"); pline("\"Thy audience hath been summoned, Sire!\""); while(cnt--) (void) makemon(courtmon(), u.ux, u.uy); break; case 8: You("hear a voice echo:"); pline("\"By thy Imperious order, Sire...\""); do_genocide(1); break; case 9: You("hear a voice echo:"); pline("\"A curse upon thee for sitting upon this most holy throne!\""); if (Luck > 0) { make_blinded(Blinded + rn1(100,250),TRUE); } else rndcurse(); break; case 10: if (Luck < 0 || (HSee_invisible & INTRINSIC)) { pline("An image forms in your mind."); do_mapping(); } else { Your("vision clarifies."); HSee_invisible |= INTRINSIC; } break; case 11: if (Luck < 0) { You("feel threatened."); aggravate(); } else { You("feel a wrenching sensation."); tele(); /* teleport him */ } break; case 12: You("are granted a gift of insight!"); while (!ggetobj("identify", identify, rn2(5)) && invent); break; case 13: Your("mind turns into a pretzel!"); make_confused(HConfusion + rn1(7,16),FALSE); break; default: impossible("throne effect"); break; } } else You("feel somehow out of place..."); if (!rn2(3) && IS_THRONE(levl[u.ux][u.uy].typ)) { pline("The throne vanishes in a puff of logic."); /* levl[u.ux][u.uy].scrsym = ROOM_SYM; */ levl[u.ux][u.uy].typ = ROOM; if(Invisible) newsym(u.ux,u.uy); } #endif #ifdef POLYSELF } else if (lays_eggs(uasmon) || u.umonnum == PM_QUEEN_BEE) { struct obj *uegg; if (u.uhunger < objects[EGG].nutrition) { You("are too weak to lay an egg."); return 0; } uegg = mksobj(EGG, 0); uegg->spe = 1; uegg->quan = 1; uegg->owt = weight(uegg); uegg->corpsenm = (u.umonnum==PM_QUEEN_BEE ? PM_KILLER_BEE : monsndx(uasmon)); uegg->known = uegg->dknown = 1; You("lay an egg."); dropy(uegg); stackobj(uegg); morehungry(objects[EGG].nutrition); #endif } else pline("Having fun sitting on the floor?"); return(1); }
/* monster attempts ranged weapon attack against a square */ void thrwmq(struct monst *mtmp, int xdef, int ydef) { struct obj *otmp, *mwep; schar skill; int multishot; const char *onm; /* Rearranged beginning so monsters can use polearms not in a line */ if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) { mtmp->weapon_check = NEED_RANGED_WEAPON; /* mon_wield_item resets weapon_check as appropriate */ if (mon_wield_item(mtmp) != 0) return; } /* Pick a weapon */ otmp = select_rwep(mtmp); if (!otmp) return; if (is_pole(otmp)) { int dam, hitv; if (otmp != MON_WEP(mtmp)) return; /* polearm must be wielded */ /* TODO: LOE function between two arbitrary points. */ if (dist2(mtmp->mx, mtmp->my, xdef, ydef) > POLE_LIM || (xdef == u.ux && ydef == u.uy && !couldsee(mtmp->mx, mtmp->my))) return; /* Out of range, or intervening wall */ if (mon_visible(mtmp)) { onm = singular(otmp, xname); pline("%s thrusts %s.", Monnam(mtmp), obj_is_pname(otmp) ? the(onm) : an(onm)); } if (xdef == u.ux && ydef == u.uy) { dam = dmgval(otmp, &youmonst); hitv = 3 - distmin(u.ux, u.uy, mtmp->mx, mtmp->my); if (hitv < -4) hitv = -4; if (bigmonst(youmonst.data)) hitv++; hitv += 8 + otmp->spe; if (objects[otmp->otyp].oc_class == WEAPON_CLASS || objects[otmp->otyp].oc_class == VENOM_CLASS) hitv += objects[otmp->otyp].oc_hitbon; if (dam < 1) dam = 1; thitu(hitv, dam, otmp, NULL); action_interrupted(); } else if (MON_AT(level, xdef, ydef)) (void)ohitmon(m_at(level, xdef, ydef), otmp, 0, FALSE); else if (mon_visible(mtmp)) pline("But it misses wildly."); return; } if (!qlined_up(mtmp, xdef, ydef, FALSE, FALSE) || !ai_use_at_range(BOLT_LIM - distmin(mtmp->mx, mtmp->my, xdef, ydef))) return; skill = objects[otmp->otyp].oc_skill; mwep = MON_WEP(mtmp); /* wielded weapon */ /* Multishot calculations */ multishot = 1; if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER || skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) { /* Assumes lords are skilled, princes are expert */ if (is_prince(mtmp->data)) multishot += 2; else if (is_lord(mtmp->data)) multishot++; switch (monsndx(mtmp->data)) { case PM_RANGER: multishot++; break; case PM_ROGUE: if (skill == P_DAGGER) multishot++; break; case PM_NINJA: case PM_SAMURAI: if (otmp->otyp == YA && mwep && mwep->otyp == YUMI) multishot++; break; default: break; } /* racial bonus */ if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW && mwep && mwep->otyp == ELVEN_BOW) || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW && mwep && mwep->otyp == ORCISH_BOW)) multishot++; if ((long)multishot > otmp->quan) multishot = (int)otmp->quan; if (multishot < 1) multishot = 1; else multishot = rnd(multishot); } if (mon_visible(mtmp)) { if (multishot > 1) { /* "N arrows"; multishot > 1 implies otmp->quan > 1, so xname()'s result will already be pluralized */ onm = msgprintf("%d %s", multishot, xname(otmp)); } else { /* "an arrow" */ onm = singular(otmp, xname); onm = obj_is_pname(otmp) ? the(onm) : an(onm); } m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE; pline("%s %s %s!", Monnam(mtmp), m_shot.s ? "shoots" : "throws", onm); m_shot.o = otmp->otyp; } else { m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */ } m_shot.n = multishot; for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) { m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), distmin(mtmp->mx, mtmp->my, xdef, ydef), otmp, TRUE); /* conceptually all N missiles are in flight at once, but if mtmp gets killed (shot kills adjacent gas spore and triggers explosion, perhaps), inventory will be dropped and otmp might go away via merging into another stack; if we then use it, we could cause undefined behavior */ if (mtmp->mhp <= 0 && m_shot.i < m_shot.n) { /* cancel pending shots (ought to give a message here since we gave one above about throwing/shooting N missiles) */ break; /* endmultishot(FALSE); */ } } m_shot.n = m_shot.i = 0; m_shot.o = STRANGE_OBJECT; m_shot.s = FALSE; action_interrupted(); }
/* look for gold, on the floor or in monsters' possession */ int gold_detect(struct obj *sobj, boolean *scr_known) { struct obj *obj; struct monst *mtmp; int uw = u.uinwater; struct obj *temp; boolean stale; *scr_known = stale = clear_stale_map(COIN_CLASS, sobj->blessed ? GOLD : 0); /* look for gold carried by monsters (might be in a container) */ for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */ if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { *scr_known = TRUE; goto outgoldmap; /* skip further searching */ } else for (obj = mtmp->minvent; obj; obj = obj->nobj) if (sobj->blessed && o_material(obj, GOLD)) { *scr_known = TRUE; goto outgoldmap; } else if (o_in(obj, COIN_CLASS)) { *scr_known = TRUE; goto outgoldmap; /* skip further searching */ } } /* look for gold objects */ for (obj = level->objlist; obj; obj = obj->nobj) { if (sobj->blessed && o_material(obj, GOLD)) { *scr_known = TRUE; if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; } else if (o_in(obj, COIN_CLASS)) { *scr_known = TRUE; if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; } } if (!*scr_known) { /* no gold found on floor or monster's inventory. adjust message if you have gold in your inventory */ char buf[BUFSZ]; if (youmonst.data == &mons[PM_GOLD_GOLEM]) { sprintf(buf, "You feel like a million %s!", currency(2L)); } else if (hidden_gold() || money_cnt(invent)) strcpy(buf, "You feel worried about your future financial situation."); else strcpy(buf, "You feel materially poor."); strange_feeling(sobj, buf); return 1; } /* only under me - no separate display required */ if (stale) doredraw(); pline("You notice some gold between your %s.", makeplural(body_part(FOOT))); return 0; outgoldmap: cls(); u.uinwater = 0; /* Discover gold locations. */ for (obj = level->objlist; obj; obj = obj->nobj) { if (sobj->blessed && (temp = o_material(obj, GOLD))) { if (temp != obj) { temp->ox = obj->ox; temp->oy = obj->oy; } map_object(temp,1); } else if ((temp = o_in(obj, COIN_CLASS))) { if (temp != obj) { temp->ox = obj->ox; temp->oy = obj->oy; } map_object(temp,1); } } for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; /* probably overkill here */ if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { struct obj gold; gold.otyp = GOLD_PIECE; gold.ox = mtmp->mx; gold.oy = mtmp->my; map_object(&gold,1); } else for (obj = mtmp->minvent; obj; obj = obj->nobj) if (sobj->blessed && (temp = o_material(obj, GOLD))) { temp->ox = mtmp->mx; temp->oy = mtmp->my; map_object(temp,1); break; } else if ((temp = o_in(obj, COIN_CLASS))) { temp->ox = mtmp->mx; temp->oy = mtmp->my; map_object(temp,1); break; } } newsym(u.ux,u.uy); pline("You feel very greedy, and sense gold!"); exercise(A_WIS, TRUE); win_pause_output(P_MAP); doredraw(); u.uinwater = uw; if (Underwater) under_water(2); if (u.uburied) under_ground(2); return 0; }
/* mon summons a monster */ void msummon(struct monst *mon) { const struct permonst *ptr; int dtype = NON_PM, cnt = 0; aligntyp atyp; struct monst *mtmp; struct d_level *dlev; if (mon) { ptr = mon->data; dlev = &mon->dlevel->z; atyp = (ptr->maligntyp == A_NONE) ? A_NONE : sgn(ptr->maligntyp); if (mon->ispriest || roamer_type(mon->data)) atyp = EPRI(mon)->shralign; } else { ptr = &mons[PM_WIZARD_OF_YENDOR]; atyp = (ptr->maligntyp == A_NONE) ? A_NONE : sgn(ptr->maligntyp); dlev = &u.uz; } if (is_dprince(ptr) || (ptr == &mons[PM_WIZARD_OF_YENDOR])) { dtype = (!rn2(20)) ? dprince(atyp) : (!rn2(4)) ? dlord(atyp) : ndemon(dlev, atyp); cnt = (!rn2(4) && is_ndemon(&mons[dtype])) ? 2 : 1; } else if (is_dlord(ptr)) { dtype = (!rn2(50)) ? dprince(atyp) : (!rn2(20)) ? dlord(atyp) : ndemon(dlev, atyp); cnt = (!rn2(4) && is_ndemon(&mons[dtype])) ? 2 : 1; } else if (is_ndemon(ptr)) { dtype = (!rn2(20)) ? dlord(atyp) : (!rn2(6)) ? ndemon(dlev, atyp) : monsndx(ptr); cnt = 1; } else if (mon && is_lminion(mon)) { dtype = (is_lord(ptr) && !rn2(20)) ? llord() : (is_lord(ptr) || !rn2(6)) ? lminion() : monsndx(ptr); cnt = (!rn2(4) && !is_lord(&mons[dtype])) ? 2 : 1; } else if (ptr == &mons[PM_ANGEL]) { /* non-lawful angels can also summon */ if (!rn2(6)) { switch (atyp) { /* see summon_minion */ case A_NEUTRAL: dtype = PM_AIR_ELEMENTAL + rn2(4); break; case A_CHAOTIC: case A_NONE: dtype = ndemon(dlev, atyp); break; } } else { dtype = PM_ANGEL; } cnt = (!rn2(4) && !is_lord(&mons[dtype])) ? 2 : 1; } if (dtype == NON_PM) return; /* sanity checks */ if (cnt > 1 && (mons[dtype].geno & G_UNIQ)) cnt = 1; /* * If this daemon is unique and being re-summoned (the only way we * could get this far with an extinct dtype), try another. */ if (mvitals[dtype].mvflags & G_GONE) { dtype = ndemon(dlev, atyp); if (dtype == NON_PM) return; } while (cnt > 0) { mtmp = makemon(&mons[dtype], level, u.ux, u.uy, NO_MM_FLAGS); if (mtmp && roamer_type(&mons[dtype])) { /* alignment should match the summoner */ EPRI(mtmp)->shralign = atyp; } cnt--; } }
/* Bug: if the monster is a priest or shopkeeper, not every one of these * options works, since those are special cases. */ char *x_monnam(const struct monst *mtmp, int article, /* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious * ARTICLE_YOUR: "your" on pets, "the" on everything * else * If the monster would be referred to as "it" or if the monster has a name * _and_ there is no adjective, "invisible", "saddled", etc., override this * and always use no article. */ const char *adjective, int suppress, /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE. * EXACT_NAME: combination of all the above */ boolean called) { static char buf[BUFSZ]; const struct permonst *mdat = mtmp->data; boolean do_hallu, do_invis, do_it, do_saddle; boolean name_at_start, has_adjectives; char *bp; if (program_state.gameover) suppress |= SUPPRESS_HALLUCINATION; if (article == ARTICLE_YOUR && !mtmp->mtame) article = ARTICLE_THE; do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION); do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE); do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR && !program_state.gameover && mtmp != u.usteed && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT); do_saddle = !(suppress & SUPPRESS_SADDLE); buf[0] = 0; /* unseen monsters, etc. Use "it" */ if (do_it) { strcpy(buf, "it"); return buf; } /* priests and minions: don't even use this function */ if (mtmp->ispriest || mtmp->isminion) { struct monst *priestmon = newmonst(mtmp->mxtyp, mtmp->mnamelth); char priestnambuf[BUFSZ]; char *name; long save_prop = EHalluc_resistance; memcpy(priestmon, mtmp, sizeof(struct monst) + mtmp->mxlth + mtmp->mnamelth); /* when true name is wanted, explicitly block Hallucination */ if (!do_hallu) EHalluc_resistance = 1L; if (!do_invis) priestmon->minvis = 0; name = priestname(priestmon, priestnambuf); EHalluc_resistance = save_prop; if (article == ARTICLE_NONE && !strncmp(name, "the ", 4)) name += 4; strcpy(buf, name); free(priestmon); return buf; } /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating, * none of this applies. */ if (mtmp->isshk && !do_hallu) { if (adjective && article == ARTICLE_THE) { /* pathological case: "the angry Asidonhopo the blue dragon" sounds silly */ strcpy(buf, "the "); strcat(strcat(buf, adjective), " "); strcat(buf, shkname(mtmp)); return buf; } strcat(buf, shkname(mtmp)); if (mdat == &mons[PM_SHOPKEEPER] && !do_invis) return buf; strcat(buf, " the "); if (do_invis) strcat(buf, "invisible "); strcat(buf, mdat->mname); return buf; } /* Put the adjectives in the buffer */ if (adjective) strcat(strcat(buf, adjective), " "); if (do_invis) strcat(buf, "invisible "); if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind && !Hallucination) strcat(buf, "saddled "); if (buf[0] != 0) has_adjectives = TRUE; else has_adjectives = FALSE; /* Put the actual monster name or type into the buffer now */ /* Be sure to remember whether the buffer starts with a name */ if (do_hallu) { strcat(buf, rndmonnam()); name_at_start = FALSE; } else if (mtmp->mnamelth) { char *name = NAME(mtmp); if (mdat == &mons[PM_GHOST]) { sprintf(eos(buf), "%s ghost", s_suffix(name)); name_at_start = TRUE; } else if (called) { sprintf(eos(buf), "%s called %s", mdat->mname, name); name_at_start = (boolean)type_is_pname(mdat); } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) { /* <name> the <adjective> <invisible> <saddled> <rank> */ char pbuf[BUFSZ]; strcpy(pbuf, name); pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */ if (has_adjectives) strcat(pbuf, buf); strcat(pbuf, bp + 5); /* append the rest of the name */ strcpy(buf, pbuf); article = ARTICLE_NONE; name_at_start = TRUE; } else { strcat(buf, name); name_at_start = TRUE; } } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) { char pbuf[BUFSZ]; strcpy(pbuf, rank_of((int)mtmp->m_lev, monsndx(mdat), (boolean)mtmp->female)); strcat(buf, lcase(pbuf)); name_at_start = FALSE; } else { strcat(buf, mdat->mname); name_at_start = (boolean)type_is_pname(mdat); } if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) { if (mdat == &mons[PM_WIZARD_OF_YENDOR]) article = ARTICLE_THE; else article = ARTICLE_NONE; } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) { article = ARTICLE_THE; } { char buf2[BUFSZ]; switch(article) { case ARTICLE_YOUR: strcpy(buf2, "your "); strcat(buf2, buf); strcpy(buf, buf2); return buf; case ARTICLE_THE: strcpy(buf2, "the "); strcat(buf2, buf); strcpy(buf, buf2); return buf; case ARTICLE_A: return an(buf); case ARTICLE_NONE: default: return buf; } } }