diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 0b9220403..d32fda191 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2908,6 +2908,7 @@ the game now automatically tracks which sell prices and buy prices you have 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 +early-game monsters always miss with their first use of an attack wand Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 7c5dd9a49..69fca8969 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3969,7 +3969,8 @@ extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); extern void buzz(int, int, coordxy, coordxy, int, int); -extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean, boolean); +extern void dobuzz(int, int, coordxy, coordxy, int, int, + boolean, boolean, boolean); extern void melt_ice(coordxy, coordxy, const char *) NO_NNARGS; extern void start_melt_ice_timeout(coordxy, coordxy, long); extern void melt_ice_away(union any *, long) NONNULLARG1; diff --git a/include/monst.h b/include/monst.h index 15114c600..f1fb3b9c2 100644 --- a/include/monst.h +++ b/include/monst.h @@ -162,6 +162,8 @@ struct monst { Bitfield(meverseen, 1); /* mon has been seen at some point */ Bitfield(mspotted, 1); /* mon is currently seen by hero */ + Bitfield(mwandexp, 1); /* mon has experience with wands */ + /* 6 spare bits */ unsigned long mstrategy; /* for monsters with mflag3: current strategy */ #ifdef NHSTDC diff --git a/src/makemon.c b/src/makemon.c index 130694401..4f12e12cb 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1288,6 +1288,10 @@ makemon( /* quest leader and nemesis both know about all trap types */ if (ptr->msound == MS_LEADER || ptr->msound == MS_NEMESIS) mon_learns_traps(mtmp, ALL_TRAPS); + /* locations where monsters are already experienced with wands */ + if (Is_stronghold(&u.uz) || Is_knox(&u.uz) || In_endgame(&u.uz) || + In_hell(&u.uz) || In_V_tower(&u.uz) || In_quest(&u.uz)) + mtmp->mwandexp = TRUE; place_monster(mtmp, x, y); mtmp->mcansee = mtmp->mcanmove = TRUE; diff --git a/src/mthrowu.c b/src/mthrowu.c index 613150656..88066266d 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1094,7 +1094,8 @@ breamm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) Monnam(mtmp), breathwep_name(typ)); gb.buzzer = mtmp; dobuzz(BZ_M_BREATH(BZ_OFS_AD(typ)), (int) mattk->damn, - mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget, utarget); + mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), + utarget, utarget, FALSE); gb.buzzer = 0; nomul(0); /* breath runs out sometimes. Also, give monster some diff --git a/src/muse.c b/src/muse.c index aa0474e4e..2d5e82bd4 100644 --- a/src/muse.c +++ b/src/muse.c @@ -31,6 +31,7 @@ staticfn boolean hero_behind_chokepoint(struct monst *); staticfn boolean mon_has_friends(struct monst *); staticfn boolean mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) NONNULLARG1; staticfn int mbhitm(struct monst *, struct obj *); +staticfn void buzz_force_miss(int, int, coordxy, coordxy, int, int); staticfn boolean fhito_loc(struct obj *obj, coordxy x, coordxy y, int (*fhito)(OBJ_P, OBJ_P)); staticfn void mbhit(struct monst *, int, int (*)(MONST_P, OBJ_P), @@ -1614,7 +1615,8 @@ mbhitm(struct monst *mtmp, struct obj *otmp) Soundeffect(se_boing, 40); pline("Boing!"); learnit = TRUE; - } else if (rnd(20) < 10 + u.uac) { + } else if (rnd(20) < 10 + u.uac && + !(gb.buzzer && !gb.buzzer->mwandexp)) { monstunseesu(M_SEEN_MAGR); /* mons see hero not resisting */ pline_The("wand hits you!"); tmp = d(2, 12); @@ -1809,6 +1811,12 @@ mbhit( } } +staticfn void +buzz_force_miss(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) +{ + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, TRUE); +} + /* Perform an offensive action for a monster. Must be called immediately * after find_offensive(). Return values are same as use_defensive(). */ @@ -1819,6 +1827,12 @@ use_offensive(struct monst *mtmp) struct obj *otmp = gm.m.offensive; boolean oseen; + /* if a monster has never used an attack wand before, it takes them some + time to get used to holding that much power, so the first shot always + misses */ + void (*buzzfn)(int, int, coordxy, coordxy, int, int) = + mtmp->mwandexp ? buzz : buzz_force_miss; + /* offensive potions are not drunk, they're thrown */ if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0) return i; @@ -1837,12 +1851,13 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gc.current_wand = otmp; gb.buzzer = mtmp; - buzz(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), - (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, - sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); + buzzfn(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), + (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, + sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_FIRE_HORN: case MUSE_FROST_HORN: @@ -1850,13 +1865,14 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gb.buzzer = mtmp; gc.current_wand = otmp; /* needed by zhitu() */ - buzz(BZ_M_WAND(BZ_OFS_AD((otmp->otyp == FROST_HORN) ? AD_COLD - : AD_FIRE)), + buzzfn(BZ_M_WAND(BZ_OFS_AD( + (otmp->otyp == FROST_HORN) ? AD_COLD : AD_FIRE)), rn1(6, 6), mtmp->mx, mtmp->my, sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_WAN_TELEPORTATION: case MUSE_WAN_UNDEAD_TURNING: @@ -1864,9 +1880,13 @@ use_offensive(struct monst *mtmp) gz.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; + gb.buzzer = mtmp; mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp); + gb.buzzer = 0; /* note: 'otmp' might have been destroyed (drawbridge destruction) */ gm.m_using = FALSE; + if (gm.m.has_offense == MUSE_WAN_STRIKING) + mtmp->mwandexp = TRUE; return 2; case MUSE_SCR_EARTH: { /* TODO: handle steeds */ diff --git a/src/zap.c b/src/zap.c index adecdbeca..12c482227 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4750,13 +4750,13 @@ disintegrate_mon( void ubuzz(int type, int nd) { - dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE); + dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE, FALSE); } void buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) { - dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE); + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, FALSE); } /* @@ -4774,7 +4774,8 @@ dobuzz( int nd, /* damage strength ('number of dice') */ coordxy sx, coordxy sy, /* starting point */ int dx, int dy, /* direction delta */ - boolean sayhit, boolean saymiss) /* report out of sight hit/miss events */ + boolean sayhit, boolean saymiss, /* report out of sight hit/miss events */ + boolean forcemiss) { int range, fltyp = zaptype(type), damgtype = fltyp % 10; coordxy lsx, lsy; @@ -4860,7 +4861,7 @@ dobuzz( buzzmonst: gn.notonhead = (mon->mx != gb.bhitpos.x || mon->my != gb.bhitpos.y); - if (zap_hit(find_mac(mon), spell_type)) { + if (!forcemiss && zap_hit(find_mac(mon), spell_type)) { if (mon_reflects(mon, (char *) 0)) { if (cansee(mon->mx, mon->my)) { hit(flash_str(fltyp, FALSE), mon, exclam(0)); @@ -4950,7 +4951,7 @@ dobuzz( if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) { mon = u.usteed; goto buzzmonst; - } else if (zap_hit((int) u.uac, 0)) { + } else if (!forcemiss && zap_hit((int) u.uac, 0)) { range -= 2; pline_dir(xytodir(-dx, -dy), "%s hits you!", The(flash_str(fltyp, FALSE)));