diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 464e6e701..0b9220403 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2907,6 +2907,7 @@ the game now automatically tracks which sell prices and buy prices you have seen for each type of item; these are visible in the discoveries list, and can also be shown elsewhere using the new 'price_quotes' option new shields: shield of shock resistance, shield of drain resistance +new wand: wand of stasis Platform- and/or Interface-Specific New Features diff --git a/include/objects.h b/include/objects.h index 932d063f6..faa630a50 100644 --- a/include/objects.h +++ b/include/objects.h @@ -1457,9 +1457,11 @@ WAND("create monster", "maple", 45, 200, 1, NODIR, WOOD, HI_WOOD, WAN_CREATE_MONSTER), WAND("wishing", "pine", 5, 500, 1, NODIR, WOOD, HI_WOOD, WAN_WISHING), +WAND("stasis", "redwood", 45, 150, 1, NODIR, WOOD, CLR_RED, + WAN_STASIS), WAND("nothing", "oak", 25, 100, 0, IMMEDIATE, WOOD, HI_WOOD, WAN_NOTHING), -WAND("striking", "ebony", 75, 150, 1, IMMEDIATE, WOOD, HI_WOOD, +WAND("striking", "ebony", 30, 150, 1, IMMEDIATE, WOOD, HI_WOOD, WAN_STRIKING), WAND("make invisible", "marble", 45, 150, 1, IMMEDIATE, MINERAL, HI_MINERAL, WAN_MAKE_INVISIBLE), diff --git a/include/rm.h b/include/rm.h index 1c9f49f14..8811deae1 100644 --- a/include/rm.h +++ b/include/rm.h @@ -454,6 +454,7 @@ struct levelflags { Bitfield(stormy, 1); /* clouds create lightning bolts at random */ schar temperature; /* +1 == hot, -1 == cold */ + long stasis_until; /* wand of stasis effect lasts until when? */ }; typedef struct { diff --git a/src/apply.c b/src/apply.c index 6cbcb30a8..3abfc5b6c 100644 --- a/src/apply.c +++ b/src/apply.c @@ -526,6 +526,10 @@ magic_whistled(struct obj *obj) already_discovered = objects[obj->otyp].oc_name_known != 0; int omx, omy, shift = 0, appear = 0, disappear = 0, trapped = 0; + /* stasis prevents magic-whistling */ + if (svl.level.flags.stasis_until >= svm.moves) + return; + /* need to copy (up to 3) names as they're collected rather than just save pointers to them, otherwise churning through every mbuf[] might clobber the ones we care about */ @@ -3984,6 +3988,7 @@ do_break_wand(struct obj *obj) case WAN_PROBING: case WAN_ENLIGHTENMENT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: pline(nothing_else_happens); discard_broken_wand(); return ECMD_TIME; diff --git a/src/do.c b/src/do.c index 875d0c62f..26485069c 100644 --- a/src/do.c +++ b/src/do.c @@ -2248,7 +2248,8 @@ revive_mon(anything *arg, long timeout UNUSED) /* corpse will revive somewhere else if there is a monster in the way; Riders get a chance to try to bump the obstacle out of their way */ if (is_displacer(mptr) && body->where == OBJ_FLOOR - && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0) { + && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0 && + svl.level.flags.stasis_until < svm.moves) { boolean notice_it = canseemon(mtmp); /* before rloc() */ char *monname = Monnam(mtmp); diff --git a/src/engrave.c b/src/engrave.c index c84908cf1..22ca30aaf 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -589,6 +589,7 @@ doengrave_sfx_item_WAN(struct _doengrave_ctx *de) /* NODIR wands */ case WAN_LIGHT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: case WAN_CREATE_MONSTER: case WAN_WISHING: case WAN_ENLIGHTENMENT: diff --git a/src/mklev.c b/src/mklev.c index 0350562f3..703133ca4 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -891,6 +891,7 @@ clear_level_structures(void) svl.level.flags.noautosearch = 0; svl.level.flags.fumaroles = 0; svl.level.flags.stormy = 0; + svl.level.flags.stasis_until = 0L; svn.nroom = 0; svr.rooms[0].hx = -1; diff --git a/src/mkobj.c b/src/mkobj.c index f2adf2185..ab0f98fb3 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1115,6 +1115,10 @@ mksobj_init(struct obj **obj, boolean artif) case WAND_CLASS: if (otmp->otyp == WAN_WISHING) otmp->spe = 1; + else if (otmp->otyp == WAN_STASIS) + /* just as easy to recharge as other NODIR wands, but starts with + fewer charges */ + otmp->spe = rn1(4, 3); else otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); diff --git a/src/monmove.c b/src/monmove.c index 3830e3713..6489d19d4 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1794,11 +1794,10 @@ m_move(struct monst *mtmp, int after) if (covetousattack & M_ATTK_AGR_DIED) return MMOVE_DIED; mmoved = MMOVE_MOVED; - } else { - mmoved = MMOVE_NOTHING; + return postmov(mtmp, ptr, omx, omy, mmoved, + seenflgs, can_tunnel, can_unlock, can_open); } - return postmov(mtmp, ptr, omx, omy, mmoved, - seenflgs, can_tunnel, can_unlock, can_open); + /* otherwise continue with normal AI routine */ } /* likewise for shopkeeper, guard, or priest */ diff --git a/src/teleport.c b/src/teleport.c index 9858dfe49..b235cb238 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -34,8 +34,13 @@ noteleport_level(struct monst *mon) if (get_iter_mons(m_blocks_teleporting)) return TRUE; - /* natural no-teleport level */ - if (svl.level.flags.noteleport) + /* natural no-teleport level; covetous monsters can bypass these */ + if (svl.level.flags.noteleport && !is_covetous(mon->data)) + return TRUE; + + /* wand of stasis prevents teleportation while the effect is active + (even for covetous monsters) */ + if (svl.level.flags.stasis_until >= svm.moves) return TRUE; return FALSE; @@ -1958,8 +1963,11 @@ mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) { char *monname; - if (tele_restrict(mtmp)) + /* don't print feedback here: a monster stepping on a trap and not + teleporting from it isn't visible */ + if (noteleport_level(mtmp)) return; + if (teleport_pet(mtmp, FALSE)) { /* save name with pre-movement visibility */ monname = Monnam(mtmp); @@ -2257,7 +2265,12 @@ u_teleport_mon( { coord cc; - if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { + if (svl.level.flags.stasis_until >= svm.moves) { + if (give_feedback) + pline("A mysterious force prevents you teleporting %s!", + mon_nam(mtmp)); + return FALSE; + } else if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { if (give_feedback) pline("%s resists your magic!", Monnam(mtmp)); return FALSE; diff --git a/src/wizard.c b/src/wizard.c index 674b4db6e..c89e3296f 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -386,10 +386,12 @@ tactics(struct monst *mtmp) mtmp->mavenge = 1; /* covetous monsters attack while fleeing */ if (In_W_tower(mx, my, &u.uz) || (mtmp->iswiz && !sx && !mon_has_amulet(mtmp))) { - if (!rn2(3 + mtmp->mhp / 10)) + if (!noteleport_level(mtmp) && + !rn2(3 + mtmp->mhp / 10)) (void) rloc(mtmp, RLOC_MSG); } else if (sx && (mx != sx || my != sy)) { - if (!mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { + if (!noteleport_level(mtmp) && + !mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { /* couldn't move to the target spot for some reason, so stay where we are (don't actually need rloc_to() because mtmp is still on the map at ... */ @@ -408,7 +410,7 @@ tactics(struct monst *mtmp) /*FALLTHRU*/ case STRAT_NONE: /* harass */ - if (!rn2(!mtmp->mflee ? 5 : 33)) + if (!noteleport_level(mtmp) && !rn2(!mtmp->mflee ? 5 : 33)) mnexto(mtmp, RLOC_MSG); return 0; @@ -419,13 +421,16 @@ tactics(struct monst *mtmp) int targ = (int) (strat & STRAT_GOAL); struct obj *otmp; - if (!targ) { /* simply wants you to close */ + if (!targ || !isok(tx, ty)) { /* simply wants you to close */ return 0; } + if (noteleport_level(mtmp) && !monnear(mtmp, tx, ty)) + return 0; if (u_at(tx, ty) || where == STRAT_PLAYER) { /* player is standing on it (or has it) */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (noteleport_level(mtmp) || + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } @@ -445,13 +450,14 @@ tactics(struct monst *mtmp) return 0; } else { /* a monster is standing on it - cause some trouble */ - if (!rn2(5)) + if (!rn2(5) && !noteleport_level(mtmp)) mnexto(mtmp, RLOC_MSG); return 0; } } else { /* a monster has it - 'port beside it. */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (!noteleport_level(mtmp) && + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } diff --git a/src/zap.c b/src/zap.c index 96dc3d44b..adecdbeca 100644 --- a/src/zap.c +++ b/src/zap.c @@ -2555,6 +2555,11 @@ zapnodir(struct obj *obj) known = !!obj->dknown; (void) findit(); break; + case WAN_STASIS: + /* no immediately obvious effect, and no message so that it isn't + distinguishable from other NODIR wands that produce no message */ + svl.level.flags.stasis_until = svm.moves + rn1(21, 10); + break; case WAN_CREATE_MONSTER: /* create_critters() returns True iff hero sees a new monster appear */ if (create_critters(rn2(23) ? 1 : rn1(7, 2), diff --git a/win/share/objects.txt b/win/share/objects.txt index 64a53c16a..986d88e6f 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -7973,6 +7973,25 @@ Z = (195, 195, 195) ................ ................ } +# tile 413 (redwood / stasis) +{ + ................ + ................ + ................ + ...........NO... + ..........DDAA.. + .........DDAA... + ........DDAA.... + .......DDAA..... + ......DDAA...... + .....DDAA....... + ....DDAA........ + ...NOAA......... + ....AA.......... + ................ + ................ + ................ +} # tile 414 (oak / nothing) { ................