From 0ef702878f3da7b1f80c19fb29463ea6637c9c18 Mon Sep 17 00:00:00 2001 From: alexpolo1 Date: Fri, 4 Sep 2026 12:23:41 +0200 Subject: [PATCH] fix(skills): correct the hammerdin binds and check Blessed Hammer where it lives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probed the live client by pressing F1-F8 and reading the right skill slot; D2R renders the key label into the icon, so the crops are self-proving. Actual binds: f2=Holy Shield, f3=Redemption, f4=a charged item skill, f5=TELEPORT, f6=Concentration, f8=an aura with no template (Conviction). f1 and f7 are UNBOUND. Against that, [hammerdin] was wrong three ways: - conviction=f5 pointed at TELEPORT, so every attack-aura cast would have teleported the character — the exact hazard the [fohdin] profile section already warns about. Conviction also does nothing for magic-damage hammers (Bug 17), so it is now unbound rather than remapped. - concentration=f8 pointed at Conviction; Concentration is on f6. The build was running with no Concentration at all — less hammer damage and no party aura for the merc, which plausibly fed the Travincal chickens. - blessed_hammer=f1 pointed at nothing. The hammer lives permanently on LEFT-click and no hotkey moves it to the right slot, so the preflight's right-slot check could never pass; it scored 44.8% all session while the hammer sat correctly on the left the whole time. The check now inspects the left slot and presses no key, matching the comment that was already above it. The stored blessed_hammer.png was a right-slot capture with "F1" baked into the image, which is why it scored 34.4% against the unlabelled left slot. Recaptured from the live left slot. Co-Authored-By: Claude Opus 5 (cherry picked from commit 4c915f17acbb7d9831e5d24907dde725a9a872e7) --- assets/templates/ui/skills/blessed_hammer.png | Bin 3459 -> 3643 bytes config/params.ini | 20 +++++++--- src/utils/skill_preflight.py | 36 +++++++++++------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/assets/templates/ui/skills/blessed_hammer.png b/assets/templates/ui/skills/blessed_hammer.png index 2023602b6b05ccf0862c08646312730f5b25b597..043712b9c805b2fb9609b18c2849086d7ae30c8f 100644 GIT binary patch literal 3643 zcmV-B4#e?^P)HT`zhv zpdcm%G85DL>f-h?wW^!JwW1Dzi5?u3uT2*N@9OCLEQRx`^5!hHx!VX%kyio&cA;?i z%%FT|?AA$cOuC0R;>&m2p{F*h05F0l>;xPEBN9RC6%fg+aJ0-?htTHC0D;Lwk;niF z#$MD+3DAIA69i16f)tp})R7(EJYKzj=esztvbDzc^u^np5Lg5omJNU~D-gkP z(rLCww=4YEuv(8;Fpk*Xn93w|>)xHK<0@w-5(D*O2vj=^!Bruq+;w8A7DJGf&mDB@vs+H%F4@O9Ao1uL1 z&cREcd;S;y?n@szwXr*!Eyd8Rj&7Eu_6({=y)xKwHgfD8|M+-ys&JbnePz=ALhXlD zz^Cfs{y`WxQ|DjWnJ(2l;+1jb0usr5p#b37P#_!l7z>pL*Vq2(_WsMi`MF7(&Q|3w z|NWOA9QsAC>ki5Dve&}#qUlx!i(YDiL<~fwb1zM13h=?oGgw&!e5xu=L@FJhD0OEJ z!I6kjG)uB*wen~Nts$8e4h-q+sD62O`r>ClH|@Hkt{eKWUBu^J_?t`XW!vSYSmAgr z`dR9_l$UCPn9Yn>)uN+bWWdyj3=5ARu8MP&OCsxR$H^9I1>zuLm!VbgU>%?XV?+XY zU|jy^{`@~a{h7OM+o`T{Hh@!Qwc9i=zW&miixj~M=YXhB>29x%P}MpE&)6G8fR&jV zipuFh#GfBl+a5;|S1G67%7VJ2h8QCruQVVqlS2cKLv*hkuD<#kpWSPkNxK4|jUo~f z0si~Vx8u>fZ_eg|Zcr9xc4aj9$G6{=Sglh(OQK*%7>P!aCXED>B6@tfF3%7As>>%l z&axtDn=vA)8Zw&NQEyujS4q#W6`y(Z*`4`p;Jqh8046@_x>fEA;=g$1n*c}v6~M|2 zi2m)@Klx|hdo3Q{`u5?fB&t1j*)jkU4naG|_;^`d9QsyqK-zX194wO*j#gQ#$cL*u zaA*M0(b>9u<6!pulh0flk0x!yh||<1)sgqJEEQ9)W&j9Kdw<+@`)&IVZ@m72(hi#* z07e8Iz^L+vt6ZbD?C@Fx~O7hu4CvW7YuDL71_NE_*fAy4m^QwN50qOd9RJycFCRCQEwy9a&f8w5&)jNwFCy?S?_C%0amtt1N&mW~3E_o$*#<)AmbR>q5iFiZW}wQ-X( zrxgJ;S$ZN<9S2r10az-OU}~rcrN@IF-dpo)hwZOEa`kjscy^u0BIm5S5o444!>$Ka zo4oVggL}e^gmomRuo3}-5!pdDyk5quCpUMO%lFk`XPHW}MYbf;18jR-WzB$WCd6o( z)dC`VhCUCY`_V#v>-yu3C?jUmMHU1&S(YX9k7jcZ_OGvhH!>x|vvnL7<=%3>RfYX| zGh$d8;uB@Oe-tOn?n4{(ew){0yw|2y6u?RlBU}g?BC16bW#X)Zik6KP(fwf7eg4U( z4^tulLKucP>(V&H(z$6$25@)B( z6)I+kB!Vi8c-To~EFI6gzGCdeh)C2_6#Jwh)k zhG6OuvKpB_d2(|$*t)!Z>eg&#rh$3Z^`2*4qKm@pGk6%Ar{pm+P-bt=}NFuAgzi%{pcd^Klzj)(YS5KYXpG_~+@Z(v= z$IEysQWD!3kLL3^7<6W%STwowW`T%8akyLtA|ztOr#8lmdgJ=(GxM(f#?Fo|C%2E5 z4L~w*U<|kv{lZ5-ZU)CG&AN2D9*F5tYQK2nmFM33o_A)`dFscc#{&3?wNVAR&oZw4 zs*zT7Z9E#s@~4vtsCYyKsF^a@0~_oAb!YPX*RG!!)W<1(b9Z-ouyb>nNuCEmZBO+iO{0}Ave|6)=$IhPo>Ak}YNQ48>gow`!>n+x0);hX*C#JSF8ZP?0 zY8uCsL{TjeZjI~A^&dU@=(l!vhB3anx6iA?*AC~1NC3SWAry{-^MfM3HJSe5(;vyI zok|w{^BXswIyro2)(8LsMzoqC8Q>FDvB6+qqcTiWZaXOnfk4EBNX??!<& z9=koC?=F|M_y6+!?;WSk&he}O_u0r-YFue0Ryz6L^NbPzrMMv_yy(Jwsq=-zTM?K&|0+B>(mpnG-ykii(BNAiv(XAxBZAVRZjz-XT6 z+}iN=bnXxl01(WK37I&W%+&B&5ig9wQPZm`BDPs0Ap)4$rLDoOTz>w_BRlh1R?DWZ z?CqD$(d}eS+hnsEp$F^?JYp*fU`!a;^{So;5S{%$Q58f40JFj|BXoT>NBBtaXG^cD z1ez3v&Mi{^krVanvqlD6zkB)NpBx2`cXmrz{BSZWh&nR{Km=kps_V&o?h#X; z>ljouOLjzRkj)T)2_50M2)XNkPz_Vx$1>v8815UyRq6slqb6#q#$Yde`m=8w9)5pv zuvx_YRr8(wyF*>vJX)RJ*gRS;7F{v}X3ANSAd4Xa7?h5(SV5owh>#Vc!>kqoV)UJA zKvP7{(h}o`f?j6b)rm!0Flul zbOzbL1N5rSJG>nHg+W;KY9>yhGa$w+_MSRGy}4BuH{N;offDu?T?>#6h%g&$6!EA{ z1~Bm6GcDSb!3uVaU}hyl+Y2)VQvm1`fsZluT=d zpQZes+HXXj8Sl;8W!Fc?J>a4jM-UMcfJZWbtTrUbfB_*RsF{gGCM4*xA;7TYET)16 zdN0YjjdDIuF)%*p{iVtSf~ehErsvMp-`Z`~qVGiL;Ih(JT&yBBfJvBdn?z8k%R9~+mnv>vVNfSOptnILIX%@grEi)aHUFsj#y2` zWsp9%VhUh@Xo?6mBLj#*uUb2*9c2Y(+*@{2v53$DKC$N15Fd{Il;<pOUC%)lQX(Gf0V45$!J8 zfhUKs?Bvqs@XmaxV0fj7Cp?`gVY_gRnj=0~_CQ!TG_}G9k>rV2V!^pS_l7u(^`c)o zKu;6^Gnkkbo&zx=sp-$uJ}z_424f}<24*84)G~se2w}SnfBn|({{fK1w@9_yaR2}S N002ovPDHLkV1j+N+8O`= delta 3457 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 diff --git a/config/params.ini b/config/params.ini index 83fdea1..cc10d1d 100644 --- a/config/params.ini +++ b/config/params.ini @@ -449,9 +449,6 @@ runs_per_repair=50 ; repair_npc: preferred repair vendor strategy. ; - a5_larzuk (recommended: stays in A5, no cross-act trip; falls back to Halbu) ; - a4_halbu (requires WP trip to A4 every repair — act desync risk if it fails) -; - in_act (never leave the current act to repair; repairs only if the act has a -; vendor, otherwise skips. Use this for routes whose town has no repair vendor, -; e.g. Travincal/A3, where any other setting means a cross-act trip every cycle) ; 2026-06-10: switched to a5_larzuk — session logs showed Halbu detection failing ; 100% (body score ~0.39) and each failed A4 trip desynced the bot's act state. repair_npc=a5_larzuk @@ -582,8 +579,21 @@ foh=f6 holy_bolt=f7 [hammerdin] -blessed_hammer=f9 -concentration=f1 +; Verified against the live client 2026-09-04 by pressing F1-F8 and reading the +; right skill slot (D2R renders the key label into the icon, so the crops prove +; themselves): f2=Holy Shield, f3=Redemption, f4=a charged item skill, +; f5=TELEPORT, f6=Concentration, f8=an aura with no template (Conviction). +; f1 and f7 are UNBOUND — pressing them changes nothing, which is why +; concentration=f1 silently ran the whole build with no aura at all. +; +; Blessed Hammer sits permanently on LEFT-click, so it needs no key: _cast_hammers +; keeps it off the F-keys deliberately, because selecting it onto the right slot +; would REPLACE the damage aura. The preflight verifies the left slot instead. +blessed_hammer= +concentration=f6 +; Leave conviction unbound. It does nothing for magic-damage hammers (Bug 17), +; and on this client f5 is TELEPORT — pointing it there teleports the character +; on every attack-aura cast. conviction= ; ========================= diff --git a/src/utils/skill_preflight.py b/src/utils/skill_preflight.py index 4483902..296eb0f 100644 --- a/src/utils/skill_preflight.py +++ b/src/utils/skill_preflight.py @@ -17,6 +17,10 @@ class SkillCheck: template: str side: str required: bool + # True when the skill sits permanently on its slot and no hotkey selects it + # (Blessed Hammer on left-click). Such a check inspects the slot as-is, so a + # blank hotkey is correct rather than a misconfiguration. + permanent: bool = False SORC_TEMPLATE_ALIASES = { @@ -61,17 +65,18 @@ def get_build_skill_checks(config_instance: Config, char_type: str | None = None if char_type in ("hammerdin", "fohdin"): build_cfg = getattr(config_instance, char_type, {}) - # blessed hammer lives on left-click; auras/teleport go to right - # hotkeys select onto the RIGHT slot; left holds the hammer permanently. - # NOT required: having no hotkey is the CORRECT configuration. _cast_hammers - # deliberately keeps the hammer off the F-keys — pressing one would select it - # onto the right slot and REPLACE the damage aura. Marking this required made - # every startup log "blessed_hammer has no configured hotkey" and advise running - # set_binds_from_params.py, which would bind it and break the aura. If a hotkey - # IS configured it is still visually validated below. - checks.append(SkillCheck(char_type, "blessed_hammer", - _configured_hotkey(build_cfg, "blessed_hammer"), - "BLESSED_HAMMER", "right", False)) + # Blessed Hammer lives permanently on LEFT-click. No hotkey moves it there — + # F-keys select onto the RIGHT slot only — so the old right-slot check could + # never pass: it scored 44.8% all session while the hammer sat correctly on the + # left the whole time. Verify the LEFT slot instead, pressing nothing. + # + # Deliberately NOT required, and deliberately hotkey-less: _cast_hammers keeps + # the hammer off the F-keys because pressing one would select it onto the right + # slot and REPLACE the damage aura. Marking it required made startup advise + # running set_binds_from_params.py, which would bind it and break exactly that. + # A stale icon template should report a score, not block the bot. + checks.append(SkillCheck(char_type, "blessed_hammer", "", + "BLESSED_HAMMER", "left", False, permanent=True)) checks.append(SkillCheck(char_type, "concentration", _configured_hotkey(build_cfg, "concentration"), "CONCENTRATION", "right", True)) @@ -127,8 +132,11 @@ def _check_skill_icon(check: SkillCheck, threshold: float = 0.84) -> tuple[bool return True, check.template, 1.0 return None, check.template, -1.0 - keyboard.send(check.hotkey) - wait(0.15, 0.25) + # A skill that lives permanently on a slot (Blessed Hammer on left-click) has no + # hotkey to press — inspect the slot as-is rather than sending an empty keystroke. + if check.hotkey: + keyboard.send(check.hotkey) + wait(0.15, 0.25) roi = _skill_roi(check.side) match = template_finder.search(template_name, grab(force_new=True), threshold=threshold, roi=roi) return match.valid, template_name, match.score @@ -164,7 +172,7 @@ def validate_build_skill_icons(config_instance: Config, char_type: str | None = errors = [] for check in checks: - if not check.hotkey: + if not check.hotkey and not check.permanent: if check.required: errors.append(f"{check.skill} has no configured hotkey") continue