let monsters use wand of undead turning

when hero is wielding a cockatrice corpse.  Wands of undead turning
aren't generated as starting equipment but they will now be picked
up if come across while the hero is carrying any corpse, and used
in preference to any other item when carried and non-empty and hero
is wielding a petrifier's corpse.
This commit is contained in:
PatR
2020-04-10 18:39:46 -07:00
parent 05028b4c3e
commit dc6b2f5de1
4 changed files with 99 additions and 31 deletions

View File

@@ -251,6 +251,8 @@ for !fixinv option where inventory letters normally don't stick, try to put
thrown from; only works if it does return and is successfully caught
wizard mode #wizborn command
include more skill information in ^X output when dual-wielding
item-using monsters will zap wand of undead turning at corpse-wielding hero
when the corpse is harmful
Platform- and/or Interface-Specific New Features

View File

@@ -3120,6 +3120,7 @@ E struct monst *FDECL(get_container_location,
E struct monst *FDECL(montraits, (struct obj *, coord *, BOOLEAN_P));
E struct monst *FDECL(revive, (struct obj *, BOOLEAN_P));
E int FDECL(unturn_dead, (struct monst *));
E void NDECL(unturn_you);
E void FDECL(cancel_item, (struct obj *));
E boolean FDECL(drain_item, (struct obj *, BOOLEAN_P));
E struct obj *FDECL(poly_obj, (struct obj *, int));

View File

@@ -155,6 +155,10 @@ struct monst *mtmp;
struct obj *otmp;
boolean self;
{
if (otmp->spe < 1) {
impossible("Mon zapping wand with %d charges?", otmp->spe);
return;
}
if (!canseemon(mtmp)) {
int range = couldsee(mtmp->mx, mtmp->my) /* 9 or 5 */
? (BOLT_LIM + 1) : (BOLT_LIM - 3);
@@ -281,6 +285,7 @@ struct obj *otmp;
#define MUSE_UNICORN_HORN 17
#define MUSE_POT_FULL_HEALING 18
#define MUSE_LIZARD_CORPSE 19
#define MUSE_WAN_UNDEAD_TURNING 20
/*
#define MUSE_INNATE_TPT 9999
* We cannot use this. Since monsters get unlimited teleportation, if they
@@ -320,10 +325,9 @@ struct monst *mtmp;
{
register struct obj *obj = 0;
struct trap *t;
int x = mtmp->mx, y = mtmp->my;
boolean stuck = (mtmp == u.ustuck);
boolean immobile = (mtmp->data->mmove == 0);
int fraction;
int fraction, x = mtmp->mx, y = mtmp->my;
boolean stuck = (mtmp == u.ustuck),
immobile = (mtmp->data->mmove == 0);
if (is_animal(mtmp->data) || mindless(mtmp->data))
return FALSE;
@@ -386,6 +390,24 @@ struct monst *mtmp;
return TRUE;
}
/* monsters aren't given wands of undead turning but if they
happen to have picked one up, use it against corpse wielder;
when applicable, use it now even if 'mtmp' isn't wounded */
if (!mtmp->mpeaceful && !nohands(mtmp->data)
&& uwep && uwep->otyp == CORPSE
&& touch_petrifies(&mons[uwep->corpsenm])
&& !poly_when_stoned(mtmp->data) && !resists_ston(mtmp)
&& lined_up(mtmp)) { /* only lines up if distu range is within 5*5 */
/* could use m_carrying(), then nxtobj() when matching wand
is empty, but direct traversal is actually simpler here */
for (obj = mtmp->minvent; obj; obj = obj->nobj)
if (obj->otyp == WAN_UNDEAD_TURNING && obj->spe > 0) {
g.m.defensive = obj;
g.m.has_defense = MUSE_WAN_UNDEAD_TURNING;
return TRUE;
}
}
fraction = u.ulevel < 10 ? 5 : u.ulevel < 14 ? 4 : 3;
if (mtmp->mhp >= mtmp->mhpmax
|| (mtmp->mhp >= 10 && mtmp->mhp * fraction >= mtmp->mhpmax))
@@ -768,14 +790,19 @@ struct monst *mtmp;
(coord *) 0);
return 2;
}
case MUSE_WAN_UNDEAD_TURNING:
g.zap_oseen = oseen;
mzapwand(mtmp, otmp, FALSE);
g.m_using = TRUE;
mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp);
g.m_using = FALSE;
return 2;
case MUSE_WAN_CREATE_MONSTER: {
coord cc;
/* pm: 0 => random, eel => aquatic, croc => amphibious */
struct permonst *pm =
!is_pool(mtmp->mx, mtmp->my)
? 0
: &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
struct monst *mon;
/* pm: 0 => random, eel => aquatic, croc => amphibious */
struct permonst *pm = !is_pool(mtmp->mx, mtmp->my) ? 0
: &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
if (!enexto(&cc, mtmp->mx, mtmp->my, pm))
return 0;
@@ -1225,9 +1252,9 @@ register struct monst *mtmp;
register struct obj *otmp;
{
int tmp;
boolean reveal_invis = FALSE;
boolean reveal_invis = FALSE, hits_you = (mtmp == &g.youmonst);
if (mtmp != &g.youmonst) {
if (!hits_you && otmp->otyp != WAN_UNDEAD_TURNING) {
mtmp->msleeping = 0;
if (mtmp->m_ap_type)
seemimic(mtmp);
@@ -1235,9 +1262,7 @@ register struct obj *otmp;
switch (otmp->otyp) {
case WAN_STRIKING:
reveal_invis = TRUE;
if (mtmp == &g.youmonst) {
if (g.zap_oseen)
makeknown(WAN_STRIKING);
if (hits_you) {
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline("Boing!");
@@ -1268,10 +1293,10 @@ register struct obj *otmp;
break;
#if 0 /* disabled because find_offensive() never picks WAN_TELEPORTATION */
case WAN_TELEPORTATION:
if (mtmp == &g.youmonst) {
if (hits_you) {
tele();
if (g.zap_oseen)
makeknown(WAN_TELEPORTATION);
tele();
} else {
/* for consistency with zap.c, don't identify */
if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) {
@@ -1286,12 +1311,42 @@ register struct obj *otmp;
case SPE_CANCELLATION:
(void) cancel_monst(mtmp, otmp, FALSE, TRUE, FALSE);
break;
case WAN_UNDEAD_TURNING: {
boolean learnit = FALSE;
if (hits_you) {
unturn_you();
learnit = g.zap_oseen;
} else {
boolean wake = FALSE;
if (unturn_dead(mtmp)) /* affects mtmp's invent, not mtmp */
wake = TRUE;
if (is_undead(mtmp->data) || is_vampshifter(mtmp)) {
wake = reveal_invis = TRUE;
/* context.bypasses=True: if resist() happens to be fatal,
make_corpse() will set obj->bypass on the new corpse
so that mbhito() will skip it instead of reviving it */
g.context.bypasses = TRUE; /* for make_corpse() */
(void) resist(mtmp, WAND_CLASS, rnd(8), NOTELL);
}
if (wake) {
if (!DEADMONSTER(mtmp))
wakeup(mtmp, FALSE);
learnit = g.zap_oseen;
}
}
if (learnit)
makeknown(WAN_UNDEAD_TURNING);
break;
}
if (reveal_invis) {
if (!DEADMONSTER(mtmp) && cansee(g.bhitpos.x, g.bhitpos.y)
&& !canspotmon(mtmp))
map_invisible(g.bhitpos.x, g.bhitpos.y);
default:
break;
}
if (reveal_invis && !DEADMONSTER(mtmp)
&& cansee(g.bhitpos.x, g.bhitpos.y) && !canspotmon(mtmp))
map_invisible(g.bhitpos.x, g.bhitpos.y);
return 0;
}
@@ -2175,8 +2230,8 @@ struct obj *obj;
int typ = obj->otyp;
/* don't let monsters interact with protected items on the floor */
if ((obj->where == OBJ_FLOOR)
&& (obj->ox == mon->mx) && (obj->oy == mon->my)
if (obj->where == OBJ_FLOOR
&& (obj->ox == mon->mx && obj->oy == mon->my)
&& onscary(obj->ox, obj->oy, mon)) {
return FALSE;
}
@@ -2202,6 +2257,8 @@ struct obj *obj;
if (objects[typ].oc_dir == RAY || typ == WAN_STRIKING
|| typ == WAN_TELEPORTATION || typ == WAN_CREATE_MONSTER)
return TRUE;
if (typ == WAN_UNDEAD_TURNING)
return carrying(CORPSE) || (Upolyd && is_undead(g.youmonst.data));
break;
case POTION_CLASS:
if (typ == POT_HEALING || typ == POT_EXTRA_HEALING

View File

@@ -989,6 +989,19 @@ struct monst *mon;
return res;
}
void
unturn_you()
{
(void) unturn_dead(&g.youmonst); /* hit carried corpses and eggs */
if (is_undead(g.youmonst.data)) {
You_feel("frightened and %sstunned.", Stunned ? "even more " : "");
make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
} else {
You("shudder in dread.");
}
}
/* cancel obj, possibly carried by you or a monster */
void
cancel_item(obj)
@@ -1998,6 +2011,7 @@ struct obj *obj, *otmp;
} else if (obj->otyp == CORPSE) {
struct monst *mtmp;
xchar ox, oy;
boolean by_u = !g.context.mon_moving;
int corpsenm = corpse_revive_type(obj);
char *corpsname = cxname_singular(obj);
@@ -2013,7 +2027,7 @@ struct obj *obj, *otmp;
if (canspotmon(mtmp)) {
pline("%s is resurrected!",
upstart(noname_monnam(mtmp, ARTICLE_THE)));
learn_it = TRUE;
learn_it = by_u ? TRUE : g.zap_oseen;
} else {
/* saw corpse but don't see monster: maybe
mtmp is invisible, or has been placed at
@@ -2032,7 +2046,7 @@ struct obj *obj, *otmp;
You_hear("%s reviving.", corpsname);
else
You_hear("a defibrillator.");
learn_it = TRUE;
learn_it = by_u ? TRUE : g.zap_oseen;
}
if (canspotmon(mtmp))
/* didn't see corpse but do see monster: it
@@ -2479,13 +2493,7 @@ boolean ordinary;
case WAN_UNDEAD_TURNING:
case SPE_TURN_UNDEAD:
learn_it = TRUE;
(void) unturn_dead(&g.youmonst);
if (is_undead(g.youmonst.data)) {
You_feel("frightened and %sstunned.",
Stunned ? "even more " : "");
make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
} else
You("shudder in dread.");
unturn_you();
break;
case SPE_HEALING:
case SPE_EXTRA_HEALING: