4.9 KiB
GEMS-tab transmute flow (rewritten in commit 48d8445)
src/transmute/transmute.py — Transmute.convert_all_gems() and helpers.
What changed and why
The old flow drove gem upgrades through the Horadric Cube UI: it required the cube to live in
the PERSONAL stash tab, opened the cube, ctrl+shift+right-clicked 3 gems into it, clicked the cube's
Transmute button, then pulled the result back out. This was fragile — it depended on cube placement,
cube-open detection (ScreenObjects.CubeOpened via wait_until_visible), and a pre-flight
"empty the cube" pass.
The rewrite uses D2R's native GEMS-tab convert panel instead of the cube. Gems are moved directly between the GEMS stack grid and the convert panel, and conversion uses the GEMS tab's own convert button. The cube is no longer required to be in the stash and, if present in the character inventory, is left untouched.
Fixed stack-coordinate grid
GEMS_TAB_STACK_COORDS is a new module-level dict mapping every gem template
(INVENTORY_<FAMILY>_<TIER>) to a fixed (x, y) screen coordinate at 1280×720. The grid is:
- Columns by family (x): diamond 85, emerald 132, ruby 180, topaz 227, amethyst 275, sapphire 322, skull 370.
- Rows by tier (y): chipped 154, flawed 195, standard 234, flawless 273, perfect 311.
Because gem stacks always render at these fixed slots on the GEMS tab, the flow can click them by
coordinate instead of template-searching for them each time. _gems_stack_monitor_for(template)
converts the screen coord to monitor coords (returns None if the template isn't in the grid, so
the caller can fall back to a template search).
Panel constants (1280×720)
| Constant | Value | Meaning |
|---|---|---|
GEMS_TAB_X |
223 |
GEMS tab click X (was 200) |
GEMS_CONVERT_BUTTON |
(225, 500) |
native convert button in the GEMS panel |
GEMS_CONVERT_PANEL_ROI |
(160, 296, 128, 160) |
ROI to search for the converted result |
GEMS_CONVERT_FIRST_SLOT |
(181, 318) |
fallback slot to grab the result if the search misses |
Per-transmute flow (new)
For each gem type/tier with enough gems to convert:
- Switch to GEMS tab.
- Load 3 gems —
_ctrl_shift_left_click_monitorthe stack slot ×3 to move 3 gems into the convert panel. If exactly 3 weren't loaded, abort that batch. - Convert —
_click_gems_tab_convert_button()clicks the panel's native convert button. - Recover the result — template-search
gems_outwithinGEMS_CONVERT_PANEL_ROI; if found, ctrl+shift+left-click it back onto the GEMS tab. If not found, fall back to ctrl+shift+left-click onGEMS_CONVERT_FIRST_SLOT(instead of the old behavior of clearing the cube and bailing). - Leave the inventory cube untouched and return to the GEMS tab for the next iteration.
The old pre-flight "open cube → empty 12 cube slots → reopen stash" pass and the per-iteration "close cube / reopen stash" steps are removed.
Helper changes
_open_cube_from_stash()/_open_cube_from_gems_tab()/_open_available_cube_for_gems_tab()now operate from the GEMS tab rather than PERSONAL, and can open the cube from character inventory (_open_cube_from_inventory) when the GEMS tab is active. These remain for older inventory flows; the new gem flow doesn't need them._locate_cube()can now return'opened'(cube UI already open on the GEMS tab) in addition to'stash'/'inventory'._ensure_cube_available()now treatsinventoryas usable ("it will stay there") instead of an error — the cube no longer has to be moved to PERSONAL._ensure_cube_in_stash()dropped the loot-column safety guard: it now ctrl+clicks any positiveCubeInventorymatch, so the cube can be moved from reserved inventory columns too.- Keyboard modifier handling switched from the low-level
win_input.key_down/key_up(_get_vk(...))tokeyboard.send(..., do_release=False)/keyboard.release(...)wrapped intry/finally, so ctrl+shift are always released even if the click raises. Applies to both_ctrl_shift_click_monitor(right) and_ctrl_shift_left_click_monitor(left). _empty_cube_to_gems_tab()now uses_ctrl_click_monitor(plain ctrl) rather than ctrl+shift.
Gem counting (_count_gems_by_ocr)
Counting no longer template-searches the left-inventory ROI for each gem. It now reads each fixed
slot from GEMS_TAB_STACK_COORDS: crop the slot icon, and skip empty slots by checking the
95th-percentile grayscale brightness (< 45 ⇒ no gem present). Occupied slots still OCR the count
badge in the bottom-right quadrant. This is faster and immune to template-match drift, at the cost
of depending on the fixed 1280×720 grid.
Resolution assumption
The whole flow is hard-coded to 1280×720. All coordinates above are screen coordinates at that
resolution, converted to monitor coordinates at click time via convert_screen_to_monitor.