bullwhip inconsistencies

Reported by entrez:  applying a bullwhip towards a medium or small
peaceful monster used to be an attack but that stopped working when
'safepet' was extended to peacefuls in order for the hero to be able
to swap places which those.  Also, side-effects were different when
hero applied the whip from within a pit compared to when not in one.

This allows the hero trapped in a pit to try to snag furniture or a
boulder even when a small or medium is present, and escaping that
pit if successful.  (It still snags big monsters in preference to
furniture/boulder at their location.)  When no such non-monster
target is available, it attacks the monster if hostile or peaceful
but not when tame.  When revealing a previously unseen monster it
prevents snagging that monster's wielded weapon because hero couldn't
possibly target the weapon in that situation.

This makes other changes, mostly dealing with finding and exposing
concealed monsters, which may introduce some new bugs.

Since I was already in the right place, implement snagging an item
off the floor while flying.  It isn't necessary since a flyer can
pick things up off the floor directly, but there isn't any pressing
reason to disallow it.  Supersedes the commit in pull request #632
by RojjaCebolla.

Closes #632
This commit is contained in:
PatR
2021-12-03 04:34:08 -08:00
parent 8a6a58367a
commit dbc8a70271
4 changed files with 88 additions and 43 deletions

View File

@@ -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

View File

@@ -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 *,

View File

@@ -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 */

View File

@@ -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.