From e3a66a1aadbf79aeff6d48551b97a0934d7bddb6 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:53 +0000 Subject: [PATCH] *** empty log message *** --- src/uhitm.c | 2284 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2284 insertions(+) create mode 100644 src/uhitm.c diff --git a/src/uhitm.c b/src/uhitm.c new file mode 100644 index 000000000..5167d818c --- /dev/null +++ b/src/uhitm.c @@ -0,0 +1,2284 @@ +/* SCCS Id: @(#)uhitm.c 3.3 2001/09/06 */ +/* 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_CORR: 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 */ + } +} + +/* FALSE means it's OK to attack */ +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 with exceptions for + * monsters that hide and monsters you've been warned about. + * The former 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. The latter shows a glyph on + * the screen, so you know something is there. + */ + if (!canspotmon(mtmp) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && + !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) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy))) { + /* 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) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && + (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; + } + } + + /* + * make sure to wake up a monster from the above cases if the + * hero can sense that the monster is there. + */ + if ((mtmp->mundetected || mtmp->m_ap_type) && sensemon(mtmp)) { + mtmp->mundetected = 0; + wakeup(mtmp); + } + + 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 && !mtmp->mavenge)) && + 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. + */ + boolean foo = (Punished || !rn2(7) || is_longworm(mtmp->data)), + inshop = FALSE; + char *p; + + for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++) + if (tended_shop(&rooms[*p - ROOMOFFSET])) { + inshop = TRUE; + break; + } + + if (inshop || foo || + (IS_ROCK(levl[u.ux][u.uy].typ) && + !passes_walls(mtmp->data))) { + char buf[BUFSZ]; + + monflee(mtmp, rnd(6), FALSE, FALSE); + 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); + /* This assumes that Stormbringer was uwep not uswapwep */ + if (malive && u.twoweap && !override_confirmation) + 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? */ + if(!rn2(3)) { + monflee(mon, rnd(100), FALSE, TRUE); + } else monflee(mon, 0, FALSE, TRUE); + + 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; + /* a miss does not break conduct */ + if (uwep && + (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) + --u.uconduct.weaphit; + } + 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(obj), 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).] + */ + setmnotwielded(mon,monwep); + 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)) { + monflee(mon, d(2,3), TRUE, TRUE); + } + 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) && !stale_egg(obj)) { + 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)) { + monflee(mon, d(2, 4), FALSE, TRUE); + } + 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 %s from your powerful strike!", Monnam(mon), + makeplural(stagger(mon->data, "stagger"))); + mon->mstun = 1; + hittxt = TRUE; + if (mon->mcanmove && mon != u.ustuck && !mon->mtrapped) { + 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) && + m_in_out_region(mon, mdx, mdy)) { + remove_monster(mon->mx, mon->my); + newsym(mon->mx, mon->my); + place_monster(mon, mdx, mdy); + newsym(mon->mx, mon->my); + set_apparxy(mon); + (void) mintrap(mon); + } + } + } + } + + mon->mhp -= tmp; + if(mon->mhp < 1) + destroyed = TRUE; + if (mon->mtame && (!mon->mflee || mon->mfleetim) && tmp > 0) { + abuse_dog(mon); + monflee(mon, (unsigned)(10 * rnd(tmp)), FALSE, FALSE); + } + 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 && /*( thrown => obj exists )*/ + (!destroyed || (thrown && m_shot.n > 1 && m_shot.o == obj->otyp))) { + if (thrown) hit(mshot_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_simple_name(obj)); + + 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() +{ + int i; + struct permonst *pm; + struct monst *dtmp; + + pline("Some hell-p has arrived!"); + i = !rn2(6) ? ndemon(u.ualign.type) : NON_PM; + pm = i != NON_PM ? &mons[i] : 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), mhe(mdef), mhis(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; + if (otmp->owornmask & W_WEP) + setmnotwielded(mdef,otmp); + 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), mhis(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 %s for a moment.", Monnam(mdef), + makeplural(stagger(mdef->data, "stagger"))); + 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), + mdef->data == &mons[PM_WATER_ELEMENTAL] ? "boiling" : + 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: +#ifndef GOLDOBJ + if (mdef->mgold) { + u.ugold += mdef->mgold; + mdef->mgold = 0; + Your("purse feels heavier."); + } +#else + /* This you as a leprechaun, so steal + real gold only, no lesser coins */ + { + struct obj *mongold = findgold(mdef->minvent); + if (mongold) { + obj_extract_self(mongold); + if (merge_choice(invent, mongold) || inv_cnt() < 52) { + addinv(mongold); + Your("purse feels heavier."); + } else { + You("grab %s's gold, but find no room in your knapsack.", mon_nam(mdef)); + dropy(mongold); + } + } + } +#endif + 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_CORR: + hurtmarmor(mdef, AD_CORR); + 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; + if (!Unchanging && mdef->data == &mons[PM_GREEN_SLIME]) { + if (!Slimed) { + You("suck in some slime and don't feel very well."); + Slimed = 10L; + } + } + 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)), mhis(mdef)); + break; + } + + You("eat %s brain!", s_suffix(mon_nam(mdef))); + u.uconduct.food++; + if (touch_petrifies(mdef->data) && !Stone_resistance && !Stoned) { + Stoned = 5; + killer_format = KILLED_BY_AN; + delayed_killer = mdef->data->mname; + } + 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], FALSE); + 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) && + (Unchanging || mdef->data != &mons[PM_GREEN_SLIME])) { +#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 %s.", + body_part(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] = dhit; + /* 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) { + if (aatyp == AT_KICK) { + if (uarmf && !rn2(6)) + (void)rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst); + } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || + aatyp == AT_MAGC || aatyp == AT_TUCH) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + 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) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + break; + case AD_CORR: + 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) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + 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) */ + if (mhit) { + struct obj *obj = (struct obj *)0; + + if (aatyp == AT_KICK) { + obj = uarmf; + if (!obj) break; + } + passive_obj(mon, obj, &(ptr->mattk[i])); + } + 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)); + nomovemsg = 0; /* default: "you can move again" */ + 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); +} + +/* + * Special (passive) attacks on an attacking object by monsters done here. + * Assumes the attack was successful. + */ +void +passive_obj(mon, obj, mattk) +register struct monst *mon; +register struct obj *obj; /* null means pick uwep, uswapwep or uarmg */ +struct attack *mattk; /* null means we find one internally */ +{ + register struct permonst *ptr = mon->data; + register int i; + + /* if caller hasn't specified an object, use uwep, uswapwep or uarmg */ + if (!obj) { + obj = (u.twoweap && uswapwep && !rn2(2)) ? uswapwep : uwep; + if (!obj && mattk->adtyp == AD_ENCH) + obj = uarmg; /* no weapon? then must be gloves */ + if (!obj) return; /* no object to affect */ + } + + /* if caller hasn't specified an attack, find one */ + if (!mattk) { + for(i = 0; ; i++) { + if(i >= NATTK) return; /* no passive attacks */ + if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */ + } + mattk = &(ptr->mattk[i]); + } + + switch(mattk->adtyp) { + + case AD_ACID: + if(!rn2(6)) { + erode_obj(obj, TRUE, FALSE); + } + break; + case AD_RUST: + if(!mon->mcan) { + erode_obj(obj, FALSE, FALSE); + } + break; + case AD_CORR: + if(!mon->mcan) { + erode_obj(obj, TRUE, FALSE); + } + break; + case AD_ENCH: + if (!mon->mcan) { + if (drain_item(obj) && carried(obj) && + (obj->known || obj->oclass == ARMOR_CLASS)) { + Your("%s less effective.", aobjnam(obj, "seem")); + } + break; + } + default: + break; + } +} + +/* 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)) { + if (rn2(4)) + monflee(mtmp, rnd(100), FALSE, TRUE); + else + monflee(mtmp, 0, FALSE, TRUE); + } + mtmp->mcansee = 0; + mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50/tmp); + } + } + } + return res; +} + +/*uhitm.c*/