diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 2cc231da5..85439697a 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -707,6 +707,7 @@ when filling a special room with monsters, if one that can come in groups got picked the group could spill to outside of the room extend 3.6.1 fix to explicitly use name of unseen shopkeeper instead of "It" in various shop related messages (ones issued outside of shk.c) +fix some inconsistencies with applied bullwhip vs monster Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index a8bedf7ef..15652b67b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2707,6 +2707,7 @@ extern void erode_armor(struct monst *, int); extern boolean attack_checks(struct monst *, struct obj *); extern void check_caitiff(struct monst *); extern int find_roll_to_hit(struct monst *, uchar, struct obj *, int *, int *); +extern boolean force_attack(struct monst *, boolean); extern boolean do_attack(struct monst *); extern boolean hmon(struct monst *, struct obj *, int, int); extern boolean shade_miss(struct monst *, struct monst *, struct obj *, diff --git a/src/apply.c b/src/apply.c index 98794aff7..048b0536d 100644 --- a/src/apply.c +++ b/src/apply.c @@ -2702,8 +2702,7 @@ use_whip(struct obj *obj) if (obj != uwep) { if (!wield_tool(obj, "lash")) return 0; - else - res = 1; + res = 1; } if (!getdir((char *) 0)) return res; @@ -2739,7 +2738,7 @@ use_whip(struct obj *obj) if (proficient < 0) proficient = 0; - if (u.uswallow && do_attack(u.ustuck)) { + if (u.uswallow) { There("is not enough room to flick your bullwhip."); } else if (Underwater) { @@ -2763,8 +2762,10 @@ use_whip(struct obj *obj) (void) fire_damage(uwep, FALSE, u.ux, u.uy); return 1; } - if (Levitation || u.usteed) { - /* Have a shot at snaring something on the floor */ + if (Levitation || u.usteed || Flying) { + /* Have a shot at snaring something on the floor. A flyer + can reach the floor so could just pick an item up, but + allow snagging by whip too. */ otmp = g.level.objects[u.ux][u.uy]; if (otmp && otmp->otyp == CORPSE && otmp->corpsenm == PM_HORSE) { pline("Why beat a dead horse?"); @@ -2796,34 +2797,34 @@ use_whip(struct obj *obj) * * if you're in a pit * - you are attempting to get out of the pit - * or, if you are applying it towards a small monster - * - then it is assumed that you are trying to hit it - * else if the monster is wielding a weapon - * - you are attempting to disarm a monster - * else - * - you are attempting to hit the monster. + * - if there is no suitable boulder or furniture to target, + * target a big monster for that, or if a small or medium + * monster is present, attack it + * [if both boulder and furniture are present, target the + * former because it is on top of the latter] + * else if you are applying it towards a monster + * - if monster is concealed, reveal it and proceed; + * - if it was not concealed and is wielding a weapon, attempt + * to disarm it; + * - otherwise attack it. * * if you're confused (and thus off the mark) * - you only end up hitting. - * */ - const char *wrapped_what = (char *) 0; + const char *wrapped_what = sobj_at(BOULDER, rx, ry) ? "a boulder" + : IS_FURNITURE(levl[rx][ry].typ) + ? something : (char *) 0; if (mtmp) { - if (bigmonst(mtmp->data)) { + /* if a big monster is known to be present, target it in + preference to boulder or furniture; if any small or medium + monster is present, or an unseen big one, use the boulder + or furniture if available, otherwise attack */ + if (bigmonst(mtmp->data) && canspotmon(mtmp)) wrapped_what = strcpy(buf, mon_nam(mtmp)); - } else if (proficient) { - if (do_attack(mtmp)) - return 1; - else - pline1(msg_snap); - } - } - if (!wrapped_what) { - if (IS_FURNITURE(levl[rx][ry].typ)) - wrapped_what = something; - else if (sobj_at(BOULDER, rx, ry)) - wrapped_what = "a boulder"; + + if (!wrapped_what) + goto whipattack; } if (wrapped_what) { coord cc; @@ -2834,8 +2835,10 @@ use_whip(struct obj *obj) if (proficient && rn2(proficient + 2)) { if (!mtmp || enexto(&cc, rx, ry, g.youmonst.data)) { You("yank yourself out of the pit!"); + reset_utrap(TRUE); /* [was after teleds(); do this before + * in case it has no alternative other + * than to put hero in another trap] */ teleds(cc.x, cc.y, TELEDS_ALLOW_DRAG); - reset_utrap(TRUE); g.vision_full_recalc = 1; } } else { @@ -2847,11 +2850,31 @@ use_whip(struct obj *obj) pline1(msg_snap); } else if (mtmp) { - if (!canspotmon(mtmp) && !glyph_is_invisible(levl[rx][ry].glyph)) { - pline("A monster is there that you couldn't see."); - map_invisible(rx, ry); + whipattack: + otmp = 0; /* if monster is unseen, can't attempt to disarm it */ + if (!canspotmon(mtmp)) { + boolean spotitnow; + + mtmp->mundetected = 0; /* bring non-mimic hider out of hiding */ + /* check visibility again after mundetected=0 in case being + brought out of hiding has exposed it (might not if hero is + blind or formerly hidden monster is also invisible) */ + spotitnow = canspotmon(mtmp); + if (spotitnow || !glyph_is_invisible(levl[rx][ry].glyph)) { + pline("%s is there that you %s.", + !spotitnow ? "A monster" : Amonnam(mtmp), + !Blind ? "couldn't see" : "hadn't noticed"); + if (!spotitnow) + map_invisible(rx, ry); + else + newsym(rx, ry); + } + } else { + /* monster is known so if it is wielding something, try to + disarm it rather than make a direct attack */ + otmp = MON_WEP(mtmp); } - otmp = MON_WEP(mtmp); /* can be null */ + if (otmp) { char onambuf[BUFSZ]; const char *mon_hand; @@ -2943,20 +2966,23 @@ use_whip(struct obj *obj) } else { pline1(msg_slipsfree); } - wakeup(mtmp, TRUE); - } else { + } else { /* mtmp isn't wielding a weapon; attack it */ + boolean do_snap = TRUE; + if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers - && !sensemon(mtmp)) + && !sensemon(mtmp)) { stumble_onto_mimic(mtmp); - else + do_snap = FALSE; + } else { You("flick your bullwhip towards %s.", mon_nam(mtmp)); - if (proficient) { - if (do_attack(mtmp)) - return 1; - else - pline1(msg_snap); } + if (proficient && force_attack(mtmp, FALSE)) + return 1; + if (do_snap) + pline1(msg_snap); } + /* regardless of mtmp's weapon or hero's proficiency */ + wakeup(mtmp, TRUE); } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) { /* it must be air -- water checked above */ diff --git a/src/uhitm.c b/src/uhitm.c index 928acae7d..8c8348ff7 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -115,8 +115,9 @@ erode_armor(struct monst *mdef, int hurt) /* FALSE means it's OK to attack */ boolean -attack_checks(struct monst *mtmp, - struct obj *wep) /* uwep for do_attack(), null for kick_monster() */ +attack_checks( + struct monst *mtmp, /* target */ + struct obj *wep) /* uwep for do_attack(), null for kick_monster() */ { int glyph; @@ -346,12 +347,28 @@ find_roll_to_hit(struct monst *mtmp, return tmp; } +/* temporarily override 'safepet' (by faking use of 'F' prefix) when possibly + unintentionally attacking peaceful monsters and optionally pets */ +boolean +force_attack(struct monst *mtmp, boolean pets_too) +{ + boolean attacked, save_Forcefight; + + save_Forcefight = g.context.forcefight; + /* always set forcefight On for hostiles and peacefuls, maybe for pets */ + if (pets_too || !mtmp->mtame) + g.context.forcefight = TRUE; + attacked = do_attack(mtmp); + g.context.forcefight = save_Forcefight; + return attacked; +} + /* try to attack; return False if monster evaded; u.dx and u.dy must be set */ boolean do_attack(struct monst *mtmp) { - register struct permonst *mdat = mtmp->data; + struct permonst *mdat = mtmp->data; /* This section of code provides protection against accidentally * hitting peaceful (like '@') and tame (like 'd') monsters.