diff --git a/assets/templates/ui/skills/blessed_hammer.png b/assets/templates/ui/skills/blessed_hammer.png index 2023602..043712b 100644 Binary files a/assets/templates/ui/skills/blessed_hammer.png and b/assets/templates/ui/skills/blessed_hammer.png differ 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