From: nethack.allison Date: Tue, 15 Jun 2004 11:52:04 +0000 (+0000) Subject: vampires now shapeshift [trunk only] X-Git-Tag: MOVE2GIT~1439 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9b3521e50356f2a4f19bcc5842395352fe3be206;p=nethack vampires now shapeshift [trunk only] - can shift into fog clouds, vampire bats, and vampire lords into wolves - after being "killed" in shifted form, they transform back rather than get destroyed, and you must take them on in vampire form to defeat them - can deliberately shift into fog clouds to pass under closed doors --- diff --git a/doc/fixes35.0 b/doc/fixes35.0 index f9660cd53..2cfc8611a 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -109,6 +109,10 @@ health-food store that stocks monk-appropriate foods in mine town when monk give more information about your attributes in debug mode polywarn to give intrinsic monster detection of limited species while polymorphed rocks can skip on the water sometimes allowing them to pass over water creatures +vampires can now shapeshift into bats and fog clouds; the latter can be done at + will to slip through locked doors +shapeshifted vampire will transform back to vampire form after you defeat it and + continue to fight in its native form Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 6d0ebf806..3e0c4da9b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1196,6 +1196,7 @@ E void NDECL(kill_genocided_monsters); E void FDECL(golemeffects, (struct monst *,int,int)); E boolean FDECL(angry_guards, (BOOLEAN_P)); E void NDECL(pacify_guards); +E void FDECL(decide_to_shapeshift, (struct monst *,int)); /* ### mondata.c ### */ @@ -1209,6 +1210,7 @@ E boolean FDECL(resists_blnd, (struct monst *)); E boolean FDECL(can_blnd, (struct monst *,struct monst *,UCHAR_P,struct obj *)); E boolean FDECL(ranged_attk, (struct permonst *)); E boolean FDECL(hates_silver, (struct permonst *)); +E boolean FDECL(mon_hates_silver, (struct monst *)); E boolean FDECL(passes_bars, (struct permonst *)); E boolean FDECL(can_blow, (struct monst *)); E boolean FDECL(can_track, (struct permonst *)); @@ -1247,6 +1249,7 @@ E boolean FDECL(closed_door, (int,int)); E boolean FDECL(accessible, (int,int)); E void FDECL(set_apparxy, (struct monst *)); E boolean FDECL(can_ooze, (struct monst *)); +E boolean FDECL(can_fog, (struct monst *)); #ifdef BARGETHROUGH E boolean FDECL(should_displace, (struct monst *,coord *,long *,int, XCHAR_P,XCHAR_P)); @@ -1591,6 +1594,7 @@ E int NDECL(dospinweb); E int NDECL(dosummon); E int NDECL(dogaze); E int NDECL(dohide); +E int NDECL(dopoly); E int NDECL(domindblast); E void FDECL(skinback, (BOOLEAN_P)); E const char *FDECL(mbodypart, (struct monst *,int)); diff --git a/include/hack.h b/include/hack.h index ba77608c1..d0a02f07c 100644 --- a/include/hack.h +++ b/include/hack.h @@ -153,6 +153,10 @@ NEARDATA extern coord bhitpos; /* place where throw or zap hits or stops */ #define CORPSTAT_INIT 0x01 /* pass init flag to mkcorpstat */ #define CORPSTAT_BURIED 0x02 /* bury the corpse or statue */ +/* flags for decide_to_shift() */ +#define SHIFT_SEENMSG 0x01 /* put out a message if in sight */ +#define SHIFT_MSG 0x02 /* always put out a message */ + /* special mhpmax value when loading bones monster to flag as extinct or genocided */ #define DEFUNCT_MONSTER (-100) diff --git a/include/monst.h b/include/monst.h index c98239905..a1ba25f0f 100644 --- a/include/monst.h +++ b/include/monst.h @@ -176,5 +176,7 @@ struct monst { #define DEADMONSTER(mon) ((mon)->mhp < 1) #define is_starting_pet(mon) ((mon)->m_id == context.startingpet_mid) - +#define is_vampshifter(mon) ((mon)->cham == PM_VAMPIRE || \ + (mon)->cham == PM_VAMPIRE_LORD || \ + (mon)->cham == PM_VLAD_THE_IMPALER) #endif /* MONST_H */ diff --git a/src/apply.c b/src/apply.c index c2138aa20..45df4545d 100644 --- a/src/apply.c +++ b/src/apply.c @@ -763,7 +763,7 @@ struct obj *obj; if (vis) pline("%s can't see anything right now.", Monnam(mtmp)); /* some monsters do special things */ - } else if (mlet == S_VAMPIRE || mlet == S_GHOST) { + } else if (mlet == S_VAMPIRE || mlet == S_GHOST || is_vampshifter(mtmp)) { if (vis) pline ("%s doesn't have a reflection.", Monnam(mtmp)); } else if(!mtmp->mcan && !mtmp->minvis && diff --git a/src/cmd.c b/src/cmd.c index c434f0ed2..301fb87c9 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -464,7 +464,8 @@ domonability() if(u.uburied) pline("Unfortunately sound does not carry well through rock."); else aggravate(); - } else if (Upolyd) + } else if (youmonst.data->mlet == S_VAMPIRE) return dopoly(); + else if (Upolyd) pline("Any special ability you may have is purely reflexive."); else You("don't have a special ability in your normal form!"); return 0; diff --git a/src/dog.c b/src/dog.c index 98301e8f7..a6a94d433 100644 --- a/src/dog.c +++ b/src/dog.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)dog.c 3.4 2002/09/08 */ +/* SCCS Id: @(#)dog.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -689,7 +689,7 @@ register struct obj *obj; return (herbi ? CADAVER : MANFOOD); else return (carni ? CADAVER : MANFOOD); case CLOVE_OF_GARLIC: - return (is_undead(mon->data) ? TABU : + return ((is_undead(mon->data) || is_vampshifter(mon)) ? TABU : ((herbi || starving) ? ACCFOOD : MANFOOD)); case TIN: return (metallivorous(mon->data) ? ACCFOOD : MANFOOD); @@ -709,7 +709,7 @@ register struct obj *obj; if (obj->otyp == AMULET_OF_STRANGULATION || obj->otyp == RIN_SLOW_DIGESTION) return TABU; - if (hates_silver(mon->data) && + if (mon_hates_silver(mon) && objects[obj->otyp].oc_material == SILVER) return(TABU); if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj)) diff --git a/src/dokick.c b/src/dokick.c index 15265e13b..5f81348f6 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)dokick.c 3.4 2003/12/04 */ +/* SCCS Id: @(#)dokick.c 3.4 2004/06/12 */ /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -49,8 +49,8 @@ register boolean clumsy; if (mon->data == &mons[PM_SHADE]) dmg = 0; - if ((is_undead(mon->data) || is_demon(mon->data)) && uarmf && - uarmf->blessed) + if ((is_undead(mon->data) || is_demon(mon->data) || is_vampshifter(mon)) + && uarmf && uarmf->blessed) blessed_foot_damage = 1; if (mon->data == &mons[PM_SHADE] && !blessed_foot_damage) { diff --git a/src/explode.c b/src/explode.c index c0d707bec..6885a3489 100644 --- a/src/explode.c +++ b/src/explode.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)explode.c 3.4 2003/10/21 */ +/* SCCS Id: @(#)explode.c 3.4 2004/06/12 */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -157,7 +157,7 @@ int expltype; break; case AD_DISN: explmask[i][j] |= (olet == WAND_CLASS) ? - (nonliving(mtmp->data) || is_demon(mtmp->data)) : + (nonliving(mtmp->data) || is_demon(mtmp->data) || is_vampshifter(mtmp)) : resists_disint(mtmp); break; case AD_ELEC: diff --git a/src/makemon.c b/src/makemon.c index cfacb34ea..f1ca79d92 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)makemon.c 3.4 2004/05/21 */ +/* SCCS Id: @(#)makemon.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1001,18 +1001,28 @@ register int mmflags; LS_MONSTER, (genericptr_t)mtmp); mitem = 0; /* extra inventory item for this monster */ + if (mndx == PM_VLAD_THE_IMPALER) + mitem = CANDELABRUM_OF_INVOCATION; mtmp->cham = CHAM_ORDINARY; /* default is "not a shapechanger" */ if ((mcham = pm_to_cham(mndx)) != CHAM_ORDINARY) { /* this is a shapechanger after all */ if (Protection_from_shape_changers) { ; /* stuck in its natural form (CHAM_ORDINARY) */ } else { - /* new shapechanger starts out with random form + /* General shapechangers start out with random form (this explicitly picks something from the normal selection for current difficulty level rather - than from among shapechanger's preferred forms) */ + than from among shapechanger's preferred forms). + Vampires are the exception. */ + struct permonst *tmpcham = rndmonst(); mtmp->cham = mcham; - (void) newcham(mtmp, rndmonst(), FALSE, FALSE); + if (is_vampshifter(mtmp)){ + int chamidx = select_newcham_form(mtmp); + if (chamidx != NON_PM) + tmpcham = &mons[chamidx]; + } + if (mtmp->cham != PM_VLAD_THE_IMPALER) + (void) newcham(mtmp,tmpcham,FALSE, FALSE); } } else if (mndx == PM_WIZARD_OF_YENDOR) { mtmp->iswiz = TRUE; @@ -1021,8 +1031,6 @@ register int mmflags; mitem = SPE_DIG; } else if (mndx == PM_GHOST && !(mmflags & MM_NONAME)) { mtmp = christen_monst(mtmp, rndghostname()); - } else if (mndx == PM_VLAD_THE_IMPALER) { - mitem = CANDELABRUM_OF_INVOCATION; } else if (mndx == PM_CROESUS) { mitem = TWO_HANDED_SWORD; } else if (ptr->msound == MS_NEMESIS) { diff --git a/src/mhitu.c b/src/mhitu.c index ff0b6bc04..bed5b1bc2 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -890,7 +890,7 @@ hitmu(mtmp, mattk) /* First determine the base damage done */ dmg = d((int)mattk->damn, (int)mattk->damd); - if(is_undead(mdat) && midnight()) + if((is_undead(mdat) || is_vampshifter(mtmp)) && midnight()) dmg += d((int)mattk->damn, (int)mattk->damd); /* extra damage */ /* Next a cancellation factor */ @@ -1550,7 +1550,8 @@ dopois: if (Half_physical_damage /* Mitre of Holiness */ || (Role_if(PM_PRIEST) && uarmh && is_quest_artifact(uarmh) && - (is_undead(mtmp->data) || is_demon(mtmp->data)))) + (is_undead(mtmp->data) || + is_demon(mtmp->data) || is_vampshifter(mtmp)))) dmg = (dmg+1) / 2; if (permdmg) { /* Death's life force drain */ diff --git a/src/mon.c b/src/mon.c index 2355b857f..c0bbdecb9 100644 --- a/src/mon.c +++ b/src/mon.c @@ -18,7 +18,6 @@ STATIC_DCL long FDECL(mm_aggression, (struct monst *,struct monst *)); STATIC_DCL long FDECL(mm_displacement, (struct monst *,struct monst *)); #endif STATIC_DCL int NDECL(pick_animal); -STATIC_DCL int FDECL(select_newcham_form, (struct monst *)); STATIC_DCL void FDECL(kill_eggs, (struct obj *)); #ifdef REINCARNATION @@ -530,8 +529,12 @@ mcalcdistress() mon_regen(mtmp, FALSE); /* possibly polymorph shapechangers and lycanthropes */ - if (mtmp->cham && !rn2(6)) - (void) newcham(mtmp, (struct permonst *)0, FALSE, FALSE); + if (mtmp->cham != CHAM_ORDINARY) { + 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); + } were_change(mtmp); /* gradually time out temporary problems */ @@ -917,7 +920,6 @@ max_mon_load(mtmp) register struct monst *mtmp; { register long maxload; - /* Base monster carrying capacity is equal to human maximum * carrying capacity, or half human maximum if not strong. * (for a polymorphed player, the value used would be the @@ -956,7 +958,7 @@ struct obj *otmp; return FALSE; if (otyp == CORPSE && is_rider(&mons[otmp->corpsenm])) return FALSE; - if (objects[otyp].oc_material == SILVER && hates_silver(mdat) && + if (objects[otyp].oc_material == SILVER && mon_hates_silver(mtmp) && (otyp != BELL_OF_OPENING || !is_covetous(mdat))) return FALSE; @@ -1049,7 +1051,8 @@ nexttry: /* eels prefer the water, but if there is no water nearby, !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx,ny))) continue; /* KMH -- Added iron bars */ if (ntyp == IRONBARS && !(flag & ALLOW_BARS)) continue; - if(IS_DOOR(ntyp) && !amorphous(mdat) && + if(IS_DOOR(ntyp) && + !(amorphous(mdat) || (!amorphous(mdat) && can_fog(mon))) && ((levl[nx][ny].doormask & D_CLOSED && !(flag & OPENDOOR)) || (levl[nx][ny].doormask & D_LOCKED && !(flag & UNLOCKDOOR))) && !thrudoor) continue; @@ -1373,7 +1376,7 @@ struct obj * mlifesaver(mon) struct monst *mon; { - if (!nonliving(mon->data)) { + if (!nonliving(mon->data) || is_vampshifter(mon)) { struct obj *otmp = which_armor(mon, W_AMUL); if (otmp && otmp->otyp == AMULET_OF_LIFE_SAVING) @@ -1441,6 +1444,52 @@ register struct monst *mtmp; lifesaved_monster(mtmp); if (mtmp->mhp > 0) return; + if (is_vampshifter(mtmp)) { + int mndx = mtmp->cham; + int x = mtmp->mx, y = mtmp->my; + /* this only happens if shapeshifted */ + if (mndx != CHAM_ORDINARY && mndx != monsndx(mtmp->data)) { + char buf[BUFSZ]; + boolean in_door = amorphous(mtmp->data) && + closed_door(mtmp->mx,mtmp->my); + Sprintf(buf, + "The %s%s suddenly %s and rises as %%s!", + (nonliving(mtmp->data) || + noncorporeal(mtmp->data) || + amorphous(mtmp->data)) ? "" : "seemingly dead ", + x_monnam(mtmp, ARTICLE_NONE, (char *)0, + SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION | + SUPPRESS_INVISIBLE | SUPPRESS_IT, FALSE), + (nonliving(mtmp->data) || + noncorporeal(mtmp->data) || + amorphous(mtmp->data)) ? + "reconstitutes" : "transforms"); + 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); + } + } + newcham(mtmp, &mons[mndx], FALSE, FALSE); + if (mtmp->data == &mons[mndx]) + mtmp->cham = CHAM_ORDINARY; + else + mtmp->cham = mndx; + if ((!Blind && canseemon(mtmp)) || sensemon(mtmp)) + pline(buf, a_monnam(mtmp)); + newsym(x,y); + return; + } + } + #ifdef STEED /* Player is thrown from his steed when it dies */ if (mtmp == u.usteed) @@ -2306,6 +2355,31 @@ pick_animal() return animal_list[rn2(animal_list_count)]; } +void +decide_to_shapeshift(mon, shiftflags) +struct monst *mon; +int shiftflags; +{ + boolean msg = FALSE; + + if ((shiftflags & SHIFT_MSG) || + ((shiftflags & SHIFT_SEENMSG) && sensemon(mon))) msg = TRUE; + + if (is_vampshifter(mon)) { + /* 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 not already shifted and in good health, maybe shift. + */ + if ((mon->mhp <= mon->mhpmax / 6) && rn2(4)) + (void) newcham(mon, &mons[mon->cham], FALSE, msg); + } else if (mon->data->mlet == S_VAMPIRE && mon->cham == CHAM_ORDINARY + && !rn2(6) && (mon->mhp > mon->mhpmax - ((mon->mhpmax / 10) + 1))) { + (void) newcham(mon, (struct permonst *)0, FALSE, msg); + } +} + int select_newcham_form(mon) struct monst *mon; diff --git a/src/mondata.c b/src/mondata.c index b580c4d83..33d0bd0f7 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)mondata.c 3.4 2003/06/02 */ +/* SCCS Id: @(#)mondata.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -63,7 +63,7 @@ struct monst *mon; struct obj *wep = ((mon == &youmonst) ? uwep : MON_WEP(mon)); return (boolean)(is_undead(ptr) || is_demon(ptr) || is_were(ptr) || - ptr == &mons[PM_DEATH] || + ptr == &mons[PM_DEATH] || is_vampshifter(mon) || (wep && wep->oartifact && defends(AD_DRLI, wep))); } @@ -226,6 +226,13 @@ struct permonst *ptr; return FALSE; } +boolean +mon_hates_silver(mon) +struct monst *mon; +{ + return (is_vampshifter(mon) || hates_silver(mon->data)); +} + boolean hates_silver(ptr) register struct permonst *ptr; diff --git a/src/monmove.c b/src/monmove.c index d63481acf..10fcc7207 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)monmove.c 3.4 2002/04/06 */ +/* SCCS Id: @(#)monmove.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,6 +13,9 @@ STATIC_DCL int FDECL(disturb,(struct monst *)); STATIC_DCL void FDECL(distfleeck,(struct monst *,int *,int *,int *)); STATIC_DCL int FDECL(m_arrival, (struct monst *)); STATIC_DCL void FDECL(watch_on_duty,(struct monst *)); +STATIC_DCL boolean FDECL(stuff_prevents_passage, (struct monst *)); +STATIC_OVL int FDECL(vamp_shift, (struct monst *,struct permonst *)); + boolean /* TRUE : mtmp died */ mb_trapped(mtmp) @@ -936,6 +939,7 @@ not_special: if (can_tunnel) flag |= ALLOW_DIG; if (is_human(ptr) || ptr == &mons[PM_MINOTAUR]) flag |= ALLOW_SSM; if (is_undead(ptr) && ptr->mlet != S_GHOST) flag |= NOGARLIC; + if (is_vampshifter(mtmp)) flag |= NOGARLIC; if (throws_rocks(ptr)) flag |= ALLOW_ROCK; if (can_open) flag |= OPENDOOR; if (can_unlock) flag |= UNLOCKDOOR; @@ -1118,7 +1122,9 @@ postmov: struct rm *here = &levl[mtmp->mx][mtmp->my]; boolean btrapped = (here->doormask & D_TRAPPED); - if(here->doormask & (D_LOCKED|D_CLOSED) && amorphous(ptr)) { + if(here->doormask & (D_LOCKED|D_CLOSED) && + (amorphous(ptr) || (!amorphous(ptr) && can_fog(mtmp) && + vamp_shift(mtmp, &mons[PM_FOG_CLOUD])))) { if (flags.verbose && canseemon(mtmp)) pline("%s %s under the door.", Monnam(mtmp), (ptr == &mons[PM_FOG_CLOUD] || @@ -1346,7 +1352,8 @@ register struct monst *mtmp; || ((mx != u.ux || my != u.uy) && !passes_walls(mtmp->data) && (!ACCESSIBLE(levl[mx][my].typ) || - (closed_door(mx, my) && !can_ooze(mtmp)))) + (closed_door(mx, my) && + !(can_ooze(mtmp) || can_fog(mtmp))))) || !couldsee(mx, my)); } else { found_you: @@ -1392,21 +1399,24 @@ xchar x,y; } #endif /* BARGETHROUGH */ -boolean -can_ooze(mtmp) +/* + * Inventory prevents passage under door. + * Used by can_ooze() and can_fog(). + */ +STATIC_OVL boolean +stuff_prevents_passage(mtmp) struct monst *mtmp; { struct obj *chain, *obj; - if (!amorphous(mtmp->data)) return FALSE; if (mtmp == &youmonst) { #ifndef GOLDOBJ - if (u.ugold > 100L) return FALSE; + if (u.ugold > 100L) return TRUE; #endif chain = invent; } else { #ifndef GOLDOBJ - if (mtmp->mgold > 100L) return FALSE; + if (mtmp->mgold > 100L) return TRUE; #endif chain = mtmp->minvent; } @@ -1414,7 +1424,7 @@ struct monst *mtmp; int typ = obj->otyp; #ifdef GOLDOBJ - if (typ == COIN_CLASS && obj->quan > 100L) return FALSE; + if (typ == COIN_CLASS && obj->quan > 100L) return TRUE; #endif if (obj->oclass != GEM_CLASS && !(typ >= ARROW && typ <= BOOMERANG) && @@ -1441,11 +1451,47 @@ struct monst *mtmp; typ != TIN_WHISTLE && typ != MAGIC_WHISTLE && typ != MAGIC_MARKER && typ != TIN_OPENER && typ != SKELETON_KEY && typ != LOCK_PICK - ) return FALSE; - if (Is_container(obj) && obj->cobj) return FALSE; - + ) return TRUE; + if (Is_container(obj) && obj->cobj) return TRUE; } + return FALSE; +} + +boolean +can_ooze(mtmp) +struct monst *mtmp; +{ + if (!amorphous(mtmp->data) || + stuff_prevents_passage(mtmp)) + return FALSE; return TRUE; } +/* monster can change form into a fog if necessary */ +boolean +can_fog(mtmp) +struct monst *mtmp; +{ + if ((is_vampshifter(mtmp) || mtmp->data->mlet == S_VAMPIRE) && + !Protection_from_shape_changers && !stuff_prevents_passage(mtmp)) + return TRUE; + return FALSE; +} + +STATIC_OVL int +vamp_shift(mon,ptr) +struct monst *mon; +struct permonst *ptr; +{ + int reslt = 0; + if (mon->cham != CHAM_ORDINARY) { + if (ptr == &mons[mon->cham]) + mon->cham = CHAM_ORDINARY; + reslt = newcham(mon, ptr, FALSE, FALSE); + } else if (mon->cham == CHAM_ORDINARY && ptr != mon->data) { + mon->cham = monsndx(mon->data); + reslt = newcham(mon, ptr, FALSE, FALSE); + } + return reslt; +} /*monmove.c*/ diff --git a/src/monst.c b/src/monst.c index c7ba3353a..03356569c 100644 --- a/src/monst.c +++ b/src/monst.c @@ -1997,16 +1997,16 @@ struct permonst _mons2[] = { NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, - M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE, - CLR_RED), + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_SHAPESHIFTER, + M3_INFRAVISIBLE, CLR_RED), MON("vampire lord", S_VAMPIRE, LVL(12, 14, 0, 50, -9), (G_GENO|G_NOCORPSE|1), A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, - M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_LORD|M2_MALE, - M3_INFRAVISIBLE, CLR_BLUE), + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_LORD|M2_MALE| + M2_SHAPESHIFTER, M3_INFRAVISIBLE, CLR_BLUE), #if 0 /* DEFERRED */ MON("vampire mage", S_VAMPIRE, LVL(20, 14, -4, 50, -9), (G_GENO|G_NOCORPSE|1), @@ -2015,7 +2015,7 @@ struct permonst _mons2[] = { SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_LORD|M2_MALE| - M2_MAGIC, M3_INFRAVISIBLE, HI_ZAP), + M2_MAGIC|M2_SHAPESHIFTER, M3_INFRAVISIBLE, HI_ZAP), #endif MON("Vlad the Impaler", S_VAMPIRE, LVL(14, 18, -3, 80, -10), (G_NOGEN|G_NOCORPSE|G_UNIQ), @@ -2024,7 +2024,7 @@ struct permonst _mons2[] = { SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG| - M2_NASTY|M2_PRINCE|M2_MALE, + M2_NASTY|M2_PRINCE|M2_MALE|M2_SHAPESHIFTER, M3_WAITFORU|M3_WANTSCAND|M3_INFRAVISIBLE, HI_LORD), /* * Wraiths diff --git a/src/mthrowu.c b/src/mthrowu.c index f965df824..3fbc6c862 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)mthrowu.c 3.4 2003/11/26 */ +/* SCCS Id: @(#)mthrowu.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -182,7 +182,7 @@ boolean verbose; /* give message(s) even when you can't see what happened */ } } if (objects[otmp->otyp].oc_material == SILVER && - hates_silver(mtmp->data)) { + mon_hates_silver(mtmp)) { if (vis) pline_The("silver sears %s flesh!", s_suffix(mon_nam(mtmp))); else if (verbose) pline("Its flesh is seared!"); @@ -201,7 +201,8 @@ boolean verbose; /* give message(s) even when you can't see what happened */ if (mtmp->mhp < 1) { if (vis || verbose) pline("%s is %s!", Monnam(mtmp), - (nonliving(mtmp->data) || !canspotmon(mtmp)) + (nonliving(mtmp->data) || + is_vampshifter(mtmp) || !canspotmon(mtmp)) ? "destroyed" : "killed"); /* don't blame hero for unknown rolling boulder trap */ if (!context.mon_moving && diff --git a/src/muse.c b/src/muse.c index 107f432fd..59072edd5 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1848,7 +1848,7 @@ skipmsg: if (!where_to) { pline_The("whip slips free."); /* not `The_whip' */ return 1; - } else if (where_to == 3 && hates_silver(mtmp->data) && + } else if (where_to == 3 && mon_hates_silver(mtmp) && objects[obj->otyp].oc_material == SILVER) { /* this monster won't want to catch a silver weapon; drop it at hero's feet instead */ @@ -1929,7 +1929,8 @@ struct monst *mtmp; if (difficulty < 6 && !rn2(30)) return rn2(6) ? POT_POLYMORPH : WAN_POLYMORPH; - if (!rn2(40) && !nonliving(pm)) return AMULET_OF_LIFE_SAVING; + if (!rn2(40) && !nonliving(pm) && !is_vampshifter(mtmp)) + return AMULET_OF_LIFE_SAVING; switch (rn2(3)) { case 0: @@ -1997,7 +1998,7 @@ struct obj *obj; break; case AMULET_CLASS: if (typ == AMULET_OF_LIFE_SAVING) - return (boolean)(!nonliving(mon->data)); + return (boolean)(!(nonliving(mon->data) || is_vampshifter(mon))); if (typ == AMULET_OF_REFLECTION) return TRUE; break; diff --git a/src/polyself.c b/src/polyself.c index 948de3fde..7c76e4da9 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -233,11 +233,12 @@ int psflags; int mntmp = NON_PM; int tries=0; boolean forcecontrol = (psflags == 1); + boolean monsterpoly = (psflags == 2); boolean draconian = (uarm && uarm->otyp >= GRAY_DRAGON_SCALE_MAIL && uarm->otyp <= YELLOW_DRAGON_SCALES); boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data)); - boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT); + boolean isvamp = (youmonst.data->mlet == S_VAMPIRE); boolean was_floating = (Levitation || Flying); if(!Polymorph_control && !forcecontrol && !draconian && !iswere && !isvamp) { @@ -250,7 +251,7 @@ int psflags; } old_light = Upolyd ? emits_light(youmonst.data) : 0; - if (Polymorph_control || forcecontrol) { + if ((Polymorph_control || forcecontrol) && !monsterpoly) { do { getlin("Become what kind of monster? [type the name]", buf); @@ -288,10 +289,21 @@ int psflags; else mntmp = u.ulycn; } else { - if (youmonst.data->mlet == S_VAMPIRE) - mntmp = PM_VAMPIRE_BAT; - else - mntmp = PM_VAMPIRE; + if (youmonst.data->mlet == S_VAMPIRE) { + mntmp = (youmonst.data != &mons[PM_VAMPIRE] && + !rn2(10)) ? PM_WOLF : + !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT; + if (Polymorph_control) { + char buf[BUFSZ]; + Sprintf(buf, "Become %s?", + an(mons[mntmp].mname)); + if (yn(buf) != 'y') return; + } + if (Unchanging) { + pline("You fail to transform!"); + return; + } + } } /* if polymon fails, "you feel" message has been given so don't follow up with another polymon or newman */ @@ -1077,6 +1089,21 @@ dohide() return(1); } +int +dopoly() +{ + boolean isvampire = youmonst.data->mlet == S_VAMPIRE; + struct permonst *savedat = youmonst.data; + if (isvampire) { + polyself(2); + if (savedat != youmonst.data) { + You("transform into %s.", an(youmonst.data->mname)); + newsym(u.ux,u.uy); + } + } + return(1); +} + int domindblast() { diff --git a/src/potion.c b/src/potion.c index 79b1ba73c..e08605f2c 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1211,7 +1211,7 @@ boolean your_fault; break; case POT_WATER: if (is_undead(mon->data) || is_demon(mon->data) || - is_were(mon->data)) { + is_were(mon->data) || is_vampshifter(mon)) { if (obj->blessed) { pline("%s %s in pain!", Monnam(mon), is_silent(mon->data) ? "writhes" : "shrieks"); diff --git a/src/pray.c b/src/pray.c index d1d9c9052..b5fd74e8e 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)pray.c 3.4 2003/10/26 */ +/* SCCS Id: @(#)pray.c 3.4 2004/06/12 */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1697,6 +1697,7 @@ doturn() distu(mtmp->mx,mtmp->my) > range) continue; if (!mtmp->mpeaceful && (is_undead(mtmp->data) || + is_vampshifter(mtmp) || (is_demon(mtmp->data) && (u.ulevel > (MAXULEV/2))))) { mtmp->msleeping = 0; diff --git a/src/region.c b/src/region.c index ddbbcf48d..302a4599b 100644 --- a/src/region.c +++ b/src/region.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)region.c 3.4 2002/10/15 */ +/* SCCS Id: @(#)region.c 3.4 2004/06/12 */ /* Copyright (c) 1996 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -922,7 +922,8 @@ genericptr_t p2; mtmp = (struct monst *) p2; /* Non living and non breathing monsters are not concerned */ - if (!nonliving(mtmp->data) && !breathless(mtmp->data)) { + if (!(nonliving(mtmp->data) || is_vampshifter(mtmp)) + && !breathless(mtmp->data)) { if (cansee(mtmp->mx, mtmp->my)) pline("%s coughs!", Monnam(mtmp)); if (heros_fault(reg)) setmangry(mtmp); diff --git a/src/sounds.c b/src/sounds.c index 8ae8db550..bb0095c36 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)sounds.c 3.4 2002/05/06 */ +/* SCCS Id: @(#)sounds.c 3.4 2004/06/12 */ /* Copyright (c) 1989 Janet Walz, Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -157,7 +157,7 @@ dosounds() if (level.flags.has_morgue && !rn2(200)) { for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if (is_undead(mtmp->data) && + if ((is_undead(mtmp->data) || is_vampshifter(mtmp)) && mon_in_room(mtmp, MORGUE)) { switch (rn2(2)+hallu) { case 0: diff --git a/src/spell.c b/src/spell.c index 5828b8532..6900addae 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)spell.c 3.4 2003/01/17 */ +/* SCCS Id: @(#)spell.c 3.4 2004/06/12 */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ @@ -284,7 +284,8 @@ raise_dead: mtmp2 = mtmp->nmon; /* tamedog() changes chain */ if (DEADMONSTER(mtmp)) continue; - if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) { + if ((is_undead(mtmp->data) || is_vampshifter(mtmp)) && + cansee(mtmp->mx, mtmp->my)) { mtmp->mpeaceful = TRUE; if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type) && distu(mtmp->mx, mtmp->my) < 4) diff --git a/src/trap.c b/src/trap.c index 689420f15..63c3d4cc3 100644 --- a/src/trap.c +++ b/src/trap.c @@ -472,7 +472,8 @@ int *fail_reason; else mon->mundetected = FALSE; if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) { - const char *comes_to_life = nonliving(mon->data) ? + const char *comes_to_life = (nonliving(mon->data) || + is_vampshifter(mon)) ? "moves" : "comes to life"; if (cause == ANIMATE_SPELL) pline("%s %s!", upstart(statuename), diff --git a/src/uhitm.c b/src/uhitm.c index 8db83f292..d1a7f4d31 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)uhitm.c 3.4 2003/03/14 */ +/* SCCS Id: @(#)uhitm.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -570,7 +570,8 @@ int thrown; 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))) + if (uarmg && uarmg->blessed && + (is_undead(mdat) || is_demon(mdat) || is_vampshifter(mon))) tmp += rnd(4); /* So do silver rings. Note: rings are worn under gloves, so you * don't get both bonuses. @@ -580,7 +581,7 @@ int thrown; barehand_silver_rings++; if (uright && objects[uright->otyp].oc_material == SILVER) barehand_silver_rings++; - if (barehand_silver_rings && hates_silver(mdat)) { + if (barehand_silver_rings && mon_hates_silver(mon)) { tmp += rnd(20); silvermsg = TRUE; } @@ -609,7 +610,7 @@ int thrown; else tmp = rnd(2); if (objects[obj->otyp].oc_material == SILVER - && hates_silver(mdat)) { + && mon_hates_silver(mon)) { silvermsg = TRUE; silverobj = TRUE; /* if it will already inflict dmg, make it worse */ tmp += rnd((tmp) ? 20 : 10); @@ -676,7 +677,7 @@ int thrown; hittxt = TRUE; } if (objects[obj->otyp].oc_material == SILVER - && hates_silver(mdat)) { + && mon_hates_silver(mon)) { silvermsg = TRUE; silverobj = TRUE; } #ifdef STEED @@ -833,7 +834,7 @@ int thrown; #undef useup_eggs } case CLOVE_OF_GARLIC: /* no effect against demons */ - if (is_undead(mdat)) { + if (is_undead(mdat) || is_vampshifter(mon)) { monflee(mon, d(2, 4), FALSE, TRUE); } tmp = 1; @@ -905,7 +906,7 @@ int thrown; * so we need another silver check. */ if (objects[obj->otyp].oc_material == SILVER - && hates_silver(mdat)) { + && mon_hates_silver(mon)) { tmp += rnd(20); silvermsg = TRUE; silverobj = TRUE; } @@ -1067,7 +1068,7 @@ int thrown; fmt = "%s is seared!"; } /* note: s_suffix returns a modifiable buffer */ - if (!noncorporeal(mdat)) + if (!noncorporeal(mdat) && !amorphous(mdat)) whom = strcat(s_suffix(whom), " flesh"); pline(fmt, whom); } diff --git a/src/weapon.c b/src/weapon.c index e986d7087..9275fb38a 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)weapon.c 3.4 2002/11/07 */ +/* SCCS Id: @(#)weapon.c 3.4 2004/06/12 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -124,7 +124,7 @@ struct monst *mon; /* Blessed weapons used against undead or demons */ if (Is_weapon && otmp->blessed && - (is_demon(ptr) || is_undead(ptr))) tmp += 2; + (is_demon(ptr) || is_undead(ptr) || is_vampshifter(mon))) tmp += 2; if (is_spear(otmp) && index(kebabable, ptr->mlet)) tmp += 2; @@ -270,11 +270,12 @@ struct monst *mon; otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) { int bonus = 0; - if (otmp->blessed && (is_undead(ptr) || is_demon(ptr))) + if (otmp->blessed && + (is_undead(ptr) || is_demon(ptr) || is_vampshifter(mon))) bonus += rnd(4); if (is_axe(otmp) && is_wooden(ptr)) bonus += rnd(4); - if (objects[otyp].oc_material == SILVER && hates_silver(ptr)) + if (objects[otyp].oc_material == SILVER && mon_hates_silver(mon)) bonus += rnd(20); /* if the weapon is going to get a double damage bonus, adjust @@ -370,7 +371,7 @@ register struct monst *mtmp; if (((strongmonst(mtmp->data) && (mtmp->misc_worn_check & W_ARMS) == 0) || !objects[pwep[i]].oc_bimanual) && (objects[pwep[i]].oc_material != SILVER - || !hates_silver(mtmp->data))) { + || !mon_hates_silver(mtmp))) { if ((otmp = oselect(mtmp, pwep[i])) != 0) { propellor = otmp; /* force the monster to wield it */ return otmp; @@ -492,7 +493,7 @@ register struct monst *mtmp; if (((strong && !wearing_shield) || !objects[hwep[i]].oc_bimanual) && (objects[hwep[i]].oc_material != SILVER - || !hates_silver(mtmp->data))) + || !mon_hates_silver(mtmp))) Oselect(hwep[i]); } diff --git a/src/zap.c b/src/zap.c index 7610ec101..41f2f7305 100644 --- a/src/zap.c +++ b/src/zap.c @@ -153,7 +153,7 @@ struct obj *otmp; case SPE_TURN_UNDEAD: wake = FALSE; if (unturn_dead(mtmp)) wake = TRUE; - if (is_undead(mtmp->data)) { + if (is_undead(mtmp->data) || is_vampshifter(mtmp)) { reveal_invis = TRUE; wake = TRUE; dmg = rnd(8); @@ -3034,7 +3034,8 @@ struct obj **ootmp; /* to return worn armor for caller to disintegrate */ break; } if (nonliving(mon->data) || is_demon(mon->data) || - resists_magm(mon)) { /* similar to player */ + is_vampshifter(mon) || resists_magm(mon)) { + /* similar to player */ sho_shieldeff = TRUE; break; }