CRITICAL loot-loss bug: buy_consumables failure called end_game and
returned BEFORE the stash step, so Pindle runes/items piled in inventory
and were never banked (zero stash events across a whole session). Buying
pots is optional (belt refills from drops); stashing loot is the point.
Now warn + re-anchor + fall through to stash. Also raise
max_maintenance_time_s 120->240 so the A5 vendor thrash completes and
reaches stash instead of timing out first (transitional — disappears once
a Diablo run shifts spawns to A4).
Co-Authored-By: Claude Fable 5 <[email protected]>
A5-spawn games burn on the Malah vendor step (stale patch templates);
A4-spawn games sail through Jamella. The Diablo run ends with a TP to
A4 town, so running it LAST makes every next game spawn in A4.
Co-Authored-By: Claude Fable 5 <[email protected]>
A full CS clear takes ~10 min; the 600s watchdog force-quit a game that
had all three seals open and was waiting for Diablo to spawn.
Co-Authored-By: Claude Fable 5 <[email protected]>
Root causes from the 2026-06-09 session (51 games, 45 failed):
- Act desync: add TownManager.detect_current_act() and
Bot._verify_town_location(); every maintenance/end-run retry and
fallback now verifies the physical act instead of hardcoding town
starts. open_wp/go_to_act self-heal act mismatches. Never run A1
pathing when travel to A1 failed.
- NPC dialogue: poll action buttons up to 2.5s after click instead of
a single-frame check (premature retry click was closing the dialog).
- Stats integrity: log_end_game skips duplicate calls (phantom 0s
"successful" games were resetting the consecutive-fail breaker);
clear stale failure reason at game start; set chicken flag before
bot.stop() so chickens are no longer labeled "Bot stopped".
- Repair: prefer in-act Larzuk over cross-act Halbu trip (Halbu
detection failed 100% last session and desynced the act state).
Documented as Bugs 9-12 in CLAUDE.md.
Co-Authored-By: Claude Fable 5 <[email protected]>
NPC body templates find Akara/Malah correctly but the name tag templates
score ~0.28, below the 0.35 threshold, causing 20s spinning when the NPC
is detected but the hover confirmation never passes.
Three targeted changes in open_npc_menu():
- Use a 240x140px ROI directly above the hover cursor for name tag search
instead of the full screen, containing false-positive risk
- Lower name tag threshold from 0.35 to 0.26 so Akara at ~0.28 now passes
- Check ScreenObjects.NPCDialogue after click as primary confirmation;
this is UI-state based and immune to stale template images
- Reduce per-NPC search timeout from 20s to 8s for faster failure recovery
Also lower max_maintenance_time_s from 120 to 60 to cap total stuck time.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Reliability
- All run approach() methods now set approach_fail_step before every return False;
bot.py _run_wrapper reads it and includes [step: X] in Discord/log failure reasons
- on_maintenance() sets _maintenance_step before each town step (heal, identify,
buy_consumables, stash_items, repair, resurrect_merc, gamble) for the same coverage
- on_init() logs startup line: char=X | difficulty=Y | routes=[...] each game
Per-run disable (bot runs as long as possible)
- GameStats tracks per-run failure counts across Bot instances (previously reset each game)
- After disable_run_after_failures consecutive failures a run is disabled for the session;
game-level consecutive-fail counter resets so the bot continues on remaining routes
- game_controller no longer quits on max_consecutive_fails if active routes remain;
only exits when all routes are disabled
Maintenance timeout (params.ini: max_maintenance_time_s=120)
- Hard 120 s wall on the entire town maintenance loop; checked between each major step
and before every retry (buy_consumables, stash, repair, resurrect_merc, gamble)
- On timeout: error screenshot + Discord, then trigger end_game → save-and-exit → rejoin
Bug fixes
- Win11 mouse overshoot: mouse_move() uses SetCursorPos + zero-delta MOUSEEVENTF_MOVE
- _curr_loc = True propagation: TownManager.identify() now returns the act Location enum
- DAMAGED KeyError in pickit: added ItemQualityKeyword.Damaged.value to NTIP_ALIAS_QUALITY_MAP
- A4 WP interaction range: force-move character to WP stone before select_by_template
- NPC click blocked by equipped-area guard: open_npc_menu() closes inventory if open
Docs
- Added CLAUDE.md: AI working guide with step tables, bug history, debugging tips
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds Messenger.send_error across the Discord (red embed with screenshot attached)
and generic (text-only) APIs. Bot._save_error_screenshot now also pushes the
failure to the configured messenger after saving the screenshot to disk, so each
approach/battle/exception failure is reviewable in Discord with the visual.
Gated by new config discord_log_errors ([general], default 1) and the
[discord_events] error toggle. Both optional/backward-compatible. Verified wiring
and the suppression path via a stubbed messenger.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Recovery: each run now has its own consecutive-failure counter. After
disable_run_after_failures (default 5) consecutive failures, that single run is
disabled for the rest of the session and the bot keeps doing the other runs
instead of stopping. A success resets the counter. If every run is disabled the
bot stops for investigation. Disable is in-memory only (restart re-enables).
Diagnostics: on every run failure (approach, battle, or exception) the bot saves
a timestamped screenshot to log/screenshots/error/ named with the run, reason,
and game/run counters so logs and visuals can be cross-referenced. Gated by the
new error_screenshots config (falls back to info_screenshots). The error/ dir is
routed through log rotation.
Adds config keys error_screenshots and disable_run_after_failures (both optional,
backward compatible) and docs/recovery_and_error_logging.md. Verified bot startup,
config parsing, Bot construction, the disable/reset logic, and screenshot writing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Implementation of centralized cooperative shutdown mechanism (C10) to replace dangerous thread killing.
- Consolidation of all timing signatures through jittered wait() for anti-cheat stealth (C1).
- Enhanced HealthManager reactivity with smart mana potion fallback and premature panel-closing protection.
- Refactored waypoint stealth logic to simulate human-like mis-aim instead of area-breaking misclicks.
- Implementation of stateless 'garbage item' filtering in PickIt to eliminate ghost item loops and OCR artifacts.
- Optimization of inventory management to automatically stash protected items that cannot be sold.
- Suppression of individual game failure notifications on Discord; alerts now trigger on 5+ consecutive fails.
- Implementation of centralized cooperative shutdown mechanism (C10) to replace dangerous thread killing.
- Consolidation of all timing signatures through jittered wait() for anti-cheat stealth (C1).
- Enhanced HealthManager reactivity with smart mana potion fallback and premature panel-closing protection.
- Refactored waypoint stealth logic to simulate human-like mis-aim instead of area-breaking misclicks.
- Implementation of stateless 'garbage item' filtering in PickIt to eliminate ghost item loops and OCR artifacts.
- Optimization of inventory management to automatically stash protected items that cannot be sold.
- Suppression of individual game failure notifications on Discord; alerts now trigger on 5+ consecutive fails.
Alex's value (2000) preserved over FiskenPoul's (200) for transmute cadence.
stash_destination=0,1,2,3 accepted from remote (matches fill_shared_stash_first=0).
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The FiskenPoul merge (95f520e) resolved conflicts by dropping three comment
blocks that documented Hammerdin-specific tuning guidance:
- Hammerdin difficulty guide near the 'difficulty' setting (Normal/NM/Hell
gear targets, chicken-rate advice)
- Hammerdin attack-length reference table near atk_len_* values (per-boss
HP ranges, Conviction interaction, CS seal boss breakdown)
- auto_downgrade_threshold parameter and its description (the feature is
parsed by config.py but not yet active; restored with a note)
No logic changes — comments and one dormant config key only.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- i_char.py: rewrite _pre_buff_cta() to verify each weapon switch via BC
skill-bar template; detects wrong slot on game start (leftover from
interrupted buff cycle), retries failed switches once, logs clearly
when stuck on CTA slot to prevent dying with wrong weapon in combat
- personal.py: protect needed consumables from sell/drop in inspect_items;
check get_needs() before marking a pot for discard — if the belt needs
that pot type, skip it so fill_up_belt_from_inventory can restock later
- bot.py: add fill_up_belt_from_inventory + update_pot_needs after
buy_consumables so inventory pots reach the belt even when out of gold;
add town-heal loop at start of on_maintenance to drink health/rejuv pots
until HP >= 95% before the next run (health manager is paused in town)
- game_stats.py: add rolling 10-game pickup health check; warns in log and
sends Discord alert when zero item pickups occur across 10 games while
chickens or merc deaths are present
- params.ini: fix show_belt=n -> show_belt=k (belt key was wrong, causing
1.5s wasted recovery attempt every first game in a session)
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Updated bot behavior to end the game when no TP charges are left, instead of attempting to walk back to town.
- Refined item processing logic in `processing_helpers.py` to use identity checks for item comparisons and improved item removal methods.
- Enhanced game stats logging to include failure reasons and send Discord notifications for failed games.
- Improved health management logic to trigger chicken behavior based on health percentage after rapid rejuvination.
- Added error handling for item pickup failures in `pickit.py` to blacklist items that cannot be picked up.
- Introduced walking fallback nodes for Countess, Andariel, Mephisto, and Baal runs in `pather.py`.
- Implemented path data validation in run scripts for Countess, Andariel, Mephisto, and Baal to ensure paths are correctly defined.
- Enhanced item identification and stash processes in `diablo.py` with retry logic for failures.
- Updated town management to handle identification failures gracefully and log appropriate warnings.
- Improved character selection logic to provide clearer logging messages.
- Modified game restart logic to launch the game via PowerShell for better session handling.
- Added documentation for recording paths for the Countess run and created a PowerShell script for scheduled task management.
- BC and BO cast with left-click (targets self) instead of right-click
- Swap-back loop simplified to 2 attempts with match score logging
- Added full test route order: trav, pindle, eldritch_shenk, nihlathak, arcane, diablo, vizier
- All retry paths (stash, buy_consumables, heal, repair) now use known-good
location constants instead of corrupted self._curr_loc (False)
- Guard gamble->stash chain against False location
- Guard wait_for_tp against False location
- Add run_trav to route order (travincal then pindle)
- Fix merc icon detection: expanded ROI from 40x40 to 60x60 (matchTemplate requires search area > template)
- Add MercPanelText screen object using mercenary_screen.png for reliable merc alive check
- Bot merc check now uses MercPanelText instead of generic LeftPanel
- Diablo: wait for boss to spawn before attacking (up to 15s), use conviction aura
- Diablo/Pindle attack lengths increased (diablo 3->10s, pindle 3->8s)
- Aura key switch delay increased from 50-100ms to 200-300ms for reliable activation
- Add conviction hotkey (F5) to hammerdin config
- Move Pindle CTA pre-buff from town to after entering red portal
- More aggressive healing thresholds (health potion at 98%, rejuv at 85%)
- Max game length increased from 160s to 600s for full Diablo runs
- Crop A5_RED_PORTAL template to remove empty space (center now aligns with portal)
- Fix merc_icon ROI from 10,9,50,50 to 15,14,40,40 (matches 40x40 merc templates)
- Lower MercIcon threshold from 0.9 to 0.85, add MERC_A2_2 variant
- Add merc panel confirmation with 'O' key before resurrect (prevents false resurrect when icon detection fails)
- Override capabilities to can_teleport_natively
- src/inventory/vendor.py: Log screenshots when NotEnoughGold detected
during gambling, purchasing, and repairing
- src/ui_manager.py: Update NotEnoughGold to use new MISSING_GOLD template
from assets/npc/missing_gold/ (full dialog match instead of color-based)
- assets/npc/missing_gold/missing_gold.png: New template for the popup
- src/screen.py: Add force param to find_and_set_window_position/set_window_position
to bypass early-return guard and skip wait when called programmatically
- src/run/pindle.py: Force window re-detection before each traverse_nodes and
select_by_template call to handle offset drift between runs
- src/pather.py: Add fallback full-image search with lower threshold (0.55) when
cropped ROI search fails in find_abs_node_pos
- src/bot.py: Move D2R window to stable position (0,0) at game start via
move_d2r_window; skip merc resurrect after first failure (no gold)
- src/game_stats.py: Add _merc_resurrect_failed flag (reset each game)
- src/utils/misc.py: Add move_d2r_window() function
- config/params.ini: Updated teleport=b, show_belt=k, override_capabilities,
restore_settings_from_backup_key=insert, graphic_debugger_key=delete
- Add utils/log_rotation.py: safe_imwrite() auto-rotates screenshot dirs
when they exceed configured file count or size limits
- Add [log_rotation] config section in params.ini (pickit/info/items
max_files, max_mb, discord_notify_rotation)
- Replace 40 cv2.imwrite() calls with safe_imwrite() across 13 files
- Add failure reason tracking: bot.py catches run exceptions, stores
reason in game_stats, Discord message includes the error
- Add Discord notification when log rotation deletes old files
- Prevents disk-full crashes that stopped botty on May 30
- config/params.ini: show_belt k->n matches the actual D2R keybind so
the bot can now correctly read and manage belt potion inventory.
pickit_screenshots=1 enables per-run loot scan evidence.
- blizz_sorc.py: kill_pindle() now waits 1.5-2.0s (was ~0.33s) after
the attack loop before teleporting to the loot area. Blizzard has an
~1.8s fall duration, so the old wait left enemies alive when the sorc
arrived. Also adds _cast_static() at the start to reduce pack HP
immediately.
- pickit.py: rename loop var i->ground_item (fixes potential NameError
when items list is empty) and improve tele-fail warning message.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Auto-launch of D2R and bnet credential injection are now gated behind
an explicit `auto_login=0` flag in params.ini. Setting it to 1 restores
the previous behaviour. Credentials stored in bnet_name/bnet_pass are
never appended to launch options while auto_login=0.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds a tkinter run-selector window accessible from the botty console via
the new select_runs_key hotkey (default F6). Users can check/uncheck any
of the 11 supported boss/farm runs; the selection updates Config in-memory
and takes effect on the next game without restarting botty.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- README: add step-by-step Installation section (Miniforge → download
ZIP → install.bat → config → run_botty.bat) so a non-technical user
can follow it without reading development.md
- config/params.ini: reset personal fields (name, char_name,
saved_games_folder) to generic defaults so the downloaded zip
works out of the box for anyone
- install.bat: apply os.add_dll_directory before the tesserocr smoke
test so it stops emitting a false warning on every install
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>