From e11c136bc9d5e254ab97fa533d32cc8f25c4e53e Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Mon, 23 Feb 2009 01:20:32 +0000 Subject: [PATCH] fix #H1816 - polearm/grapnel/whip fixes (trunk only) From a bug report, applying a polearm to make a short-range ranged attack didn't scuff any engraving you were standing on, unlike ordinary melee and throwing/shooting attacks. Grappling hooks had the same omission. Fixing it led to several other minor bugs. Attempting to target an unseen monster's spot with polearm or grapnel would yield some permutation of "wait, there's something there" but draw the 'I' glyph at the wrong spot. It used instead of the actual target, so put the 'I' one step in front of your most recent move (or throw or zap or whatever last set u.dx and u.dy). Giving ESC when prompted for target spot failed to use up a turn even when the polearm or grappling hook had just been auto-wielded for use. Neither use_pole() nor use_grapple() set `notonhead' for hmon() (called via thitmonst() in their cases; this was academic since plain physical damage attacks don't actually care about it). [The bad 'I' placement was a post-3.4.3 bug.] Applying a bullwhip to attack an adjacent monster didn't have any of those issues but did have the possibility of targetting off the edge of the map when standing at that edge while confused or stunned. Applying a polearm to target an 'I' would yield "nothing happens" if the unseen monster wasn't there anymore, and it didn't bother to remove that 'I' from the map. After changing it to do so, the phrasing no longer made any sense. This led to a slightly bigger change than I intended: since statues are now shown as gray monsters (does that work for tiles?) instead of as chunks of stone, they are likely to be intentional targets sometimes, so polearm attacks now handle them differently from other non-monster locations. [I hope that other attack forms don't need similar handling. Melee certainly doesn't, since walking onto the spot is enough to distinguish statues from monsters. Having the missile pass right through a statue's location probably suffices for ranged attacks.] --- doc/fixes35.0 | 4 +++ src/apply.c | 95 ++++++++++++++++++++++++++++++++++++++++----------- src/uhitm.c | 20 ++++++----- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 4021ae6be..d6e80d968 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -310,6 +310,10 @@ poly'd or mimicking hero who was hidden from monsters would still be treated as a normal target for their ranged attacks hero would remain stuck to an adjacent monster after rehumanizing if he had been attacked while hiding via #monster when poly'd into a small mimic +attacking via applied polearm never scuffed engraving underneath hero +auto-wielding a polearm took no time if ESC was used to cancel target choice +applying a bullwhip while at very edge of map could target beyond edge when + hero was stunned or confused, potentially leading to a panic or crash Platform- and/or Interface-Specific Fixes diff --git a/src/apply.c b/src/apply.c index 20f735094..0f874899e 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)apply.c 3.5 2008/10/14 */ +/* SCCS Id: @(#)apply.c 3.5 2009/02/21 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2328,10 +2328,20 @@ struct obj *obj; } if (!getdir((char *)0)) return res; - if (Stunned || (Confusion && !rn2(5))) confdir(); - rx = u.ux + u.dx; - ry = u.uy + u.dy; - mtmp = m_at(rx, ry); + if (u.uswallow) { + mtmp = u.ustuck; + rx = mtmp->mx; + ry = mtmp->my; + } else { + if (Stunned || (Confusion && !rn2(5))) confdir(); + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if (!isok(rx, ry)) { + You("miss."); + return res; + } + mtmp = m_at(rx, ry); + } /* fake some proficiency checks */ proficient = 0; @@ -2571,11 +2581,10 @@ STATIC_OVL int use_pole(obj) struct obj *obj; { - int res = 0, typ, max_range = 4, min_range = 4; + int res = 0, typ, max_range, min_range, glyph; coord cc; struct monst *mtmp; - /* Are you allowed to use the pole? */ if (u.uswallow) { pline(not_enough_room); @@ -2592,13 +2601,29 @@ use_pole(obj) cc.x = u.ux; cc.y = u.uy; if (getpos(&cc, TRUE, "the spot to hit") < 0) - return 0; /* user pressed ESC */ + return res; /* ESC; uses turn iff polearm became wielded */ - /* Calculate range */ + glyph = glyph_at(cc.x, cc.y); + /* + * Calculate allowable range (pole's reach is always 2 steps): + * unskilled and basic: orthogonal direction, 4..4; + * skilled: as basic, plus knight's jump position, 4..5; + * expert: as skilled, plus diagonal, 4..8. + * ...9... + * .85458. + * .52125. + * 9410149 + * .52125. + * .85458. + * ...9... + * (Note: no roles in nethack can become expert or better + * for polearm skill; Yeoman in slash'em can become expert.) + */ + min_range = 4; typ = uwep_skill_type(); if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) max_range = 4; else if (P_SKILL(typ) == P_SKILLED) max_range = 5; - else max_range = 8; + else max_range = 8; /* (P_SKILL(typ) >= P_EXPERT) */ if (distu(cc.x, cc.y) > max_range) { pline("Too far!"); return (res); @@ -2606,8 +2631,9 @@ use_pole(obj) pline("Too close!"); return (res); } else if (!cansee(cc.x, cc.y) && - ((mtmp = m_at(cc.x, cc.y)) == (struct monst *)0 || - !canseemon(mtmp))) { + !glyph_is_monster(glyph) && + !glyph_is_invisible(glyph) && + !glyph_is_statue(glyph)) { You(cant_see_spot); return (res); } else if (!couldsee(cc.x, cc.y)) { /* Eyes of the Overworld */ @@ -2616,14 +2642,38 @@ use_pole(obj) } /* Attack the monster there */ - if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *)0) { - bhitpos = cc; + bhitpos = cc; + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != (struct monst *)0) { if (attack_checks(mtmp, uwep)) return res; check_caitiff(mtmp); + notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my); (void) thitmonst(mtmp, uwep); - } else - /* Now you know that nothing is there... */ - pline(nothing_happens); + } else if (glyph_is_statue(glyph) && /* might be hallucinatory */ + sobj_at(STATUE, bhitpos.x, bhitpos.y)) { + struct trap *t = t_at(bhitpos.x, bhitpos.y); + + if (t && t->ttyp == STATUE_TRAP && + activate_statue_trap(t, t->tx, t->ty, FALSE)) { + ; /* feedback has been give by animate_statue() */ + } else { + /* Since statues look like monsters now, we say something + different from "you miss" or "there's nobody there". + Note: we only do this when a statue is displayed here, + because the player is probably attempting to attack it; + other statues obscured by anything are just ignored. */ + pline("Thump! Your blow bounces harmlessly off the statue."); + wake_nearto(bhitpos.x, bhitpos.y, 25); + } + } else { + /* no monster here and no statue seen or remembered here */ + if (glyph_is_invisible(glyph)) { + /* now you know that nothing is there... */ + unmap_object(bhitpos.x, bhitpos.y); + newsym(bhitpos.x, bhitpos.y); + } + You("miss; there is no one there to hit."); + } + u_wipe_engr(2); /* same as for melee or throwing */ return (1); } @@ -2667,7 +2717,7 @@ struct obj *obj; } STATIC_OVL int -use_grapple (obj) +use_grapple(obj) struct obj *obj; { int res = 0, typ, max_range = 4, tohit; @@ -2692,9 +2742,9 @@ use_grapple (obj) cc.x = u.ux; cc.y = u.uy; if (getpos(&cc, TRUE, "the spot to hit") < 0) - return 0; /* user pressed ESC */ + return res; /* ESC; uses turn iff grapnel became wielded */ - /* Calculate range */ + /* Calculate range; unlike use_pole(), there's no minimum for range */ typ = uwep_skill_type(); if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) max_range = 4; else if (P_SKILL(typ) == P_SKILLED) max_range = 5; @@ -2741,6 +2791,10 @@ use_grapple (obj) destroy_nhwindow(tmpwin); } + /* possibly scuff engraving at your feet; + any engraving at the target location is unaffected */ + if (tohit == 2 || !rn2(2)) u_wipe_engr(rnd(2)); + /* What did you hit? */ switch (tohit) { case 0: /* Trap */ @@ -2758,6 +2812,7 @@ use_grapple (obj) case 2: /* Monster */ bhitpos = cc; if ((mtmp = m_at(cc.x, cc.y)) == (struct monst *)0) break; + notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my); save_confirm = flags.confirm; if (verysmall(mtmp->data) && !rn2(4) && enexto(&cc, u.ux, u.uy, (struct permonst *)0)) { diff --git a/src/uhitm.c b/src/uhitm.c index 1bcab9479..8bccd02fd 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)uhitm.c 3.5 2007/12/19 */ +/* SCCS Id: @(#)uhitm.c 3.5 2009/02/21 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -116,8 +116,8 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */ * not stay there, so the player will have suddenly forgotten * the square's contents for no apparent reason. if (!canspotmon(mtmp) && - !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph)) - map_invisible(u.ux+u.dx, u.uy+u.dy); + !glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) + map_invisible(bhitpos.x, bhitpos.y); */ return FALSE; } @@ -131,12 +131,12 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */ * the screen, so you know something is there. */ if (!canspotmon(mtmp) && - !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && - !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) && + !glyph_is_warning(glyph_at(bhitpos.x,bhitpos.y)) && + !glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph) && !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) { pline("Wait! There's %s there you can't see!", something); - map_invisible(u.ux+u.dx, u.uy+u.dy); + map_invisible(bhitpos.x, bhitpos.y); /* if it was an invisible mimic, treat it as if we stumbled * onto a visible mimic */ @@ -152,7 +152,7 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */ if (mtmp->m_ap_type && !Protection_from_shape_changers && !sensemon(mtmp) && - !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy))) { + !glyph_is_warning(glyph_at(bhitpos.x,bhitpos.y))) { /* If a hidden mimic was in a square where a player remembers * some (probably different) unseen monster, the player is in * luck--he attacks it even though it's hidden. @@ -166,7 +166,7 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */ } if (mtmp->mundetected && !canseemon(mtmp) && - !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && + !glyph_is_warning(glyph_at(bhitpos.x,bhitpos.y)) && (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { mtmp->mundetected = mtmp->msleeping = 0; newsym(mtmp->mx, mtmp->my); @@ -360,6 +360,10 @@ register struct monst *mtmp; /* possibly set in attack_checks; examined in known_hitum, called via hitum or hmonas below */ override_confirmation = FALSE; + /* attack_checks() used to use directly, now + it uses bhitpos instead; it might map an invisible monster there */ + bhitpos.x = u.ux + u.dx; + bhitpos.y = u.uy + u.dy; if (attack_checks(mtmp, uwep)) return(TRUE); if (Upolyd) {