/* SCCS Id: @(#)uhitm.c 3.3 2000/02/20 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" STATIC_DCL boolean FDECL(known_hitum, (struct monst *,int *,struct attack *)); STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *)); STATIC_DCL boolean FDECL(hitum, (struct monst *,int,struct attack *)); STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *,struct obj *,int)); STATIC_DCL boolean FDECL(m_slips_free, (struct monst *mtmp,struct attack *mattk)); STATIC_DCL int FDECL(explum, (struct monst *,struct attack *)); STATIC_DCL void FDECL(start_engulf, (struct monst *)); STATIC_DCL void NDECL(end_engulf); STATIC_DCL int FDECL(gulpum, (struct monst *,struct attack *)); STATIC_DCL boolean FDECL(hmonas, (struct monst *,int)); STATIC_DCL void FDECL(nohandglow, (struct monst *)); extern boolean notonhead; /* for long worms */ /* The below might become a parameter instead if we use it a lot */ static int dieroll; /* Used to flag attacks caused by Stormbringer's maliciousness. */ static boolean override_confirmation = FALSE; #define PROJECTILE(obj) ((obj) && is_ammo(obj)) /* modified from hurtarmor() in mhitu.c */ /* This is not static because it is also used for monsters rusting monsters */ void hurtmarmor(mdef, attk) struct monst *mdef; int attk; { int hurt; struct obj *target; switch(attk) { /* 0 is burning, which we should never be called with */ case AD_RUST: hurt = 1; break; case AD_CORRODE: hurt = 3; break; default: hurt = 2; break; } /* What the following code does: it keeps looping until it * finds a target for the rust monster. * Head, feet, etc... not covered by metal, or covered by * rusty metal, are not targets. However, your body always * is, no matter what covers it. */ while (1) { switch(rn2(5)) { case 0: target = which_armor(mdef, W_ARMH); if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) continue; break; case 1: target = which_armor(mdef, W_ARMC); if (target) { (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); break; } if ((target = which_armor(mdef, W_ARM)) != (struct obj *)0) { (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); #ifdef TOURIST } else if ((target = which_armor(mdef, W_ARMU)) != (struct obj *)0) { (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); #endif } break; case 2: target = which_armor(mdef, W_ARMS); if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) continue; break; case 3: target = which_armor(mdef, W_ARMG); if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) continue; break; case 4: target = which_armor(mdef, W_ARMF); if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) continue; break; } break; /* Out of while loop */ } } boolean attack_checks(mtmp, wep) register struct monst *mtmp; struct obj *wep; /* uwep for attack(), null for kick_monster() */ { char qbuf[QBUFSZ]; /* if you're close enough to attack, alert any waiting monster */ mtmp->mstrategy &= ~STRAT_WAITMASK; if (u.uswallow && mtmp == u.ustuck) return FALSE; if (flags.forcefight) { /* Do this in the caller, after we checked that the monster * didn't die from the blow. Reason: putting the 'I' there * causes the hero to forget the square's contents since * both 'I' and remembered contents are stored in .glyph. * If the monster dies immediately from the blow, the 'I' will * not stay there, so the player will have suddenly forgotten * the square's contents for no apparent reason. if (!canspotmon(mtmp) && !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph)) map_invisible(u.ux+u.dx, u.uy+u.dy); */ return FALSE; } /* Put up an invisible monster marker, but one exception is for * monsters that hide. That already prints a warning message and * prevents you from hitting the monster just via the hidden monster * code below; if we also did that here, similar behavior would be * happening two turns in a row. */ if (!canspotmon(mtmp) && !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) && !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) { pline("Wait! There's %s there you can't see!", something); map_invisible(u.ux+u.dx, u.uy+u.dy); /* if it was an invisible mimic, treat it as if we stumbled * onto a visible mimic */ if(mtmp->m_ap_type && !Protection_from_shape_changers) { if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK)) u.ustuck = mtmp; } wakeup(mtmp); /* always necessary; also un-mimics mimics */ return TRUE; } if(mtmp->m_ap_type && !Protection_from_shape_changers && !sensemon(mtmp)) { /* If a hidden mimic was in a square where a player remembers * some (probably different) unseen monster, the player is in * luck--he attacks it even though it's hidden. */ if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) { seemimic(mtmp); return(FALSE); } stumble_onto_mimic(mtmp); return TRUE; } if (mtmp->mundetected && !canseemon(mtmp) && (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { mtmp->mundetected = mtmp->msleeping = 0; newsym(mtmp->mx, mtmp->my); if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) { seemimic(mtmp); return(FALSE); } if (!(Blind ? Blind_telepat : Unblind_telepat)) { struct obj *obj; if (Blind || (is_pool(mtmp->mx,mtmp->my) && !Underwater)) pline("Wait! There's a hidden monster there!"); else if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0) pline("Wait! There's %s hiding under %s!", an(l_monnam(mtmp)), doname(obj)); return TRUE; } } if (flags.confirm && mtmp->mpeaceful && !Confusion && !Hallucination && !Stunned) { /* Intelligent chaotic weapons (Stormbringer) want blood */ if (wep && wep->oartifact == ART_STORMBRINGER) { override_confirmation = TRUE; return(FALSE); } if (canspotmon(mtmp)) { Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp)); if (yn(qbuf) != 'y') { flags.move = 0; return(TRUE); } } } return(FALSE); } schar find_roll_to_hit(mtmp) register struct monst *mtmp; { schar tmp; int tmp2; tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc + maybe_polyd(youmonst.data->mlevel, u.ulevel); /* it is unchivalrous to attack the defenseless or from behind */ if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL && (!mtmp->mcanmove || mtmp->msleeping || mtmp->mflee) && u.ualign.record > -10) { You("caitiff!"); adjalign(-1); } /* attacking peaceful creatures is bad for the samurai's giri */ if (Role_if(PM_SAMURAI) && mtmp->mpeaceful && u.ualign.record > -10) { You("dishonorably attack the innocent!"); adjalign(-1); } /* Adjust vs. (and possibly modify) monster state. */ if(mtmp->mstun) tmp += 2; if(mtmp->mflee) tmp += 2; if (mtmp->msleeping) { mtmp->msleeping = 0; tmp += 2; } if(!mtmp->mcanmove) { tmp += 4; if(!rn2(10)) { mtmp->mcanmove = 1; mtmp->mfrozen = 0; } } if (is_orc(mtmp->data) && maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF))) tmp++; if(Role_if(PM_MONK) && !Upolyd) { if (uarm) { Your("armor is rather cumbersome..."); tmp -= urole.spelarmr; } else if (!uwep) tmp += (u.ulevel / 3) + 2; } /* with a lot of luggage, your agility diminishes */ if ((tmp2 = near_capacity()) != 0) tmp -= (tmp2*2) - 1; if (u.utrap) tmp -= 3; /* Some monsters have a combination of weapon attacks and non-weapon * attacks. It is therefore wrong to add hitval to tmp; we must add * it only for the specific attack (in hmonas()). */ if (uwep && !Upolyd) { tmp += hitval(uwep, mtmp); tmp += weapon_hit_bonus(uwep); } return tmp; } /* try to attack; return FALSE if monster evaded */ /* u.dx and u.dy must be set */ boolean attack(mtmp) register struct monst *mtmp; { schar tmp; register struct permonst *mdat = mtmp->data; /* This section of code provides protection against accidentally * hitting peaceful (like '@') and tame (like 'd') monsters. * Protection is provided as long as player is not: blind, confused, * hallucinating or stunned. * changes by wwp 5/16/85 * More changes 12/90, -dkh-. if its tame and safepet, (and protected * 07/92) then we assume that you're not trying to attack. Instead, * you'll usually just swap places if this is a movement command */ /* Intelligent chaotic weapons (Stormbringer) want blood */ if (is_safepet(mtmp) && !flags.forcefight) { if (!uwep || uwep->oartifact != ART_STORMBRINGER) { /* there are some additional considerations: this won't work * if in a shop or Punished or you miss a random roll or * if you can walk thru walls and your pet cannot (KAA) or * if your pet is a long worm (unless someone does better). * there's also a chance of displacing a "frozen" monster. * sleeping monsters might magically walk in their sleep. */ unsigned int foo = (Punished || !rn2(7) || is_longworm(mtmp->data)); if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || foo || (IS_ROCK(levl[u.ux][u.uy].typ) && !passes_walls(mtmp->data))) { char buf[BUFSZ]; mtmp->mflee = 1; mtmp->mfleetim = rnd(6); Strcpy(buf, y_monnam(mtmp)); buf[0] = highc(buf[0]); You("stop. %s is in the way!", buf); return(TRUE); } else if ((mtmp->mfrozen || (! mtmp->mcanmove) || (mtmp->data->mmove == 0)) && rn2(6)) { pline("%s doesn't seem to move!", Monnam(mtmp)); return(TRUE); } else return(FALSE); } } /* possibly set in attack_checks; examined in known_hitum, called via hitum or hmonas below */ override_confirmation = FALSE; if (attack_checks(mtmp, uwep)) return(TRUE); if (Upolyd) { /* certain "pacifist" monsters don't attack */ if(noattacks(youmonst.data)) { You("have no way to attack monsters physically."); mtmp->mstrategy &= ~STRAT_WAITMASK; goto atk_done; } } if(check_capacity("You cannot fight while so heavily loaded.")) goto atk_done; if (u.twoweap && !can_twoweapon()) untwoweapon(); if(unweapon) { unweapon = FALSE; if(flags.verbose) { if(uwep) You("begin bashing monsters with your %s.", aobjnam(uwep, (char *)0)); else if (!cantwield(youmonst.data)) You("begin %sing monsters with your %s %s.", Role_if(PM_MONK) ? "strik" : "bash", uarmg ? "gloved" : "bare", /* Del Lamb */ makeplural(body_part(HAND))); } } exercise(A_STR, TRUE); /* you're exercising muscles */ /* andrew@orca: prevent unlimited pick-axe attacks */ u_wipe_engr(3); /* Is the "it died" check actually correct? */ if(mdat->mlet == S_LEPRECHAUN && !mtmp->mfrozen && !mtmp->msleeping && !mtmp->mconf && mtmp->mcansee && !rn2(7) && (m_move(mtmp, 0) == 2 || /* it died */ mtmp->mx != u.ux+u.dx || mtmp->my != u.uy+u.dy)) /* it moved */ return(FALSE); tmp = find_roll_to_hit(mtmp); if (Upolyd) (void) hmonas(mtmp, tmp); else (void) hitum(mtmp, tmp, youmonst.data->mattk); mtmp->mstrategy &= ~STRAT_WAITMASK; atk_done: /* see comment in attack_checks() */ /* we only need to check for this if we did an attack_checks() * and it returned 0 (it's okay to attack), and the monster didn't * evade. */ if (flags.forcefight && mtmp->mhp > 0 && !canspotmon(mtmp) && !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) && !(u.uswallow && mtmp == u.ustuck)) map_invisible(u.ux+u.dx, u.uy+u.dy); return(TRUE); } STATIC_OVL boolean known_hitum(mon, mhit, uattk) /* returns TRUE if monster still lives */ register struct monst *mon; register int *mhit; struct attack *uattk; { register boolean malive = TRUE; if (override_confirmation) { /* this may need to be generalized if weapons other than Stormbringer acquire similar anti-social behavior... */ if (flags.verbose) Your("bloodthirsty blade attacks!"); } if(!*mhit) { missum(mon, uattk); } else { int oldhp = mon->mhp; /* KMH, conduct */ if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) u.uconduct.weaphit++; /* we hit the monster; be careful: it might die! */ notonhead = (mon->mx != u.ux+u.dx || mon->my != u.uy+u.dy); malive = hmon(mon, uwep, 0); if (malive && u.twoweap) malive = hmon(mon, uswapwep, 0); if (malive) { /* monster still alive */ if(!rn2(25) && mon->mhp < mon->mhpmax/2 && !(u.uswallow && mon == u.ustuck)) { /* maybe should regurgitate if swallowed? */ mon->mflee = 1; if(!rn2(3)) { mon->mfleetim = rnd(100); if (!Blind) pline("%s turns to flee!", (Monnam(mon))); } if(u.ustuck == mon && !u.uswallow && !sticks(youmonst.data)) u.ustuck = 0; } /* Vorpal Blade hit converted to miss */ /* could be headless monster or worm tail */ if (mon->mhp == oldhp) *mhit = 0; if (mon->wormno && *mhit) cutworm(mon, u.ux+u.dx, u.uy+u.dy, uwep); } } return(malive); } STATIC_OVL boolean hitum(mon, tmp, uattk) /* returns TRUE if monster still lives */ struct monst *mon; int tmp; struct attack *uattk; { boolean malive; int mhit = (tmp > (dieroll = rnd(20)) || u.uswallow); if(tmp > dieroll) exercise(A_DEX, TRUE); malive = known_hitum(mon, &mhit, uattk); (void) passive(mon, mhit, malive, AT_WEAP); return(malive); } boolean /* general "damage monster" routine */ hmon(mon, obj, thrown) /* return TRUE if mon still alive */ struct monst *mon; struct obj *obj; int thrown; { boolean result, anger_guards; anger_guards = (mon->mpeaceful && (mon->ispriest || mon->isshk || mon->data == &mons[PM_WATCHMAN] || mon->data == &mons[PM_WATCH_CAPTAIN])); result = hmon_hitmon(mon, obj, thrown); if (mon->ispriest && !rn2(2)) ghod_hitsu(mon); if (anger_guards) (void)angry_guards(!flags.soundok); return result; } /* guts of hmon() */ STATIC_OVL boolean hmon_hitmon(mon, obj, thrown) struct monst *mon; struct obj *obj; int thrown; { int tmp; struct permonst *mdat = mon->data; int barehand_silver_rings = 0; /* The basic reason we need all these booleans is that we don't want * a "hit" message when a monster dies, so we have to know how much * damage it did _before_ outputting a hit message, but any messages * associated with the damage don't come out until _after_ outputting * a hit message. */ boolean hittxt = FALSE, destroyed = FALSE; boolean get_dmg_bonus = TRUE; boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE; boolean silvermsg = FALSE; boolean valid_weapon_attack = FALSE; int wtype; struct obj *monwep; char yourbuf[BUFSZ]; wakeup(mon); if(!obj) { /* attack with bare hands */ if (mdat == &mons[PM_SHADE]) tmp = 0; else if (martial_bonus()) tmp = rnd(4); /* bonus for martial arts */ else tmp = rnd(2); valid_weapon_attack = (tmp > 1); /* blessed gloves give bonuses when fighting 'bare-handed' */ if (uarmg && uarmg->blessed && (is_undead(mdat) || is_demon(mdat))) tmp += rnd(4); /* So do silver rings. Note: rings are worn under gloves, so you * don't get both bonuses. */ if (!uarmg) { if (uleft && objects[uleft->otyp].oc_material == SILVER) barehand_silver_rings++; if (uright && objects[uright->otyp].oc_material == SILVER) barehand_silver_rings++; if (barehand_silver_rings && hates_silver(mdat)) { tmp += rnd(20); silvermsg = TRUE; } } } else { if(obj->oclass == WEAPON_CLASS || is_weptool(obj) || obj->oclass == GEM_CLASS) { /* is it not a melee weapon? */ if (/* if you strike with a bow... */ is_launcher(obj) || /* or strike with a missile in your hand... */ (!thrown && (is_missile(obj) || is_ammo(obj))) || /* or use a pole at short range and not mounted... */ (!thrown && #ifdef STEED !u.usteed && #endif is_pole(obj)) || /* or throw a missile without the proper bow... */ (is_ammo(obj) && !ammo_and_launcher(obj, uwep))) { /* then do only 1-2 points of damage */ if (mdat == &mons[PM_SHADE] && obj->otyp != SILVER_ARROW) tmp = 0; else tmp = rnd(2); } else { tmp = dmgval(obj, mon); /* a minimal hit doesn't exercise proficiency */ valid_weapon_attack = (tmp > 1); if (!valid_weapon_attack || mon == u.ustuck || u.twoweap) { ; /* no special bonuses */ } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd) { You("strike %s from behind!", mon_nam(mon)); tmp += rnd(u.ulevel); hittxt = TRUE; } else if (dieroll == 2 && obj == uwep && obj->oclass == WEAPON_CLASS && (bimanual(obj) || (Role_if(PM_SAMURAI) && obj->otyp == KATANA && !uarms)) && ((wtype = uwep_skill_type()) != P_NONE && P_SKILL(wtype) >= P_SKILLED) && ((monwep = MON_WEP(mon)) != 0 && weapon_type(monwep) != P_WHIP && !obj_resists(monwep, 50 + 15 * greatest_erosion(monwep), 100))) { /* * 2.5% chance of shattering defender's weapon when * using a two-handed weapon; less if uwep is rusted. * [dieroll == 2 is most successful non-beheading or * -bisecting hit, in case of special artifact damage; * the percentage chance is (1/20)*(50/100).] */ monwep->owornmask &= ~W_WEP; MON_NOWEP(mon); mon->weapon_check = NEED_WEAPON; pline("%s %s shatter%s from the force of your blow!", s_suffix(Monnam(mon)), xname(monwep), (monwep->quan) == 1L ? "s" : ""); m_useup(mon, monwep); /* If someone just shattered MY weapon, I'd flee! */ if (rn2(4) && !mon->mflee) { mon->mflee = 1; mon->mfleetim = d(2,3); } hittxt = TRUE; } if (obj->oartifact && artifact_hit(&youmonst, mon, obj, &tmp, dieroll)) { if(mon->mhp <= 0) /* artifact killed monster */ return FALSE; if (tmp == 0) return TRUE; hittxt = TRUE; } if (objects[obj->otyp].oc_material == SILVER && hates_silver(mdat)) silvermsg = TRUE; if(!thrown && obj == uwep && obj->otyp == BOOMERANG && !rnl(3)) { pline("As you hit %s, %s breaks into splinters.", mon_nam(mon), the(xname(obj))); useup(obj); obj = (struct obj *) 0; hittxt = TRUE; if (mdat != &mons[PM_SHADE]) tmp++; } else if(thrown && (is_ammo(obj) || is_missile(obj))) { if (ammo_and_launcher(obj, uwep)) { /* Elves and Samurai do extra damage using * their bows&arrows; they're highly trained. */ if (Role_if(PM_SAMURAI) && obj->otyp == YA && uwep->otyp == YUMI) tmp++; else if (Race_if(PM_ELF) && obj->otyp == ELVEN_ARROW && uwep->otyp == ELVEN_BOW) tmp++; } if(obj->opoisoned && is_poisonable(obj)) ispoisoned = TRUE; } } } else if(obj->oclass == POTION_CLASS) { if (obj->quan > 1L) setworn(splitobj(obj, 1L), W_WEP); else setuwep((struct obj *)0); freeinv(obj); potionhit(mon, obj, TRUE); if (mon->mhp <= 0) return FALSE; /* killed */ hittxt = TRUE; /* in case potion effect causes transformation */ mdat = mon->data; tmp = (mdat == &mons[PM_SHADE]) ? 0 : 1; } else { boolean shade_aware = FALSE; switch(obj->otyp) { case BOULDER: /* 1d20 */ case HEAVY_IRON_BALL: /* 1d25 */ case IRON_CHAIN: /* 1d4+1 */ tmp = dmgval(obj, mon); shade_aware = TRUE; /* dmgval handles it */ break; case MIRROR: if (breaktest(obj)) { You("break %s mirror. That's bad luck!", shk_your(yourbuf, obj)); change_luck(-2); useup(obj); obj = (struct obj *) 0; hittxt = TRUE; } tmp = 1; break; #ifdef TOURIST case EXPENSIVE_CAMERA: You("succeed in destroying %s camera. Congratulations!", shk_your(yourbuf, obj)); useup(obj); return(TRUE); #endif case CORPSE: /* fixed by polder@cs.vu.nl */ if (touch_petrifies(&mons[obj->corpsenm])) { tmp = 1; hittxt = TRUE; You("hit %s with %s corpse.", mon_nam(mon), obj->dknown ? the(mons[obj->corpsenm].mname) : an(mons[obj->corpsenm].mname)); if (!munstone(mon, TRUE)) minstapetrify(mon, TRUE); if (resists_ston(mon)) break; /* note: hp may be <= 0 even if munstoned==TRUE */ return (boolean) (mon->mhp > 0); #if 0 } else if (touch_petrifies(mdat)) { /* maybe turn the corpse into a statue? */ #endif } tmp = (obj->corpsenm >= LOW_PM ? mons[obj->corpsenm].msize : 0) + 1; break; case EGG: { #define useup_eggs(o) { if (thrown) obfree(o,(struct obj *)0); \ else useupall(o); \ o = (struct obj *)0; } /* now gone */ long cnt = obj->quan; tmp = 1; /* nominal physical damage */ get_dmg_bonus = FALSE; hittxt = TRUE; /* message always given */ /* egg is always either used up or transformed, so next hand-to-hand attack should yield a "bashing" mesg */ if (obj == uwep) unweapon = TRUE; if (obj->spe && obj->corpsenm >= LOW_PM) { if (obj->quan < 5) change_luck((schar) -(obj->quan)); else change_luck(-5); } if (touch_petrifies(&mons[obj->corpsenm])) { /*learn_egg_type(obj->corpsenm);*/ You("hit %s with %s %s egg%s. Splat!", mon_nam(mon), obj->known ? "the" : cnt > 1L ? "some" : "a", obj->known ? mons[obj->corpsenm].mname : "petrifying", plur(cnt)); obj->known = 1; /* (not much point...) */ useup_eggs(obj); if (!munstone(mon, TRUE)) minstapetrify(mon, TRUE); if (resists_ston(mon)) break; return (boolean) (mon->mhp > 0); } else { /* ordinary egg(s) */ const char *eggp = (obj->corpsenm != NON_PM && obj->known) ? the(mons[obj->corpsenm].mname) : (cnt > 1L) ? "some" : "an"; You("hit %s with %s egg%s.", mon_nam(mon), eggp, plur(cnt)); if (touch_petrifies(mdat)) { pline_The("egg%s %s alive any more...", plur(cnt), (cnt == 1L) ? "isn't" : "aren't"); if (obj->timed) obj_stop_timers(obj); obj->otyp = ROCK; obj->oclass = GEM_CLASS; obj->oartifact = 0; obj->spe = 0; obj->known = obj->dknown = obj->bknown = 0; obj->owt = weight(obj); if (thrown) place_object(obj, mon->mx, mon->my); } else { pline("Splat!"); useup_eggs(obj); exercise(A_WIS, FALSE); } } break; #undef useup_eggs } case CLOVE_OF_GARLIC: /* no effect against demons */ if (is_undead(mdat)) { mon->mflee = 1; mon->mfleetim += d(2,4); pline("%s turns to flee!", Monnam(mon)); } tmp = 1; break; case CREAM_PIE: case BLINDING_VENOM: mon->msleeping = 0; if (can_blnd(&youmonst, mon, (uchar) (obj->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), obj)) { if (Blind) { pline(obj->otyp == CREAM_PIE ? "Splat!" : "Splash!"); } else if (obj->otyp == BLINDING_VENOM) { pline_The("venom blinds %s%s!", mon_nam(mon), mon->mcansee ? "" : " further"); } else { char *whom = mon_nam(mon); /* note: s_suffix returns a modifiable buffer */ if (haseyes(mdat) && mdat != &mons[PM_FLOATING_EYE]) whom = strcat(s_suffix(whom), " face"); pline_The("%s splashes over %s!", xname(obj), whom); } setmangry(mon); mon->mcansee = 0; tmp = rn1(25, 21); if(((int) mon->mblinded + tmp) > 127) mon->mblinded = 127; else mon->mblinded += tmp; } else { pline(obj->otyp==CREAM_PIE ? "Splat!" : "Splash!"); setmangry(mon); } if (thrown) obfree(obj, (struct obj *)0); else useup(obj); hittxt = TRUE; get_dmg_bonus = FALSE; tmp = 0; break; case ACID_VENOM: /* thrown (or spit) */ if (resists_acid(mon)) { Your("venom hits %s harmlessly.", mon_nam(mon)); tmp = 0; } else { Your("venom burns %s!", mon_nam(mon)); tmp = dmgval(obj, mon); } if (thrown) obfree(obj, (struct obj *)0); else useup(obj); hittxt = TRUE; get_dmg_bonus = FALSE; break; default: /* non-weapons can damage because of their weight */ /* (but not too much) */ tmp = obj->owt/100; if(tmp < 1) tmp = 1; else tmp = rnd(tmp); if(tmp > 6) tmp = 6; } if (!shade_aware && mdat == &mons[PM_SHADE] && obj && objects[obj->otyp].oc_material != SILVER) tmp = 0; } } /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG) * *OR* if attacking bare-handed!! */ if (get_dmg_bonus && tmp > 0) { tmp += u.udaminc; /* If you throw using a propellor, you don't get a strength * bonus but you do get an increase-damage bonus. */ if(!thrown || !obj || !uwep || !ammo_and_launcher(obj, uwep)) tmp += dbon(); } if (valid_weapon_attack) { struct obj *wep; /* to be valid a projectile must have had the correct projector */ wep = PROJECTILE(obj) ? uwep : obj; tmp += weapon_dam_bonus(wep); /* [this assumes that `!thrown' implies wielded...] */ wtype = thrown ? weapon_type(wep) : uwep_skill_type(); use_skill(wtype, 1); } if (ispoisoned) { int nopoison = (10 - (obj->owt/10)); if(nopoison < 2) nopoison = 2; if Role_if(PM_SAMURAI) { You("dishonorably use a poisoned weapon!"); adjalign(-sgn(u.ualign.type)); } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) { You_feel("like an evil coward for using a poisoned weapon."); adjalign(-1); } if (obj && !rn2(nopoison)) { obj->opoisoned = FALSE; Your("%s%s no longer poisoned.", xname(obj), (obj->quan == 1L) ? " is" : "s are"); /**FIXME**/ } if (resists_poison(mon)) needpoismsg = TRUE; else if (rn2(10)) tmp += rnd(6); else poiskilled = TRUE; } if (tmp < 1) { /* make sure that negative damage adjustment can't result in inadvertently boosting the victim's hit points */ tmp = 0; if (mdat == &mons[PM_SHADE]) { if (!hittxt) { Your("attack passes harmlessly through %s.", mon_nam(mon)); hittxt = TRUE; } } else { if (get_dmg_bonus) tmp = 1; } } /* VERY small chance of stunning opponent if unarmed. */ if (tmp > 1 && !thrown && !obj && !uwep && !uarm && !uarms && !Upolyd) { if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) && !bigmonst(mdat) && !thick_skinned(mdat)) { if (canspotmon(mon)) pline("%s staggers from your powerful strike!", Monnam(mon)); mon->mstun = 1; hittxt = TRUE; if (mon->mcanmove && mon != u.ustuck) { xchar mdx, mdy; /* see if the monster has a place to move into */ mdx = mon->mx + u.dx; mdy = mon->my + u.dy; if (goodpos(mdx, mdy, mon)) { remove_monster(mon->mx, mon->my); newsym(mon->mx, mon->my); place_monster(mon, mdx, mdy); newsym(mon->mx, mon->my); set_apparxy(mon); } } } } mon->mhp -= tmp; if(mon->mhp < 1) destroyed = TRUE; if (mon->mtame && (!mon->mflee || mon->mfleetim) && tmp > 0) { unsigned fleetim; abuse_dog(mon); mon->mflee = TRUE; /* Rick Richardson */ fleetim = mon->mfleetim + (unsigned)(10 * rnd(tmp)); mon->mfleetim = min(fleetim,127); } if((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING]) && obj && obj == uwep && objects[obj->otyp].oc_material == IRON && mon->mhp > 1 && !thrown && !mon->mcan /* && !destroyed -- guaranteed by mhp > 1 */ ) { if (clone_mon(mon)) { pline("%s divides as you hit it!", Monnam(mon)); hittxt = TRUE; } } if(!hittxt && !destroyed) { if(thrown) /* thrown => obj exists */ hit(xname(obj), mon, exclam(tmp) ); else if(!flags.verbose) You("hit it."); else You("%s %s%s", Role_if(PM_BARBARIAN) ? "smite" : "hit", mon_nam(mon), canseemon(mon) ? exclam(tmp) : "."); } if (silvermsg) { const char *fmt; char *whom = mon_nam(mon); if (canspotmon(mon)) { if (barehand_silver_rings == 1) fmt = "Your silver ring sears %s!"; else if (barehand_silver_rings == 2) fmt = "Your silver rings sear %s!"; else fmt = "The silver sears %s!"; } else { *whom = highc(*whom); /* "it" -> "It" */ fmt = "%s is seared!"; } /* note: s_suffix returns a modifiable buffer */ if (!noncorporeal(mdat)) whom = strcat(s_suffix(whom), " flesh"); pline(fmt, whom); } if (needpoismsg) pline_The("poison doesn't seem to affect %s.", mon_nam(mon)); if (poiskilled) { pline_The("poison was deadly..."); xkilled(mon, 0); return FALSE; } else if (destroyed) { killed(mon); /* takes care of most messages */ } else if(u.umconf && !thrown) { nohandglow(mon); if(!mon->mconf && !resist(mon, '+', 0, NOTELL)) { mon->mconf = 1; if (!mon->mstun && mon->mcanmove && !mon->msleeping && canseemon(mon)) pline("%s appears confused.", Monnam(mon)); } } return((boolean)(destroyed ? FALSE : TRUE)); } /* check whether slippery clothing protects from hug or wrap attack */ /* [currently assumes that you are the attacker] */ STATIC_OVL boolean m_slips_free(mdef, mattk) struct monst *mdef; struct attack *mattk; { struct obj *obj; if (mattk->adtyp == AD_DRIN) { /* intelligence drain attacks the head */ obj = which_armor(mdef, W_ARMH); } else { /* grabbing attacks the body */ obj = which_armor(mdef, W_ARMC); /* cloak */ if (!obj) obj = which_armor(mdef, W_ARM); /* suit */ #ifdef TOURIST if (!obj) obj = which_armor(mdef, W_ARMU); /* shirt */ #endif } /* if your cloak/armor is greased, monster slips off; this protection might fail (33% chance) when the armor is cursed */ if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) && (!obj->cursed || rn2(3))) { You("%s %s %s %s!", mattk->adtyp == AD_WRAP ? "slip off of" : "grab, but cannot hold onto", s_suffix(mon_nam(mdef)), obj->greased ? "greased" : "slippery", /* avoid "slippery slippery cloak" for undiscovered oilskin cloak */ (obj->greased || objects[obj->otyp].oc_name_known) ? xname(obj) : "cloak"); if (obj->greased && !rn2(2)) { pline_The("grease wears off."); obj->greased = 0; } return TRUE; } return FALSE; } STATIC_DCL void NDECL(demonpet); /* * Send in a demon pet for the hero. Exercise wisdom. * * This function used to be inline to damageum(), but the Metrowerks compiler * (DR4 and DR4.5) screws up with an internal error 5 "Expression Too Complex." * Pulling it out makes it work. */ STATIC_OVL void demonpet() { struct permonst *pm; struct monst *dtmp; pline("Some hell-p has arrived!"); pm = !rn2(6) ? &mons[ndemon(u.ualign.type)] : youmonst.data; if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0) (void)tamedog(dtmp, (struct obj *)0); exercise(A_WIS, TRUE); } /* * Player uses theft attack against monster. * * If the target is wearing body armor, take all of its possesions; * otherwise, take one object. [Is this really the behavior we want?] * * This routine implicitly assumes that there is no way to be able to * resist petfication (ie, be polymorphed into a xorn or golem) at the * same time as being able to steal (poly'd into nymph or succubus). * If that ever changes, the check for touching a cockatrice corpse * will need to be smarter about whether to break out of the theft loop. */ STATIC_OVL void steal_it(mdef, mattk) struct monst *mdef; struct attack *mattk; { struct obj *otmp, *stealoid, **minvent_ptr; long unwornmask; if (!mdef->minvent) return; /* nothing to take */ /* look for worn body armor */ stealoid = (struct obj *)0; if (could_seduce(&youmonst, mdef, mattk)) { /* find armor, and move it to end of inventory in the process */ minvent_ptr = &mdef->minvent; while ((otmp = *minvent_ptr) != 0) if (otmp->owornmask & W_ARM) { if (stealoid) panic("steal_it: multiple worn suits"); *minvent_ptr = otmp->nobj; /* take armor out of minvent */ stealoid = otmp; stealoid->nobj = (struct obj *)0; } else { minvent_ptr = &otmp->nobj; } *minvent_ptr = stealoid; /* put armor back into minvent */ } if (stealoid) { /* we will be taking everything */ if (gender(mdef) == (int) u.mfemale && youmonst.data->mlet == S_NYMPH) You("charm %s. She gladly hands over her possessions.", mon_nam(mdef)); else You("seduce %s and %s starts to take off %s clothes.", mon_nam(mdef), he[pronoun_gender(mdef)], his[pronoun_gender(mdef)]); } while ((otmp = mdef->minvent) != 0) { /* take the object away from the monster */ obj_extract_self(otmp); if ((unwornmask = otmp->owornmask) != 0L) { mdef->misc_worn_check &= ~unwornmask; otmp->owornmask = 0L; update_mon_intrinsics(mdef, otmp, FALSE); if (otmp == stealoid) /* special message for final item */ pline("%s finishes taking off %s suit.", Monnam(mdef), his[pronoun_gender(mdef)]); } /* give the object to the character */ otmp = hold_another_object(otmp, "You steal %s.", doname(otmp), "You steal: "); if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]) && !uarmg) { char kbuf[BUFSZ]; Sprintf(kbuf, "stolen %s corpse", mons[otmp->corpsenm].mname); instapetrify(kbuf); break; /* stop the theft even if hero survives */ } /* more take-away handling, after theft message */ if (unwornmask & W_WEP) { /* stole wielded weapon */ possibly_unwield(mdef); } else if (unwornmask & W_ARMG) { /* stole worn gloves */ mselftouch(mdef, (const char *)0, TRUE); if (mdef->mhp <= 0) /* it's now a statue */ return; /* can't continue stealing */ } if (!stealoid) break; /* only taking one item */ } } int damageum(mdef, mattk) register struct monst *mdef; register struct attack *mattk; { register struct permonst *pd = mdef->data; register int tmp = d((int)mattk->damn, (int)mattk->damd); if (is_demon(youmonst.data) && !rn2(13) && !uwep && u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS && u.umonnum != PM_BALROG) { demonpet(); return(0); } switch(mattk->adtyp) { case AD_STUN: if(!Blind) pline("%s staggers for a moment.", Monnam(mdef)); mdef->mstun = 1; /* fall through to next case */ case AD_WERE: /* no effect on monsters */ case AD_HEAL: case AD_LEGS: case AD_PHYS: if(mattk->aatyp == AT_WEAP) { if(uwep) tmp = 0; } else if(mattk->aatyp == AT_KICK) { if(thick_skinned(mdef->data)) tmp = 0; if(mdef->data == &mons[PM_SHADE]) { if (!(uarmf && uarmf->blessed)) { impossible("bad shade attack function flow?"); tmp = 0; } else tmp = rnd(4); /* bless damage */ } } break; case AD_FIRE: if (!Blind) pline("%s is %s!", Monnam(mdef), mattk->aatyp == AT_HUGS ? "being roasted" : "on fire"); if (pd == &mons[PM_STRAW_GOLEM] || pd == &mons[PM_PAPER_GOLEM]) { if (!Blind) pline("%s burns completely!", Monnam(mdef)); xkilled(mdef,0); } tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE); tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE); if (resists_fire(mdef)) { if (!Blind) pline_The("fire doesn't heat %s!", mon_nam(mdef)); golemeffects(mdef, AD_FIRE, tmp); shieldeff(mdef->mx, mdef->my); tmp = 0; } /* only potions damage resistant players in destroy_item */ tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE); break; case AD_COLD: if (!Blind) pline("%s is covered in frost!", Monnam(mdef)); if (resists_cold(mdef)) { shieldeff(mdef->mx, mdef->my); if (!Blind) pline_The("frost doesn't chill %s!", mon_nam(mdef)); golemeffects(mdef, AD_COLD, tmp); tmp = 0; } tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD); break; case AD_ELEC: if (!Blind) pline("%s is zapped!", Monnam(mdef)); tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC); if (resists_elec(mdef)) { if (!Blind) pline_The("zap doesn't shock %s!", mon_nam(mdef)); golemeffects(mdef, AD_ELEC, tmp); shieldeff(mdef->mx, mdef->my); tmp = 0; } /* only rings damage resistant players in destroy_item */ tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC); break; case AD_ACID: if (resists_acid(mdef)) tmp = 0; break; case AD_STON: if (!munstone(mdef, TRUE)) minstapetrify(mdef, TRUE); tmp = 0; break; #ifdef SEDUCE case AD_SSEX: #endif case AD_SEDU: case AD_SITM: steal_it(mdef, mattk); tmp = 0; break; case AD_SGLD: if (mdef->mgold) { u.ugold += mdef->mgold; mdef->mgold = 0; Your("purse feels heavier."); } exercise(A_DEX, TRUE); tmp = 0; break; case AD_TLPT: if(tmp <= 0) tmp = 1; if(tmp < mdef->mhp) { char nambuf[BUFSZ]; boolean u_saw_mon = canseemon(mdef); /* record the name before losing sight of monster */ Strcpy(nambuf, Monnam(mdef)); if (u_teleport_mon(mdef, FALSE) && u_saw_mon && !canseemon(mdef)) pline("%s suddenly disappears!", nambuf); } break; case AD_BLND: if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj*)0)) { if(!Blind && mdef->mcansee) pline("%s is blinded.", Monnam(mdef)); mdef->mcansee = 0; tmp += mdef->mblinded; if (tmp > 127) tmp = 127; mdef->mblinded = tmp; } tmp = 0; break; case AD_CURS: if (night() && !rn2(10) && !mdef->mcan) { if (mdef->data == &mons[PM_CLAY_GOLEM]) { if (!Blind) pline("Some writing vanishes from %s head!", s_suffix(mon_nam(mdef))); xkilled(mdef, 0); /* Don't return yet; keep hp<1 and tmp=0 for pet msg */ } else { mdef->mcan = 1; You("chuckle."); } } tmp = 0; break; case AD_DRLI: if (rn2(2) && !resists_drli(mdef)) { int xtmp = d(2,6); pline("%s suddenly seems weaker!", Monnam(mdef)); mdef->mhpmax -= xtmp; if ((mdef->mhp -= xtmp) <= 0 || !mdef->m_lev) { pline("%s dies!", Monnam(mdef)); xkilled(mdef,0); } else mdef->m_lev--; } tmp = 0; break; case AD_RUST: if (pd == &mons[PM_IRON_GOLEM]) { pline("%s falls to pieces!", Monnam(mdef)); xkilled(mdef,0); } hurtmarmor(mdef, AD_RUST); tmp = 0; break; case AD_CORRODE: hurtmarmor(mdef, AD_CORRODE); tmp = 0; break; case AD_DCAY: if (pd == &mons[PM_WOOD_GOLEM] || pd == &mons[PM_LEATHER_GOLEM]) { pline("%s falls to pieces!", Monnam(mdef)); xkilled(mdef,0); } hurtmarmor(mdef, AD_DCAY); tmp = 0; break; case AD_DRST: case AD_DRDX: case AD_DRCO: if (!rn2(8)) { Your("%s was poisoned!", mpoisons_subj(&youmonst, mattk)); if (resists_poison(mdef)) pline_The("poison doesn't seem to affect %s.", mon_nam(mdef)); else { if (!rn2(10)) { Your("poison was deadly..."); tmp = mdef->mhp; } else tmp += rn1(10,6); } } break; case AD_DRIN: if (notonhead || !has_head(mdef->data)) { pline("%s doesn't seem harmed.", Monnam(mdef)); tmp = 0; break; } if (m_slips_free(mdef, mattk)) break; if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) { pline("%s helmet blocks your attack to %s head.", s_suffix(Monnam(mdef)), his[pronoun_gender(mdef)]); break; } You("eat %s brain!", s_suffix(mon_nam(mdef))); u.uconduct.food++; if (!vegan(mdef->data)) u.uconduct.unvegan++; if (!vegetarian(mdef->data)) violated_vegetarian(); if (mindless(mdef->data)) { pline("%s doesn't notice.", Monnam(mdef)); break; } tmp += rnd(10); morehungry(-rnd(30)); /* cannot choke */ if (ABASE(A_INT) < AMAX(A_INT)) { ABASE(A_INT) += rnd(4); if (ABASE(A_INT) > AMAX(A_INT)) ABASE(A_INT) = AMAX(A_INT); flags.botl = 1; } exercise(A_WIS, TRUE); break; case AD_STCK: if (!sticks(mdef->data)) u.ustuck = mdef; /* it's now stuck to you */ break; case AD_WRAP: if (!sticks(mdef->data)) { if (!u.ustuck && !rn2(10)) { if (m_slips_free(mdef, mattk)) { tmp = 0; } else { You("swing yourself around %s!", mon_nam(mdef)); u.ustuck = mdef; } } else if(u.ustuck == mdef) { /* Monsters don't wear amulets of magical breathing */ if (is_pool(u.ux,u.uy) && !is_swimmer(mdef->data)) { You("drown %s...", mon_nam(mdef)); tmp = mdef->mhp; } else if(mattk->aatyp == AT_HUGS) pline("%s is being crushed.", Monnam(mdef)); } else { tmp = 0; if (flags.verbose) You("brush against %s %s.", s_suffix(mon_nam(mdef)), mbodypart(mdef, LEG)); } } else tmp = 0; break; case AD_PLYS: if (mdef->mcanmove && !rn2(3) && tmp < mdef->mhp) { if (!Blind) pline("%s is frozen by you!", Monnam(mdef)); mdef->mcanmove = 0; mdef->mfrozen = rnd(10); } break; case AD_SLEE: if (!mdef->msleeping && sleep_monst(mdef, rnd(10), -1)) { if (!Blind) pline("%s is put to sleep by you!", Monnam(mdef)); slept_monst(mdef); } break; case AD_SLIM: if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] && mdef->data != &mons[PM_FIRE_ELEMENTAL] && mdef->data != &mons[PM_GREEN_SLIME]) { You("turn %s into slime.", mon_nam(mdef)); (void) newcham(mdef, &mons[PM_GREEN_SLIME]); tmp = 0; } break; case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ /* There's no msomearmor() function, so just do damage */ break; default: tmp = 0; break; } if((mdef->mhp -= tmp) < 1) { if (mdef->mtame && !cansee(mdef->mx,mdef->my)) { You_feel("embarrassed for a moment."); if (tmp) xkilled(mdef, 0); /* !tmp but hp<1: already killed */ } else if (!flags.verbose) { You("destroy it!"); if (tmp) xkilled(mdef, 0); } else if (tmp) killed(mdef); return(2); } return(1); } STATIC_OVL int explum(mdef, mattk) register struct monst *mdef; register struct attack *mattk; { register int tmp = d((int)mattk->damn, (int)mattk->damd); You("explode!"); switch(mattk->adtyp) { boolean resistance; /* only for cold/fire/elec */ case AD_BLND: if (!resists_blnd(mdef)) { pline("%s is blinded by your flash of light!", Monnam(mdef)); mdef->mblinded = min((int)mdef->mblinded + tmp, 127); mdef->mcansee = 0; } break; case AD_HALU: if (haseyes(mdef->data) && mdef->mcansee) { pline("%s is affected by your flash of light!", Monnam(mdef)); mdef->mconf = 1; } break; case AD_COLD: resistance = resists_cold(mdef); goto common; case AD_FIRE: resistance = resists_fire(mdef); goto common; case AD_ELEC: resistance = resists_elec(mdef); common: if (!resistance) { pline("%s gets blasted!", Monnam(mdef)); mdef->mhp -= tmp; if (mdef->mhp <= 0) { killed(mdef); return(2); } } else { shieldeff(mdef->mx, mdef->my); if (is_golem(mdef->data)) golemeffects(mdef, (int)mattk->adtyp, tmp); else pline_The("blast doesn't seem to affect %s.", mon_nam(mdef)); } break; default: break; } return(1); } STATIC_OVL void start_engulf(mdef) struct monst *mdef; { if (!Invisible) { map_location(u.ux, u.uy, TRUE); tmp_at(DISP_ALWAYS, mon_to_glyph(&youmonst)); tmp_at(mdef->mx, mdef->my); } You("engulf %s!", mon_nam(mdef)); delay_output(); delay_output(); } STATIC_OVL void end_engulf() { if (!Invisible) { tmp_at(DISP_END, 0); newsym(u.ux, u.uy); } } STATIC_OVL int gulpum(mdef,mattk) register struct monst *mdef; register struct attack *mattk; { register int tmp; register int dam = d((int)mattk->damn, (int)mattk->damd); struct obj *otmp; /* Not totally the same as for real monsters. Specifically, these * don't take multiple moves. (It's just too hard, for too little * result, to program monsters which attack from inside you, which * would be necessary if done accurately.) Instead, we arbitrarily * kill the monster immediately for AD_DGST and we regurgitate them * after exactly 1 round of attack otherwise. -KAA */ if(mdef->data->msize >= MZ_HUGE) return 0; if(u.uhunger < 1500 && !u.uswallow) { for (otmp = mdef->minvent; otmp; otmp = otmp->nobj) (void) snuff_lit(otmp); if(!touch_petrifies(mdef->data) || Stone_resistance) { #ifdef LINT /* static char msgbuf[BUFSZ]; */ char msgbuf[BUFSZ]; #else static char msgbuf[BUFSZ]; #endif start_engulf(mdef); switch(mattk->adtyp) { case AD_DGST: /* eating a Rider or its corpse is fatal */ if (is_rider(mdef->data)) { pline("Unfortunately, digesting any of it is fatal."); end_engulf(); Sprintf(msgbuf, "unwisely tried to eat %s", mdef->data->mname); killer = msgbuf; killer_format = NO_KILLER_PREFIX; done(DIED); return 0; /* lifesaved */ } if (Slow_digestion) { dam = 0; break; } /* KMH, conduct */ u.uconduct.food++; if (!vegan(mdef->data)) u.uconduct.unvegan++; if (!vegetarian(mdef->data)) violated_vegetarian(); /* Use up amulet of life saving */ if (!!(otmp = mlifesaver(mdef))) m_useup(mdef, otmp); newuhs(FALSE); xkilled(mdef,2); if (mdef->mhp > 0) { /* monster lifesaved */ You("hurriedly regurgitate the sizzling in your stomach."); } else { u.uhunger += mdef->data->cnutrit; Sprintf(msgbuf, "You totally digest %s.", mon_nam(mdef)); if ((tmp = 3 + (mdef->data->cwt >> 6)) != 0) { /* setting afternmv = end_engulf is tempting, * but will cause problems if the player is * attacked (which uses his real location) or * if his See_invisible wears off */ You("digest %s.", mon_nam(mdef)); if (Slow_digestion) tmp *= 2; nomul(-tmp); nomovemsg = msgbuf; } else pline("%s", msgbuf); exercise(A_CON, TRUE); } end_engulf(); return(2); case AD_PHYS: pline("%s is pummeled with your debris!",Monnam(mdef)); break; case AD_ACID: pline("%s is covered with your goo!", Monnam(mdef)); if (resists_acid(mdef)) { pline("It seems harmless to %s.", mon_nam(mdef)); dam = 0; } break; case AD_BLND: if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj *)0)) { if (mdef->mcansee) pline("%s can't see in there!", Monnam(mdef)); mdef->mcansee = 0; dam += mdef->mblinded; if (dam > 127) dam = 127; mdef->mblinded = dam; } dam = 0; break; case AD_ELEC: if (rn2(2)) { pline_The("air around %s crackles with electricity.", mon_nam(mdef)); if (resists_elec(mdef)) { pline("%s seems unhurt.", Monnam(mdef)); dam = 0; } golemeffects(mdef,(int)mattk->adtyp,dam); } else dam = 0; break; case AD_COLD: if (rn2(2)) { if (resists_cold(mdef)) { pline("%s seems mildly chilly.", Monnam(mdef)); dam = 0; } else pline("%s is freezing to death!",Monnam(mdef)); golemeffects(mdef,(int)mattk->adtyp,dam); } else dam = 0; break; case AD_FIRE: if (rn2(2)) { if (resists_fire(mdef)) { pline("%s seems mildly hot.", Monnam(mdef)); dam = 0; } else pline("%s is burning to a crisp!",Monnam(mdef)); golemeffects(mdef,(int)mattk->adtyp,dam); } else dam = 0; break; } end_engulf(); if ((mdef->mhp -= dam) <= 0) { killed(mdef); if (mdef->mhp <= 0) /* not lifesaved */ return(2); } You("%s %s!", is_animal(youmonst.data) ? "regurgitate" : "expel", mon_nam(mdef)); if (Slow_digestion || is_animal(youmonst.data)) { pline("Obviously, you didn't like %s taste.", s_suffix(mon_nam(mdef))); } } else { char kbuf[BUFSZ]; You("bite into %s.", mon_nam(mdef)); Sprintf(kbuf, "swallowing %s whole", an(mdef->data->mname)); instapetrify(kbuf); } } return(0); } void missum(mdef,mattk) register struct monst *mdef; register struct attack *mattk; { if (could_seduce(&youmonst, mdef, mattk)) You("pretend to be friendly to %s.", mon_nam(mdef)); else if(canspotmon(mdef) && flags.verbose) You("miss %s.", mon_nam(mdef)); else You("miss it."); if (!mdef->msleeping && mdef->mcanmove) wakeup(mdef); } STATIC_OVL boolean hmonas(mon, tmp) /* attack monster as a monster. */ register struct monst *mon; register int tmp; { register struct attack *mattk; int i, sum[NATTK], hittmp = 0; int nsum = 0; int dhit = 0; for(i = 0; i < NATTK; i++) { sum[i] = 0; mattk = &(youmonst.data->mattk[i]); switch(mattk->aatyp) { case AT_WEAP: use_weapon: /* Certain monsters don't use weapons when encountered as enemies, * but players who polymorph into them have hands or claws and thus * should be able to use weapons. This shouldn't prohibit the use * of most special abilities, either. */ /* Potential problem: if the monster gets multiple weapon attacks, * we currently allow the player to get each of these as a weapon * attack. Is this really desirable? */ if (uwep) { hittmp = hitval(uwep, mon); hittmp += weapon_hit_bonus(uwep); tmp += hittmp; } dhit = (tmp > (dieroll = rnd(20)) || u.uswallow); /* KMH -- Don't accumulate to-hit bonuses */ if (uwep) tmp -= hittmp; /* Enemy dead, before any special abilities used */ if (!known_hitum(mon,&dhit,mattk)) { sum[i] = 2; break; } else sum[i] = 1; /* might be a worm that gets cut in half */ if (m_at(u.ux+u.dx, u.uy+u.dy) != mon) return((boolean)(nsum != 0)); /* Do not print "You hit" message, since known_hitum * already did it. */ if (dhit && mattk->adtyp != AD_SPEL && mattk->adtyp != AD_PHYS) sum[i] = damageum(mon,mattk); break; case AT_CLAW: if (i==0 && uwep && !cantwield(youmonst.data)) goto use_weapon; #ifdef SEDUCE /* succubi/incubi are humanoid, but their _second_ * attack is AT_CLAW, not their first... */ if (i==1 && uwep && (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS)) goto use_weapon; #endif case AT_KICK: case AT_BITE: case AT_STNG: case AT_TUCH: case AT_BUTT: case AT_TENT: if (i==0 && uwep && (youmonst.data->mlet==S_LICH)) goto use_weapon; if ((dhit = (tmp > rnd(20) || u.uswallow)) != 0) { int compat; if (!u.uswallow && (compat=could_seduce(&youmonst, mon, mattk))) { You("%s %s %s.", mon->mcansee && haseyes(mon->data) ? "smile at" : "talk to", mon_nam(mon), compat == 2 ? "engagingly":"seductively"); /* doesn't anger it; no wakeup() */ sum[i] = damageum(mon, mattk); break; } wakeup(mon); /* maybe this check should be in damageum()? */ if (mon->data == &mons[PM_SHADE] && !(mattk->aatyp == AT_KICK && uarmf && uarmf->blessed)) { Your("attack passes harmlessly through %s.", mon_nam(mon)); break; } if (mattk->aatyp == AT_KICK) You("kick %s.", mon_nam(mon)); else if (mattk->aatyp == AT_BITE) You("bite %s.", mon_nam(mon)); else if (mattk->aatyp == AT_STNG) You("sting %s.", mon_nam(mon)); else if (mattk->aatyp == AT_BUTT) You("butt %s.", mon_nam(mon)); else if (mattk->aatyp == AT_TUCH) You("touch %s.", mon_nam(mon)); else if (mattk->aatyp == AT_TENT) Your("tentacles suck %s.", mon_nam(mon)); else You("hit %s.", mon_nam(mon)); sum[i] = damageum(mon, mattk); } else missum(mon, mattk); break; case AT_HUGS: /* automatic if prev two attacks succeed, or if * already grabbed in a previous attack */ dhit = 1; wakeup(mon); if (mon->data == &mons[PM_SHADE]) Your("hug passes harmlessly through %s.", mon_nam(mon)); else if (!sticks(mon->data) && !u.uswallow) { if (mon==u.ustuck) { pline("%s is being %s.", Monnam(mon), u.umonnum==PM_ROPE_GOLEM ? "choked": "crushed"); sum[i] = damageum(mon, mattk); } else if(i >= 2 && sum[i-1] && sum[i-2]) { You("grab %s!", mon_nam(mon)); u.ustuck = mon; sum[i] = damageum(mon, mattk); } } break; case AT_EXPL: /* automatic hit if next to */ dhit = -1; wakeup(mon); sum[i] = explum(mon, mattk); break; case AT_ENGL: if((dhit = (tmp > rnd(20+i)))) { wakeup(mon); if (mon->data == &mons[PM_SHADE]) Your("attempt to surround %s is harmless.", mon_nam(mon)); else sum[i]= gulpum(mon,mattk); } else missum(mon, mattk); break; case AT_MAGC: /* No check for uwep; if wielding nothing we want to * do the normal 1-2 points bare hand damage... */ if (i==0 && (youmonst.data->mlet==S_KOBOLD || youmonst.data->mlet==S_ORC || youmonst.data->mlet==S_GNOME )) goto use_weapon; case AT_NONE: case AT_BOOM: continue; /* Not break--avoid passive attacks from enemy */ case AT_BREA: case AT_SPIT: case AT_GAZE: /* all done using #monster command */ dhit = 0; break; default: /* Strange... */ impossible("strange attack of yours (%d)", mattk->aatyp); } if (dhit == -1) rehumanize(); if (sum[i] == 2) return((boolean)passive(mon, 1, 0, mattk->aatyp)); /* defender dead */ else { (void) passive(mon, sum[i], 1, mattk->aatyp); nsum |= sum[i]; } if (!Upolyd) break; /* No extra attacks if no longer a monster */ if (multi < 0) break; /* If paralyzed while attacking, i.e. floating eye */ } return((boolean)(nsum != 0)); } /* Special (passive) attacks on you by monsters done here. */ int passive(mon, mhit, malive, aatyp) register struct monst *mon; register boolean mhit; register int malive; uchar aatyp; { register struct permonst *ptr = mon->data; register int i, tmp; for(i = 0; ; i++) { if(i >= NATTK) return(malive | mhit); /* no passive attacks */ if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */ } /* Note: tmp not always used */ if (ptr->mattk[i].damn) tmp = d((int)ptr->mattk[i].damn, (int)ptr->mattk[i].damd); else if(ptr->mattk[i].damd) tmp = d((int)mon->m_lev+1, (int)ptr->mattk[i].damd); else tmp = 0; /* These affect you even if they just died */ switch(ptr->mattk[i].adtyp) { case AD_ACID: if(mhit && rn2(2)) { if (Blind || !flags.verbose) You("are splashed!"); else You("are splashed by %s acid!", s_suffix(mon_nam(mon))); if (!Acid_resistance) mdamageu(mon, tmp); if(!rn2(30)) erode_armor(&youmonst, TRUE); } if(mhit && !rn2(6)) { if (aatyp == AT_KICK) { if (uarmf) (void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) erode_weapon(uwep, TRUE); } exercise(A_STR, FALSE); break; case AD_STON: if(mhit) { /* mhit does not mean you physically hit; it just means the attack was successful */ if ((aatyp == AT_KICK && !uarmf) || ((aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) && !uwep && !uarmg) || aatyp == AT_BITE || aatyp == AT_STNG || aatyp == AT_BUTT || aatyp == AT_TENT || aatyp == AT_HUGS || aatyp == AT_ENGL) { if (!Stone_resistance && !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { You("turn to stone..."); done_in_by(mon); return 2; } } } break; case AD_RUST: if(mhit && !mon->mcan) { if (aatyp == AT_KICK) { if (uarmf) (void) rust_dmg(uarmf, xname(uarmf), 1, TRUE, &youmonst); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) erode_weapon(uwep, FALSE); } break; case AD_CORRODE: if(mhit && !mon->mcan) { if (aatyp == AT_KICK) { if (uarmf) (void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) erode_weapon(uwep, TRUE); } break; case AD_MAGM: /* wrath of gods for attacking Oracle */ if(Antimagic) { shieldeff(u.ux, u.uy); pline("A hail of magic missiles narrowly misses you!"); } else { You("are hit by magic missiles appearing from thin air!"); mdamageu(mon, tmp); } break; case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ { struct obj *obj = (aatyp == AT_KICK) ? uarmf : uwep ? uwep : uarmg; if (mhit && !mon->mcan && obj) { if (drain_item(obj) && (obj->known || obj->oclass == ARMOR_CLASS)) Your("%s less effective.", aobjnam(obj, "seem")); } break; } default: break; } /* These only affect you if they still live */ if(malive && !mon->mcan && rn2(3)) { switch(ptr->mattk[i].adtyp) { case AD_PLYS: if(ptr == &mons[PM_FLOATING_EYE]) { if (!canseemon(mon)) { break; } if(mon->mcansee) { if (ureflects("%s gaze is reflected by your %s.", s_suffix(Monnam(mon)))) ; else if (Free_action) You("momentarily stiffen under %s gaze!", s_suffix(mon_nam(mon))); else { You("are frozen by %s gaze!", s_suffix(mon_nam(mon))); nomul((ACURR(A_WIS) > 12 || rn2(4)) ? -tmp : -127); } } else { pline("%s cannot defend itself.", Adjmonnam(mon,"blind")); if(!rn2(500)) change_luck(-1); } } else if (Free_action) { You("momentarily stiffen."); } else { /* gelatinous cube */ You("are frozen by %s!", mon_nam(mon)); nomul(-tmp); exercise(A_DEX, FALSE); } break; case AD_COLD: /* brown mold or blue jelly */ if(monnear(mon, u.ux, u.uy)) { if(Cold_resistance) { shieldeff(u.ux, u.uy); You_feel("a mild chill."); ugolemeffects(AD_COLD, tmp); break; } You("are suddenly very cold!"); mdamageu(mon, tmp); /* monster gets stronger with your heat! */ mon->mhp += tmp / 2; if (mon->mhpmax < mon->mhp) mon->mhpmax = mon->mhp; /* at a certain point, the monster will reproduce! */ if(mon->mhpmax > ((int) (mon->m_lev+1) * 8)) (void)split_mon(mon, &youmonst); } break; case AD_STUN: /* specifically yellow mold */ if(!Stunned) make_stunned((long)tmp, TRUE); break; case AD_FIRE: if(monnear(mon, u.ux, u.uy)) { if(Fire_resistance) { shieldeff(u.ux, u.uy); You_feel("mildly warm."); ugolemeffects(AD_FIRE, tmp); break; } You("are suddenly very hot!"); mdamageu(mon, tmp); } break; case AD_ELEC: if(Shock_resistance) { shieldeff(u.ux, u.uy); You_feel("a mild tingle."); ugolemeffects(AD_ELEC, tmp); break; } You("are jolted with electricity!"); mdamageu(mon, tmp); break; default: break; } } return(malive | mhit); } /* Note: caller must ascertain mtmp is mimicking... */ void stumble_onto_mimic(mtmp) struct monst *mtmp; { const char *fmt = "Wait! That's %s!", *generic = "a monster", *what = 0; if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK)) u.ustuck = mtmp; if (Blind) { if (!Blind_telepat) what = generic; /* with default fmt */ else if (mtmp->m_ap_type == M_AP_MONSTER) what = a_monnam(mtmp); /* differs from what was sensed */ } else { int glyph = levl[u.ux+u.dx][u.uy+u.dy].glyph; if (glyph_is_cmap(glyph) && (glyph_to_cmap(glyph) == S_hcdoor || glyph_to_cmap(glyph) == S_vcdoor)) fmt = "The door actually was %s!"; else if (glyph_is_object(glyph) && glyph_to_obj(glyph) == GOLD_PIECE) fmt = "That gold was %s!"; /* cloned Wiz starts out mimicking some other monster and might make himself invisible before being revealed */ if (mtmp->minvis && !See_invisible) what = generic; else what = a_monnam(mtmp); } if (what) pline(fmt, what); wakeup(mtmp); /* clears mimicking */ } STATIC_OVL void nohandglow(mon) struct monst *mon; { char *hands=makeplural(body_part(HAND)); if (!u.umconf || mon->mconf) return; if (u.umconf == 1) { if (Blind) Your("%s stop tingling.", hands); else Your("%s stop glowing %s.", hands, hcolor(red)); } else { if (Blind) pline_The("tingling in your %s lessens.", hands); else Your("%s no longer glow so brightly %s.", hands, hcolor(red)); } u.umconf--; } int flash_hits_mon(mtmp, otmp) struct monst *mtmp; struct obj *otmp; /* source of flash */ { int tmp, amt, res = 0, useeit = canseemon(mtmp); if (mtmp->msleeping) { mtmp->msleeping = 0; if (useeit) { pline_The("flash awakens %s.", mon_nam(mtmp)); res = 1; } } else if (mtmp->data->mlet != S_LIGHT) { if (!resists_blnd(mtmp)) { tmp = dist2(otmp->ox, otmp->oy, mtmp->mx, mtmp->my); if (useeit) { pline("%s is blinded by the flash!", Monnam(mtmp)); res = 1; } if (mtmp->data == &mons[PM_GREMLIN]) { /* Rule #1: Keep them out of the light. */ amt = otmp->otyp == WAN_LIGHT ? d(1 + otmp->spe, 4) : rn2(min(mtmp->mhp,4)); pline("%s %s!", Monnam(mtmp), amt > mtmp->mhp / 2 ? "wails in agony" : "cries out in pain"); if ((mtmp->mhp -= amt) <= 0) { if (flags.mon_moving) monkilled(mtmp, (char *)0, AD_BLND); else killed(mtmp); } else if (cansee(mtmp->mx,mtmp->my) && !canspotmon(mtmp)){ map_invisible(mtmp->mx, mtmp->my); } } if (mtmp->mhp > 0) { if (!flags.mon_moving) setmangry(mtmp); if (tmp < 9 && !mtmp->isshk && rn2(4)) { mtmp->mflee = 1; if (rn2(4)) mtmp->mfleetim = rnd(100); } mtmp->mcansee = 0; mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50/tmp); } } } return res; } /*uhitm.c*/