Skill hotkeys select the RIGHT slot - fix cast flow, prove auto-binding live

Live finding (fresh game, 2026-06-12): D2R skill hotkeys select onto the
RIGHT skill slot, not the left as the old comments assumed. The left slot
permanently holds Blessed Hammer. Consequence: _cast_hammers pressing the
hammer hotkey after activating an aura was REPLACING the aura on the right
slot every cast cycle - the true root cause of fights running without
Concentration. _cast_hammers no longer touches the hammer hotkey: select
aura (lands on right, stays active), hold stand-still, spam left-click.
Verified live: full Diablo kill at 09:04, ~55s from last seal to kill.

Auto skill binding proven end-to-end (tools/set_binds_from_params.py):
blessed_hammer/concentration/redemption/vigor/holy_shield/teleport all
bound via the in-game picker and visually verified (6/7 OK; conviction
correctly reported missing - not skilled on this char).

- capture tool + preflight verify now watch the RIGHT slot
- fresh skill slot templates + clean PICKER_* cell templates captured at
  current settings (blessed_hammer, concentration, redemption, vigor,
  holy_shield, teleport)
- removed bogus conviction.png (had captured vigor's icon)

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
alexpolo1
2026-06-12 09:13:52 +02:00
co-authored by Claude Fable 5
parent 1b4773b42e
commit 40e4dd8a4f
13 changed files with 24 additions and 25 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

+8 -16
View File
@@ -35,31 +35,23 @@ class Hammerdin(Paladin):
else:
aura = None
# D2R: hotkey always puts skill on LEFT click.
# Right-clicking with it selected moves it to RIGHT + activates.
# Sequence: press aura (left) -> right-click (moves to right, activates)
# press hammer (left) -> left-click spam.
# Skip re-selection when the slot already holds the wanted skill
# (tracked in _active_skill; pre_move/_weapon_switch invalidate it) —
# consecutive casts then just hold the aura and spam left-click.
# D2R (verified live 2026-06-12): skill hotkeys select onto the RIGHT
# slot. The LEFT slot permanently holds Blessed Hammer (set once in the
# left-button menu) — pressing the hammer hotkey here would put the
# hammer on the RIGHT slot and REPLACE the just-activated aura, which is
# exactly why fights used to run without Concentration. So: select the
# aura (lands on right, activates), then pure left-click spam.
if aura and self._active_skill["right"] != aura:
keyboard.send(self._skill_hotkeys[aura]) # aura goes to left-click
keyboard.send(self._skill_hotkeys[aura]) # aura -> right slot
wait(0.15, 0.2)
mouse.click(button="right") # moves aura to right-click + activates it
mouse.click(button="right") # ensure the aura is the active right skill
self._set_active_skill("right", aura)
self._set_active_skill("left", "")
wait(0.15, 0.2)
# Hold stand_still so we don't walk while attacking
keyboard.send(Config().char["stand_still"], do_release=False)
wait(0.05, 0.1)
# Set Blessed Hammer on LEFT click
if self._skill_hotkeys["blessed_hammer"] and self._active_skill["left"] != "blessed_hammer":
keyboard.send(self._skill_hotkeys["blessed_hammer"])
self._set_active_skill("left", "blessed_hammer")
wait(0.05, 0.1)
# Spam left-clicks to cast hammers (aura stays active on right-click)
start = time.time()
while (time.time() - start) < time_in_s:
+11 -7
View File
@@ -60,20 +60,24 @@ def get_build_skill_checks(config_instance: Config, char_type: str | None = None
checks = []
if char_type == "hammerdin":
char_cfg = config_instance.char
build_cfg = getattr(config_instance, "hammerdin", {})
# blessed hammer lives on left-click; auras/teleport go to right
# hotkeys select onto the RIGHT slot; left holds the hammer permanently
checks.append(SkillCheck(char_type, "blessed_hammer",
_configured_hotkey(char_cfg, "blessed_hammer"),
"BLESSED_HAMMER", "left", True))
_configured_hotkey(build_cfg, "blessed_hammer"),
"BLESSED_HAMMER", "right", True))
checks.append(SkillCheck(char_type, "concentration",
_configured_hotkey(char_cfg, "concentration"),
_configured_hotkey(build_cfg, "concentration"),
"CONCENTRATION", "right", True))
for skill, template in [("redemption", "REDEMPTION"), ("vigor", "VIGOR"),
("conviction", "CONVICTION"), ("holy_shield", "HOLY_SHIELD"),
("teleport", "TELE_ACTIVE")]:
hotkey = _configured_hotkey(char_cfg, skill)
("conviction", "CONVICTION"), ("holy_shield", "HOLY_SHIELD")]:
hotkey = _configured_hotkey(build_cfg, skill)
if hotkey:
checks.append(SkillCheck(char_type, skill, hotkey, template, "right", False))
# teleport key sits in [char]
tp_key = _configured_hotkey(config_instance.char, "teleport")
if tp_key:
checks.append(SkillCheck(char_type, "teleport", tp_key, "TELE_ACTIVE", "right", False))
return checks
if char_type != "blizz_sorc":
+5 -2
View File
@@ -48,12 +48,15 @@ def main():
start_detecting_window()
wait(0.5)
roi = Config().ui_roi["skill_left"]
# hotkeys select onto the RIGHT slot (verified live 2026-06-12)
roi = Config().ui_roi["skill_right"]
results = []
prev_icon = None
try:
build_cfg = getattr(Config(), Config().char.get("type", ""), {})
for skill in SKILLS:
hotkey = Config().char.get(skill)
# build skills live in the [hammerdin]-style section; teleport in [char]
hotkey = build_cfg.get(skill) or Config().char.get(skill)
if not hotkey:
results.append((skill, "-", "no hotkey configured"))
continue