diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 6e602bac6..c888f8353 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -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 diff --git a/include/extern.h b/include/extern.h index a1f9ff99b..fb6fb5ad5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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)); diff --git a/src/muse.c b/src/muse.c index 266282995..db124f0a3 100644 --- a/src/muse.c +++ b/src/muse.c @@ -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 diff --git a/src/zap.c b/src/zap.c index dd7ee93fa..998781488 100644 --- a/src/zap.c +++ b/src/zap.c @@ -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: