/* * Close the drawbridge located at x,y. * Returns TRUE if the drawbridge was closed, FALSE otherwise. */ boolean close_drawbridge(int x, int y) { struct rm *loc1, *loc2; struct monst *m; struct trap *t; int x2, y2; loc1 = &level->locations[x][y]; if (loc1->typ != DRAWBRIDGE_DOWN) return FALSE; /* A huge monster will block the drawbridge. */ if ((m = m_at(level, x, y)) && hugemonst(m->data)) { pline("%s blocks the drawbridge with %s weight!", canseemon(level, m) ? Amonnam(m) : "Something", canseemon(level, m) ? mhis(level, m) : "its"); return FALSE; } if (rn2(5) == 0) { pline("The mechanism seems to have something stuck in it and won't close."); return FALSE; } x2 = x; y2 = y; get_wall_for_db(&x2,&y2); if (cansee(x,y) || cansee(x2,y2)) pline("You see a drawbridge %s up!", (((u.ux == x || u.uy == y) && !Underwater) || distu(x2,y2) < distu(x,y)) ? "coming" : "going"); loc1->typ = DRAWBRIDGE_UP; loc2 = &level->locations[x2][y2]; loc2->typ = DBWALL; switch (loc1->drawbridgemask & DB_DIR) { case DB_NORTH: case DB_SOUTH: loc2->horizontal = TRUE; break; case DB_WEST: case DB_EAST: loc2->horizontal = FALSE; break; } loc2->wall_info = W_NONDIGGABLE; set_entity(x, y, &(occupants[0])); set_entity(x2, y2, &(occupants[1])); do_entity(&(occupants[0])); /* Do set_entity after first */ set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tail */ do_entity(&(occupants[1])); if (OBJ_AT(x, y) && flags.soundok) You_hear("smashing and crushing."); revive_nasty(x,y,NULL); revive_nasty(x2,y2,NULL); delallobj(x, y); delallobj(x2, y2); if ((t = t_at(level, x, y)) != 0) deltrap(level, t); if ((t = t_at(level, x2, y2)) != 0) deltrap(level, t); newsym(x, y); newsym(x2, y2); block_point(x2,y2); /* vision */ return TRUE; }
/* Charm snakes in range. Note that the snakes are NOT tamed. */ static void charm_snakes(int distance) { struct monst *mtmp = level->monlist; int could_see_mon, was_peaceful; while (mtmp) { if (!DEADMONSTER(mtmp) && mtmp->data->mlet == S_SNAKE && mtmp->mcanmove && distu(mtmp->mx, mtmp->my) < distance) { was_peaceful = mtmp->mpeaceful; mtmp->mavenge = 0; could_see_mon = canspotmon(mtmp); mtmp->mundetected = 0; msethostility(mtmp, FALSE, FALSE); /* does a newsym() */ if (canseemon(mtmp)) { if (!could_see_mon) pline(msgc_youdiscover, "You notice %s, swaying with the music.", a_monnam(mtmp)); else pline(msgc_actionok, "%s freezes, then sways with the music%s.", Monnam(mtmp), was_peaceful ? "" : ", and now seems quieter"); } } mtmp = mtmp->nmon; } }
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); }
void were_change(struct monst *mon) { if (!is_were(mon->data)) return; if (is_human(mon->data)) { if (!Protection_from_shape_changers && !rn2(night() ? (flags.moonphase == FULL_MOON ? 3 : 30) : (flags.moonphase == FULL_MOON ? 10 : 50))) { new_were(mon); /* change into animal form */ if (flags.soundok && !canseemon(mon)) { const char *howler; switch (mon->mnum) { case PM_WEREWOLF: howler = "wolf"; break; case PM_WEREJACKAL: howler = "jackal"; break; default: howler = NULL; break; } if (howler) You_hear("a %s howling at the moon.", howler); } } } else if (!rn2(30) || Protection_from_shape_changers) { new_were(mon); /* change back into human form */ } }
void mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) { const char *monname; if (tele_restrict(mtmp)) return; if (teleport_pet(mtmp, FALSE)) { /* save name with pre-movement visibility */ monname = Monnam(mtmp); /* Note: don't remove the trap if a vault. Otherwise the monster will be stuck there, since the guard isn't going to come for it... */ if (trap->once) mvault_tele(mtmp); else rloc(mtmp, TRUE); if (in_sight) { if (canseemon(mtmp)) pline("%s seems disoriented.", monname); else pline("%s suddenly disappears!", monname); seetrap(trap); } } }
boolean tele_restrict(struct monst *mon) { if (level->flags.noteleport) { if (canseemon(mon)) pline("A mysterious force prevents %s from teleporting!", mon_nam(mon)); return TRUE; } return FALSE; }
/* Calm nymphs in range. */ static void calm_nymphs(int distance) { struct monst *mtmp = level->monlist; while (mtmp) { if (!DEADMONSTER(mtmp) && mtmp->data->mlet == S_NYMPH && mtmp->mcanmove && distu(mtmp->mx, mtmp->my) < distance) { mtmp->msleeping = 0; msethostility(mtmp, FALSE, FALSE); mtmp->mavenge = 0; if (canseemon(mtmp)) pline(msgc_actionok, "%s listens cheerfully to the music, then seems quieter.", Monnam(mtmp)); } mtmp = mtmp->nmon; } }
static void prisoner_speaks(struct monst *mtmp) { if (mtmp->data == &mons[PM_PRISONER] && (mtmp->mstrategy & STRAT_WAITMASK)) { /* Awaken the prisoner */ if (canseemon(mtmp)) pline("%s speaks:", Monnam(mtmp)); verbalize("I'm finally free!"); mtmp->mstrategy &= ~STRAT_WAITMASK; mtmp->mpeaceful = 1; /* Your god is happy... */ adjalign(3); /* ...But the guards are not */ angry_guards(FALSE); } return; }
/* 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; }
/* Awake only soldiers of the level. */ void awaken_soldiers(struct monst *culprit) { struct monst *mtmp; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (!DEADMONSTER(mtmp) && is_mercenary(mtmp->data) && !mx_egd(mtmp)) { mtmp->mfrozen = 0; msethostility(mtmp, TRUE, FALSE); mtmp->mcanmove = 1; mtmp->msleeping = 0; if (canseemon(mtmp)) pline(culprit == &youmonst ? msgc_actionok : msgc_moncombatbad, "%s is now ready for battle!", Monnam(mtmp)); else pline_once(msgc_levelsound, "You hear the rattle of battle gear being readied."); } } }
/* feedback when frustrated monster couldn't cast a spell */ static void cursetxt(struct monst *mtmp, boolean undirected) { if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { const char *point_msg; /* spellcasting monsters are impolite */ if (undirected) point_msg = "all around, then curses"; else if ((Invis && !perceives(mtmp->data) && (mtmp->mux != u.ux || mtmp->muy != u.uy)) || (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == STRANGE_OBJECT) || u.uundetected) point_msg = "and curses in your general direction"; else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) point_msg = "and curses at your displaced image"; else point_msg = "at you, then curses"; pline("%s points %s.", Monnam(mtmp), point_msg); } else if (!(moves % 4) || !rn2(4)) { if (flags.soundok) Norep("You hear a mumbled curse."); } }
/* monster uses spell (ranged) */ int buzzmu(struct monst *mtmp, const struct attack *mattk) { /* don't print constant stream of curse messages for 'normal' spellcasting monsters at range */ if (mattk->adtyp > AD_SPC2) return 0; if (mtmp->mcan) { cursetxt(mtmp, FALSE); return 0; } if (lined_up(mtmp) && rn2(3)) { nomul(0, NULL); if (mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */ if (canseemon(mtmp)) pline("%s zaps you with a %s!", Monnam(mtmp), flash_types[ad_to_typ(mattk->adtyp)]); buzz(-ad_to_typ(mattk->adtyp), (int)mattk->damn, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby)); } } return 1; }
void mon_adjust_speed(struct monst *mon, int adjust, /* positive => increase speed, negative => decrease */ struct obj *obj) /* item to make known if effect can be seen */ { struct obj *otmp; boolean give_msg = !in_mklev, petrify = FALSE; unsigned int oldspeed = mon->mspeed; switch (adjust) { case 2: mon->permspeed = MFAST; give_msg = FALSE; /* special case monster creation */ break; case 1: if (mon->permspeed == MSLOW) mon->permspeed = 0; else mon->permspeed = MFAST; break; case 0: /* just check for worn speed boots */ break; case -1: if (mon->permspeed == MFAST) mon->permspeed = 0; else mon->permspeed = MSLOW; break; case -2: mon->permspeed = MSLOW; give_msg = FALSE; /* (not currently used) */ break; case -3: /* petrification */ /* take away intrinsic speed but don't reduce normal speed */ if (mon->permspeed == MFAST) mon->permspeed = 0; petrify = TRUE; break; } for (otmp = mon->minvent; otmp; otmp = otmp->nobj) if (otmp->owornmask && objects[otmp->otyp].oc_oprop == FAST) break; if (otmp) /* speed boots */ mon->mspeed = MFAST; else mon->mspeed = mon->permspeed; if (give_msg && (mon->mspeed != oldspeed || petrify) && canseemon(mon)) { /* fast to slow (skipping intermediate state) or vice versa */ const char *howmuch = (mon->mspeed + oldspeed == MFAST + MSLOW) ? "much " : ""; if (petrify) { /* mimic the player's petrification countdown; "slowing down" even if fast movement rate retained via worn speed boots */ if (flags.verbose) pline("%s is slowing down.", Monnam(mon)); } else if (adjust > 0 || mon->mspeed == MFAST) pline("%s is suddenly moving %sfaster.", Monnam(mon), howmuch); else pline("%s seems to be moving %sslower.", Monnam(mon), howmuch); /* might discover an object if we see the speed change happen, but avoid making possibly forgotten book known when casting its spell */ if (obj != 0 && obj->dknown && objects[obj->otyp].oc_class != SPBOOK_CLASS) makeknown(obj->otyp); } }
/* If dmg is zero, then the monster is not casting at you. If the monster is intentionally not casting at you, we have previously called spell_would_be_useless() and spellnum should always be a valid undirected spell. If you modify either of these, be sure to change is_undirected_spell() and spell_would_be_useless(). */ static void cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) { if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); return; } switch (spellnum) { case MGC_DEATH_TOUCH: pline("Oh no, %s's using the touch of death!", mhe(mtmp)); if (nonliving(youmonst.data) || is_demon(youmonst.data)) { pline("You seem no deader than before."); } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { if (Hallucination) { pline("You have an out of body experience."); } else { killer_format = KILLED_BY_AN; killer = "touch of death"; done(DIED); } } else { if (Antimagic) shieldeff(u.ux, u.uy); pline("Lucky for you, it didn't work!"); } dmg = 0; break; case MGC_CLONE_WIZ: if (mtmp->iswiz && flags.no_of_wizards == 1) { pline("Double Trouble..."); clonewiz(); dmg = 0; } else impossible("bad wizard cloning?"); break; case MGC_SUMMON_MONS: { int count; count = nasty(mtmp); /* summon something nasty */ if (mtmp->iswiz) verbalize("Destroy the thief, my pet%s!", plur(count)); else { const char *mappear = (count == 1) ? "A monster appears" : "Monsters appear"; /* messages not quite right if plural monsters created but only a single monster is seen */ if (Invisible && !perceives(mtmp->data) && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s around a spot near you!", mappear); else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s around your displaced image!", mappear); else pline("%s from nowhere!", mappear); } dmg = 0; break; } case MGC_AGGRAVATION: pline("You feel that monsters are aware of your presence."); aggravate(); dmg = 0; break; case MGC_CURSE_ITEMS: pline("You feel as if you need some help."); rndcurse(); dmg = 0; break; case MGC_DESTRY_ARMR: if (Antimagic) { shieldeff(u.ux, u.uy); pline("A field of force surrounds you!"); } else if (!destroy_arm(some_armor(&youmonst))) { pline("Your skin itches."); } dmg = 0; break; case MGC_WEAKEN_YOU: /* drain strength */ if (Antimagic) { shieldeff(u.ux, u.uy); pline("You feel momentarily weakened."); } else { pline("You suddenly feel weaker!"); dmg = mtmp->m_lev - 6; if (Half_spell_damage) dmg = (dmg + 1) / 2; losestr(rnd(dmg)); if (u.uhp < 1) done_in_by(mtmp); } dmg = 0; break; case MGC_DISAPPEAR: /* makes self invisible */ if (!mtmp->minvis && !mtmp->invis_blkd) { if (canseemon(mtmp)) pline("%s suddenly %s!", Monnam(mtmp), !See_invisible ? "disappears" : "becomes transparent"); mon_set_minvis(mtmp); dmg = 0; } else impossible("no reason for monster to cast disappear spell?"); break; case MGC_STUN_YOU: if (Antimagic || Free_action) { shieldeff(u.ux, u.uy); if (!Stunned) pline("You feel momentarily disoriented."); make_stunned(1L, FALSE); } else { pline(Stunned ? "You struggle to keep your balance." : "You reel..."); dmg = dice(ACURR(A_DEX) < 12 ? 6 : 4, 4); if (Half_spell_damage) dmg = (dmg + 1) / 2; make_stunned(HStun + dmg, FALSE); } dmg = 0; break; case MGC_HASTE_SELF: mon_adjust_speed(mtmp, 1, NULL); dmg = 0; break; case MGC_CURE_SELF: if (mtmp->mhp < mtmp->mhpmax) { if (canseemon(mtmp)) pline("%s looks better.", Monnam(mtmp)); /* note: player healing does 6d4; this used to do 1d8 */ if ((mtmp->mhp += dice(3,6)) > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; dmg = 0; } break; case MGC_PSI_BOLT: /* prior to 3.4.0 Antimagic was setting the damage to 1--this made the spell virtually harmless to players with magic res. */ if (Antimagic) { shieldeff(u.ux, u.uy); dmg = (dmg + 1) / 2; } if (dmg <= 5) pline("You get a slight %sache.", body_part(HEAD)); else if (dmg <= 10) pline("Your brain is on fire!"); else if (dmg <= 20) pline("Your %s suddenly aches painfully!", body_part(HEAD)); else pline("Your %s suddenly aches very painfully!", body_part(HEAD)); break; default: impossible("mcastu: invalid magic spell (%d)", spellnum); dmg = 0; break; } if (dmg) mdamageu(mtmp, dmg); }
struct monst * tamedog(struct monst *mtmp, struct obj *obj) { struct monst *mtmp2; /* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */ if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA] || (mtmp->data->mflags3 & M3_WANTSARTI)) return NULL; /* worst case, at least it'll be peaceful; this uses the main RNG because realtime effects means that this won't really sync anyway; this also calls set_malign (thus there's no need for the caller to call it after calling tamedog()) */ msethostility(mtmp, FALSE, TRUE); if (flags.moonphase == FULL_MOON && night() && rn2(6) && obj && mtmp->data->mlet == S_DOG) return NULL; /* If we cannot tame it, at least it's no longer afraid. */ mtmp->mflee = 0; mtmp->mfleetim = 0; /* make grabber let go now, whether it becomes tame or not */ if (mtmp == u.ustuck) { if (Engulfed) expels(mtmp, mtmp->data, TRUE); else if (!(Upolyd && sticks(youmonst.data))) unstuck(mtmp); } /* feeding it treats makes it tamer */ if (mtmp->mtame && obj) { int tasty; if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating && ((tasty = dogfood(mtmp, obj)) == DOGFOOD || (tasty <= ACCFOOD && CONST_EDOG(mtmp)->hungrytime <= moves))) { /* pet will "catch" and eat this thrown food */ if (canseemon(mtmp)) { boolean big_corpse = (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM && mons[obj->corpsenm].msize > mtmp->data->msize); pline("%s catches %s%s", Monnam(mtmp), the(xname(obj)), !big_corpse ? "." : ", or vice versa!"); } else if (cansee(mtmp->mx, mtmp->my)) pline("%s.", Tobjnam(obj, "stop")); /* dog_eat expects a floor object */ place_object(obj, level, mtmp->mx, mtmp->my); dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE); /* eating might have killed it, but that doesn't matter here; a non-null result suppresses "miss" message for thrown food and also implies that the object has been deleted */ return mtmp; } else return NULL; } if (mtmp->mtame || !mtmp->mcanmove || /* monsters with conflicting structures cannot be tamed */ mtmp->isshk || mtmp->isgd || mtmp->ispriest || mtmp->isminion || is_covetous(mtmp->data) || is_human(mtmp->data) || (is_demon(mtmp->data) && !is_demon(youmonst.data)) || (obj && dogfood(mtmp, obj) >= MANFOOD)) return NULL; if (mtmp->m_id == u.quest_status.leader_m_id) return NULL; /* make a new monster which has the pet extension */ mtmp2 = newmonst(MX_EDOG, mtmp->mnamelth); *mtmp2 = *mtmp; mtmp2->mxtyp = MX_EDOG; mtmp2->mxlth = sizeof (struct edog); if (mtmp->mnamelth) strcpy(NAME_MUTABLE(mtmp2), NAME(mtmp)); initedog(mtmp2); replmon(mtmp, mtmp2); /* `mtmp' is now obsolete */ if (obj) { /* thrown food */ /* defer eating until the edog extension has been set up */ place_object(obj, level, mtmp2->mx, mtmp2->my); /* put on floor */ /* devour the food (might grow into larger, genocided monster) */ if (dog_eat(mtmp2, obj, mtmp2->mx, mtmp2->my, TRUE) == 2) return mtmp2; /* oops, it died... */ /* `obj' is now obsolete */ } if (mtmp2->dlevel == level) newsym(mtmp2->mx, mtmp2->my); if (attacktype(mtmp2->data, AT_WEAP)) { mtmp2->weapon_check = NEED_HTH_WEAPON; mon_wield_item(mtmp2); } return mtmp2; }
void m_throw(struct monst *mon, int x, int y, int dx, int dy, int range, struct obj *obj, boolean verbose) { struct monst *mtmp; struct obj *singleobj; struct tmp_sym *tsym = 0; int hitu, blindinc = 0; bhitpos.x = x; bhitpos.y = y; if (obj->quan == 1L) { /* * Remove object from minvent. This cannot be done later on; * what if the player dies before then, leaving the monster * with 0 daggers? (This caused the infamous 2^32-1 orcish * dagger bug). * * VENOM is not in minvent - it should already be OBJ_FREE. * The extract below does nothing. */ /* not possibly_unwield, which checks the object's */ /* location, not its existence */ if (MON_WEP(mon) == obj) { setmnotwielded(mon, obj); MON_NOWEP(mon); } obj_extract_self(obj); singleobj = obj; obj = NULL; } else { singleobj = splitobj(obj, 1L); obj_extract_self(singleobj); } singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */ singleobj->olev = level; /* object is on the same level as monster */ if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) { if (canseemon(mon) && flags.verbose) { if (is_ammo(singleobj)) pline("%s misfires!", Monnam(mon)); else pline("%s as %s throws it!", Tobjnam(singleobj, "slip"), mon_nam(mon)); } dx = rn2(3) - 1; dy = rn2(3) - 1; /* check validity of new direction */ if (!dx && !dy) { drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } } /* pre-check for doors, walls and boundaries. Also need to pre-check for bars regardless of direction; the random chance for small objects hitting bars is skipped when reaching them at point blank range */ if (!isok(bhitpos.x + dx, bhitpos.y + dy) || IS_ROCK(level->locations[bhitpos.x + dx][bhitpos.y + dy].typ) || closed_door(level, bhitpos.x + dx, bhitpos.y + dy) || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS && hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) { drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } /* Note: drop_throw may destroy singleobj. Since obj must be destroyed early to avoid the dagger bug, anyone who modifies this code should be careful not to use either one after it's been freed. */ tsym = tmpsym_initobj(singleobj); while (range-- > 0) { /* Actually the loop is always exited by break */ bhitpos.x += dx; bhitpos.y += dy; if ((mtmp = m_at(level, bhitpos.x, bhitpos.y)) != 0) { if (ohitmon(mtmp, singleobj, range, verbose)) break; } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { action_interrupted(); if (singleobj->oclass == GEM_CLASS && singleobj->otyp <= LAST_GEM + 9 /* 9 glass colors */ && is_unicorn(youmonst.data) && !u_helpless(hm_all)) { if (singleobj->otyp > LAST_GEM) { pline("You catch the %s.", xname(singleobj)); pline("You are not interested in %s junk.", s_suffix(mon_nam(mon))); makeknown(singleobj->otyp); dropy(singleobj); } else { pline("You accept %s gift in the spirit in which it was " "intended.", s_suffix(mon_nam(mon))); hold_another_object(singleobj, "You catch, but drop, %s.", xname(singleobj), "You catch:"); } break; } if (singleobj->oclass == POTION_CLASS) { if (!Blind) singleobj->dknown = 1; potionhit(&youmonst, singleobj, FALSE); break; } switch (singleobj->otyp) { int dam, hitv; case EGG: if (!touch_petrifies(&mons[singleobj->corpsenm])) { impossible("monster throwing egg type %d", singleobj->corpsenm); hitu = 0; break; } /* fall through */ case CREAM_PIE: case BLINDING_VENOM: hitu = thitu(8, 0, singleobj, NULL); break; default: dam = dmgval(singleobj, &youmonst); hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); if (hitv < -4) hitv = -4; if (is_elf(mon->data) && objects[singleobj->otyp].oc_skill == P_BOW) { hitv++; if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW) hitv++; if (singleobj->otyp == ELVEN_ARROW) dam++; } if (bigmonst(youmonst.data)) hitv++; hitv += 8 + singleobj->spe; if (dam < 1) dam = 1; if (objects[singleobj->otyp].oc_class == WEAPON_CLASS || objects[singleobj->otyp].oc_class == VENOM_CLASS) { hitv += objects[singleobj->otyp].oc_hitbon; } hitu = thitu(hitv, dam, singleobj, NULL); } if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) { poisoned(xname(singleobj), A_STR, killer_msg_obj(POISONING, singleobj), -10); } if (hitu && can_blnd(NULL, &youmonst, (uchar) (singleobj->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), singleobj)) { blindinc = rnd(25); if (singleobj->otyp == CREAM_PIE) { if (!Blind) pline("Yecch! You've been creamed."); else pline("There's something sticky all over your %s.", body_part(FACE)); } else if (singleobj->otyp == BLINDING_VENOM) { int num_eyes = eyecount(youmonst.data); /* venom in the eyes */ if (!Blind) pline("The venom blinds you."); else pline("Your %s sting%s.", (num_eyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)), (num_eyes == 1) ? "s" : ""); } } if (hitu && singleobj->otyp == VAMPIRE_BLOOD) { if (!Drain_resistance) { losexp("vampire blood", FALSE); } } if (hitu && singleobj->otyp == EGG) { if (touched_monster(singleobj->corpsenm)) Stoned = 5; } action_interrupted(); if (hitu || !range) { drop_throw(singleobj, hitu, u.ux, u.uy); break; } } else if (!range /* reached end of path */ /* missile hits edge of screen */ || !isok(bhitpos.x + dx, bhitpos.y + dy) /* missile hits the wall */ || IS_ROCK(level-> locations[bhitpos.x + dx][bhitpos.y + dy].typ) /* missile hit closed door */ || closed_door(level, bhitpos.x + dx, bhitpos.y + dy) /* missile might hit iron bars */ || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS && hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0)) /* Thrown objects "sink" */ || IS_SINK(level->locations[bhitpos.x][bhitpos.y].typ)) { if (singleobj) /* hits_bars might have destroyed it */ drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); break; } tmpsym_at(tsym, bhitpos.x, bhitpos.y); win_delay_output(); } tmpsym_at(tsym, bhitpos.x, bhitpos.y); win_delay_output(); tmpsym_end(tsym); if (blindinc) { u.ucreamed += blindinc; make_blinded(Blinded + (long)blindinc, FALSE); if (!Blind) pline("Your vision quickly clears."); else if (flags.verbose) pline("Use the command #wipe to clean your %s.", body_part(FACE)); } }
int dogaze(void) { struct monst *mtmp; int looked = 0; char qbuf[QBUFSZ]; int i; uchar adtyp = 0; for (i = 0; i < NATTK; i++) { if (youmonst.data->mattk[i].aatyp == AT_GAZE) { adtyp = youmonst.data->mattk[i].adtyp; break; } } if (adtyp != AD_CONF && adtyp != AD_FIRE) { impossible("gaze attack %d?", adtyp); return 0; } if (Blind) { pline("You can't see anything to gaze at."); return 0; } if (u.uen < 15) { pline("You lack the energy to use your special gaze!"); return 0; } u.uen -= 15; iflags.botl = 1; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { looked++; if (Invis && !perceives(mtmp->data)) pline("%s seems not to notice your gaze.", Monnam(mtmp)); else if (mtmp->minvis && !See_invisible) pline("You can't 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) { pline("You avoid gazing at %s.", y_monnam(mtmp)); } else { if (flags.confirm && mtmp->mpeaceful && !Confusion && !Hallucination) { sprintf(qbuf, "Really %s %s?", (adtyp == AD_CONF) ? "confuse" : "attack", mon_nam(mtmp)); if (yn(qbuf) != 'y') continue; setmangry(mtmp); } if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping || !mtmp->mcansee || !haseyes(mtmp->data)) { looked--; continue; } /* No reflection check for consistency with when a monster * gazes at *you*--only medusa gaze gets reflected then. */ if (adtyp == AD_CONF) { if (!mtmp->mconf) pline("Your gaze confuses %s!", mon_nam(mtmp)); else pline("%s is getting more and more confused.", Monnam(mtmp)); mtmp->mconf = 1; } else if (adtyp == AD_FIRE) { int dmg = dice(2,6); pline("You attack %s with a fiery gaze!", mon_nam(mtmp)); if (resists_fire(mtmp)) { pline("The fire doesn't burn %s!", mon_nam(mtmp)); dmg = 0; } if ((int) u.ulevel > rn2(20)) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); if ((int) u.ulevel > rn2(20)) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); if ((int) u.ulevel > rn2(25)) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg; if (mtmp->mhp <= 0) killed(mtmp); } /* For consistency with passive() in uhitm.c, this only * affects you if the monster is still alive. */ if (!DEADMONSTER(mtmp) && (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) { if (!Free_action) { pline("You are frozen by %s gaze!", s_suffix(mon_nam(mtmp))); nomul((u.ulevel > 6 || rn2(4)) ? -dice((int)mtmp->m_lev+1, (int)mtmp->data->mattk[0].damd) : -200, "frozen by a monster's gaze"); return 1; } else pline("You stiffen momentarily under %s gaze.", s_suffix(mon_nam(mtmp))); } /* Technically this one shouldn't affect you at all because * the Medusa gaze is an active monster attack that only * works on the monster's turn, but for it to *not* have an * effect would be too weird. */ if (!DEADMONSTER(mtmp) && (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... */ pline("You turn to stone..."); killer_format = KILLED_BY; killer = "deliberately meeting Medusa's gaze"; done(STONING); } } } } if (!looked) pline("You gaze at no place in particular."); return 1; }
void dosounds(void) { struct mkroom *sroom; int hallu, vx, vy; struct monst *mtmp; if (!canhear() || Engulfed || Underwater) return; hallu = Hallucination ? 1 : 0; if (has_terrain(level, FOUNTAIN) && !rn2(400)) { static const char *const fountain_msg[4] = { "bubbling water.", "water falling on coins.", "the splashing of a naiad.", "a soda fountain!", }; You_hear("%s", fountain_msg[rn2(3) + hallu]); } if (has_terrain(level, SINK) && !rn2(300)) { static const char *const sink_msg[3] = { "a slow drip.", "a gurgling noise.", "dishes being washed!", }; You_hear("%s", sink_msg[rn2(2) + hallu]); } if (search_special(level, COURT) && !rn2(200)) { static const char *const throne_msg[4] = { "the tones of courtly conversation.", "a sceptre pounded in judgment.", "Someone shouts \"Off with %s head!\"", "Queen Beruthiel's cats!", }; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->msleeping || is_lord(mtmp->data) || is_prince(mtmp->data)) && !is_animal(mtmp->data) && mon_in_room(mtmp, COURT)) { /* finding one is enough, at least for now */ int which = rn2(3) + hallu; if (which != 2) You_hear("%s", throne_msg[which]); else pline(throne_msg[2], uhis()); return; } } } if (search_special(level, SWAMP) && !rn2(200)) { static const char *const swamp_msg[3] = { "You hear mosquitoes!", "You smell marsh gas!", /* so it's a smell... */ "You hear Donald Duck!", }; pline("%s", swamp_msg[rn2(2) + hallu]); return; } if ((sroom = search_special(level, VAULT)) && !rn2(200)) { if (gd_sound()) switch (rn2(2) + hallu) { case 1:{ boolean gold_in_vault = FALSE; for (vx = sroom->lx; vx <= sroom->hx; vx++) for (vy = sroom->ly; vy <= sroom->hy; vy++) if (gold_at(level, vx, vy)) gold_in_vault = TRUE; if (vault_occupied(u.urooms) != (ROOM_INDEX(sroom) + ROOMOFFSET)) { if (gold_in_vault) You_hear(!hallu ? "someone counting money." : "the quarterback calling the play."); else You_hear("someone searching."); break; } /* fall into... (yes, even for hallucination) */ } case 0: You_hear("the footsteps of a guard on patrol."); break; case 2: You_hear("Ebenezer Scrooge!"); break; } return; } if (search_special(level, BEEHIVE) && !rn2(200)) { for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->data->mlet == S_ANT && is_flyer(mtmp->data)) && mon_in_room(mtmp, BEEHIVE)) { switch (rn2(2) + hallu) { case 0: You_hear("a low buzzing."); break; case 1: You_hear("an angry drone."); break; case 2: You_hear("bees in your %sbonnet!", uarmh ? "" : "(nonexistent) "); break; } return; } } } if (search_special(level, MORGUE) && !rn2(200)) { for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (is_undead(mtmp->data) && mon_in_room(mtmp, MORGUE)) { switch (rn2(2) + hallu) { case 1: if (!strcmp(body_part(HAIR), "hair")) { pline("The %s on the back of your %s stands up.", body_part(HAIR), body_part(NECK)); break; } /* fall through */ case 2: if (!strcmp(body_part(HAIR), "hair")) { pline("The %s on your %s seems to stand up.", body_part(HAIR), body_part(HEAD)); break; } /* fall through */ case 0: pline("You suddenly realize it is unnaturally quiet."); break; } return; } } } if (search_special(level, BARRACKS) && !rn2(200)) { static const char *const barracks_msg[4] = { "blades being honed.", "loud snoring.", "dice being thrown.", "General MacArthur!", }; int count = 0; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (is_mercenary(mtmp->data) && mon_in_room(mtmp, BARRACKS) && /* sleeping implies not-yet-disturbed (usually) */ (mtmp->msleeping || ++count > 5)) { You_hear("%s", barracks_msg[rn2(3) + hallu]); return; } } } if (search_special(level, ZOO) && !rn2(200)) { static const char *const zoo_msg[3] = { "a sound reminiscent of an elephant stepping on a peanut.", "a sound reminiscent of a seal barking.", "Doctor Dolittle!", }; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->msleeping || is_animal(mtmp->data)) && mon_in_room(mtmp, ZOO)) { You_hear("%s", zoo_msg[rn2(2) + hallu]); return; } } } if ((sroom = search_special(level, ANY_SHOP)) && !rn2(200)) { if (tended_shop(sroom) && !strchr(u.ushops, ROOM_INDEX(sroom) + ROOMOFFSET)) { static const char *const shop_msg[3] = { "someone cursing shoplifters.", "the chime of a cash register.", "Neiman and Marcus arguing!", }; You_hear("%s", shop_msg[rn2(2) + hallu]); } return; } if (search_special(level, DELPHI) && !rn2(400)) { /* make sure the Oracle is still here */ for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_POTTER]) break; /* and don't produce silly effects when he's clearly visible */ if (mtmp && (hallu || !canseemon(mtmp))) { static const char *const ora_msg[5] = { "political commentary.", "convulsive ravings about WLAN controllers.", "an Adirondack woodsman.", "someone ask you for your punchcards.", /* if(hallucinating) */ "loud praise for Netgear devices." /* if(hallucinating) */ }; You_hear("%s", ora_msg[rn2(3) + hallu * 2]); } return; } }
/* return values: * 1: successful spell * 0: unsuccessful spell */ int castmu(struct monst *mtmp, const struct attack *mattk, boolean thinks_it_foundyou, boolean foundyou) { int dmg, ml = mtmp->m_lev; int ret; int spellnum = 0; /* Three cases: * -- monster is attacking you. Search for a useful spell. * -- monster thinks it's attacking you. Search for a useful spell, * without checking for undirected. If the spell found is directed, * it fails with cursetxt() and loss of mspec_used. * -- monster isn't trying to attack. Select a spell once. Don't keep * searching; if that spell is not useful (or if it's directed), * return and do something else. * Since most spells are directed, this means that a monster that isn't * attacking casts spells only a small portion of the time that an * attacking monster does. */ if ((mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) && ml) { int cnt = 40; do { spellnum = rn2(ml); if (mattk->adtyp == AD_SPEL) spellnum = choose_magic_spell(spellnum); else spellnum = choose_clerical_spell(spellnum); /* not trying to attack? don't allow directed spells */ if (!thinks_it_foundyou) { if (!is_undirected_spell(mattk->adtyp, spellnum) || spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) { if (foundyou) impossible("spellcasting monster found you and doesn't know it?"); return 0; } break; } } while (--cnt > 0 && spell_would_be_useless(mtmp, mattk->adtyp, spellnum)); if (cnt == 0) return 0; } /* monster unable to cast spells? */ if (mtmp->mcan || mtmp->mspec_used || !ml) { cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum)); return 0; } if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) { mtmp->mspec_used = 10 - mtmp->m_lev; if (mtmp->mspec_used < 2) mtmp->mspec_used = 2; } /* monster can cast spells, but is casting a directed spell at the wrong place? If so, give a message, and return. Do this *after* penalizing mspec_used. */ if (!foundyou && thinks_it_foundyou && !is_undirected_spell(mattk->adtyp, spellnum)) { pline("%s casts a spell at %s!", canseemon(mtmp) ? Monnam(mtmp) : "Something", level->locations[mtmp->mux][mtmp->muy].typ == WATER ? "empty water" : "thin air"); return 0; } nomul(0, NULL); if (rn2(ml*10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */ if (canseemon(mtmp) && flags.soundok) pline("The air crackles around %s.", mon_nam(mtmp)); return 0; } if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { pline("%s casts a spell%s!", canspotmon(mtmp) ? Monnam(mtmp) : "Something", is_undirected_spell(mattk->adtyp, spellnum) ? "" : (Invisible && !perceives(mtmp->data) && (mtmp->mux != u.ux || mtmp->muy != u.uy)) ? " at a spot near you" : (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) ? " at your displaced image" : " at you"); } /* * As these are spells, the damage is related to the level * of the monster casting the spell. */ if (!foundyou) { dmg = 0; if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) { impossible( "%s casting non-hand-to-hand version of hand-to-hand spell %d?", Monnam(mtmp), mattk->adtyp); return 0; } } else if (mattk->damd) dmg = dice((int)((ml/2) + mattk->damn), (int)mattk->damd); else dmg = dice((int)((ml/2) + 1), 6); if (Half_spell_damage) dmg = (dmg+1) / 2; ret = 1; switch (mattk->adtyp) { case AD_FIRE: pline("You're enveloped in flames."); if (Fire_resistance) { shieldeff(u.ux, u.uy); pline("But you resist the effects."); dmg = 0; } burn_away_slime(); break; case AD_COLD: pline("You're covered in frost."); if (Cold_resistance) { shieldeff(u.ux, u.uy); pline("But you resist the effects."); dmg = 0; } break; case AD_MAGM: pline("You are hit by a shower of missiles!"); if (Antimagic) { shieldeff(u.ux, u.uy); pline("The missiles bounce off!"); dmg = 0; } else dmg = dice((int)mtmp->m_lev/2 + 1,6); break; case AD_SPEL: /* wizard spell */ case AD_CLRC: /* clerical spell */ { if (mattk->adtyp == AD_SPEL) cast_wizard_spell(mtmp, dmg, spellnum); else cast_cleric_spell(mtmp, dmg, spellnum); dmg = 0; /* done by the spell casting functions */ break; } } if (dmg) mdamageu(mtmp, dmg); return ret; }
/* called from check_special_room() when the player enters the temple room */ void intemple(int roomno) { struct monst *priest = findpriest((char)roomno); boolean tended = (priest != NULL); boolean sanctum, can_speak; xchar shrined; const char *msg1, *msg2; if (In_mines(&u.uz) && !historysearch("entered the Minetown temple", TRUE)) historic_event(FALSE, TRUE, "entered the Minetown temple"); if (!temple_occupied(u.urooms0)) { if (tended) { shrined = has_shrine(priest); sanctum = (priest->data == &mons[PM_HIGH_PRIEST] && (shrined & AM_SANCTUM)); can_speak = (priest->mcanmove && !priest->msleeping && canhear()); if (can_speak) { unsigned save_priest = priest->ispriest; /* don't reveal the altar's owner upon temple entry in the endgame; for the Sanctum, the next message names Moloch so suppress the "of Moloch" for him here too */ if (sanctum && !Hallucination) priest->ispriest = 0; pline("%s intones:", canseemon(priest) ? Monnam(priest) : "A nearby voice"); priest->ispriest = save_priest; } msg2 = 0; if (sanctum && CONST_EPRI(priest)->shralign == A_NONE) { if (priest->mpeaceful) { msg1 = "Infidel, you have entered Moloch's Sanctum!"; msg2 = "Be gone!"; msethostility(priest, TRUE, TRUE); } else msg1 = "You desecrate this place by your presence!"; } else { msg1 = msgprintf("Pilgrim, you enter a %s place!", !shrined ? "desecrated" : "sacred"); } if (can_speak) { verbalize("%s", msg1); if (msg2) verbalize("%s", msg2); } if (!sanctum) { /* !tended -> !shrined */ if (!shrined || !p_coaligned(priest) || u.ualign.record <= ALGN_SINNED) pline("You have a%s forbidding feeling...", (!shrined) ? "" : " strange"); else pline("You experience a strange sense of peace."); } } else { switch (rn2(3)) { case 0: pline("You have an eerie feeling..."); break; case 1: pline("You feel like you are being watched."); break; default: pline("A shiver runs down your %s.", body_part(SPINE)); break; } if (!rn2(5)) { struct monst *mtmp; if (!((mtmp = makemon(&mons[PM_GHOST], level, u.ux, u.uy, NO_MM_FLAGS)))) return; if (!Blind || sensemon(mtmp)) pline("An enormous ghost appears next to you!"); else pline("You sense a presence close by!"); msethostility(mtmp, TRUE, TRUE); if (flags.verbose) pline("You are frightened to death, and unable to move."); helpless(3, hr_afraid, "frightened to death", "You regain your composure."); } } } }
void inrange(struct monst *mtmp) { schar tx,ty; /* do nothing if cancelled (but make '1' say something) */ if(mtmp->data->mlet != '1' && mtmp->mcan) return; /* spit fire only when both in a room or both in a corridor */ if(inroom(u.ux,u.uy) != inroom(mtmp->mx,mtmp->my)) return; tx = u.ux - mtmp->mx; ty = u.uy - mtmp->my; if((!tx && abs(ty) < BOLT_LIM) || (!ty && abs(tx) < BOLT_LIM) || (abs(tx) == abs(ty) && abs(tx) < BOLT_LIM)){ switch(mtmp->data->mlet) { case 'D': /* spit fire in the direction of @ (not nec. hitting) */ buzz(-1,mtmp->mx,mtmp->my,sgn(tx),sgn(ty)); break; case '1': if(rn2(WIZSHOT)) break; /* if you zapped wizard with wand of cancellation, he has to shake off the effects before he can throw spells successfully. 1/2 the time they fail anyway */ if(mtmp->mcan || rn2(2)) { if(canseemon(mtmp)) pline("%s makes a gesture, then curses.", Monnam(mtmp)); else pline("You hear mumbled cursing."); if(!rn2(3)) { mtmp->mspeed = 0; mtmp->minvis = 0; } if(!rn2(3)) mtmp->mcan = 0; } else { if(canseemon(mtmp)){ if(!rn2(6) && !Invis) { pline("%s hypnotizes you.", Monnam(mtmp)); nomul(rn2(3) + 3); break; } else pline("%s chants an incantation.", Monnam(mtmp)); } else pline("You hear a mumbled incantation."); switch(rn2(Invis ? 5 : 6)) { case 0: /* create a nasty monster from a deep level */ /* (for the moment, 'nasty' is not implemented) */ (void) makemon((struct permonst *)0, u.ux, u.uy); break; case 1: pline("\"Destroy the thief, my pets!\""); aggravate(); /* aggravate all the monsters */ /* fall into next case */ case 2: if (flags.no_of_wizards == 1 && rnd(5) == 0) /* if only 1 wizard, clone himself */ clonewiz(mtmp); break; case 3: if(mtmp->mspeed == MSLOW) mtmp->mspeed = 0; else mtmp->mspeed = MFAST; break; case 4: mtmp->minvis = 1; break; case 5: /* Only if not Invisible */ pline("You hear a clap of thunder!"); /* shoot a bolt of fire or cold, or a sleep ray */ buzz(-rnd(3),mtmp->mx,mtmp->my,sgn(tx),sgn(ty)); break; } } } if(u.uhp < 1) done_in_by(mtmp); } }
/* called when you move to another level * pets_only: true for ascension or final escape */ void keepdogs(boolean pets_only) { struct monst *mtmp, *mtmp2; struct obj *obj; int num_segs; boolean stay_behind; for (mtmp = level->monlist; mtmp; mtmp = mtmp2) { mtmp2 = mtmp->nmon; if (DEADMONSTER(mtmp)) continue; if (pets_only && !mtmp->mtame) continue; if (((monnear(mtmp, u.ux, u.uy) && levl_follower(mtmp)) || (mtmp == u.usteed) || /* the wiz will level t-port from anywhere to chase the amulet; if you don't have it, will chase you only if in range. -3. */ (Uhave_amulet && mtmp->iswiz)) && ((!mtmp->msleeping && mtmp->mcanmove) /* eg if level teleport or new trap, steed has no control to avoid following */ || (mtmp == u.usteed)) /* monster won't follow if it hasn't noticed you yet */ && !(mtmp->mstrategy & STRAT_WAITFORU)) { stay_behind = FALSE; if (!pets_only && mtmp->mtame && mtmp->meating) { if (canseemon(mtmp)) pline("%s is still eating.", Monnam(mtmp)); stay_behind = TRUE; } else if (mon_has_amulet(mtmp)) { if (canseemon(mtmp)) pline("%s seems very disoriented for a moment.", Monnam(mtmp)); stay_behind = TRUE; } else if (!pets_only && mtmp->mtame && mtmp->mtrapped) { if (canseemon(mtmp)) pline("%s is still trapped.", Monnam(mtmp)); stay_behind = TRUE; } if (mtmp == u.usteed) stay_behind = FALSE; if (stay_behind) { if (mtmp->mleashed) { pline("%s leash suddenly comes loose.", humanoid(mtmp->data) ? (mtmp->female ? "Her" : "His") : "Its"); m_unleash(mtmp, FALSE); } continue; } if (mtmp->isshk) set_residency(mtmp, TRUE); if (mtmp->wormno) { int cnt; /* NOTE: worm is truncated to # segs = max wormno size */ cnt = count_wsegs(mtmp); num_segs = min(cnt, MAX_NUM_WORMS - 1); wormgone(mtmp); } else num_segs = 0; /* set minvent's obj->no_charge to 0 */ for (obj = mtmp->minvent; obj; obj = obj->nobj) { if (Has_contents(obj)) picked_container(obj); /* does the right thing */ obj->no_charge = 0; } relmon(mtmp); newsym(mtmp->mx, mtmp->my); mtmp->mx = COLNO; /* avoid mnexto()/MON_AT() problem */ mtmp->my = ROWNO; mtmp->wormno = num_segs; mtmp->mlstmv = moves; mtmp->nmon = turnstate.migrating_pets; turnstate.migrating_pets = mtmp; } else if (mtmp->iswiz) { /* we want to be able to find him when his next resurrection chance comes up, but have him resume his present location if player returns to this level before that time */ migrate_to_level(mtmp, ledger_no(&u.uz), MIGR_EXACT_XY, NULL); } else if (mtmp->mleashed) { /* this can happen if your quest leader ejects you from the "home" level while a leashed pet isn't next to you */ pline("%s leash goes slack.", s_suffix(Monnam(mtmp))); m_unleash(mtmp, FALSE); } } }
/* u.dx and u.dy must be set */ bool attack(struct monst *mtmp) { schar tmp; boolean malive = TRUE; struct permonst *mdat; mdat = mtmp->data; u_wipe_engr(3); /* andrew@orca: prevent unlimited pick-axe attacks */ if (mdat->mlet == 'L' && !mtmp->mfroz && !mtmp->msleep && !mtmp->mconf && mtmp->mcansee && !rn2(7) && (m_move(mtmp, 0) == 2 /* he died */ || /* he moved: */ mtmp->mx != u.ux + u.dx || mtmp->my != u.uy + u.dy)) return (FALSE); if (mtmp->mimic) { if (!u.ustuck && !mtmp->mflee) u.ustuck = mtmp; switch (levl[u.ux + u.dx][u.uy + u.dy].scrsym) { case '+': pline("The door actually was a Mimic."); break; case '$': pline("The chest was a Mimic!"); break; default: pline("Wait! That's a Mimic!"); } wakeup(mtmp); /* clears mtmp->mimic */ return (TRUE); } wakeup(mtmp); if (mtmp->mhide && mtmp->mundetected) { struct obj *obj; mtmp->mundetected = 0; if ((obj = o_at(mtmp->mx, mtmp->my)) && !Blind) pline("Wait! There's a %s hiding under %s!", mdat->mname, doname(obj)); return (TRUE); } tmp = u.uluck + u.ulevel + mdat->ac + abon(); if (uwep) { if (uwep->olet == WEAPON_SYM || uwep->otyp == PICK_AXE) tmp += uwep->spe; if (uwep->otyp == TWO_HANDED_SWORD) tmp -= 1; else if (uwep->otyp == DAGGER) tmp += 2; else if (uwep->otyp == CRYSKNIFE) tmp += 3; else if (uwep->otyp == SPEAR && strchr("XDne", mdat->mlet)) tmp += 2; } if (mtmp->msleep) { mtmp->msleep = 0; tmp += 2; } if (mtmp->mfroz) { tmp += 4; if (!rn2(10)) mtmp->mfroz = 0; } if (mtmp->mflee) tmp += 2; if (u.utrap) tmp -= 3; /* with a lot of luggage, your agility diminishes */ tmp -= (inv_weight() + 40) / 20; if (tmp <= rnd(20) && !u.uswallow) { if (Blind) pline("You miss it."); else pline("You miss %s.", monnam(mtmp)); } else { /* we hit the monster; be careful: it might die! */ if ((malive = hmon(mtmp, uwep, 0)) == TRUE) { /* monster still alive */ if (!rn2(25) && mtmp->mhp < mtmp->mhpmax / 2) { mtmp->mflee = 1; if (!rn2(3)) mtmp->mfleetim = rnd(100); if (u.ustuck == mtmp && !u.uswallow) u.ustuck = 0; } #ifndef NOWORM if (mtmp->wormno) cutworm(mtmp, u.ux + u.dx, u.uy + u.dy, uwep ? uwep->otyp : 0); #endif /* NOWORM */ } if (mdat->mlet == 'a') { if (rn2(2)) { pline("You are splashed by the blob's acid!"); losehp_m(rnd(6), mtmp); if (!rn2(30)) corrode_armor(); } if (!rn2(6)) corrode_weapon(); } } if (malive && mdat->mlet == 'E' && canseemon(mtmp) && !mtmp->mcan && rn2(3)) { if (mtmp->mcansee) { pline("You are frozen by the floating eye's gaze!"); nomul((u.ulevel > 6 || rn2(4)) ? rn1(20, -21) : -200); } else { pline("The blinded floating eye cannot defend itself."); if (!rn2(500)) if ((int)u.uluck > LUCKMIN) u.uluck--; } } return (TRUE); }
static void mon_vision_summary(const struct monst *mtmp, char *outbuf) { int ways_seen = 0, normal = 0, xraydist; boolean useemon = (boolean) canseemon(mtmp); outbuf[0] = '\0'; xraydist = (u.xray_range<0) ? -1 : u.xray_range * u.xray_range; /* normal vision */ if ((mtmp->wormno ? worm_known(mtmp) : cansee(mtmp->mx, mtmp->my)) && mon_visible(mtmp) && !mtmp->minvis) { ways_seen++; normal++; } /* see invisible */ if (useemon && mtmp->minvis) ways_seen++; /* infravision */ if ((!mtmp->minvis || See_invisible) && see_with_infrared(mtmp)) ways_seen++; /* telepathy */ if (tp_sensemon(mtmp)) ways_seen++; /* xray */ if (useemon && xraydist > 0 && distu(mtmp->mx, mtmp->my) <= xraydist) ways_seen++; if (Detect_monsters) ways_seen++; if (MATCH_WARN_OF_MON(mtmp)) ways_seen++; if (ways_seen > 1 || !normal) { if (normal) { strcat(outbuf, "normal vision"); /* can't actually be 1 yet here */ if (ways_seen-- > 1) strcat(outbuf, ", "); } if (useemon && mtmp->minvis) { strcat(outbuf, "see invisible"); if (ways_seen-- > 1) strcat(outbuf, ", "); } if ((!mtmp->minvis || See_invisible) && see_with_infrared(mtmp)) { strcat(outbuf, "infravision"); if (ways_seen-- > 1) strcat(outbuf, ", "); } if (tp_sensemon(mtmp)) { strcat(outbuf, "telepathy"); if (ways_seen-- > 1) strcat(outbuf, ", "); } if (useemon && xraydist > 0 && distu(mtmp->mx, mtmp->my) <= xraydist) { /* Eyes of the Overworld */ strcat(outbuf, "astral vision"); if (ways_seen-- > 1) strcat(outbuf, ", "); } if (Detect_monsters) { strcat(outbuf, "monster detection"); if (ways_seen-- > 1) strcat(outbuf, ", "); } if (MATCH_WARN_OF_MON(mtmp)) { char wbuf[BUFSZ]; if (Hallucination) strcat(outbuf, "paranoid delusion"); else { sprintf(wbuf, "warned of %s", makeplural(mtmp->data->mname)); strcat(outbuf, wbuf); } if (ways_seen-- > 1) strcat(outbuf, ", "); } } }
/* Maybe rust object, or corrode it if acid damage is called for */ void erode_obj(struct obj *target, /* object (e.g. weapon or armor) to erode */ boolean acid_dmg, boolean fade_scrolls) { int erosion; struct monst *victim; boolean vismon; boolean visobj; if (!target) return; victim = carried(target) ? &youmonst : mcarried(target) ? target->ocarry : NULL; vismon = victim && (victim != &youmonst) && canseemon(victim); visobj = !victim && cansee(bhitpos.x, bhitpos.y); /* assume thrown */ erosion = acid_dmg ? target->oeroded2 : target->oeroded; if (target->greased) { grease_protect(target,NULL,victim); } else if (target->oclass == SCROLL_CLASS) { if (fade_scrolls && target->otyp != SCR_BLANK_PAPER) { if (!Blind) { if (victim == &youmonst) pline("Your %s.", aobjnam(target, "fade")); else if (vismon) pline("%s's %s.", Monnam(victim), aobjnam(target, "fade")); else if (visobj) pline("The %s.", aobjnam(target, "fade")); } target->otyp = SCR_BLANK_PAPER; target->spe = 0; } } else if (target->oerodeproof || (acid_dmg ? !is_corrodeable(target) : !is_rustprone(target))) { if (flags.verbose || !(target->oerodeproof && target->rknown)) { if (victim == &youmonst) pline("Your %s not affected.", aobjnam(target, "are")); else if (vismon) pline("%s's %s not affected.", Monnam(victim), aobjnam(target, "are")); /* no message if not carried */ } if (target->oerodeproof) target->rknown = TRUE; } else if (erosion < MAX_ERODE) { if (victim == &youmonst) pline("Your %s%s!", aobjnam(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); else if (vismon) pline("%s's %s%s!", Monnam(victim), aobjnam(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); else if (visobj) pline("The %s%s!", aobjnam(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); if (acid_dmg) target->oeroded2++; else target->oeroded++; } else { if (flags.verbose) { if (victim == &youmonst) pline("Your %s completely %s.", aobjnam(target, Blind ? "feel" : "look"), acid_dmg ? "corroded" : "rusty"); else if (vismon) pline("%s's %s completely %s.", Monnam(victim), aobjnam(target, "look"), acid_dmg ? "corroded" : "rusty"); else if (visobj) pline("The %s completely %s.", aobjnam(target, "look"), acid_dmg ? "corroded" : "rusty"); } } }
/* armor put on or taken off; might be magical variety */ void update_mon_intrinsics(struct monst *mon, struct obj *obj, boolean on, boolean silently) { int unseen; uchar mask; struct obj *otmp; int which = (int) objects[obj->otyp].oc_oprop; unseen = !canseemon(mon); if (!which) goto maybe_blocks; if (on) { switch (which) { case INVIS: mon->minvis = !mon->invis_blkd; break; case FAST: { boolean save_in_mklev = in_mklev; if (silently) in_mklev = TRUE; mon_adjust_speed(mon, 0, obj); in_mklev = save_in_mklev; break; } /* properties handled elsewhere */ case ANTIMAGIC: case REFLECTING: break; /* properties which have no effect for monsters */ case CLAIRVOYANT: case STEALTH: case TELEPAT: break; /* properties which should have an effect but aren't implemented */ case LEVITATION: case WWALKING: break; /* properties which maybe should have an effect but don't */ case DISPLACED: case FUMBLING: case JUMPING: case PROTECTION: break; default: if (which <= 8) { /* 1 thru 8 correspond to MR_xxx mask values */ /* FIRE,COLD,SLEEP,DISINT,SHOCK,POISON,ACID,STONE */ mask = (uchar) (1 << (which - 1)); mon->mintrinsics |= (unsigned short) mask; } break; } } else { /* off */ switch (which) { case INVIS: mon->minvis = mon->perminvis; break; case FAST: { boolean save_in_mklev = in_mklev; if (silently) in_mklev = TRUE; mon_adjust_speed(mon, 0, obj); in_mklev = save_in_mklev; break; } case FIRE_RES: case COLD_RES: case SLEEP_RES: case DISINT_RES: case SHOCK_RES: case POISON_RES: case ACID_RES: case STONE_RES: mask = (uchar) (1 << (which - 1)); /* If the monster doesn't have this resistance intrinsically, check whether any other worn item confers it. Note that we don't currently check for anything conferred via simply carrying an object. */ if (!(mon->data->mresists & mask)) { for (otmp = mon->minvent; otmp; otmp = otmp->nobj) if (otmp->owornmask && (int) objects[otmp->otyp].oc_oprop == which) break; if (!otmp) mon->mintrinsics &= ~((unsigned short) mask); } break; default: break; } } maybe_blocks: /* obj->owornmask has been cleared by this point, so we can't use it. However, since monsters don't wield armor, we don't have to guard against that and can get away with a blanket worn-mask value. */ switch (w_blocks(obj,~0L)) { case INVIS: mon->invis_blkd = on ? 1 : 0; mon->minvis = on ? 0 : mon->perminvis; break; default: break; } if (!on && mon == u.usteed && obj->otyp == SADDLE) dismount_steed(DISMOUNT_FELL); /* if couldn't see it but now can, or vice versa, update display */ if (!silently && (unseen ^ !canseemon(mon))) newsym(mon->mx, mon->my); }
static void m_dowear_type(struct monst *mon, long flag, boolean creation, boolean racialexception) { struct obj *old, *best, *obj; int m_delay = 0; int unseen = !canseemon(mon); char nambuf[BUFSZ]; if (mon->mfrozen) return; /* probably putting previous item on */ /* Get a copy of monster's name before altering its visibility */ strcpy(nambuf, See_invisible ? Monnam(mon) : mon_nam(mon)); old = which_armor(mon, flag); if (old && old->cursed) return; if (old && flag == W_AMUL) return; /* no such thing as better amulets */ best = old; for (obj = mon->minvent; obj; obj = obj->nobj) { switch(flag) { case W_AMUL: if (obj->oclass != AMULET_CLASS || (obj->otyp != AMULET_OF_LIFE_SAVING && obj->otyp != AMULET_OF_REFLECTION)) continue; best = obj; goto outer_break; /* no such thing as better amulets */ case W_ARMU: if (!is_shirt(obj)) continue; break; case W_ARMC: if (!is_cloak(obj)) continue; break; case W_ARMH: if (!is_helmet(obj)) continue; /* (flimsy exception matches polyself handling) */ if (has_horns(mon->data) && !is_flimsy(obj)) continue; break; case W_ARMS: if (!is_shield(obj)) continue; break; case W_ARMG: if (!is_gloves(obj)) continue; break; case W_ARMF: if (!is_boots(obj)) continue; break; case W_ARM: if (!is_suit(obj)) continue; if (racialexception && (racial_exception(mon, obj) < 1)) continue; break; } if (obj->owornmask) continue; /* I'd like to define a VISIBLE_ARM_BONUS which doesn't assume the * monster knows obj->spe, but if I did that, a monster would keep * switching forever between two -2 caps since when it took off one * it would forget spe and once again think the object is better * than what it already has. */ if (best && (ARM_BONUS(best) + extra_pref(mon,best) >= ARM_BONUS(obj) + extra_pref(mon,obj))) continue; best = obj; } outer_break: if (!best || best == old) return; /* if wearing a cloak, account for the time spent removing and re-wearing it when putting on a suit or shirt */ if ((flag == W_ARM || flag == W_ARMU) && (mon->misc_worn_check & W_ARMC)) m_delay += 2; /* when upgrading a piece of armor, account for time spent taking off current one */ if (old) m_delay += objects[old->otyp].oc_delay; if (old) /* do this first to avoid "(being worn)" */ old->owornmask = 0L; if (!creation) { if (canseemon(mon)) { char buf[BUFSZ]; if (old) sprintf(buf, " removes %s and", distant_name(old, doname)); else buf[0] = '\0'; pline("%s%s puts on %s.", Monnam(mon), buf, distant_name(best,doname)); } /* can see it */ m_delay += objects[best->otyp].oc_delay; mon->mfrozen = m_delay; if (mon->mfrozen) mon->mcanmove = 0; } if (old) update_mon_intrinsics(mon, old, FALSE, creation); mon->misc_worn_check |= flag; best->owornmask |= flag; update_mon_intrinsics(mon, best, TRUE, creation); /* if couldn't see it but now can, or vice versa, */ if (!creation && (unseen ^ !canseemon(mon))) { if (mon->minvis && !See_invisible) { pline("Suddenly you cannot see %s.", nambuf); makeknown(best->otyp); } /* else if (!mon->minvis) pline("%s suddenly appears!", Amonnam(mon)); */ } }
void dosounds(void) { struct mkroom *sroom; int hallu, vx, vy; struct monst *mtmp; if (!flags.soundok || u.uswallow || Underwater) return; if (level->sounds && !rn2(level->sounds->freq)) { int idx = rn2(level->sounds->n_sounds); char *buf; struct lvl_sound_bite *snd = &level->sounds->sounds[idx]; buf = string_subst(snd->msg); switch (snd->flags) { default: case LVLSND_HEARD: You_hear(buf); break; case LVLSND_PLINED: pline(buf); break; case LVLSND_VERBAL: verbalize(buf); break; case LVLSND_FELT: pline("You feel %s", buf); break; } } hallu = Hallucination ? 1 : 0; if (level->flags.nfountains && !rn2(400)) { static const char * const fountain_msg[4] = { "bubbling water.", "water falling on coins.", "the splashing of a naiad.", "a soda fountain!", }; You_hear(fountain_msg[rn2(3)+hallu]); } if (level->flags.nsinks && !rn2(300)) { static const char * const sink_msg[3] = { "a slow drip.", "a gurgling noise.", "dishes being washed!", }; You_hear(sink_msg[rn2(2)+hallu]); } if (level->flags.has_court && !rn2(200)) { static const char * const throne_msg[4] = { "the tones of courtly conversation.", "a sceptre pounded in judgment.", "Someone shouts \"Off with %s head!\"", "Queen Beruthiel's cats!", }; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->msleeping || is_lord(mtmp->data) || is_prince(mtmp->data)) && !is_animal(mtmp->data) && mon_in_room(mtmp, COURT)) { /* finding one is enough, at least for now */ int which = rn2(3)+hallu; if (which != 2) You_hear(throne_msg[which]); else pline(throne_msg[2], uhis()); return; } } } if (level->flags.has_garden && !rn2(200)) { static const char * const garden_msg[4] = { "crickets chirping.", "birds singing.", "grass growing!", "wind in the willows!", }; You_hear(garden_msg[rn2(2) + 2 * hallu]); return; } if (level->flags.has_swamp && !rn2(200)) { static const char * const swamp_msg[3] = { "You hear mosquitoes!", "You smell marsh gas!", /* so it's a smell...*/ "You hear Donald Duck!", }; pline(swamp_msg[rn2(2)+hallu]); return; } if (level->flags.has_vault && !rn2(200)) { if (!(sroom = search_special(level, VAULT))) { /* strange ... */ level->flags.has_vault = 0; return; } if (gd_sound()) switch (rn2(2)+hallu) { case 1: { boolean gold_in_vault = FALSE; for (vx = sroom->lx;vx <= sroom->hx; vx++) for (vy = sroom->ly; vy <= sroom->hy; vy++) if (gold_at(level, vx, vy)) gold_in_vault = TRUE; if (vault_occupied(u.urooms) != (ROOM_INDEX(sroom) + ROOMOFFSET)) { if (gold_in_vault) You_hear(!hallu ? "someone counting money." : "the quarterback calling the play."); else You_hear("someone searching."); break; } /* fall into... (yes, even for hallucination) */ } case 0: You_hear("the footsteps of a guard on patrol."); break; case 2: You_hear("Ebenezer Scrooge!"); break; } return; } if (level->flags.has_beehive && !rn2(200)) { for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->data->mlet == S_ANT && is_flyer(mtmp->data)) && mon_in_room(mtmp, BEEHIVE)) { switch (rn2(2)+hallu) { case 0: You_hear("a low buzzing."); break; case 1: You_hear("an angry drone."); break; case 2: You_hear("bees in your %sbonnet!", uarmh ? "" : "(nonexistent) "); break; } return; } } } if (level->flags.has_lemurepit && !rn2(20)) { for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (mtmp->data == &mons[PM_LEMURE] && mon_in_room(mtmp, LEMUREPIT)) { if (hallu) { switch (rn2(3)) { case 0: You_hear("screams of lust!"); break; case 1: You_hear("the crack of your mistress's whip!"); break; case 2: You_hear("a weeping willow!"); break; } } else { switch (rn2(6)) { case 0: You_hear("the crack of a barbed whip!"); break; case 1: You_hear("the screams of tortured souls!"); break; case 2: You_hear("a wail of eternal anguish!"); break; case 3: You_hear("diabolical laughter!"); break; case 4: You_hear("cries of repentance!"); break; case 5: You_hear("futile pleas for mercy!"); break; } } return; } } } if (level->flags.has_morgue && !rn2(200)) { for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (is_undead(mtmp->data) && mon_in_room(mtmp, MORGUE)) { switch (rn2(2)+hallu) { case 1: if (!strcmp(body_part(HAIR), "hair")) { pline("The %s on the back of your %s stands up.", body_part(HAIR), body_part(NECK)); break; } /* fall through */ case 2: if (!strcmp(body_part(HAIR), "hair")) { pline("The %s on your %s seems to stand up.", body_part(HAIR), body_part(HEAD)); break; } /* fall through */ case 0: pline("You suddenly realize it is unnaturally quiet."); break; } return; } } } if (level->flags.has_barracks && !rn2(200)) { static const char * const barracks_msg[4] = { "blades being honed.", "loud snoring.", "dice being thrown.", "General MacArthur!", }; int count = 0; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (is_mercenary(mtmp->data) && mon_in_room(mtmp, BARRACKS) && /* sleeping implies not-yet-disturbed (usually) */ (mtmp->msleeping || ++count > 5)) { You_hear(barracks_msg[rn2(3)+hallu]); return; } } } if (level->flags.has_zoo && !rn2(200)) { static const char * const zoo_msg[3] = { "a sound reminiscent of an elephant stepping on a peanut.", "a sound reminiscent of a seal barking.", "Doctor Dolittle!", }; for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if ((mtmp->msleeping || is_animal(mtmp->data)) && mon_in_room(mtmp, ZOO)) { You_hear(zoo_msg[rn2(2)+hallu]); return; } } } if (level->flags.has_shop && !rn2(200)) { if (!(sroom = search_special(level, ANY_SHOP))) { /* strange... */ level->flags.has_shop = 0; return; } if (tended_shop(sroom) && !strchr(u.ushops, ROOM_INDEX(sroom) + ROOMOFFSET)) { static const char * const shop_msg[3] = { "someone cursing shoplifters.", "the chime of a cash register.", "Neiman and Marcus arguing!", }; You_hear(shop_msg[rn2(2)+hallu]); } return; } if (Is_oracle_level(&u.uz) && !rn2(400)) { /* make sure the Oracle is still here */ for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_ORACLE]) break; /* and don't produce silly effects when she's clearly visible */ if (mtmp && (hallu || !canseemon(level, mtmp))) { static const char * const ora_msg[5] = { "a strange wind.", /* Jupiter at Dodona */ "convulsive ravings.", /* Apollo at Delphi */ "snoring snakes.", /* AEsculapius at Epidaurus */ "someone say \"No more woodchucks!\"", "a loud ZOT!" /* both rec.humor.oracle */ }; You_hear(ora_msg[rn2(3)+hallu*2]); } return; } if (!Is_blackmarket(&u.uz) && at_dgn_entrance(&u.uz, "One-eyed Sam's Market") && !rn2(200)) { static const char *blkmar_msg[3] = { "You hear someone complaining about the prices.", "Somebody whispers: \"Food rations? Only 900 zorkmids.\"", "You feel like searching for more gold.", }; pline(blkmar_msg[rn2(2)+hallu]); } }
/* pick a lock on a chest or door with a given object */ int pick_lock(struct obj *pick, const struct nh_cmd_arg *arg) { int picktyp, c; coord cc; schar dx, dy, dz; struct rm *door; struct obj *otmp; const char *qbuf; if (!getargdir(arg, NULL, &dx, &dy, &dz)) return 0; cc.x = youmonst.mx + dx; cc.y = youmonst.my + dy; if (!isok(cc.x, cc.y)) return 0; picktyp = pick->otyp; pick->lastused = moves; /* Check whether we're resuming an interrupted previous attempt. For a floor pick, we have u.utracked[tos_lock] as a non-zeroobj and dx and dy as 0. For a door, we have u.utracked_location[tl_lock] specifying the location and u.utracked[tos_lock] as &zeroobj. */ if (u.uoccupation_progress[tos_lock] && ((u.utracked_location[tl_lock].x == cc.x && u.utracked_location[tl_lock].y == cc.y && u.utracked[tos_lock] == &zeroobj) || (dx == 0 && dy == 0 && u.utracked[tos_lock] != &zeroobj))) { static const char no_longer[] = "Unfortunately, you can no longer %s %s."; if (nohands(youmonst.data)) { const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; if (picktyp == CREDIT_CARD) what = "card"; pline(msgc_interrupted, no_longer, "hold the", what); return reset_pick(); } else if (u.utracked[tos_lock] != &zeroobj && !can_reach_floor()) { pline(msgc_interrupted, no_longer, "reach the", "lock"); return reset_pick(); } else { const char *action = lock_action(); if (turnstate.continue_message) pline(msgc_occstart, "You resume your attempt at %s.", action); one_occupation_turn(picklock, "picking the lock", occ_lock); return 1; } } if (nohands(youmonst.data)) { pline(msgc_cancelled, "You can't hold %s -- you have no hands!", doname(pick)); return 0; } if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD && picktyp != SKELETON_KEY)) { impossible("picking lock with object %d?", picktyp); return 0; } if (!dx && !dy) { /* pick lock on a container */ const char *verb; boolean it; int count; if (dz < 0) { pline(msgc_cancelled, "There isn't any sort of lock up %s.", Levitation ? "here" : "there"); return 0; } else if (is_lava(level, youmonst.mx, youmonst.my)) { pline(msgc_cancelled, "Doing that would probably melt your %s.", xname(pick)); return 0; } else if (is_pool(level, youmonst.mx, youmonst.my) && !Underwater) { /* better YAFM - AIS */ pline(msgc_cancelled, "Canals might have locks, but this water doesn't."); return 0; } count = 0; c = 'n'; /* in case there are no boxes here */ for (otmp = level->objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere) if (Is_box(otmp)) { ++count; if (!can_reach_floor()) { pline(msgc_cancelled, "You can't reach %s from up here.", the(xname(otmp))); return 0; } it = 0; if (otmp->obroken) verb = "fix"; else if (!otmp->olocked) verb = "lock", it = 1; else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; else verb = "pick"; qbuf = msgprintf( "There is %s here, %s %s?", safe_qbuf("", sizeof ("There is here, unlock its lock?"), doname(otmp), an(simple_typename(otmp->otyp)), "a box"), verb, it ? "it" : "its lock"); c = ynq(qbuf); if (c == 'q') return 0; if (c == 'n') continue; if (otmp->obroken) { pline(msgc_cancelled, "You can't fix its broken lock with %s.", doname(pick)); return 0; } else if (picktyp == CREDIT_CARD && !otmp->olocked) { /* credit cards are only good for unlocking */ pline(msgc_cancelled, "You can't do that with %s.", doname(pick)); return 0; } u.utracked[tos_lock] = otmp; u.uoccupation_progress[tos_lock] = 0; break; } if (c != 'y') { if (!count) pline(msgc_cancelled, "There doesn't seem to be any sort of lock here."); return 0; /* decided against all boxes */ } } else { /* pick the lock in a door */ struct monst *mtmp; if (u.utrap && u.utraptype == TT_PIT) { pline(msgc_cancelled, "You can't reach over the edge of the pit."); return 0; } door = &level->locations[cc.x][cc.y]; if ((mtmp = m_at(level, cc.x, cc.y)) && canseemon(mtmp)) { if (picktyp == CREDIT_CARD && (mx_eshk(mtmp) || mtmp->data == &mons[PM_ORACLE])) verbalize(msgc_npcvoice, "No checks, no credit, no problem."); else pline(msgc_mispaste, "I don't think %s would appreciate that.", mon_nam(mtmp)); return 0; } if (mtmp && (mtmp->m_ap_type == M_AP_FURNITURE) && (mtmp->mappearance == S_hcdoor || mtmp->mappearance == S_vcdoor) && !Protection_from_shape_changers) { stumble_onto_mimic(mtmp, dx, dy); return 1; } if (!IS_DOOR(door->typ)) { if (is_drawbridge_wall(cc.x, cc.y) >= 0) pline(msgc_cancelled, "You %s no lock on the drawbridge.", Blind ? "feel" : "see"); else pline(msgc_mispaste, "You %s no door there.", Blind ? "feel" : "see"); return 0; } switch (door->doormask) { case D_NODOOR: pline(msgc_cancelled, "This doorway has no door."); return 0; case D_ISOPEN: pline(msgc_cancelled, "You cannot lock an open door."); return 0; case D_BROKEN: pline(msgc_cancelled, "This door is broken."); return 0; default: /* credit cards are only good for unlocking */ if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { pline(msgc_cancelled, "You can't lock a door with a credit card."); return 0; } /* At this point, the player knows that the door is a door, and whether it's locked, but not whether it's trapped; to do this, we set the mem_door_l flag and call map_background, which will clear it if necessary (i.e. not a door after all). */ level->locations[cc.x][cc.y].mem_door_l = 1; map_background(cc.x, cc.y, TRUE); u.utracked[tos_lock] = &zeroobj; u.utracked_location[tl_lock] = cc; u.uoccupation_progress[tos_lock] = 0; } } one_occupation_turn(picklock, "picking the lock", occ_lock); return 1; }
static void cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) { if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) { impossible("cast directed cleric spell (%d) with dmg=0?", spellnum); return; } switch (spellnum) { case CLC_GEYSER: /* this is physical damage, not magical damage */ pline("A sudden geyser slams into you from nowhere!"); dmg = dice(8, 6); if (Half_physical_damage) dmg = (dmg + 1) / 2; break; case CLC_FIRE_PILLAR: pline("A pillar of fire strikes all around you!"); if (Fire_resistance) { shieldeff(u.ux, u.uy); dmg = 0; } else dmg = dice(8, 6); if (Half_spell_damage) dmg = (dmg + 1) / 2; burn_away_slime(); burnarmor(&youmonst); destroy_item(SCROLL_CLASS, AD_FIRE); destroy_item(POTION_CLASS, AD_FIRE); destroy_item(SPBOOK_CLASS, AD_FIRE); burn_floor_paper(u.ux, u.uy, TRUE, FALSE); break; case CLC_LIGHTNING: { boolean reflects; pline("A bolt of lightning strikes down at you from above!"); reflects = ureflects("It bounces off your %s%s.", ""); if (reflects || Shock_resistance) { shieldeff(u.ux, u.uy); dmg = 0; if (reflects) break; } else dmg = dice(8, 6); if (Half_spell_damage) dmg = (dmg + 1) / 2; destroy_item(WAND_CLASS, AD_ELEC); destroy_item(RING_CLASS, AD_ELEC); break; } case CLC_CURSE_ITEMS: pline("You feel as if you need some help."); rndcurse(); dmg = 0; break; case CLC_INSECTS: { /* Try for insects, and if there are none left, go for (sticks to) snakes. -3. */ const struct permonst *pm = mkclass(&u.uz, S_ANT,0); struct monst *mtmp2 = NULL; char let = (pm ? S_ANT : S_SNAKE); boolean success; int i; coord bypos; int quan; quan = (mtmp->m_lev < 2) ? 1 : rnd((int)mtmp->m_lev / 2); if (quan < 3) quan = 3; success = pm ? TRUE : FALSE; for (i = 0; i <= quan; i++) { if (!enexto(&bypos, level, mtmp->mux, mtmp->muy, mtmp->data)) break; if ((pm = mkclass(&u.uz, let,0)) != 0 && (mtmp2 = makemon(pm, level, bypos.x, bypos.y, NO_MM_FLAGS)) != 0) { success = TRUE; mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; set_malign(mtmp2); } } /* Not quite right: * -- message doesn't always make sense for unseen caster (particularly * the first message) * -- message assumes plural monsters summoned (non-plural should be * very rare, unlike in nasty()) * -- message assumes plural monsters seen */ if (!success) pline("%s casts at a clump of sticks, but nothing happens.", Monnam(mtmp)); else if (let == S_SNAKE) pline("%s transforms a clump of sticks into snakes!", Monnam(mtmp)); else if (Invisible && !perceives(mtmp->data) && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s summons insects around a spot near you!", Monnam(mtmp)); else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s summons insects around your displaced image!", Monnam(mtmp)); else pline("%s summons insects!", Monnam(mtmp)); dmg = 0; break; } case CLC_BLIND_YOU: /* note: resists_blnd() doesn't apply here */ if (!Blinded) { int num_eyes = eyecount(youmonst.data); pline("Scales cover your %s!", (num_eyes == 1) ? body_part(EYE) : makeplural(body_part(EYE))); make_blinded(Half_spell_damage ? 100L : 200L, FALSE); if (!Blind) pline("Your vision quickly clears."); dmg = 0; } else impossible("no reason for monster to cast blindness spell?"); break; case CLC_PARALYZE: if (Antimagic || Free_action) { shieldeff(u.ux, u.uy); if (multi >= 0) pline("You stiffen briefly."); nomul(-1, "paralyzed by a monster"); } else { if (multi >= 0) pline("You are frozen in place!"); dmg = 4 + (int)mtmp->m_lev; if (Half_spell_damage) dmg = (dmg + 1) / 2; nomul(-dmg, "paralyzed by a monster"); } dmg = 0; break; case CLC_CONFUSE_YOU: if (Antimagic) { shieldeff(u.ux, u.uy); pline("You feel momentarily dizzy."); } else { boolean oldprop = !!Confusion; dmg = (int)mtmp->m_lev; if (Half_spell_damage) dmg = (dmg + 1) / 2; make_confused(HConfusion + dmg, TRUE); if (Hallucination) pline("You feel %s!", oldprop ? "trippier" : "trippy"); else pline("You feel %sconfused!", oldprop ? "more " : ""); } dmg = 0; break; case CLC_CURE_SELF: if (mtmp->mhp < mtmp->mhpmax) { if (canseemon(mtmp)) pline("%s looks better.", Monnam(mtmp)); /* note: player healing does 6d4; this used to do 1d8 */ if ((mtmp->mhp += dice(3,6)) > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; dmg = 0; } break; case CLC_OPEN_WOUNDS: if (Antimagic) { shieldeff(u.ux, u.uy); dmg = (dmg + 1) / 2; } if (dmg <= 5) pline("Your skin itches badly for a moment."); else if (dmg <= 10) pline("Wounds appear on your body!"); else if (dmg <= 20) pline("Severe wounds appear on your body!"); else pline("Your body is covered with painful wounds!"); break; default: impossible("mcastu: invalid clerical spell (%d)", spellnum); dmg = 0; break; } if (dmg) mdamageu(mtmp, dmg); }