-/* NetHack 3.6 mon.c $NHDT-Date: 1449269918 2015/12/04 22:58:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.199 $ */
+/* NetHack 3.6 mon.c $NHDT-Date: 1569276991 2019/09/23 22:16:31 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.297 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
/* JNetHack Copyright */
/* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
/* JNetHack may be freely redistributed. See license for details. */
/* If you're using precompiled headers, you don't want this either */
#include "mfndpos.h"
#include <ctype.h>
-STATIC_VAR boolean vamp_rise_msg;
+STATIC_VAR boolean vamp_rise_msg, disintegested;
-STATIC_DCL void FDECL(sanity_check_single_mon, (struct monst *, const char *));
+STATIC_DCL void FDECL(sanity_check_single_mon, (struct monst *, BOOLEAN_P,
+ const char *));
STATIC_DCL boolean FDECL(restrap, (struct monst *));
STATIC_DCL long FDECL(mm_aggression, (struct monst *, struct monst *));
STATIC_DCL long FDECL(mm_displacement, (struct monst *, struct monst *));
STATIC_DCL int FDECL(pickvampshape, (struct monst *));
STATIC_DCL boolean FDECL(isspecmon, (struct monst *));
STATIC_DCL boolean FDECL(validspecmon, (struct monst *, int));
-STATIC_DCL boolean FDECL(validvamp, (struct monst *, int *, int));
-STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int));
+STATIC_DCL struct permonst *FDECL(accept_newcham_form, (struct monst *, int));
STATIC_DCL struct obj *FDECL(make_corpse, (struct monst *, unsigned));
STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *));
STATIC_DCL void FDECL(lifesaved_monster, (struct monst *));
+STATIC_DCL void FDECL(migrate_mon, (struct monst *, XCHAR_P, XCHAR_P));
+STATIC_DCL boolean FDECL(ok_to_obliterate, (struct monst *));
+STATIC_DCL void FDECL(deal_with_overcrowding, (struct monst *));
+/* note: duplicated in dog.c */
#define LEVEL_SPECIFIC_NOCORPSE(mdat) \
(Is_rogue_level(&u.uz) \
|| (level.flags.graveyard && is_undead(mdat) && rn2(3)))
#endif /* 0 */
-void
-sanity_check_single_mon(mtmp, msg)
+STATIC_OVL void
+sanity_check_single_mon(mtmp, chk_geno, msg)
struct monst *mtmp;
+boolean chk_geno;
const char *msg;
{
- if (DEADMONSTER(mtmp))
- return;
- if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS])
- impossible("illegal mon data (%s)", msg);
+ if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS]) {
+ impossible("illegal mon data %s; mnum=%d (%s)",
+ fmt_ptr((genericptr_t) mtmp->data), mtmp->mnum, msg);
+ } else {
+ int mndx = monsndx(mtmp->data);
+
+ if (mtmp->mnum != mndx) {
+ impossible("monster mnum=%d, monsndx=%d (%s)",
+ mtmp->mnum, mndx, msg);
+ mtmp->mnum = mndx;
+ }
+ if (DEADMONSTER(mtmp)) {
+#if 0
+ /* bad if not fmons list or if not vault guard */
+ if (strcmp(msg, "fmon") || !mtmp->isgd)
+ impossible("dead monster on %s; %s at <%d,%d>",
+ msg, mons[mndx].mname, mtmp->mx, mtmp->my);
+#endif
+ return;
+ }
+ if (chk_geno && (mvitals[mndx].mvflags & G_GENOD) != 0)
+ impossible("genocided %s in play (%s)", mons[mndx].mname, msg);
+ }
+ if (mtmp->isshk && !has_eshk(mtmp))
+ impossible("shk without eshk (%s)", msg);
+ if (mtmp->ispriest && !has_epri(mtmp))
+ impossible("priest without epri (%s)", msg);
+ if (mtmp->isgd && !has_egd(mtmp))
+ impossible("guard without egd (%s)", msg);
+ if (mtmp->isminion && !has_emin(mtmp))
+ impossible("minion without emin (%s)", msg);
+ /* guardian angel on astral level is tame but has emin rather than edog */
+ if (mtmp->mtame && !has_edog(mtmp) && !mtmp->isminion)
+ impossible("pet without edog (%s)", msg);
}
void
mon_sanity_check()
{
- int x,y;
- struct monst *mtmp = fmon;
+ int x, y;
+ struct monst *mtmp, *m;
- while (mtmp) {
- sanity_check_single_mon(mtmp, "fmon");
- mtmp = mtmp->nmon;
+ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+ /* dead monsters should still have sane data */
+ sanity_check_single_mon(mtmp, TRUE, "fmon");
+ if (DEADMONSTER(mtmp) && !mtmp->isgd)
+ continue;
+
+ x = mtmp->mx, y = mtmp->my;
+ if (!isok(x, y) && !(mtmp->isgd && x == 0 && y == 0)) {
+ impossible("mon (%s) claims to be at <%d,%d>?",
+ fmt_ptr((genericptr_t) mtmp), x, y);
+ } else if (mtmp == u.usteed) {
+ /* steed is in fmon list but not on the map; its
+ <mx,my> coordinates should match hero's location */
+ if (x != u.ux || y != u.uy)
+ impossible("steed (%s) claims to be at <%d,%d>?",
+ fmt_ptr((genericptr_t) mtmp), x, y);
+ } else if (level.monsters[x][y] != mtmp) {
+ impossible("mon (%s) at <%d,%d> is not there!",
+ fmt_ptr((genericptr_t) mtmp), x, y);
+ } else if (mtmp->wormno) {
+ sanity_check_worm(mtmp);
+ }
}
- for (x = 0; x < COLNO; x++)
+
+ for (x = 1; x < COLNO; x++)
for (y = 0; y < ROWNO; y++)
- if ((mtmp = m_at(x,y)) != 0)
- sanity_check_single_mon(mtmp, "m_at");
+ if ((mtmp = level.monsters[x][y]) != 0) {
+ for (m = fmon; m; m = m->nmon)
+ if (m == mtmp)
+ break;
+ if (!m)
+ impossible("map mon (%s) at <%d,%d> not in fmon list!",
+ fmt_ptr((genericptr_t) mtmp), x, y);
+ else if (mtmp == u.usteed)
+ impossible("steed (%s) is on the map at <%d,%d>!",
+ fmt_ptr((genericptr_t) mtmp), x, y);
+ else if ((mtmp->mx != x || mtmp->my != y)
+ && mtmp->data != &mons[PM_LONG_WORM])
+ impossible("map mon (%s) at <%d,%d> is found at <%d,%d>?",
+ fmt_ptr((genericptr_t) mtmp),
+ mtmp->mx, mtmp->my, x, y);
+ }
- mtmp = migrating_mons;
- while (mtmp) {
- sanity_check_single_mon(mtmp, "migr");
- mtmp = mtmp->nmon;
+ for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) {
+ sanity_check_single_mon(mtmp, FALSE, "migr");
}
}
case PM_BLACK_UNICORN:
if (mtmp->mrevived && rn2(2)) {
if (canseemon(mtmp))
-/*JP
+#if 0 /*JP:T*/
pline("%s recently regrown horn crumbles to dust.",
-*/
- pline("\8dÅ\8bß\8dÄ\90¶\82µ\82½%s\82Ì\8ap\82Í\95²\81X\82É\82È\82Á\82½\81D",
s_suffix(Monnam(mtmp)));
+#else
+ pline("\8dÅ\8bß\8dÄ\90¶\82µ\82½%s\82Ì\8ap\82Í\95²\81X\82É\82È\82Á\82½\81D",
+ Monnam(mtmp));
+#endif
} else {
obj = mksobj_at(UNICORN_HORN, x, y, TRUE, FALSE);
if (obj && mtmp->mrevived)
}
free_mname(mtmp);
return obj;
- break;
default_1:
default:
if (mvitals[mndx].mvflags & G_NOCORPSE) {
}
/* All special cases should precede the G_NOCORPSE check */
- if (!obj) return NULL;
+ if (!obj)
+ return (struct obj *) 0;
/* if polymorph or undead turning has killed this monster,
prevent the same attack beam from hitting its corpse */
* underneath it, you could be told the corpse type of a
* monster that you never knew was there without this.
* The code in hitmu() substitutes the word "something"
- * if the corpses obj->dknown is 0.
+ * if the corpse's obj->dknown is 0.
*/
if (Blind && !sensemon(mtmp))
obj->dknown = 0;
{
boolean inpool, inlava, infountain;
- /* [what about ceiling clingers?] */
+ /* [ceiling clingers are handled below] */
inpool = (is_pool(mtmp->mx, mtmp->my)
- && !(is_flyer(mtmp->data) || is_floater(mtmp->data)));
+ && (!(is_flyer(mtmp->data) || is_floater(mtmp->data))
+ /* there's no "above the surface" on the plane of water */
+ || Is_waterlevel(&u.uz)));
inlava = (is_lava(mtmp->mx, mtmp->my)
&& !(is_flyer(mtmp->data) || is_floater(mtmp->data)));
infountain = IS_FOUNTAIN(levl[mtmp->mx][mtmp->my].typ);
- /* Flying and levitation keeps our steed out of the liquid */
- /* (but not water-walking or swimming) */
+ /* Flying and levitation keeps our steed out of the liquid
+ (but not water-walking or swimming; note: if hero is in a
+ water location on the Plane of Water, flight and levitating
+ are blocked so this (Flying || Levitation) test fails there
+ and steed will be subject to water effects, as intended) */
if (mtmp == u.usteed && (Flying || Levitation))
return 0;
mtmp->mhp -= dam;
if (mtmp->mhpmax > dam)
mtmp->mhpmax -= dam;
- if (mtmp->mhp < 1) {
+ if (DEADMONSTER(mtmp)) {
mondead(mtmp);
- if (mtmp->mhp < 1)
+ if (DEADMONSTER(mtmp))
return 1;
}
water_damage_chain(mtmp->minvent, FALSE);
* themselves --ALI
*/
if (!is_clinger(mtmp->data) && !likes_lava(mtmp->data)) {
+ /* not fair...? hero doesn't automatically teleport away
+ from lava, just from water */
+ if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) {
+ if (rloc(mtmp, TRUE))
+ return 0;
+ }
if (!resists_fire(mtmp)) {
- if (cansee(mtmp->mx, mtmp->my))
+ if (cansee(mtmp->mx, mtmp->my)) {
+ struct attack *dummy = &mtmp->data->mattk[0];
+ const char *how = on_fire(mtmp->data, dummy);
+
#if 0 /*JP*/
pline("%s %s.", Monnam(mtmp),
- mtmp->data == &mons[PM_WATER_ELEMENTAL]
- ? "boils away"
- : "burns to a crisp");
-#else
+ !strcmp(how, "boiling") ? "boils away"
+ : !strcmp(how, "melting") ? "melts away"
+ : "burns to a crisp");
+#else /*mon.c:on_fire()\82Ì\95Ô\82è\92l*/
pline("%s\82Í%s\82½\81D", Monnam(mtmp),
- mtmp->data == &mons[PM_WATER_ELEMENTAL]
- ? "\95¦\93«\82µ"
- : "\94R\82¦\82Ä\83p\83\8a\83p\83\8a\82É\82È\82Á");
+ !strcmp(how, "\95¦\93«\82µ\82½") ? "\95¦\93«\82µ"
+ : !strcmp(how, "\97n\82¯\82½") ? "\97n\82¯\82½"
+ : "\94R\82¦\82Ä\83p\83\8a\83p\83\8a\82É\82È\82Á");
#endif
- mondead(mtmp);
+ }
+ /* unlike fire -> melt ice -> pool, there's no way for the
+ hero to create lava beneath a monster, so the !mon_moving
+ case is not expected to happen (and we haven't made a
+ player-against-monster variation of the message above) */
+ if (context.mon_moving)
+ mondead(mtmp);
+ else
+ xkilled(mtmp, XKILL_NOMSG);
} else {
- if (--mtmp->mhp < 1) {
+ mtmp->mhp -= 1;
+ if (DEADMONSTER(mtmp)) {
if (cansee(mtmp->mx, mtmp->my))
/*JP
pline("%s surrenders to the fire.", Monnam(mtmp));
*/
pline("%s\82Í\82¿\82å\82Á\82Æ\8fÅ\82°\82½\81D", Monnam(mtmp));
}
- if (mtmp->mhp > 0) {
+ if (!DEADMONSTER(mtmp)) {
(void) fire_damage_chain(mtmp->minvent, FALSE, FALSE,
mtmp->mx, mtmp->my);
(void) rloc(mtmp, FALSE);
*/
if (!is_clinger(mtmp->data) && !is_swimmer(mtmp->data)
&& !amphibious(mtmp->data)) {
+ /* like hero with teleport intrinsic or spell, teleport away
+ if possible */
+ if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) {
+ if (rloc(mtmp, TRUE))
+ return 0;
+ }
if (cansee(mtmp->mx, mtmp->my)) {
+ if (context.mon_moving)
+/*JP
+ pline("%s drowns.", Monnam(mtmp));
+*/
+ pline("%s\82Í\93M\82ê\82½\81D", Monnam(mtmp));
+ else
+ /* hero used fire to melt ice that monster was on */
/*JP
- pline("%s drowns.", Monnam(mtmp));
+ You("drown %s.", mon_nam(mtmp));
*/
- pline("%s\82Í\93M\82ê\82½\81D", Monnam(mtmp));
+ You("%s\82É\93M\82ê\82½\81D", mon_nam(mtmp));
}
if (u.ustuck && u.uswallow && u.ustuck == mtmp) {
/* This can happen after a purple worm plucks you off a
- flying steed while you are over water. */
-/*JP
- pline("%s sinks as water rushes in and flushes you out.",
-*/
+ flying steed while you are over water. */
+#if 0 /*JP*/
+ pline("%s sinks as %s rushes in and flushes you out.",
+ Monnam(mtmp), hliquid("water"));
+#else /*hliquid\82Í\95s\8e©\91R\82É\82È\82é\82Ì\82Å\82Æ\82è\82 \82¦\82¸\8eg\82í\82È\82¢*/
pline("%s\82Í\90\85\97¬\82É\92¾\82Ý\81D\82 \82È\82½\82ð\93f\82«\8fo\82µ\82½\81D",
Monnam(mtmp));
+#endif
}
- mondead(mtmp);
- if (mtmp->mhp > 0) {
+ if (context.mon_moving)
+ mondead(mtmp);
+ else
+ xkilled(mtmp, XKILL_NOMSG);
+ if (!DEADMONSTER(mtmp)) {
water_damage_chain(mtmp->minvent, FALSE);
- (void) rloc(mtmp, FALSE);
+ if (!rloc(mtmp, TRUE))
+ deal_with_overcrowding(mtmp);
return 0;
}
return 1;
struct monst *mon;
{
int mmove = mon->data->mmove;
+ int mmove_adj;
/* Note: MSLOW's `+ 1' prevents slowed speed 1 getting reduced to 0;
* MFAST's `+ 2' prevents hasted speed 1 from becoming a no-op;
else if (mon->mspeed == MFAST)
mmove = (4 * mmove + 2) / 3;
- if (mon == u.usteed) {
- if (u.ugallop && context.mv) {
- /* average movement is 1.50 times normal */
- mmove = ((rn2(2) ? 4 : 5) * mmove) / 3;
- }
- } else if (mmove) {
- /* vary movement points allocated to slightly reduce predictability;
- random increment (avg +2) exceeds random decrement (avg +1) by
- a small amount; normal speed monsters will occasionally get an
- extra move and slow ones won't be quite as slow */
- mmove += rn2(5) - rn2(3); /* + 0..4 - 0..2, average net +1 */
- if (mmove < 1)
- mmove = 1;
+ if (mon == u.usteed && u.ugallop && context.mv) {
+ /* increase movement by a factor of 1.5; also increase variance of
+ movement speed (if it's naturally 24, we don't want it to always
+ become 36) */
+ mmove = ((rn2(2) ? 4 : 5) * mmove) / 3;
}
+ /* Randomly round the monster's speed to a multiple of NORMAL_SPEED.
+ This makes it impossible for the player to predict when they'll get
+ a free turn (thus preventing exploits like "melee kiting"), while
+ retaining guarantees about shopkeepers not being outsped by a
+ normal-speed player, normal-speed players being unable to open up
+ a gap when fleeing a normal-speed monster, etc. */
+ mmove_adj = mmove % NORMAL_SPEED;
+ mmove -= mmove_adj;
+ if (rn2(NORMAL_SPEED) < mmove_adj)
+ mmove += NORMAL_SPEED;
+
return mmove;
}
if (DEADMONSTER(mtmp))
continue;
- /* must check non-moving monsters once/turn in case
- * they managed to end up in liquid */
+ /* must check non-moving monsters once/turn in case they managed
+ to end up in water or lava; note: when not in liquid they regen,
+ shape-shift, timeout temporary maladies just like other monsters */
if (mtmp->data->mmove == 0) {
if (vision_full_recalc)
vision_recalc(0);
mon_regen(mtmp, FALSE);
/* possibly polymorph shapechangers and lycanthropes */
- if (mtmp->cham >= LOW_PM) {
- if (is_vampshifter(mtmp) || mtmp->data->mlet == S_VAMPIRE)
- decide_to_shapeshift(mtmp, 0);
- else if (!rn2(6))
- (void) newcham(mtmp, (struct permonst *) 0, FALSE, FALSE);
- }
+ if (mtmp->cham >= LOW_PM)
+ decide_to_shapeshift(mtmp, (canspotmon(mtmp)
+ || (u.uswallow && mtmp == u.ustuck))
+ ? SHIFT_MSG : 0);
were_change(mtmp);
/* gradually time out temporary problems */
break;
}
nmtmp = mtmp->nmon;
- /* one dead monster needs to perform a move after death:
- vault guard whose temporary corridor is still on the map */
- if (mtmp->isgd && !mtmp->mx && mtmp->mhp <= 0)
- (void) gd_move(mtmp);
+ /* one dead monster needs to perform a move after death: vault
+ guard whose temporary corridor is still on the map; live
+ guards who have led the hero back to civilization get moved
+ off the map too; gd_move() decides whether the temporary
+ corridor can be removed and guard discarded (via clearing
+ mon->isgd flag so that dmonsfree() will get rid of mon) */
+ if (mtmp->isgd && !mtmp->mx) {
+ /* parked at <0,0>; eventually isgd should get set to false */
+ if (monstermoves > mtmp->mlstmv) {
+ (void) gd_move(mtmp);
+ mtmp->mlstmv = monstermoves;
+ }
+ continue;
+ }
if (DEADMONSTER(mtmp))
continue;
if (minliquid(mtmp))
continue;
+ /* after losing equipment, try to put on replacement */
+ if (mtmp->misc_worn_check & I_SPECIAL) {
+ long oldworn;
+
+ mtmp->misc_worn_check &= ~I_SPECIAL;
+ oldworn = mtmp->misc_worn_check;
+ m_dowear(mtmp, FALSE);
+ if (mtmp->misc_worn_check != oldworn || !mtmp->mcanmove)
+ continue;
+ }
+
if (is_hider(mtmp->data)) {
/* unwatched mimics and piercers may hide again [MRS] */
if (restrap(mtmp))
continue;
- if (mtmp->m_ap_type == M_AP_FURNITURE
- || mtmp->m_ap_type == M_AP_OBJECT)
+ if (M_AP_TYPE(mtmp) == M_AP_FURNITURE
+ || M_AP_TYPE(mtmp) == M_AP_OBJECT)
continue;
if (mtmp->mundetected)
continue;
&& touch_artifact(otmp, mtmp)) {
if (mtmp->data == &mons[PM_RUST_MONSTER] && otmp->oerodeproof) {
if (canseemon(mtmp) && flags.verbose) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s eats %s!", Monnam(mtmp),
distant_name(otmp, doname));
#else
otmp->oerodeproof = 0;
mtmp->mstun = 1;
if (canseemon(mtmp) && flags.verbose) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s spits %s out in disgust!", Monnam(mtmp),
distant_name(otmp, doname));
#else
}
} else {
if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s eats %s!", Monnam(mtmp),
distant_name(otmp, doname));
#else
meatobj(mtmp) /* for gelatinous cubes */
struct monst *mtmp;
{
- register struct obj *otmp, *otmp2;
+ struct obj *otmp, *otmp2;
struct permonst *ptr, *original_ptr = mtmp->data;
- int poly, grow, heal, count = 0, ecount = 0;
+ int poly, grow, heal, eyes, count = 0, ecount = 0;
char buf[BUFSZ];
buf[0] = '\0';
&& !resists_ston(mtmp))
/* don't engulf boulders and statues or ball&chain */
|| otmp->oclass == ROCK_CLASS
- || otmp == uball || otmp == uchain) {
+ || otmp == uball || otmp == uchain
+ /* normally mtmp won't have stepped onto scare monster
+ scroll, but if it does, don't eat or engulf that
+ (note: scrolls inside eaten containers will still
+ become engulfed) */
+ || otmp->otyp == SCR_SCARE_MONSTER) {
/* do nothing--neither eaten nor engulfed */
continue;
|| otmp->otyp == RIN_SLOW_DIGESTION)
/* cockatrice corpses handled above; this
touch_petrifies() check catches eggs */
- || ((otmp->otyp == CORPSE || otmp->otyp == EGG)
+ || ((otmp->otyp == CORPSE || otmp->otyp == EGG
+ || otmp->globby)
&& ((touch_petrifies(&mons[otmp->corpsenm])
&& !resists_ston(mtmp))
|| (otmp->corpsenm == PM_GREEN_SLIME
/* engulf */
++ecount;
if (ecount == 1)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
Sprintf(buf, "%s engulfs %s.", Monnam(mtmp),
distant_name(otmp, doname));
#else
} else {
/* devour */
++count;
- if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
- pline("%s eats %s!", Monnam(mtmp),
- distant_name(otmp, doname));
+ if (cansee(mtmp->mx, mtmp->my)) {
+ if (flags.verbose)
+#if 0 /*JP:T*/
+ pline("%s eats %s!", Monnam(mtmp),
+ distant_name(otmp, doname));
#else
- pline("%s\82Í%s\82ð\90H\82×\82Ä\82¢\82é\81I", Monnam(mtmp),
- distant_name(otmp, doname));
+ pline("%s\82Í%s\82ð\90H\82×\82Ä\82¢\82é\81I", Monnam(mtmp),
+ distant_name(otmp, doname));
#endif
- else if (flags.verbose)
+ /* give this one even if !verbose */
+#if 0 /*JP*//*\93ú\96{\8cê\94Å\82Å\82Í\82±\82ê\82Í\82È\82¢*/
+ if (otmp->oclass == SCROLL_CLASS
+ && !strcmpi(OBJ_DESCR(objects[otmp->otyp]), "YUM YUM"))
+ pline("Yum%c", otmp->blessed ? '!' : '.');
+#endif
+ } else {
+ if (flags.verbose)
/*JP
- You_hear("a slurping sound.");
+ You_hear("a slurping sound.");
*/
- You_hear("\82²\82\82ñ\82Æ\88ù\82Ý\8d\9e\82Þ\89¹\82ð\95·\82¢\82½\81D");
+ You_hear("\82²\82\82ñ\82Æ\88ù\82Ý\8d\9e\82Þ\89¹\82ð\95·\82¢\82½\81D");
+ }
/* Heal up to the object's weight in hp */
if (mtmp->mhp < mtmp->mhpmax) {
mtmp->mhp += objects[otmp->otyp].oc_weight;
poly = polyfodder(otmp);
grow = mlevelgain(otmp);
heal = mhealup(otmp);
+ eyes = (otmp->otyp == CARROT);
delobj(otmp); /* munch */
ptr = mtmp->data;
if (poly) {
} else if (heal) {
mtmp->mhp = mtmp->mhpmax;
}
+ if ((eyes || heal) && !mtmp->mcansee)
+ mcureblindness(mtmp, canseemon(mtmp));
/* in case it polymorphed or died */
if (ptr != original_ptr)
return !ptr ? 2 : 1;
if (cansee(mtmp->mx, mtmp->my) && flags.verbose && buf[0])
pline1(buf);
else if (flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
You_hear("%s slurping sound%s.",
(ecount == 1) ? "a" : "several", plur(ecount));
#else
register struct monst *mtmp;
{
register struct obj *gold;
+#if 0 /*JP*/
int mat_idx;
+#endif
if ((gold = g_at(mtmp->mx, mtmp->my)) != 0) {
+#if 0 /*JP*/
mat_idx = objects[gold->otyp].oc_material;
+#endif
obj_extract_self(gold);
add_to_minv(mtmp, gold);
if (cansee(mtmp->mx, mtmp->my)) {
if (flags.verbose && !mtmp->isgd)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s picks up some %s.", Monnam(mtmp),
mat_idx == GOLD ? "gold" : "money");
#else
otmp3 = splitobj(otmp, carryamt);
}
if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s picks up %s.", Monnam(mtmp),
(distu(mtmp->mx, mtmp->my) <= 5)
? doname(otmp3)
boolean wantpool, poolok, lavaok, nodiag;
boolean rockok = FALSE, treeok = FALSE, thrudoor;
int maxx, maxy;
+ boolean poisongas_ok, in_poisongas;
+ NhRegion *gas_reg;
+ int gas_glyph = cmap_to_glyph(S_poisoncloud);
x = mon->mx;
y = mon->my;
nowtyp = levl[x][y].typ;
nodiag = NODIAG(mdat - mons);
- wantpool = mdat->mlet == S_EEL;
- poolok = (is_flyer(mdat) || is_clinger(mdat)
+ wantpool = (mdat->mlet == S_EEL);
+ poolok = ((!Is_waterlevel(&u.uz)
+ && (is_flyer(mdat) || is_floater(mdat) || is_clinger(mdat)))
|| (is_swimmer(mdat) && !wantpool));
- lavaok = (is_flyer(mdat) || is_clinger(mdat) || likes_lava(mdat));
+ /* note: floating eye is the only is_floater() so this could be
+ simplified, but then adding another floater would be error prone */
+ lavaok = (is_flyer(mdat) || is_floater(mdat) || is_clinger(mdat)
+ || likes_lava(mdat));
+ if (mdat == &mons[PM_FLOATING_EYE]) /* prefers to avoid heat */
+ lavaok = FALSE;
thrudoor = ((flag & (ALLOW_WALL | BUSTDOOR)) != 0L);
+ poisongas_ok = ((nonliving(mdat) || is_vampshifter(mon)
+ || breathless(mdat)) || resists_poison(mon));
+ in_poisongas = ((gas_reg = visible_region_at(x,y)) != 0
+ && gas_reg->glyph == gas_glyph);
+
if (flag & ALLOW_DIG) {
struct obj *mw_tmp;
thrudoor = TRUE;
}
-nexttry: /* eels prefer the water, but if there is no water nearby,
- they will crawl over land */
+ nexttry: /* eels prefer the water, but if there is no water nearby,
+ they will crawl over land */
if (mon->mconf) {
flag |= ALLOW_ALL;
flag &= ~NOTONL;
&& !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx, ny)))
continue;
/* KMH -- Added iron bars */
- if (ntyp == IRONBARS && !(flag & ALLOW_BARS))
+ if (ntyp == IRONBARS
+ && (!(flag & ALLOW_BARS)
+ || ((levl[nx][ny].wall_info & W_NONDIGGABLE)
+ && (dmgtype(mdat, AD_RUST)
+ || dmgtype(mdat, AD_CORR)))))
continue;
if (IS_DOOR(ntyp) && !(amorphous(mdat) || can_fog(mon))
&& (((levl[nx][ny].doormask & D_CLOSED) && !(flag & OPENDOOR))
|| ((levl[nx][ny].doormask & D_LOCKED)
&& !(flag & UNLOCKDOOR))) && !thrudoor)
continue;
+ /* avoid poison gas? */
+ if (!poisongas_ok && !in_poisongas
+ && (gas_reg = visible_region_at(nx,ny)) != 0
+ && gas_reg->glyph == gas_glyph)
+ continue;
/* first diagonal checks (tight squeezes handled below) */
if (nx != x && ny != y
&& (nodiag
if ((ttmp->ttyp != RUST_TRAP
|| mdat == &mons[PM_IRON_GOLEM])
&& ttmp->ttyp != STATUE_TRAP
- && ((ttmp->ttyp != PIT && ttmp->ttyp != SPIKED_PIT
- && ttmp->ttyp != TRAPDOOR && ttmp->ttyp != HOLE)
+ && ttmp->ttyp != VIBRATING_SQUARE
+ && ((!is_pit(ttmp->ttyp) && !is_hole(ttmp->ttyp))
|| (!is_flyer(mdat) && !is_floater(mdat)
&& !is_clinger(mdat)) || Sokoban)
&& (ttmp->ttyp != SLP_GAS_TRAP || !resists_sleep(mon))
&& (ttmp->ttyp != FIRE_TRAP || !resists_fire(mon))
&& (ttmp->ttyp != SQKY_BOARD || !is_flyer(mdat))
&& (ttmp->ttyp != WEB
- || (!amorphous(mdat) && !webmaker(mdat)))
+ || (!amorphous(mdat) && !webmaker(mdat)
+ && !is_whirly(mdat) && !unsolid(mdat)))
&& (ttmp->ttyp != ANTI_MAGIC || !resists_magm(mon))) {
if (!(flag & ALLOW_TRAPS)) {
if (mon->mtrapseen & (1L << (ttmp->ttyp - 1)))
{
struct monst **mtmp, *freetmp;
int count = 0;
+ char buf[QBUFSZ];
+ buf[0] = '\0';
for (mtmp = &fmon; *mtmp;) {
freetmp = *mtmp;
- if (freetmp->mhp <= 0 && !freetmp->isgd) {
+ if (DEADMONSTER(freetmp) && !freetmp->isgd) {
*mtmp = freetmp->nmon;
freetmp->nmon = NULL;
dealloc_monst(freetmp);
mtmp = &(freetmp->nmon);
}
- if (count != iflags.purge_monsters)
- impossible("dmonsfree: %d removed doesn't match %d pending",
- count, iflags.purge_monsters);
+ if (count != iflags.purge_monsters) {
+ describe_level(buf);
+ impossible("dmonsfree: %d removed doesn't match %d pending on %s",
+ count, iflags.purge_monsters, buf);
+ }
iflags.purge_monsters = 0;
}
if (mtmp != u.usteed) /* don't place steed onto the map */
place_monster(mtmp2, mtmp2->mx, mtmp2->my);
if (mtmp2->wormno) /* update level.monsters[wseg->wx][wseg->wy] */
- place_wsegs(mtmp2); /* locations to mtmp2 not mtmp. */
+ place_wsegs(mtmp2, NULL); /* locations to mtmp2 not mtmp. */
if (emits_light(mtmp2->data)) {
/* since this is so rare, we don't have any `mon_move_light_source' */
new_light_source(mtmp2->mx, mtmp2->my, emits_light(mtmp2->data),
struct monst **monst_list; /* &migrating_mons or &mydogs or null */
{
struct monst *mtmp;
- boolean unhide = (monst_list != 0);
int mx = mon->mx, my = mon->my;
+ boolean on_map = (m_at(mx, my) == mon),
+ unhide = (monst_list != 0);
if (!fmon)
panic("relmon: no fmon available.");
clone can continue imitating some other monster form); also,
might be imitating a boulder so need line-of-sight unblocking */
mon->mundetected = 0;
- if (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER)
+ if (M_AP_TYPE(mon) && M_AP_TYPE(mon) != M_AP_MONSTER)
seemimic(mon);
}
- remove_monster(mx, my);
+ if (on_map) {
+ mon->mtrapped = 0;
+ if (mon->wormno)
+ remove_worm(mon);
+ else
+ remove_monster(mx, my);
+ }
if (mon == fmon) {
fmon = fmon->nmon;
}
if (unhide) {
- newsym(mx, my);
+ if (on_map)
+ newsym(mx, my);
/* insert into mydogs or migrating_mons */
mon->nmon = *monst_list;
*monst_list = mon;
if (EGD(mtmp1)) {
if (!EGD(mtmp2))
newegd(mtmp2);
- (void) memcpy((genericptr_t) EGD(mtmp2), (genericptr_t) EGD(mtmp1),
- sizeof (struct egd));
+ *EGD(mtmp2) = *EGD(mtmp1);
}
if (EPRI(mtmp1)) {
if (!EPRI(mtmp2))
newepri(mtmp2);
- (void) memcpy((genericptr_t) EPRI(mtmp2), (genericptr_t) EPRI(mtmp1),
- sizeof (struct epri));
+ *EPRI(mtmp2) = *EPRI(mtmp1);
}
if (ESHK(mtmp1)) {
if (!ESHK(mtmp2))
neweshk(mtmp2);
- (void) memcpy((genericptr_t) ESHK(mtmp2), (genericptr_t) ESHK(mtmp1),
- sizeof (struct eshk));
+ *ESHK(mtmp2) = *ESHK(mtmp1);
}
if (EMIN(mtmp1)) {
if (!EMIN(mtmp2))
newemin(mtmp2);
- (void) memcpy((genericptr_t) EMIN(mtmp2), (genericptr_t) EMIN(mtmp1),
- sizeof (struct emin));
+ *EMIN(mtmp2) = *EMIN(mtmp1);
}
if (EDOG(mtmp1)) {
if (!EDOG(mtmp2))
newedog(mtmp2);
- (void) memcpy((genericptr_t) EDOG(mtmp2), (genericptr_t) EDOG(mtmp1),
- sizeof (struct edog));
+ *EDOG(mtmp2) = *EDOG(mtmp1);
}
if (has_mcorpsenm(mtmp1))
MCORPSENM(mtmp2) = MCORPSENM(mtmp1);
dealloc_monst(mon)
struct monst *mon;
{
- if (mon->nmon)
- panic("dealloc_monst with nmon");
+ char buf[QBUFSZ];
+
+ buf[0] = '\0';
+ if (mon->nmon) {
+ describe_level(buf);
+ panic("dealloc_monst with nmon on %s", buf);
+ }
if (mon->mextra)
dealloc_mextra(mon);
free((genericptr_t) mon);
struct monst *mtmp;
struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */
{
+ boolean onmap = (mtmp->mx > 0);
+
if (mtmp == context.polearm.hitmon)
context.polearm.hitmon = 0;
if (mtmp->mleashed)
mtmp->mtrapped = 0;
mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */
relobj(mtmp, 0, FALSE);
- remove_monster(mtmp->mx, mtmp->my);
+ if (onmap || mtmp == level.monsters[0][0]) {
+ if (mtmp->wormno)
+ remove_worm(mtmp);
+ else
+ remove_monster(mtmp->mx, mtmp->my);
+ }
if (emits_light(mptr))
del_light_source(LS_MONSTER, monst_to_any(mtmp));
- newsym(mtmp->mx, mtmp->my);
+ if (M_AP_TYPE(mtmp))
+ seemimic(mtmp);
+ if (onmap)
+ newsym(mtmp->mx, mtmp->my);
unstuck(mtmp);
- fill_pit(mtmp->mx, mtmp->my);
+ if (onmap)
+ fill_pit(mtmp->mx, mtmp->my);
if (mtmp->isshk)
shkgone(mtmp);
if (mtmp->wormno)
wormgone(mtmp);
+ if (In_endgame(&u.uz))
+ mtmp->mstate |= MON_ENDGAME_FREE;
+
+ mtmp->mstate |= MON_DETACH;
iflags.purge_monsters++;
}
/*JP
pline("But wait...");
*/
- pline("\82µ\82©\82µ\81D\81D\81D");
+ pline("\82¿\82å\82Á\82Æ\82Ü\82Á\82½\81D\81D\81D");
/*JP
pline("%s medallion begins to glow!", s_suffix(Monnam(mtmp)));
*/
- pline("%s\82Ì\83\81\83_\83\8a\83I\83\93\82ª\8bP\82«\82Í\82¶\82ß\82½\81I", Monnam(mtmp));
+ pline("%s\82Ì\96\82\8f\9c\82¯\82ª\8bP\82«\82Í\82¶\82ß\82½\81I", Monnam(mtmp));
makeknown(AMULET_OF_LIFE_SAVING);
/* amulet is visible, but monster might not be */
if (canseemon(mtmp)) {
/*JP
pline_The("medallion crumbles to dust!");
*/
- pline_The("\83\81\83_\83\8a\83I\83\93\82Í\82±\82È\82²\82È\82É\82\82¾\82¯\82Ä\82µ\82Ü\82Á\82½\81I");
+ pline("\96\82\8f\9c\82¯\82Í\82±\82È\82²\82È\82É\82\82¾\82¯\82½\81I");
}
m_useup(mtmp, lifesave);
+ /* equip replacement amulet, if any, on next move */
+ mtmp->misc_worn_check |= I_SPECIAL;
surviver = !(mvitals[monsndx(mtmp->data)].mvflags & G_GENOD);
mtmp->mcanmove = 1;
if (mtmp->mhpmax <= 0)
mtmp->mhpmax = 10;
mtmp->mhp = mtmp->mhpmax;
- if (surviver)
- return;
- /* genocided monster can't be life-saved */
- if (cansee(mtmp->mx, mtmp->my))
+ if (!surviver) {
+ /* genocided monster can't be life-saved */
+ if (cansee(mtmp->mx, mtmp->my))
/*JP
- pline("Unfortunately, %s is still genocided...", mon_nam(mtmp));
+ pline("Unfortunately, %s is still genocided...",
*/
- pline("\8ec\94O\82È\82ª\82ç%s\82Í\8bs\8eE\82³\82ê\82Ä\82¢\82é\81D\81D\81D", mon_nam(mtmp));
+ pline("\8ec\94O\82È\82ª\82ç%s\82Í\8bs\8eE\82³\82ê\82Ä\82¢\82é\81D\81D\81D",
+ mon_nam(mtmp));
+ mtmp->mhp = 0;
+ }
}
- mtmp->mhp = 0;
}
void
struct permonst *mptr;
int tmp;
+ mtmp->mhp = 0; /* in case caller hasn't done this */
lifesaved_monster(mtmp);
- if (mtmp->mhp > 0)
+ if (!DEADMONSTER(mtmp))
return;
if (is_vampshifter(mtmp)) {
int x = mtmp->mx, y = mtmp->my;
/* this only happens if shapeshifted */
- if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)) {
+ if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)
+ && !(mvitals[mndx].mvflags & G_GENOD)) {
char buf[BUFSZ];
boolean in_door = (amorphous(mtmp->data)
&& closed_door(mtmp->mx, mtmp->my)),
+#if 0 /*JP*/
/* alternate message phrasing for some monster types */
spec_mon = (nonliving(mtmp->data)
|| noncorporeal(mtmp->data)
- || amorphous(mtmp->data));
+ || amorphous(mtmp->data)),
+#endif
+ spec_death = (disintegested /* disintegrated or digested */
+ || noncorporeal(mtmp->data)
+ || amorphous(mtmp->data));
- /* construct a format string before transformation */
- Sprintf(buf, "The %s%s suddenly %s and rises as %%s!",
- spec_mon ? "" : "seemingly dead ",
+ /* construct a format string before transformation;
+ will be capitalized when used, expects one %s arg */
+#if 0 /*JP*/
+ Sprintf(buf, "%s suddenly %s and rises as %%s!",
+ x_monnam(mtmp, ARTICLE_THE,
+ spec_mon ? (char *) 0 : "seemingly dead",
+ (SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+ spec_death ? "reconstitutes" : "transforms");
+#else
+ Sprintf(buf, "%s%s\82Í\93Ë\91R%s\81C%%s\82Æ\82µ\82Ä\91h\82Á\82½\81I",
+ spec_death ? "" : "\8e\80\82ñ\82¾\82æ\82¤\82É\8ev\82í\82ê\82½",
x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
| SUPPRESS_INVISIBLE | SUPPRESS_IT,
FALSE),
- spec_mon ? "reconstitutes" : "transforms");
+ spec_death ? "\8dÄ\8d\\90¬\82³\82ê" : "\95Ï\89»\82µ");
+#endif
mtmp->mcanmove = 1;
mtmp->mfrozen = 0;
if (mtmp->mhpmax <= 0)
mtmp->mhpmax = 10;
mtmp->mhp = mtmp->mhpmax;
- /* this can happen if previously a fog cloud */
- if (u.uswallow && (mtmp == u.ustuck))
- expels(mtmp, mtmp->data, FALSE);
+ /* mtmp==u.ustuck can happen if previously a fog cloud
+ or poly'd hero is hugging a vampire bat */
+ if (mtmp == u.ustuck) {
+ if (u.uswallow)
+ expels(mtmp, mtmp->data, FALSE);
+ else
+ uunstick();
+ }
if (in_door) {
coord new_xy;
else
mtmp->cham = mndx;
if (canspotmon(mtmp)) {
- pline(buf, a_monnam(mtmp));
+ /* 3.6.0 used a_monnam(mtmp); that was weird if mtmp was
+ named: "Dracula suddenly transforms and rises as Dracula";
+ 3.6.1 used mtmp->data->mname; that ignored hallucination */
+ pline(upstart(buf),
+ x_monnam(mtmp, ARTICLE_A, (char *) 0,
+ (SUPPRESS_NAME | SUPPRESS_IT
+ | SUPPRESS_INVISIBLE), FALSE));
vamp_rise_msg = TRUE;
}
newsym(x, y);
mptr = mtmp->data; /* save this for m_detach() */
/* restore chameleon, lycanthropes to true form at death */
if (mtmp->cham >= LOW_PM) {
- set_mon_data(mtmp, &mons[mtmp->cham], -1);
+ set_mon_data(mtmp, &mons[mtmp->cham]);
mtmp->cham = NON_PM;
} else if (mtmp->data == &mons[PM_WEREJACKAL])
- set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1);
+ set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL]);
else if (mtmp->data == &mons[PM_WEREWOLF])
- set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF], -1);
+ set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF]);
else if (mtmp->data == &mons[PM_WERERAT])
- set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1);
+ set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT]);
- /* if MAXMONNO monsters of a given type have died, and it
- * can be done, extinguish that monster.
- *
+ /*
* mvitals[].died does double duty as total number of dead monsters
* and as experience factor for the player killing more monsters.
* this means that a dragon dying by other means reduces the
There("is an explosion in your %s!", body_part(STOMACH));
*/
pline("%s\82Ì\92\86\82Å\94\9a\94\82ª\8bN\82«\82½\81I", body_part(STOMACH));
-#if 0 /*JP*/
+#if 0 /*JP:T*/
Sprintf(killer.name, "%s explosion",
s_suffix(mdat->mname));
#else
*/
You_hear("\94\9a\94\89¹\82ð\95·\82¢\82½\81D");
magr->mhp -= tmp;
- if (magr->mhp < 1)
+ if (DEADMONSTER(magr))
mondied(magr);
- if (magr->mhp < 1) { /* maybe lifesaved */
+ if (DEADMONSTER(magr)) { /* maybe lifesaved */
if (canspotmon(magr))
/*JP
pline("%s rips open!", Monnam(magr));
Sprintf(killer.name, "%s\82Ì\94\9a\94\82Å", mdat->mname);
killer.format = KILLED_BY_AN;
explode(mon->mx, mon->my, -1, tmp, MON_EXPLODE, EXPL_NOXIOUS);
+ killer.name[0] = '\0';
+ killer.format = 0;
return FALSE;
}
}
return FALSE;
if (((bigmonst(mdat) || mdat == &mons[PM_LIZARD]) && !mon->mcloned)
- || is_golem(mdat) || is_mplayer(mdat) || is_rider(mdat))
+ || is_golem(mdat) || is_mplayer(mdat) || is_rider(mdat) || mon->isshk)
return TRUE;
tmp = 2 + ((mdat->geno & G_FREQ) < 2) + verysmall(mdat);
return (boolean) !rn2(tmp);
register struct monst *mdef;
{
mondead(mdef);
- if (mdef->mhp > 0)
+ if (!DEADMONSTER(mdef))
return; /* lifesaved */
if (corpse_chance(mdef, (struct monst *) 0, FALSE)
/* hero is thrown from his steed when it disappears */
if (mdef == u.usteed)
dismount_steed(DISMOUNT_GENERIC);
+ /* stuck to you? release */
+ unstuck(mdef);
/* drop special items like the Amulet so that a dismissed Kop or nurse
can't remove them from the game */
mdrop_special_objs(mdef);
xchar x = mdef->mx, y = mdef->my;
boolean wasinside = FALSE;
+ /* vampshifter reverts to vampire;
+ 3.6.3: also used to unshift shape-changed sandestin */
+ if (!vamp_stone(mdef))
+ return;
+
/* we have to make the statue before calling mondead, to be able to
* put inventory in it, and we have to check for lifesaving before
* making the statue....
*/
+ mdef->mhp = 0; /* in case caller hasn't done this */
lifesaved_monster(mdef);
- if (mdef->mhp > 0)
+ if (!DEADMONSTER(mdef))
return;
mdef->mtrapped = 0; /* (see m_detach) */
otmp = oname(otmp, MNAME(mdef));
while ((obj = oldminvent) != 0) {
oldminvent = obj->nobj;
+ obj->nobj = 0; /* avoid merged-> obfree-> dealloc_obj-> panic */
(void) add_to_container(otmp, obj);
}
/* Archeologists should not break unique statues */
mondead(mdef);
if (wasinside) {
if (is_animal(mdef->data))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
You("%s through an opening in the new %s.",
locomotion(youmonst.data, "jump"), xname(otmp));
#else
be_sad = (mdef->mtame != 0);
/* no corpses if digested or disintegrated */
- if (how == AD_DGST || how == -AD_RBRE)
+ disintegested = (how == AD_DGST || how == -AD_RBRE);
+ if (disintegested)
mondead(mdef);
else
mondied(mdef);
- if (be_sad && mdef->mhp <= 0)
+ if (be_sad && DEADMONSTER(mdef))
/*JP
You("have a sad feeling for a moment, then it passes.");
*/
placebc();
vision_full_recalc = 1;
docrt();
+ /* prevent swallower (mtmp might have just poly'd into something
+ without an engulf attack) from immediately re-engulfing */
+ if (attacktype(mtmp->data, AT_ENGL) && !mtmp->mspec_used)
+ mtmp->mspec_used = rnd(2);
}
u.ustuck = 0;
}
killed(mtmp)
struct monst *mtmp;
{
- xkilled(mtmp, 1);
+ xkilled(mtmp, XKILL_GIVEMSG);
}
/* the player has killed the monster mtmp */
void
-xkilled(mtmp, dest)
+xkilled(mtmp, xkill_flags)
struct monst *mtmp;
-int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
- drop corpse either; dest==3, message but no corpse */
+int xkill_flags; /* 1: suppress message, 2: suppress corpse, 4: pacifist */
{
int tmp, mndx, x = mtmp->mx, y = mtmp->my;
struct permonst *mdat;
struct obj *otmp;
struct trap *t;
- boolean wasinside = u.uswallow && (u.ustuck == mtmp);
- boolean burycorpse = FALSE;
-
- /* KMH, conduct */
- u.uconduct.killer++;
+ boolean wasinside = u.uswallow && (u.ustuck == mtmp),
+ burycorpse = FALSE,
+ nomsg = (xkill_flags & XKILL_NOMSG) != 0,
+ nocorpse = (xkill_flags & XKILL_NOCORPSE) != 0,
+ noconduct = (xkill_flags & XKILL_NOCONDUCT) != 0;
- if (dest & 1) {
-#if 0 /*JP*/
- const char *verb = nonliving(mtmp->data) ? "destroy" : "kill";
-#endif
+ mtmp->mhp = 0; /* caller will usually have already done this */
+ if (!noconduct) /* KMH, conduct */
+ u.uconduct.killer++;
- if (!wasinside && !canspotmon(mtmp))
-/*JP
- You("%s it!", verb);
-*/
- You("\89½\8eÒ\82©\82ð\93|\82µ\82½\81I");
- else {
+ if (!nomsg) {
#if 0 /*JP*/
- You("%s %s!", verb,
- !mtmp->mtame
- ? mon_nam(mtmp)
- : x_monnam(mtmp,
- (has_mname(mtmp)) ? ARTICLE_NONE : ARTICLE_THE,
- "poor",
- (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0,
- FALSE));
+ boolean namedpet = has_mname(mtmp) && !Hallucination;
+
+ You("%s %s!",
+ nonliving(mtmp->data) ? "destroy" : "kill",
+ !(wasinside || canspotmon(mtmp)) ? "it"
+ : !mtmp->mtame ? mon_nam(mtmp)
+ : x_monnam(mtmp, namedpet ? ARTICLE_NONE : ARTICLE_THE,
+ "poor", namedpet ? SUPPRESS_SADDLE : 0, FALSE));
#else
- You("%s%s\82ð\93|\82µ\82½\81I",
- mtmp->mtame ? "\82©\82í\82¢\82»\82¤\82È" : "",
+ You("%s%s\82ð\93|\82µ\82½\81I",
+ !(wasinside || canspotmon(mtmp)) ? "\82»\82ê"
+ : mtmp->mtame ? "\82©\82í\82¢\82»\82¤\82È" : "",
mon_nam(mtmp));
#endif
- }
}
if (mtmp->mtrapped && (t = t_at(x, y)) != 0
- && (t->ttyp == PIT || t->ttyp == SPIKED_PIT)) {
+ && is_pit(t->ttyp)) {
if (sobj_at(BOULDER, x, y))
- dest |= 2; /*
- * Prevent corpses/treasure being created "on top"
- * of the boulder that is about to fall in. This is
- * out of order, but cannot be helped unless this
- * whole routine is rearranged.
- */
+ nocorpse = TRUE; /* Prevent corpses/treasure being created
+ "on top" of boulder that is about to fall in.
+ This is out of order, but cannot be helped
+ unless this whole routine is rearranged. */
if (m_carrying(mtmp, BOULDER))
burycorpse = TRUE;
}
if (mtmp->mtame && !mtmp->isminion)
EDOG(mtmp)->killed_by_u = 1;
- if (wasinside && thrownobj && thrownobj != uball) {
+ if (wasinside && thrownobj && thrownobj != uball
+ /* don't give to mon if missile is going to return to hero */
+ && thrownobj != (struct obj *) iflags.returning_missile) {
/* thrown object has killed hero's engulfer; add it to mon's
inventory now so that it will be placed with mon's other
stuff prior to lookhere/autopickup when hero is expelled
thrownobj = 0;
}
- vamp_rise_msg = FALSE; /* might get set in mondead() */
+ vamp_rise_msg = FALSE; /* might get set in mondead(); only checked below */
+ disintegested = nocorpse; /* alternate vamp_rise message needed if true */
/* dispose of monster and make cadaver */
if (stoned)
monstone(mtmp);
else
mondead(mtmp);
+ disintegested = FALSE; /* reset */
- if (mtmp->mhp > 0) { /* monster lifesaved */
+ if (!DEADMONSTER(mtmp)) { /* monster lifesaved */
/* Cannot put the non-visible lifesaving message in
- * lifesaved_monster() since the message appears only when you
- * kill it (as opposed to visible lifesaving which always
- * appears).
+ * lifesaved_monster() since the message appears only when _you_
+ * kill it (as opposed to visible lifesaving which always appears).
*/
stoned = FALSE;
if (!cansee(x, y) && !vamp_rise_msg)
goto cleanup;
}
- if ((dest & 2) || LEVEL_SPECIFIC_NOCORPSE(mdat))
+ if (nocorpse || LEVEL_SPECIFIC_NOCORPSE(mdat))
goto cleanup;
#ifdef MAIL
/* oc_big is also oc_bimanual and oc_bulky */
&& (otmp->owt > 30 || objects[otyp].oc_big)) {
delobj(otmp);
- } else if (!flooreffects(otmp, x, y, (dest & 1) ? "fall" : "")) {
+#if 0 /*JP:T*/
+ } else if (!flooreffects(otmp, x, y, nomsg ? "" : "fall")) {
+#else
+ } else if (!flooreffects(otmp, x, y, nomsg ? "" : "\97\8e\82¿\82é")) {
+#endif
place_object(otmp, x, y);
stackobj(otmp);
}
cadaver = make_corpse(mtmp, burycorpse ? CORPSTAT_BURIED
: CORPSTAT_NONE);
if (burycorpse && cadaver && cansee(x, y) && !mtmp->minvis
- && cadaver->where == OBJ_BURIED && (dest & 1)) {
+ && cadaver->where == OBJ_BURIED && !nomsg) {
+/*JP
pline("%s corpse ends up buried.", s_suffix(Monnam(mtmp)));
+*/
+ pline("%s\82Ì\8e\80\91Ì\82Í\96\84\82Ü\82Á\82Ä\82µ\82Ü\82Á\82½\81D", Monnam(mtmp));
}
}
}
/* monster is gone, corpse or other object might now be visible */
newsym(x, y);
-cleanup:
+ cleanup:
/* punish bad behaviour */
- if (is_human(mdat) && (!always_hostile(mdat) && mtmp->malign <= 0)
+ if (is_human(mdat)
+ && (!always_hostile(mdat) && mtmp->malign <= 0)
&& (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD)
&& u.ualign.type != A_CHAOTIC) {
HTelepat &= ~INTRINSIC;
/* adjust alignment points */
if (mtmp->m_id == quest_status.leader_m_id) { /* REAL BAD! */
adjalign(-(u.ualign.record + (int) ALIGNLIM / 2));
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("That was %sa bad idea...",
u.uevent.qcompleted ? "probably " : "");
#else
impossible("Can't polystone %s!", a_monnam(mtmp));
}
+boolean
+vamp_stone(mtmp)
+struct monst *mtmp;
+{
+ if (is_vampshifter(mtmp)) {
+ int mndx = mtmp->cham;
+ int x = mtmp->mx, y = mtmp->my;
+
+ /* this only happens if shapeshifted */
+ if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)
+ && !(mvitals[mndx].mvflags & G_GENOD)) {
+ char buf[BUFSZ];
+ boolean in_door = (amorphous(mtmp->data)
+ && closed_door(mtmp->mx, mtmp->my));
+
+ /* construct a format string before transformation */
+#if 0 /*JP:T*/
+ Sprintf(buf, "The lapidifying %s %s %s",
+ x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
+ (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
+ | SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+ amorphous(mtmp->data) ? "coalesces on the"
+ : is_flyer(mtmp->data) ? "drops to the"
+ : "writhes on the",
+ surface(x,y));
+#else
+ Sprintf(buf, "\90Î\89»\82µ\82Â\82Â\82 \82é%s\82ª%s%s",
+ x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
+ (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
+ | SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+ surface(x,y),
+ amorphous(mtmp->data) ? "\82Ì\8fã\82Å\97Z\8d\87\82µ\82½"
+ : is_flyer(mtmp->data) ? "\82É\97\8e\82¿\82½"
+ : "\82Ì\8fã\82Å\90g\82à\82¾\82¦\82½");
+#endif
+ mtmp->mcanmove = 1;
+ mtmp->mfrozen = 0;
+ if (mtmp->mhpmax <= 0)
+ mtmp->mhpmax = 10;
+ mtmp->mhp = mtmp->mhpmax;
+ /* this can happen if previously a fog cloud */
+ if (u.uswallow && (mtmp == u.ustuck))
+ expels(mtmp, mtmp->data, FALSE);
+ if (in_door) {
+ coord new_xy;
+
+ if (enexto(&new_xy, mtmp->mx, mtmp->my, &mons[mndx])) {
+ rloc_to(mtmp, new_xy.x, new_xy.y);
+ }
+ }
+ if (canspotmon(mtmp)) {
+/*JP
+ pline("%s!", buf);
+*/
+ pline("%s\81I", buf);
+ display_nhwindow(WIN_MESSAGE, FALSE);
+ }
+ newcham(mtmp, &mons[mndx], FALSE, FALSE);
+ if (mtmp->data == &mons[mndx])
+ mtmp->cham = NON_PM;
+ else
+ mtmp->cham = mndx;
+ if (canspotmon(mtmp)) {
+#if 0 /*JP:T*/
+ pline("%s rises from the %s with renewed agility!",
+ Amonnam(mtmp), surface(mtmp->mx, mtmp->my));
+#else
+ pline("%s\82Í\8b@\95q\82³\82ð\8eæ\82è\96ß\82µ\82Ä%s\82©\82ç\95\9c\8a\88\82µ\82½\81I",
+ Amonnam(mtmp), surface(mtmp->mx, mtmp->my));
+#endif
+ }
+ newsym(mtmp->mx, mtmp->my);
+ return FALSE; /* didn't petrify */
+ }
+ } else if (mtmp->cham >= LOW_PM
+ && (mons[mtmp->cham].mresists & MR_STONE)) {
+ /* sandestins are stoning-immune so if hit by stoning damage
+ they revert to innate shape rather than become a statue */
+ mtmp->mcanmove = 1;
+ mtmp->mfrozen = 0;
+ if (mtmp->mhpmax <= 0)
+ mtmp->mhpmax = 10;
+ mtmp->mhp = mtmp->mhpmax;
+ (void) newcham(mtmp, &mons[mtmp->cham], FALSE, TRUE);
+ newsym(mtmp->mx, mtmp->my);
+ return FALSE; /* didn't petrify */
+ }
+ return TRUE;
+}
+
+/* drop monster into "limbo" - that is, migrate to the current level */
+void
+m_into_limbo(mtmp)
+struct monst *mtmp;
+{
+ xchar target_lev = ledger_no(&u.uz), xyloc = MIGR_APPROX_XY;
+
+ mtmp->mstate |= MON_LIMBO;
+ migrate_mon(mtmp, target_lev, xyloc);
+}
+
+STATIC_OVL void
+migrate_mon(mtmp, target_lev, xyloc)
+struct monst *mtmp;
+xchar target_lev, xyloc;
+{
+ unstuck(mtmp);
+ mdrop_special_objs(mtmp);
+ migrate_to_level(mtmp, target_lev, xyloc, (coord *) 0);
+ mtmp->mstate |= MON_MIGRATING;
+}
+
+STATIC_OVL boolean
+ok_to_obliterate(mtmp)
+struct monst *mtmp;
+{
+ /*
+ * Add checks for monsters that should not be obliterated
+ * here (return FALSE).
+ */
+ if (mtmp->data == &mons[PM_WIZARD_OF_YENDOR] || is_rider(mtmp->data)
+ || has_emin(mtmp) || has_epri(mtmp) || has_eshk(mtmp)
+ || (u.ustuck == mtmp) || (u.usteed == mtmp))
+ return FALSE;
+ return TRUE;
+}
+
+void
+elemental_clog(mon)
+struct monst *mon;
+{
+ int m_lev = 0;
+ static long msgmv = 0L;
+ struct monst *mtmp, *m1, *m2, *m3, *m4, *m5, *zm;
+
+ if (In_endgame(&u.uz)) {
+ m1 = m2 = m3 = m4 = m5 = zm = (struct monst *) 0;
+ if (!msgmv || (moves - msgmv) > 200L) {
+ if (!msgmv || rn2(2))
+/*JP
+ You_feel("besieged.");
+*/
+ You_feel("\95ï\88Í\82³\82ê\82½\82æ\82¤\82É\8a´\82¶\82½\81D");
+ msgmv = moves;
+ }
+ /*
+ * m1 an elemental from another plane.
+ * m2 an elemental from this plane.
+ * m3 the least powerful monst encountered in loop so far.
+ * m4 some other non-tame monster.
+ * m5 a pet.
+ */
+ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+ if (DEADMONSTER(mtmp) || mtmp == mon)
+ continue;
+ if (mtmp->mx == 0 && mtmp->my == 0)
+ continue;
+ if (mon_has_amulet(mtmp) || !ok_to_obliterate(mtmp))
+ continue;
+ if (mtmp->data->mlet == S_ELEMENTAL) {
+ if (!is_home_elemental(mtmp->data)) {
+ if (!m1)
+ m1 = mtmp;
+ } else {
+ if (!m2)
+ m2 = mtmp;
+ }
+ } else {
+ if (!mtmp->mtame) {
+ if (!m_lev || mtmp->m_lev < m_lev) {
+ m_lev = mtmp->m_lev;
+ m3 = mtmp;
+ } else if (!m4) {
+ m4 = mtmp;
+ }
+ } else {
+ if (!m5)
+ m5 = mtmp;
+ break;
+ }
+ }
+ }
+ mtmp = m1 ? m1 : m2 ? m2 : m3 ? m3 : m4 ? m4 : m5 ? m5 : zm;
+ if (mtmp) {
+ int mx = mtmp->mx, my = mtmp->my;
+
+ mtmp->mstate |= MON_OBLITERATE;
+ mongone(mtmp);
+ /* places in the code might still reference mtmp->mx, mtmp->my */
+ /* mtmp->mx = mtmp->my = 0; */
+ rloc_to(mon, mx, my); /* note: mon, not mtmp */
+
+ /* last resort - migrate mon to the next plane */
+ } else if (!Is_astralevel(&u.uz)) {
+ d_level dest;
+ xchar target_lev;
+
+ dest = u.uz;
+ dest.dlevel--;
+ target_lev = ledger_no(&dest);
+ mon->mstate |= MON_ENDGAME_MIGR;
+ migrate_mon(mon, target_lev, MIGR_RANDOM);
+ }
+ }
+}
+
/* make monster mtmp next to you (if possible);
might place monst on far side of a wall or boulder */
void
return;
}
- if (!enexto(&mm, u.ux, u.uy, mtmp->data))
+ if (!enexto(&mm, u.ux, u.uy, mtmp->data) || !isok(mm.x, mm.y)) {
+ deal_with_overcrowding(mtmp);
return;
+ }
rloc_to(mtmp, mm.x, mm.y);
if (!in_mklev && (mtmp->mstrategy & STRAT_APPEARMSG)) {
mtmp->mstrategy &= ~STRAT_APPEARMSG; /* one chance only */
if (!couldspot && canspotmon(mtmp))
+#if 0 /*JP:T*/
pline("%s suddenly %s!", Amonnam(mtmp),
!Blind ? "appears" : "arrives");
+#else
+ pline("\93Ë\91R%s\82ª\8c»\82ê\82½\81I", Amonnam(mtmp));
+#endif
}
return;
}
+STATIC_OVL void
+deal_with_overcrowding(mtmp)
+struct monst *mtmp;
+{
+ if (In_endgame(&u.uz)) {
+ debugpline1("overcrowding: elemental_clog on %s", m_monnam(mtmp));
+ elemental_clog(mtmp);
+ } else {
+ debugpline1("overcrowding: sending %s into limbo", m_monnam(mtmp));
+ m_into_limbo(mtmp);
+ }
+}
+
/* like mnexto() but requires destination to be directly accessible */
void
maybe_mnexto(mtmp)
/* mnearto()
* Put monster near (or at) location if possible.
* Returns:
- * 1 - if a monster was moved from x, y to put mtmp at x, y.
- * 0 - in most cases.
+ * 2 if another monster was moved out of this one's way;
+ * 1 if relocation was successful (without moving another one);
+ * 0 otherwise.
+ * Note: if already at the target spot, result is 1 rather than 0.
+ *
+ * Might be called recursively if 'move_other' is True; if so, that argument
+ * will be False on the nested call so there won't be any further recursion.
*/
-boolean
+int
mnearto(mtmp, x, y, move_other)
register struct monst *mtmp;
xchar x, y;
struct monst *othermon = (struct monst *) 0;
xchar newx, newy;
coord mm;
+ int res = 1;
- if (mtmp->mx == x && mtmp->my == y)
- return FALSE;
+ if (mtmp->mx == x && mtmp->my == y && m_at(x, y) == mtmp)
+ return res;
if (move_other && (othermon = m_at(x, y)) != 0) {
if (othermon->wormno)
remove_worm(othermon);
else
remove_monster(x, y);
+
+ othermon->mx = othermon->my = 0; /* 'othermon' is not on the map */
+ othermon->mstate |= MON_OFFMAP;
}
newx = x;
* Migrating_mons that need to be placed will cause
* no end of trouble.
*/
- if (!enexto(&mm, newx, newy, mtmp->data))
- return FALSE;
+ if (!enexto(&mm, newx, newy, mtmp->data) || !isok(mm.x, mm.y)) {
+ if (othermon) {
+ /* othermon already had its mx, my set to 0 above
+ * and this would shortly cause a sanity check to fail
+ * if we just return 0 here. The caller only possesses
+ * awareness of mtmp, not othermon. */
+ deal_with_overcrowding(othermon);
+ }
+ return 0;
+ }
newx = mm.x;
newy = mm.y;
}
rloc_to(mtmp, newx, newy);
if (move_other && othermon) {
- xchar oldx = othermon->mx, oldy = othermon->my;
-
- othermon->mx = othermon->my = 0;
- (void) mnearto(othermon, x, y, FALSE);
- if (othermon->mx == 0 && othermon->my == 0) {
- /* reloc failed, dump monster into "limbo"
- (aka migrate to current level) */
- othermon->mx = oldx;
- othermon->my = oldy;
- mdrop_special_objs(othermon);
- migrate_to_level(othermon, ledger_no(&u.uz), MIGR_APPROX_XY, NULL);
- }
+ res = 2; /* moving another monster out of the way */
+ if (!mnearto(othermon, x, y, FALSE)) /* no 'move_other' this time */
+ deal_with_overcrowding(othermon);
}
- return FALSE;
+ return res;
}
/* monster responds to player action; not the same as a passive attack;
}
}
+/* Called whenever the player attacks mtmp; also called in other situations
+ where mtmp gets annoyed at the player. Handles mtmp getting annoyed at the
+ attack and any ramifications that might have. Useful also in situations
+ where mtmp was already hostile; it checks for situations where the player
+ shouldn't be attacking and any ramifications /that/ might have. */
void
-setmangry(mtmp)
+setmangry(mtmp, via_attack)
struct monst *mtmp;
+boolean via_attack;
{
+ if (via_attack && sengr_at("Elbereth", u.ux, u.uy, TRUE)
+ /* only hypocritical if monster is vulnerable to Elbereth (or
+ peaceful--not vulnerable but attacking it is hypocritical) */
+ && (onscary(u.ux, u.uy, mtmp) || mtmp->mpeaceful)) {
+/*JP
+ You_feel("like a hypocrite.");
+*/
+ You_feel("\8bU\91P\8eÒ\82Ì\82æ\82¤\82È\8bC\82ª\82µ\82½\81D");
+ /* AIS: Yes, I know alignment penalties and bonuses aren't balanced
+ at the moment. This is about correct relative to other "small"
+ penalties; it should be fairly large, as attacking while standing
+ on an Elbereth means that you're requesting peace and then
+ violating your own request. I know 5 isn't actually large, but
+ it's intentionally larger than the 1s and 2s that are normally
+ given for this sort of thing. */
+ /* reduce to 3 (average) when alignment is already very low */
+ adjalign((u.ualign.record > 5) ? -5 : -rnd(5));
+
+ if (!Blind)
+/*JP
+ pline("The engraving beneath you fades.");
+*/
+ pline("\82 \82È\82½\82Ì\91«\8c³\82Ì\95¶\8e\9a\82ª\94\96\82ê\82½\81D");
+ del_engr_at(u.ux, u.uy);
+ }
+
+ /* AIS: Should this be in both places, or just in wakeup()? */
mtmp->mstrategy &= ~STRAT_WAITMASK;
if (!mtmp->mpeaceful)
return;
++got_mad;
}
}
- if (got_mad && !Hallucination)
-#if 0 /*JP*/
- pline_The("%s appear%s to be angry too...",
- got_mad == 1 ? q_guardian->mname
- : makeplural(q_guardian->mname),
- got_mad == 1 ? "s" : "");
+ if (got_mad && !Hallucination) {
+ const char *who = q_guardian->mname;
+
+#if 0 /*JP:T*/
+ if (got_mad > 1)
+ who = makeplural(who);
+ pline_The("%s %s to be angry too...",
+ who, vtense(who, "appear"));
#else
- pline("%s\82à\93{\82Á\82½\81D\81D\81D", q_guardian->mname);
+ pline("%s\82à\93{\82Á\82½\82æ\82¤\82¾\81D\81D\81D", who);
#endif
+ }
+ }
+
+ /* make other peaceful monsters react */
+ if (!context.mon_moving) {
+ static const char *const Exclam[] = {
+/*JP
+ "Gasp!", "Uh-oh.", "Oh my!", "What?", "Why?",
+*/
+ "\82®\82Í\82Á\81I", "\82¤\82í\81D", "\82È\82ñ\82Æ\81I", "\82È\82É\82Á\81H", "\82È\82ñ\82¾\81H",
+ };
+ struct monst *mon;
+ int mndx = monsndx(mtmp->data);
+
+ for (mon = fmon; mon; mon = mon->nmon) {
+ if (DEADMONSTER(mon))
+ continue;
+ if (mon == mtmp) /* the mpeaceful test catches this since mtmp */
+ continue; /* is no longer peaceful, but be explicit... */
+
+ if (!mindless(mon->data) && mon->mpeaceful
+ && couldsee(mon->mx, mon->my) && !mon->msleeping
+ && mon->mcansee && m_canseeu(mon)) {
+ boolean exclaimed = FALSE;
+
+ if (humanoid(mon->data) || mon->isshk || mon->ispriest) {
+ if (is_watch(mon->data)) {
+/*JP
+ verbalize("Halt! You're under arrest!");
+*/
+ verbalize("\8e~\82Ü\82ê\81I\91ß\95ß\82·\82é\81I");
+ (void) angry_guards(!!Deaf);
+ } else {
+ if (!rn2(5)) {
+ verbalize("%s", Exclam[mon->m_id % SIZE(Exclam)]);
+ exclaimed = TRUE;
+ }
+ /* shopkeepers and temple priests might gasp in
+ surprise, but they won't become angry here */
+ if (mon->isshk || mon->ispriest)
+ continue;
+
+ if (mon->data->mlevel < rn2(10)) {
+ monflee(mon, rn2(50) + 25, TRUE, !exclaimed);
+ exclaimed = TRUE;
+ }
+ if (mon->mtame) {
+ /* mustn't set mpeaceful to 0 as below;
+ perhaps reduce tameness? */
+ } else {
+ mon->mpeaceful = 0;
+ adjalign(-1);
+ if (!exclaimed)
+/*JP
+ pline("%s gets angry!", Monnam(mon));
+*/
+ pline("%s\82Í\93{\82Á\82½\81I", Monnam(mon));
+ }
+ }
+ } else if (mon->data->mlet == mtmp->data->mlet
+ && big_little_match(mndx, monsndx(mon->data))
+ && !rn2(3)) {
+ if (!rn2(4)) {
+ growl(mon);
+ exclaimed = TRUE;
+ }
+ if (rn2(6))
+ monflee(mon, rn2(25) + 15, TRUE, !exclaimed);
+ }
+ }
+ }
}
}
-/* wake up a monster, usually making it angry in the process */
+/* wake up a monster, possibly making it angry in the process */
void
-wakeup(mtmp)
+wakeup(mtmp, via_attack)
register struct monst *mtmp;
+boolean via_attack;
{
mtmp->msleeping = 0;
- finish_meating(mtmp);
- setmangry(mtmp);
- if (mtmp->m_ap_type) {
+ if (M_AP_TYPE(mtmp)) {
seemimic(mtmp);
} else if (context.forcefight && !context.mon_moving
&& mtmp->mundetected) {
mtmp->mundetected = 0;
newsym(mtmp->mx, mtmp->my);
}
+ finish_meating(mtmp);
+ if (via_attack)
+ setmangry(mtmp, TRUE);
}
/* Wake up nearby monsters without angering them. */
void
wake_nearby()
{
- register struct monst *mtmp;
-
- for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
- if (DEADMONSTER(mtmp))
- continue;
- if (distu(mtmp->mx, mtmp->my) < u.ulevel * 20) {
- mtmp->msleeping = 0;
- if (!unique_corpstat(mtmp->data))
- mtmp->mstrategy &= ~STRAT_WAITMASK;
- if (mtmp->mtame && !mtmp->isminion)
- EDOG(mtmp)->whistletime = moves;
- }
- }
+ wake_nearto(u.ux, u.uy, u.ulevel * 20);
}
/* Wake up monsters near some particular location. */
void
wake_nearto(x, y, distance)
-register int x, y, distance;
+int x, y, distance;
{
- register struct monst *mtmp;
+ struct monst *mtmp;
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp))
continue;
if (distance == 0 || dist2(mtmp->mx, mtmp->my, x, y) < distance) {
- mtmp->msleeping = 0;
- if (!unique_corpstat(mtmp->data))
- mtmp->mstrategy &= ~STRAT_WAITMASK;
+ /* sleep for N turns uses mtmp->mfrozen, but so does paralysis
+ so we leave mfrozen monsters alone */
+ mtmp->msleeping = 0; /* wake indeterminate sleep */
+ if (!(mtmp->data->geno & G_UNIQ))
+ mtmp->mstrategy &= ~STRAT_WAITMASK; /* wake 'meditation' */
+ if (context.mon_moving)
+ continue;
+ if (mtmp->mtame) {
+ if (!mtmp->isminion)
+ EDOG(mtmp)->whistletime = moves;
+ /* Clear mtrack. This is to fix up a pet who is
+ stuck "fleeing" its master. */
+ memset(mtmp->mtrack, 0, sizeof mtmp->mtrack);
+ }
}
}
}
seemimic(mtmp)
register struct monst *mtmp;
{
- boolean is_blocker_appear = (is_door_mappear(mtmp)
- || is_obj_mappear(mtmp, BOULDER));
+ boolean is_blocker_appear = (is_lightblocker_mappear(mtmp));
if (has_mcorpsenm(mtmp))
freemcorpsenm(mtmp);
}
if (is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
new_were(mtmp);
- if (mtmp->m_ap_type && cansee(mtmp->mx, mtmp->my)) {
+ if (M_AP_TYPE(mtmp) && cansee(mtmp->mx, mtmp->my)) {
seemimic(mtmp);
/* we pretend that the mimic doesn't
know that it has been unmasked */
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp))
continue;
- mtmp->cham = pm_to_cham(monsndx(mtmp->data));
+ if (!mtmp->mcan)
+ mtmp->cham = pm_to_cham(monsndx(mtmp->data));
if (mtmp->data->mlet == S_MIMIC && mtmp->msleeping
&& cansee(mtmp->mx, mtmp->my)) {
set_mimic_sym(mtmp);
{
struct trap *t;
- if (mtmp->mcan || mtmp->m_ap_type || cansee(mtmp->mx, mtmp->my)
+ if (mtmp->mcan || M_AP_TYPE(mtmp) || cansee(mtmp->mx, mtmp->my)
|| rn2(3) || mtmp == u.ustuck
/* can't hide while trapped except in pits */
|| (mtmp->mtrapped && (t = t_at(mtmp->mx, mtmp->my)) != 0
- && !(t->ttyp == PIT || t->ttyp == SPIKED_PIT))
+ && !is_pit(t->ttyp))
|| (sensemon(mtmp) && distu(mtmp->mx, mtmp->my) <= 2))
return FALSE;
; /* can't hide if holding you or held by you */
} else if (is_u ? (u.utrap && u.utraptype != TT_PIT)
: (mtmp->mtrapped && (t = t_at(x, y)) != 0
- && !(t->ttyp == PIT || t->ttyp == SPIKED_PIT))) {
+ && !is_pit(t->ttyp))) {
; /* can't hide while stuck in a non-pit trap */
} else if (mtmp->data->mlet == S_EEL) {
undetected = (is_pool(x, y) && !Is_waterlevel(&u.uz));
boolean hider_under = hides_under(mon->data) || mon->data->mlet == S_EEL;
if ((is_hider(mon->data) || hider_under)
- && !(mon->mundetected || mon->m_ap_type)) {
+ && !(mon->mundetected || M_AP_TYPE(mon))) {
xchar x = mon->mx, y = mon->my;
char save_viz = viz_array[y][x];
if (is_hider(mon->data))
(void) restrap(mon);
/* try again if mimic missed its 1/3 chance to hide */
- if (mon->data->mlet == S_MIMIC && !mon->m_ap_type)
+ if (mon->data->mlet == S_MIMIC && !M_AP_TYPE(mon))
(void) restrap(mon);
if (hider_under)
(void) hideunder(mon);
struct monst *mon;
int shiftflags;
{
- struct permonst *ptr;
+ struct permonst *ptr = 0;
+ int mndx;
unsigned was_female = mon->female;
- boolean msg = FALSE;
+ boolean msg = FALSE, dochng = FALSE;
if ((shiftflags & SHIFT_MSG)
|| ((shiftflags & SHIFT_SEENMSG) && sensemon(mon)))
msg = TRUE;
- if (is_vampshifter(mon)) {
+ if (!is_vampshifter(mon)) {
+ /* regular shapeshifter */
+ if (!rn2(6))
+ dochng = TRUE;
+ } else {
/* The vampire has to be in good health (mhp) to maintain
* its shifted form.
- *
- * If we're shifted and getting low on hp, maybe shift back.
+ *
+ * If we're shifted and getting low on hp, maybe shift back, or
+ * if we're a fog cloud at full hp, maybe pick a different shape.
* If we're not already shifted and in good health, maybe shift.
*/
- if ((mon->mhp <= mon->mhpmax / 6) && rn2(4) && (mon->cham >= LOW_PM))
- (void) newcham(mon, &mons[mon->cham], FALSE, msg);
- } else if (mon->data->mlet == S_VAMPIRE && mon->cham == NON_PM && !rn2(6)
- && (mon->mhp > mon->mhpmax - ((mon->mhpmax / 10) + 1))) {
- (void) newcham(mon, (struct permonst *) 0, FALSE, msg);
+ if (mon->data->mlet != S_VAMPIRE) {
+ if ((mon->mhp <= (mon->mhpmax + 5) / 6) && rn2(4)
+ && mon->cham >= LOW_PM) {
+ ptr = &mons[mon->cham];
+ dochng = TRUE;
+ } else if (mon->data == &mons[PM_FOG_CLOUD]
+ && mon->mhp == mon->mhpmax && !rn2(4)
+ && (!canseemon(mon)
+ || distu(mon->mx, mon->my) > BOLT_LIM * BOLT_LIM)) {
+ /* if a fog cloud, maybe change to wolf or vampire bat;
+ those are more likely to take damage--at least when
+ tame--and then switch back to vampire; they'll also
+ switch to fog cloud if they encounter a closed door */
+ mndx = pickvampshape(mon);
+ if (mndx >= LOW_PM) {
+ ptr = &mons[mndx];
+ dochng = (ptr != mon->data);
+ }
+ }
+ } else {
+ if (mon->mhp >= 9 * mon->mhpmax / 10 && !rn2(6)
+ && (!canseemon(mon)
+ || distu(mon->mx, mon->my) > BOLT_LIM * BOLT_LIM))
+ dochng = TRUE; /* 'ptr' stays Null */
+ }
+ }
+ if (dochng) {
+ if (newcham(mon, ptr, FALSE, msg) && is_vampshifter(mon)) {
+ /* for vampshift, override the 10% chance for sex change */
+ ptr = mon->data;
+ if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr))
+ mon->female = was_female;
+ }
}
- /* override the 10% chance for sex change */
- ptr = mon->data;
- if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr))
- mon->female = was_female;
}
STATIC_OVL int
pickvampshape(mon)
struct monst *mon;
{
- int mndx = mon->cham;
+ int mndx = mon->cham, wolfchance = 10;
/* avoid picking monsters with lowercase display symbols ('d' for wolf
and 'v' for fog cloud) on rogue level*/
boolean uppercase_only = Is_rogue_level(&u.uz);
/* ensure Vlad can keep carrying the Candelabrum */
if (mon_has_special(mon))
break; /* leave mndx as is */
+ wolfchance = 3;
/*FALLTHRU*/
case PM_VAMPIRE_LORD: /* vampire lord or Vlad can become wolf */
- if (!rn2(10) && !uppercase_only) {
+ if (!rn2(wolfchance) && !uppercase_only) {
mndx = PM_WOLF;
break;
}
if (mndx == NON_PM)
return TRUE; /* caller wants random */
- if (!accept_newcham_form(mndx))
+ if (!accept_newcham_form(mon, mndx))
return FALSE; /* geno'd or !polyok */
if (isspecmon(mon)) {
}
/* prevent wizard mode user from specifying invalid vampshifter shape */
-STATIC_OVL boolean
+boolean
validvamp(mon, mndx_p, monclass)
struct monst *mon;
int *mndx_p, monclass;
if (!is_vampshifter(mon))
return validspecmon(mon, *mndx_p);
- if (*mndx_p == PM_VAMPIRE || *mndx_p == PM_VAMPIRE_LORD
- || *mndx_p == PM_VLAD_THE_IMPALER) {
- /* player picked some type of vampire; use mon's self */
- *mndx_p = mon->cham;
- return TRUE;
- }
if (mon->cham == PM_VLAD_THE_IMPALER && mon_has_special(mon)) {
/* Vlad with Candelabrum; override choice, then accept it */
*mndx_p = PM_VLAD_THE_IMPALER;
return TRUE;
}
+ if (*mndx_p >= LOW_PM && is_shapeshifter(&mons[*mndx_p])) {
+ /* player picked some type of shapeshifter; use mon's self
+ (vampire or chameleon) */
+ *mndx_p = mon->cham;
+ return TRUE;
+ }
/* basic vampires can't become wolves; any can become fog or bat
(we don't enforce upper-case only for rogue level here) */
if (*mndx_p == PM_WOLF)
*mndx_p = PM_WOLF;
break;
}
- /*FALLTHRU*/
+ /*FALLTHRU*/
default:
*mndx_p = NON_PM;
break;
}
break;
}
+
/* for debugging: allow control of polymorphed monster */
if (wizard && iflags.mon_polycontrol) {
- char pprompt[BUFSZ], buf[BUFSZ];
- int monclass;
-
- Sprintf(pprompt, "Change %s @ <%d,%d> into what kind of monster?",
- noit_mon_nam(mon), (int) mon->mx, (int) mon->my);
- tryct = 5;
+ char pprompt[BUFSZ], parttwo[QBUFSZ], buf[BUFSZ];
+ int monclass, len;
+
+ /* construct prompt in pieces */
+ Sprintf(pprompt, "Change %s", noit_mon_nam(mon));
+ Sprintf(parttwo, " @ %s into what?",
+ coord_desc((int) mon->mx, (int) mon->my, buf,
+ (iflags.getpos_coords != GPCOORDS_NONE)
+ ? iflags.getpos_coords : GPCOORDS_MAP));
+ /* combine the two parts, not exceeding QBUFSZ-1 in overall length;
+ if combined length is too long it has to be due to monster's
+ name so we'll chop enough of that off to fit the second part */
+ if ((len = (int) strlen(pprompt) + (int) strlen(parttwo)) >= QBUFSZ)
+ /* strlen(parttwo) is less than QBUFSZ/2 so strlen(pprompt) is
+ more than QBUFSZ/2 and excess amount being truncated can't
+ exceed pprompt's length and back up to before &pprompt[0]) */
+ *(eos(pprompt) - (len - (QBUFSZ - 1))) = '\0';
+ Strcat(pprompt, parttwo);
+
+ buf[0] = '\0'; /* clear buffer for EDIT_GETLIN */
+#define TRYLIMIT 5
+ tryct = TRYLIMIT;
do {
+ if (tryct == TRYLIMIT - 1) { /* first retry */
+ /* change "into what?" to "into what kind of monster?" */
+ if (strlen(pprompt) + sizeof " kind of monster" - 1 < QBUFSZ)
+ Strcpy(eos(pprompt) - 1, " kind of monster?");
+ }
+#undef TRYLIMIT
monclass = 0;
getlin(pprompt, buf);
mungspaces(buf);
if (*buf == '\033')
break;
/* for "*", use NON_PM to pick an arbitrary shape below */
- if (!strcmp(buf, "*") || !strcmp(buf, "random")) {
+ if (!strcmp(buf, "*") || !strcmpi(buf, "random")) {
mndx = NON_PM;
break;
}
pline("It can't become that.");
} while (--tryct > 0);
+
if (!tryct)
pline1(thats_enough_tries);
if (is_vampshifter(mon) && !validvamp(mon, &mndx, monclass))
/* this used to be inline within newcham() but monpolycontrol needs it too */
STATIC_OVL struct permonst *
-accept_newcham_form(mndx)
+accept_newcham_form(mon, mndx)
+struct monst *mon;
int mndx;
{
struct permonst *mdat;
polyok() rejects, so we need a special case here */
if (is_mplayer(mdat))
return mdat;
+ /* shapeshifters are rejected by polyok() but allow a shapeshifter
+ to take on its 'natural' form */
+ if (is_shapeshifter(mdat)
+ && mon->cham >= LOW_PM && mdat == &mons[mon->cham])
+ return mdat;
/* polyok() rules out M2_PNAME, M2_WERE, and all humans except Kops */
return polyok(mdat) ? mdat : 0;
}
int hpn, hpd;
int mndx, tryct;
struct permonst *olddata = mtmp->data;
- char oldname[BUFSZ], newname[BUFSZ];
+ char *p, oldname[BUFSZ], l_oldname[BUFSZ], newname[BUFSZ];
- /* Riders are immune to polymorph and green slime */
- if (is_rider(mtmp->data))
- return 0;
+ /* Riders are immune to polymorph and green slime
+ (but apparent Rider might actually be a doppelganger) */
+ if (mtmp->cham == NON_PM) { /* not a shapechanger */
+ if (is_rider(olddata))
+ return 0;
+ /* make Nazgul and erinyes immune too, to reduce chance of
+ anomalous extinction feedback during final disclsoure */
+ if (mbirth_limit(monsndx(olddata)) < MAXMONNO)
+ return 0;
+ /* cancelled shapechangers become uncancelled prior
+ to being given a new shape */
+ if (mtmp->mcan && !Protection_from_shape_changers) {
+ mtmp->cham = pm_to_cham(monsndx(mtmp->data));
+ if (mtmp->cham != NON_PM)
+ mtmp->mcan = 0;
+ }
+ }
if (msg) {
/* like Monnam() but never mention saddle */
SUPPRESS_SADDLE, FALSE));
oldname[0] = highc(oldname[0]);
}
+ /* we need this one whether msg is true or not */
+ Strcpy(l_oldname, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
+ has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE));
/* mdat = 0 -> caller wants a random monster shape */
if (mdat == 0) {
tryct = 20;
do {
mndx = select_newcham_form(mtmp);
- mdat = accept_newcham_form(mndx);
+ mdat = accept_newcham_form(mtmp, mndx);
/* for the first several tries we require upper-case on
the rogue level (after that, we take whatever we get) */
if (tryct > 15 && Is_rogue_level(&u.uz)
} else if (mvitals[monsndx(mdat)].mvflags & G_GENOD)
return 0; /* passed in mdat is genocided */
- mgender_from_permonst(mtmp, mdat);
-
- if (In_endgame(&u.uz) && is_mplayer(olddata) && has_mname(mtmp)) {
- /* mplayers start out as "Foo the Bar", but some of the
- * titles are inappropriate when polymorphed, particularly
- * into the opposite sex. players don't use ranks when
- * polymorphed, so dropping the rank for mplayers seems
- * reasonable.
- */
- char *p = index(MNAME(mtmp), ' ');
-
- if (p)
- *p = '\0';
- }
-
- if (mdat == mtmp->data)
+ if (mdat == olddata)
return 0; /* still the same monster */
+ mgender_from_permonst(mtmp, mdat);
+ /* Endgame mplayers start out as "Foo the Bar", but some of the
+ * titles are inappropriate when polymorphed, particularly into
+ * the opposite sex. Player characters don't use ranks when
+ * polymorphed, so dropping rank for mplayers seems reasonable.
+ */
+ if (In_endgame(&u.uz) && is_mplayer(olddata)
+ && has_mname(mtmp) && (p = strstr(MNAME(mtmp), " the ")) != 0)
+ *p = '\0';
+
if (mtmp->wormno) { /* throw tail away */
wormgone(mtmp);
place_monster(mtmp, mtmp->mx, mtmp->my);
}
- if (mtmp->m_ap_type && mdat->mlet != S_MIMIC)
+ if (M_AP_TYPE(mtmp) && mdat->mlet != S_MIMIC)
seemimic(mtmp); /* revert to normal monster */
/* (this code used to try to adjust the monster's health based on
mtmp->mhp = 1;
/* take on the new form... */
- set_mon_data(mtmp, mdat, 0);
+ set_mon_data(mtmp, mdat);
+
+ if (mtmp->mleashed && !leashable(mtmp))
+ m_unleash(mtmp, TRUE);
if (emits_light(olddata) != emits_light(mtmp->data)) {
/* used to give light, now doesn't, or vice versa,
/* Does mdat care? */
if (!noncorporeal(mdat) && !amorphous(mdat)
&& !is_whirly(mdat) && (mdat != &mons[PM_YELLOW_LIGHT])) {
-#if 0 /*JP*/
- You("break out of %s%s!", mon_nam(mtmp),
- (is_animal(mdat) ? "'s stomach" : ""));
+ char msgtrail[BUFSZ];
+
+ if (is_vampshifter(mtmp)) {
+#if 0 /*JP:T*/
+ Sprintf(msgtrail, " which was a shapeshifted %s",
+ noname_monnam(mtmp, ARTICLE_NONE));
#else
- You("%s%s\82ð\94j\82è\8fo\82½\81I", mon_nam(mtmp),
- (is_animal(mdat) ? "\82Ì\88Ý\91Ü" : ""));
+ Sprintf(msgtrail, "(\8eÀ\8dÛ\82É\82Í\8c`\82ð\95Ï\82¦\82½%s)",
+ noname_monnam(mtmp, ARTICLE_NONE));
#endif
+ } else if (is_animal(mdat)) {
+/*JP
+ Strcpy(msgtrail, "'s stomach");
+*/
+ Strcpy(msgtrail, "\82Ì\88Ý");
+ } else {
+ msgtrail[0] = '\0';
+ }
+
+ /* Do this even if msg is FALSE */
+#if 0 /*JP:T*/
+ You("%s %s%s!",
+ (amorphous(olddata) || is_whirly(olddata))
+ ? "emerge from" : "break out of",
+ l_oldname, msgtrail);
+#else
+ You("%s%s%s\81I", l_oldname, msgtrail,
+ (amorphous(olddata) || is_whirly(olddata))
+ ? "\82©\82ç\94ò\82Ñ\8fo\82µ\82½" : "\82ð\94j\82è\8fo\82½"
+ );
+#endif
+ msg = FALSE; /* message has been given */
mtmp->mhp = 1; /* almost dead */
}
expels(mtmp, olddata, FALSE);
#endif
/* we can now create worms with tails - 11/91 */
initworm(mtmp, rn2(5));
- if (count_wsegs(mtmp))
- place_worm_tail_randomly(mtmp, mtmp->mx, mtmp->my);
+ place_worm_tail_randomly(mtmp, mtmp->mx, mtmp->my);
}
newsym(mtmp->mx, mtmp->my);
if (msg) {
- char *save_mname = 0;
-
- if (has_mname(mtmp)) {
- save_mname = MNAME(mtmp);
- MNAME(mtmp) = (char *) 0;
- }
-#if 0 /*JP*/
- Strcpy(newname, (mdat == &mons[PM_GREEN_SLIME])
- ? "slime"
- : x_monnam(mtmp, ARTICLE_A, (char *) 0,
- SUPPRESS_SADDLE, FALSE));
+ Strcpy(newname, noname_monnam(mtmp, ARTICLE_A));
+ /* oldname was capitalized above; newname will be lower case */
+#if 0 /*JP:T*/
+ if (!strcmpi(newname, "it")) { /* can't see or sense it now */
+#else
+ if (!strcmpi(newname, "\89½\8eÒ\82©")) { /* can't see or sense it now */
+#endif
+#if 0 /*JP:T*/
+ if (!!strcmpi(oldname, "it")) /* could see or sense it before */
#else
- Strcpy(newname, (mdat == &mons[PM_GREEN_SLIME])
- ? "\83X\83\89\83C\83\80"
- : x_monnam(mtmp, ARTICLE_A, (char *) 0,
- SUPPRESS_SADDLE, FALSE));
+ if (!!strcmpi(oldname, "\89½\8eÒ\82©")) /* could see or sense it before */
#endif
/*JP
- if (!strcmpi(oldname, "it") && !strcmpi(newname, "it"))
+ pline("%s disappears!", oldname);
*/
- if (!strcmpi(oldname, "\89½\8eÒ\82©") && !strcmpi(newname, "\89½\8eÒ\82©"))
+ pline("%s\82Í\8fÁ\82¦\82½\81I", oldname);
(void) usmellmon(mdat);
- else
+ } else { /* can see or sense it now */
+#if 0 /*JP:T*/
+ if (!strcmpi(oldname, "it")) /* couldn't see or sense it before */
+#else
+ if (!strcmpi(oldname, "\89½\8eÒ\82©")) /* couldn't see or sense it before */
+#endif
+/*JP
+ pline("%s appears!", upstart(newname));
+*/
+ pline("%s\82ª\8c»\82ê\82½\81I", upstart(newname));
+ else
/*JP
- pline("%s turns into %s!", oldname, newname);
+ pline("%s turns into %s!", oldname, newname);
*/
- pline("%s\82Í%s\82É\82È\82Á\82½\81I", oldname, newname);
- if (save_mname)
- MNAME(mtmp) = save_mname;
+ pline("%s\82Í%s\82É\82È\82Á\82½\81I", oldname, newname);
+ }
}
+ /* when polymorph trap/wand/potion produces a vampire, turn in into
+ a full-fledged vampshifter unless shape-changing is blocked */
+ if (mtmp->cham == NON_PM && mdat->mlet == S_VAMPIRE
+ && !Protection_from_shape_changers)
+ mtmp->cham = pm_to_cham(monsndx(mdat));
+
possibly_unwield(mtmp, polyspot); /* might lose use of weapon */
mon_break_armor(mtmp, polyspot);
if (!(mtmp->misc_worn_check & W_ARMG))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
mselftouch(mtmp, "No longer petrify-resistant, ",
!context.mon_moving);
#else
if (ct) {
if (!silent) { /* do we want pline msgs? */
if (slct)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline_The("guard%s wake%s up!", slct > 1 ? "s" : "",
slct == 1 ? "s" : "");
#else
#endif
if (nct || sct) {
if (nct)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline_The("guard%s get%s angry!", nct == 1 ? "" : "s",
nct == 1 ? "s" : "");
#else
pline("\94Ô\95º\82Í\93{\82Á\82½\81I");
#endif
else if (!Blind)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
You_see("%sangry guard%s approaching!",
sct == 1 ? "an " : "", sct > 1 ? "s" : "");
#else
{
short ap = mtmp->mappearance;
- switch (mtmp->m_ap_type) {
+ switch (M_AP_TYPE(mtmp)) {
case M_AP_NOTHING:
case M_AP_FURNITURE:
case M_AP_MONSTER:
break;
case M_AP_OBJECT:
if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s seems a more vivid %s than before.",
The(simple_typename(ap)),
c_obj_colors[objects[ap].oc_color]);
switch (mndx) {
case PM_ROTHE:
case PM_MINOTAUR:
+/*JP
You("notice a bovine smell.");
+*/
+ You("\8b\8d\82Ì\82æ\82¤\82È\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
msg_given = TRUE;
break;
case PM_CAVEMAN:
case PM_CAVEWOMAN:
case PM_BARBARIAN:
case PM_NEANDERTHAL:
+/*JP
You("smell body odor.");
+*/
+ pline("\91Ì\8fL\82Ì\82æ\82¤\82È\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
/*
case PM_WERERAT:
case PM_WEREWOLF:
case PM_OWLBEAR:
+/*JP
You("detect an odor reminiscent of an animal's den.");
+*/
+ pline("\93®\95¨\82Ì\82Ë\82®\82ç\82ð\8ev\82¢\8fo\82·\82æ\82¤\82È\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
/*
break;
*/
case PM_STEAM_VORTEX:
+/*JP
You("smell steam.");
+*/
+ pline("\8fö\8bC\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
case PM_GREEN_SLIME:
+/*JP
pline("%s stinks.", Something);
+*/
+ pline("\88«\8fL\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
case PM_VIOLET_FUNGUS:
case PM_SHRIEKER:
+/*JP
You("smell mushrooms.");
+*/
+ pline("\82«\82Ì\82±\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
/* These are here to avoid triggering the
if (nonspecific)
switch (mdat->mlet) {
case S_DOG:
+/*JP
You("notice a dog smell.");
+*/
+ You("\8c¢\82Ì\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
msg_given = TRUE;
break;
case S_DRAGON:
+/*JP
You("smell a dragon!");
+*/
+ pline("\83h\83\89\83S\83\93\82Ì\82É\82¨\82¢\82ª\82·\82é\81I");
msg_given = TRUE;
break;
case S_FUNGUS:
+/*JP
pline("%s smells moldy.", Something);
+*/
+ pline("\89½\82©\91Û\82Ì\82æ\82¤\82È\82É\82¨\82¢\82ª\82·\82é\81D");
msg_given = TRUE;
break;
case S_UNICORN:
+#if 0 /*JP:T*/
You("detect a%s odor reminiscent of a stable.",
(mndx == PM_PONY) ? "n" : " strong");
+#else
+ pline("\94n\8f¬\89®\82ð\8ev\82¢\8fo\82·\82æ\82¤\82È%s\82É\82¨\82¢\82ª\82µ\82½\81D",
+ (mndx == PM_PONY) ? "" : "\8b\82¢");
+#endif
msg_given = TRUE;
break;
case S_ZOMBIE:
+/*JP
You("smell rotting flesh.");
+*/
+ pline("\95\85\82Á\82½\93÷\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
case S_EEL:
+/*JP
You("smell fish.");
+*/
+ pline("\8b\9b\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
msg_given = TRUE;
break;
case S_ORC:
if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
+/*JP
You("notice an attractive smell.");
+*/
+ You("\96£\97Í\93I\82È\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
else
+/*JP
pline("A foul stench makes you feel a little nauseated.");
+*/
+ pline("\82Þ\82©\82Â\82\82æ\82¤\82È\88«\8fL\82Å\8f\82µ\8bC\95ª\82ª\88«\82\82È\82Á\82½\81D");
msg_given = TRUE;
break;
default: