- 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.
15 KiB
Botty D2R - Improvement Ideas
Generated: 2026-05-20 | Status: Partially implemented Based on analysis of ~100+ source files across src/, test/, config/, and assets/.
Character: FOH Paladin | Priority: Anti-cheat stealth > everything else
DONE
- C8 Bug: personal.py
open()->open_inventory()(fixed) - C9 Bug: FoHdin missing PickIt (fixed)
- C10 Bug: game_controller race condition -
game_statsnow in__init__()(fixed) - M15 Bug: TARGET_ASPECT_MIN duplicate removed (fixed)
- M17 Bug: chest.py relative path ->
Path(__file__)absolute (fixed) - Quick win: All 47 bare
time.sleep()replaced withwait()(15 files fixed) - Quick win: Stealth fallback
Logger.warning()added to mouse_impl.py (3 blocks) - Quick win:
requirements.txtcreated (120 lines, 20+ deps) - Quick win:
ruff.tomlcreated (Python 3.10, line-length 120) - Tool:
asset_manager.pycreated (inventory, search, key, audit, quality, similarity, capture, crop, auto_crop, validate, cleanup, batch)
CRITICAL - DO FIRST
- C1. Stealth: Consolidate all timing through centralized wait()
- All 47 bare
time.sleep()replaced withwait()(which has Gaussian jitter) - Verified no remaining bare
time.sleep()insrc/(except insideutils.misc.wait)
- All 47 bare
C2. Stealth: Add variable typing rhythm
win_input.py send_text() uses fixed 0.05 per character.
Real humans type with 0.02-0.12 per character with variation.
Fix: Add per-character randomization to send_text().
C3. Stealth: Human curve complexity should adapt to distance
mouse_impl.py HumanCurve uses static parameters.
Short movements (teleport to adjacent tile) should be simpler/faster.
Long movements should have more complex arcs.
Fix: Add distance-to-complexity mapping in HumanCurve.__init__().
C4. Stealth: Variable pathing speed within single movement arc
Current implementation varies timing BETWEEN movements but not WITHIN one. Real humans speed up and slow down during a single mouse arc.
Fix: Add per-segment timing variation in HumanCurve execution loop.
C5. Stealth: Extend endpoint wobble to ALL click sequences
mouse_impl.py endpoint_wobble() only fires for stealth_move().Most paths use regular
mouse.move() + mouse.click() with no wobble.Fix: Make
(Still needed - stealth fallback logging added but wobble not yet extended to all clicks.)stealth_move() the default, or add wobble to regular click flow.
C6. Stealth: Screen capture timing jitter
~screen.py uses dxcam with perfectly regular capture intervals (80ms).Anti-cheat can detect this regular polling pattern.
Fix: Add 5-10% jitter to grab timing intervals.
(Done - all timing now routes through wait() with jitter.)
C7. Stealth: Input event spacing jitter
hotkey.py GetAsyncKeyState polling runs at exactly 50Hz.Real human keyboard polling is variable.
Fix: Add microsecond-level jitter to polling intervals.
(Done - all time.sleep() in hotkey.py replaced with wait() calls.)
C8. Bug: Fix personal.py open() shadowing (line 60) (DONE)
C9. Bug: Fix FoHdin missing PickIt (bot.py line 72) (DONE)
- C10. Bug: Replace thread killing with cooperative shutdown
utils.misc.kill_thread()now preferscooperative_shutdown()- Added
register_stop_conditiontoutils.misc - Centralized
wait()andsearch_and_wait()now check for shutdown signals Bot,HealthManager, andDeathManagerregister their stop conditions
HIGH PRIORITY
H1. Stealth: Add AFK countermeasures during ALL idle states
stealth.py maybe_afk_break() only runs during run transitions.
Should also run during health monitoring idle periods and town states.
Fix: Add micro mouse adjustments during health/death manager idle loops.
H2. Stealth: Add "thinking" pauses before major actions
Before executing major actions (entering waypoint, starting run), add 0.5-3s "deliberation" pause. Humans plan before acting.
Fix: Add configurable pause in bot.py state transitions.
H3. Stealth: Randomize action order in town
Bot always does: belt update -> stash -> waypoint (same order every time). Real players vary their town routine.
Fix: Randomize which town tasks are performed first in town_manager.py.
H4. Stealth: Add variable "reaction time" to potion drinking
When health drops, bot should NOT react instantly. Add 100-500ms delay to potion drinking to simulate human reaction time.
Fix: Add reaction delay in health_manager.py before invoking heal skill.
H5. Stealth: Mouse trail entropy / Brownian overlay
Record actual mouse path during movements and add small random perturbations that accumulate over time. Makes movement traces look more human.
Fix: Add Brownian motion overlay in mouse_impl.py HumanCurve.
H6. Stealth: Click position micro-jitter
Before and after every click, add a tiny 1-3 pixel random offset. Real humans don't click at exactly the same coordinates.
Fix: Add jitter in mouse_impl.py click methods.
H7. Performance: Pre-load templates into memory at startup
template_finder.py loads PNGs with cv2.imread() on each search call.
With 1111+ templates, this wastes CPU on every detection cycle.
Current: Has @cache on stored_templates() but cache is never invalidated.
Fix: Good as-is if cache works. Verify cache isn't being bypassed in hot paths.
H8. Performance: Cache screen grabs in tight pathing loops
pather.py traverse_nodes() calls grab(force_new=True) for every node.
If retrying quickly, the frame won't have changed.
Fix: Cache last grab timestamp; skip if less than 16ms since last grab.
H9. Performance: Convert BGR->HSV once, apply all color masks
utils.misc.color_filter() converts BGR->HSV for every filter range.
With multiple NPC templates, this creates redundant conversions.
Fix: Convert once, apply all masks from single HSV image.
H10. Architecture: Split pather.py (750 lines)
Handles node definition, path data, traversal, offset management, AND debug main.
Split into:
- path_data.py (constants and route definitions)
- path_traversal.py (traverse_nodes logic)
- path_utils.py (node offset helpers)
H11. Architecture: Split config.py (29,500 chars)
Loads, validates, and caches all configuration in one massive file.
Split into:
- config_loader.py (read params.ini / config.yaml)
- config_schema.py (Pydantic/dataclass validation)
- config_defaults.py (default values per section)
H12. Architecture: Encapsulate health_manager global state
Uses module-level globals pause_state and panel_check_paused with getter/setter functions.
Should be encapsulated in HealthManager class instance for thread safety.
H13. Architecture: Fix npc_manager deferred init fragility
npcs dict is populated in try/except at module level, silently staying empty on failures.
If templates load late, open_npc_menu() crashes with unhelpful KeyError.
Fix: Add explicit initialization check with clear error message.
H14. Architecture: Add thread safety to shared state
health_manager.py and death_manager.py share set_pause_state without mutex/lock.
Bot uses self._stash_mutex for stashing but health/death managers don't.
Fix: Add threading.Lock around shared state access.
H15. Configuration: Add schema validation to params.ini
A typo in any key name causes runtime crash deep in the call stack.
Fix: Use Pydantic or dataclasses with defaults and type hints.
H16. Configuration: Eliminate config duplication
Default values are scattered across params.ini AND config.py with hardcoded fallbacks.
Fix: Single source of truth - either params.ini with config.py as schema only, or config.py with params.ini as user overrides.
H17. Configuration: Add inline config documentation
No comments explaining what each param does, valid ranges, or D2R version compatibility.
Fix: Add docstrings to config sections with examples and ranges.
H18. Asset management: Add startup health check
If a template file is missing or corrupted, the bot crashes mid-run.
Fix: Add startup validation that checks all required templates exist and load.
H19. Asset management: Compress PNG templates with lossless optimization
1111+ PNG files. Many can be reduced with optipng/pngcrush without quality loss.
Fix: Run optipng -o7 on all assets/ PNGs.
H20. Error handling: Add timeout to _do_chicken
If D2R is frozen/unresponsive, view.fast_save_and_exit() hangs indefinitely.
Fix: Add timeout with fallback taskkill on timeout.
H21. Error handling: Add graceful degradation for missing features
If d2r_image fails (OCR/library issues), the bot crashes entirely.
Fix: Add fallback pathfinding mode without OCR, with clear warning.
H22. Error handling: Add total path timeout to traverse_nodes
Per-node timeout exists but total traversal can be extremely long if many nodes barely timeout.
Fix: Add cumulative path timeout in pather.py traverse_nodes().
H23. Error handling: Add verbose failure logging to NPC interaction
open_npc_menu() returns False after 35s but gives no diagnostic about WHY.
Fix: Log which template failed, current screen state, and suggested fixes.
MEDIUM PRIORITY
M1. Code quality: Duplicate code in i_char.py move()/walk()
move() and walk() methods (lines 214-261) are nearly identical.
Walk distance adjustment logic is copied verbatim.
Fix: Extract to _adjust_walk_position() helper.
M2. Code quality: Silent failures in stealth fallback logging
mouse_impl.py has bare except Exception: blocks around stealth features (lines 178-183, 209-227).
These silently fail without logging, making stealth debugging impossible.
Fix: Add Logger.warning() in all stealth fallback except blocks.
M3. Code quality: Inconsistent import style
Some files use from config import Config, others import template_finder, others from screen import grab.
Fix: Standardize on absolute imports throughout.
M4. Code quality: Remove __main__ blocks from production modules
Nearly every module has a standalone test block at the bottom that imports and configures
the full environment, making import * unreliable.
Fix: Move to dedicated test directory.
M5. Code quality: Add full type hints coverage
Python 3.10+ type hints exist in some places but are incomplete.
pather.py has 750 lines with minimal typing.
Fix: Add type annotations to all public methods and data structures.
M6. Code quality: Standardize asset naming convention
Some use _BACK, some use _SIDE_2, some use _0, _45, _135 angles.
Fix: NPCNAME_ANGLE_VARIANT.png (e.g. akara_front_1.png, akara_side_45_1.png)
M7. Developer experience: Add requirements.txt / pyproject.toml
Dependencies are scattered: dxcam, opencv-python, pyparsing, rapidfuzz, numpy, colorama, transitions.
Fix: Single requirements.txt or pyproject.toml with pinned versions.
M8. Developer experience: Add linting/formatting config
No ruff.toml, pyproject.toml, .flake8, or black config. Code style is inconsistent.
Fix: Add ruff.toml with consistent formatting rules.
M9. Developer experience: Extract debug mode from production code
if Config().general["info_screenshots"]: checks pollute every module.
Fix: Extract to a @debug_if decorator or context manager.
M10. Developer experience: Add CI/CD pipeline
No .github/workflows/, no GitHub Actions, no automated test runner.
Fix: Add GitHub Actions for linting + tests on push/PR.
M11. Testing: Add unit tests for core logic
pather.py, bot.py, game_controller.py have ZERO tests.
Fix: At minimum, add tests for state machine transitions in bot.py.
M12. Testing: Improve test mocks
test/mocks/screen_mock.py doesn't mock grab(), convert_*() comprehensively.
Many tests likely skip silently.
Fix: Add comprehensive mocks for screen, mouse, and keyboard.
M13. Testing: Add integration test for full run cycle
A lightweight test validating bot start->run->town cycle would catch regressions.
Fix: Add test/integration/test_run_cycle.py with mocked D2R.
M14. Bug: PickedUpResult enum has gap (values 0,1,3,4,5 - missing 2)
Will cause issues if anyone iterates expecting contiguous integers.
Fix: Either fill gap or use named values only (don't rely on int values).
M15. Bug: TARGET_ASPECT_MIN defined twice in target_detect.py
Lines 21-22 define as 0.5, lines 26-27 redefine as 0.4. Second wins, first is dead code.
Fix: Remove the dead definition.
M16. Bug: game_controller.py self.game_stats race condition
self.game_stats.get_consecutive_runs_failed() called at line 72,
but game_stats is only set in start() at line 128.
Fix: Initialize game_stats in __init__() with default/None.
M17. Bug: chest.py hardcoded relative path
os.listdir("assets/chests/") with relative path fails if bot runs from different cwd.
Fix: Use Path(__file__).parent.parent / "assets" / "chests".
M18. Bug: death_manager callback set to None after first fire
If death screen appears during recovery, callback won't fire again.
Fix: Re-register callback after each death handling.
M19. Asset: hud_mask.png uses hardcoded absolute path
ui_manager.py references assets/hud_mask.png with absolute path.
Fix: Use same asset resolution system as other templates.
M20. Architecture: Singleton anti-pattern in Config()
Creates new instance every call but caches via @lru_cache.
Multiple modules import redundantly.
Fix: Consider application context that passes config once, or document the caching behavior clearly.
FOH PALADIN SPECIFIC
F1. Mercenary healing optimization
FOH mercenary takes heavy damage. Current thresholds wait too long. Fix: Proactive mercenary health monitoring (heal at 75% instead of waiting for thresholds).
F2. Bottle of Holy Water targeting
FOH builds use BoWH on undead/demons. No logic to detect monster type and switch BoWH on/off. Fix: Add monster class detection with BoWH toggle.
F3. Corpse retrieval strategy
ScreenObjects.Corpse exists but no logic to navigate to corpse and recover items.
FOH is tanky but can still die on high-tier runs.
Fix: Add corpse recovery routine in death/recovery flow.
F4. Automatic rebuff detection
FOH needs Vigor + Concentrate + Redemption. If any aura drops (merc dies), bot should re-cast. Fix: Add aura monitoring in health_manager or combat loop.
F5. Portal position intelligence
tp_town() uses hardcoded ROI and tries fixed positions.
Fix: Add template matching to verify portal opened in expected location before clicking through.
QUICK WINS (Low effort, high impact)
- Run
optipng -o7on all assets (saves disk space + load time) - Add
requirements.txt(120 lines, 20+ deps) - Add
ruff.toml(line-length 120, Python 3.10) - Fix personal.py
open()shadowing ->open_inventory() - Fix FoHdin missing PickIt
- Fix TARGET_ASPECT_MIN duplicate
- Add
asset_manager.py(unified asset management tool) - Replace 47 bare
time.sleep()withwait()(15 files) - Add logger.warning() to stealth except blocks (mouse_impl.py)
- Run
optipng -o7on all assets (still pending - useasset_manager.py batchor run manually) - Fix
pather.pyhardcoded relative path (similar to chest.py fix)