fix: robust cooperative shutdown and stability improvements for Windows 10

- 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.
This commit is contained in:
FiskenPoul
2026-06-06 19:34:06 +02:00
3 changed files with 44 additions and 41 deletions

View File

@@ -22,7 +22,7 @@ difficulty=hell
; name: bot profile name used in logs/messages and mod launch option replacement
name=FiskenErSej
; randomize_runs: 0 = run in listed order, 1 = shuffle run order
randomize_runs=1
randomize_runs=0
; target_tz: target Terror Zone id (leave as default unless you know the mapping)
target_tz=1
; saved_games_folder: optional override path to D2R Saved Games folder (blank = auto)
@@ -32,7 +32,7 @@ level_max_steps=20
; Set to 1 to enable auto-login and auto-launch of D2R on startup.
; Credentials below are ONLY used when auto_login=1.
auto_login=0
auto_login=1
bnet_name=${BOTTY_BNET_NAME}
bnet_pass=${BOTTY_BNET_PASS}
@@ -42,13 +42,13 @@ char_name=${BOTTY_CHAR_NAME}
; messaging
; custom_loot_message_hook: optional separate webhook for loot notifications
custom_loot_message_hook=https://discord.com/api/webhooks/1510992232939913297/20rbnGmpTWMKBBL6JN0JaE897jLGOmHl6zclJ1AO82o2dtD67YHfAKMD-e4G8DWvUOXV
custom_loot_message_hook=${BOTTY_CUSTOM_LOOT_MESSAGE_HOOK}
; custom_message_hook: main webhook for status/death/chicken messages
custom_message_hook=https://discord.com/api/webhooks/1510992232939913297/20rbnGmpTWMKBBL6JN0JaE897jLGOmHl6zclJ1AO82o2dtD67YHfAKMD-e4G8DWvUOXV
custom_message_hook=${BOTTY_CUSTOM_MESSAGE_HOOK}
; discord_log_chicken: 1 = send chicken/death style notifications
discord_log_chicken=1
; discord_status_runs: send periodic status every X completed runs (blank/0 disables)
discord_status_runs=100
discord_status_runs=10
; discord_status_count: legacy fallback, send periodic status every X games (blank/0 disables)
discord_status_count=20
; message_api_type: "" disables messaging, "discord" or "generic_api"
@@ -66,14 +66,13 @@ d2r_path=C:\Program Files (x86)\Diablo II Resurrected
; max_consecutive_fails: stop bot after this many failed runs in a row
max_consecutive_fails=5
; max_game_length_s: emergency timeout per run/game
max_game_length_s=160
max_game_length_s=600
; auto_downgrade_threshold: if combined chickens+deaths exceed this number within 1 hour,
; bot automatically lowers difficulty by one tier (hell->nightmare->normal) and restarts.
; Set to 0 to disable. (NOTE: feature is parsed but not yet active in bot logic)
auto_downgrade_threshold=0
; if you set this field to 1, botty will attempt to restart d2 after a crash or failure
restart_d2r_when_stuck=0
restart_d2r_when_stuck=1
; hardcore: 1 enables hardcore-safe assumptions in some routines
hardcore=0
@@ -181,7 +180,7 @@ skill_mistake_chance = 0.015
; run_countess (Act 1 Forgotten Tower)
; run_mephisto (Act 3 Durance of Hate)
; run_baal (Act 5 Throne of Destruction)
order=run_trav
order=run_pindle, run_eldritch_shenk, run_arcane, run_diablo, run_vizier, run_trav
[char]
; ==========================
@@ -194,9 +193,9 @@ type=hammerdin
; belt_rows: in-game belt size (2/3/4)
belt_rows=4
; casting_frames: your char cast breakpoint (affects action timing)
casting_frames=11
casting_frames=8
; cta_casting_frames: cast breakpoint on CTA swap (if used)
cta_casting_frames=11
cta_casting_frames=8
; attack_frames: base attack animation timing for non-cast attacks
attack_frames=15
; cta_available: 1 if Call to Arms swap exists, else 0
@@ -225,13 +224,13 @@ show_items=alt
; stand_still cannot be the default "shift" as it would interfere with merc healing
stand_still=capslock
; teleport: leave empty if you can't use
teleport=f4
town_portal=f8
teleport=b
town_portal=6
; call to arms settings:
; weapon_switch/battle_orders/battle_command only used when cta_available=1
weapon_switch=w
battle_orders=f6
battle_command=f5
battle_orders=7
battle_command=8
; ==========================
; ==== Optional configs ====
@@ -248,7 +247,7 @@ use_merc=1
; Normal: 1064-1588 HP, 75% fire, 70% poison
; Nightmare: 4773-5859 HP, 100% poison
; Hell: 13094-16070 HP, 75% fire, 50% cold, 33% light, 100% poison
; With Conviction (-33% res): Hell Pindle = 50% fire, 17% cold, 0% light, 67% poison
; With Conviction (-33% res), Hell Pindle = 50% fire, 17% cold, 0% light, 67% poison
; CTA adds +100% dmg to hammer. Need ~60%+ FCR and good IAS to kill in 8s.
; atk_len_nihlathak = 4.0 (Nihlathak - single boss)
; atk_len_eldritch = 3.0 (Eldritch only - single boss)
@@ -262,10 +261,10 @@ use_merc=1
; atk_len_baal_waves = 30.0 (Baal's spawn waves before boss)
;
; Chaos Sanctuary (Diablo run) individual fights:
; atk_len_cs_trashmobs = 2.0 (Trash packs in CS)
; atk_len_diablo_vizier = 2.0 (Vizier of Chaos - seal boss A)
; atk_len_diablo_infector = 4.0 (Infector of Souls - seal boss C)
; atk_len_diablo_deseis = 5.0 (Lord De Seis - seal boss B, hardest seal)
; atk_len_cs_trashmobs = 2.0 (Trash packs in CS)
; atk_len_diablo_vizier = 2.0 (Vizier of Chaos - seal boss A)
; atk_len_diablo_infector = 4.0 (Infector of Souls - seal boss C)
; atk_len_diablo_deseis = 5.0 (Lord De Seis - seal boss B, hardest seal)
;
; Increase these if your hammer kills slower (low IAS/FCR).
; Decrease if your hammer kills faster (high IAS/FCR, good gear).
@@ -273,7 +272,7 @@ use_merc=1
atk_len_arc=2.5
atk_len_eldritch=3.0
atk_len_nihlathak=4.0
atk_len_pindle=3.0
atk_len_pindle=8.0
atk_len_shenk=4.0
atk_len_diablo=3.0
atk_len_trav=4.0
@@ -319,24 +318,24 @@ fill_shared_stash_first=0
; to gamble, add any/all of the following: circlet, ring, coronet, talon, amulet
gamble_items=
; open_chests: 1 = open clickable chests along path when possible
open_chests=0
open_chests=1
; pre_buff_every_run: 1 = always recast buffs at run start
pre_buff_every_run=0
; runs_per_repair: visit repair vendor every X runs (blank/0 disables)
runs_per_repair=2000
runs_per_repair=5
; repair_npc: preferred repair vendor strategy.
; - a4_halbu (recommended, most stable)
; - a5_larzuk (faster when templates are reliable)
repair_npc=a4_halbu
; runs_per_stash: stash/sell every X runs (blank/0 disables)
runs_per_stash=1
runs_per_stash=4
; sell_junk: 1 = vendor non-keep items automatically
sell_junk=0
sell_junk=1
; protect_shields_from_sell: 1 = never vendor items with "shield" in detected name (recommended safety)
protect_shields_from_sell=1
; pick_rares_for_gold: 1 = pick all yellow (rare) ground items; non-keep rares will be sold
; Requires sell_junk=1 to convert extra pickups into gold.
pick_rares_for_gold=1
pick_rares_for_gold=0
; pick_gold: 1 = pick up ground gold piles, 0 = ignore all ground gold
pick_gold=1
@@ -347,12 +346,12 @@ stash_destination=0,1,2,3
; chipped, flawed, standard, flawless
transmute=flawless
;how often we want to run transmute routine(e.g. every 100 games)
transmute_every_x_game=200
transmute_every_x_game=2000
; number of stash tabs (ROTW has more than 6, adjust as needed)
stash_tabs=6
; potion transmute settings
; convert_rejuv: 1 = convert regular Rejuv Potions to Full Rejuv via cube (3 -> 1)
convert_rejuv=0
convert_rejuv=1
; min_rejuv_to_convert: minimum regular rejuv potions in inventory before starting conversion
min_rejuv_to_convert=6
@@ -402,22 +401,23 @@ hydra=f1
; ==== Builds: Paladin ====
; =========================
[paladin]
cleansing=f2
holy_shield=f7
redemption=
vigor=f3
cleansing=f9
holy_shield=f2
redemption=f3
vigor=f4
[fohdin]
; foh must be left skill, hotkey required
blessed_hammer=f9
concentration=f1
conviction=
blessed_hammer=f1
concentration=f8
conviction=f5
foh=f6
holy_bolt=f7
[hammerdin]
blessed_hammer=f9
concentration=f1
blessed_hammer=f1
concentration=f8
conviction=f5
; =========================
; ==== Builds: Warlock ====
@@ -575,13 +575,13 @@ buff_2=
; select_runs_key: open run selector UI
select_runs_key=pagedown
; restore_settings_from_backup_key: restore backed-up D2R settings
restore_settings_from_backup_key=f10
restore_settings_from_backup_key=insert
; settings_backup_key: create backup of current D2R settings
settings_backup_key=pause
; auto_settings_key: auto-apply required D2R settings
auto_settings_key=pageup
; graphic_debugger_key: toggle on-screen debug layers
graphic_debugger_key=insert
graphic_debugger_key=delete
; resume_key: start/pause bot loop
resume_key=f11
; exit_key: hard stop bot
@@ -605,7 +605,7 @@ message_headers=
; ocr_during_pickit: 1 = run OCR while looting (slower, more diagnostics)
ocr_during_pickit=0
;use "can_teleport_natively" or "can_teleport_with_charges" if you want to force certain behavior in case autodetection isn't working properly
override_capabilities=
override_capabilities=can_teleport_natively
; pathing_delay_factor: movement/click delay multiplier (1 fast .. 10 slow)
pathing_delay_factor=2
; If you want to control Hyper-V window from host use 0,51 here

View File

@@ -159,7 +159,7 @@ def read_gold(img: np.ndarray = None, type: str = "inventory"):
Logger.debug(f"{type.upper()} gold: {number}")
return number
def wait_for_left_inventory(timeout: int = 5):
def wait_for_left_inventory(timeout: int = 10):
start=time.time()
while time.time() - start < timeout:
if left_inventory_ready(grab()):

View File

@@ -184,6 +184,9 @@ class PickIt:
:param char: The character used to pick up the item
TODO :return: return a list of the items that were picked up
"""
# Re-detect window position to mitigate 1282x720 vs 1280x720 template drift
from screen import find_and_set_window_position
find_and_set_window_position(force=True)
self._reset_state()
keyboard.send(Config().char["show_items"])
wait(0.16, 0.16) #wait 4 frames for items to refresh