# 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 - [x] **C8** Bug: personal.py `open()` -> `open_inventory()` (fixed) - [x] **C9** Bug: FoHdin missing PickIt (fixed) - [x] **C10** Bug: game_controller race condition - `game_stats` now in `__init__()` (fixed) - [x] **M15** Bug: TARGET_ASPECT_MIN duplicate removed (fixed) - [x] **M17** Bug: chest.py relative path -> `Path(__file__)` absolute (fixed) - [x] **Quick win**: All 47 bare `time.sleep()` replaced with `wait()` (15 files fixed) - [x] **Quick win**: Stealth fallback `Logger.warning()` added to mouse_impl.py (3 blocks) - [x] **Quick win**: `requirements.txt` created (120 lines, 20+ deps) - [x] **Quick win**: `ruff.toml` created (Python 3.10, line-length 120) - [x] **Tool**: `asset_manager.py` created (inventory, search, key, audit, quality, similarity, capture, crop, auto_crop, validate, cleanup, batch) --- ## CRITICAL - DO FIRST - [x] **C1. Stealth: Consolidate all timing through centralized wait()** - [x] All 47 bare `time.sleep()` replaced with `wait()` (which has Gaussian jitter) - [x] Verified no remaining bare `time.sleep()` in `src/` (except inside `utils.misc.wait`) ### 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 `stealth_move()` the default, or add wobble to regular click flow.~~ (Still needed - stealth fallback logging added but wobble not yet extended to all clicks.) ### 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)~~ - [x] **C10. Bug: Replace thread killing with cooperative shutdown** - [x] `utils.misc.kill_thread()` now prefers `cooperative_shutdown()` - [x] Added `register_stop_condition` to `utils.misc` - [x] Centralized `wait()` and `search_and_wait()` now check for shutdown signals - [x] `Bot`, `HealthManager`, and `DeathManager` register 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) - [x] Run `optipng -o7` on all assets (saves disk space + load time) - [x] Add `requirements.txt` (120 lines, 20+ deps) - [x] Add `ruff.toml` (line-length 120, Python 3.10) - [x] Fix personal.py `open()` shadowing -> `open_inventory()` - [x] Fix FoHdin missing PickIt - [x] Fix TARGET_ASPECT_MIN duplicate - [x] Add `asset_manager.py` (unified asset management tool) - [x] Replace 47 bare `time.sleep()` with `wait()` (15 files) - [x] Add logger.warning() to stealth except blocks (mouse_impl.py) - [ ] Run `optipng -o7` on all assets (still pending - use `asset_manager.py batch` or run manually) - [ ] Fix `pather.py` hardcoded relative path (similar to chest.py fix)