diff --git a/config/params.ini b/config/params.ini index 1ba7034..6216b64 100644 --- a/config/params.ini +++ b/config/params.ini @@ -19,7 +19,7 @@ max_runtime_before_break_m=0 ; timers / fail handling d2r_path=C:\Program Files (x86)\Diablo II Resurrected max_consecutive_fails=5 -max_game_length_s=140 +max_game_length_s=55 ; if you set this field to 1, botty will attempt to restart d2 after a crash or failure restart_d2r_when_stuck=0 @@ -38,18 +38,18 @@ pickit_screenshots=0 ; run_arcane ; run_diablo ; run_vizier -order=run_pindle,run_eldritch_shenk +order=run_pindle [char] ; ========================== ; ==== Mandatory Fields ==== ; ========================== ; These configs have to be alligned with your d2r settings and char build -type=blizzorb_sorc +type=trapsin belt_rows=4 -casting_frames=8 -attack_frames=15 -cta_available=1 +casting_frames=11 +attack_frames=10 +cta_available=0 ; safer_routines: enable for optional defensive maneuvers/etc during combat/runs at the cost of increased runtime (ex. hardcore players) safer_routines=1 @@ -70,7 +70,7 @@ 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=f7 +teleport= town_portal=f8 ; call to arms settings: weapon_switch=x @@ -87,7 +87,7 @@ use_merc=1 atk_len_arc=2.5 atk_len_eldritch=10.0 atk_len_nihlathak=8.0 -atk_len_pindle=7.0 +atk_len_pindle=8.5 atk_len_shenk=8.0 atk_len_trav=3.0 @@ -109,9 +109,9 @@ belt_rejuv_columns=2 ; Potion/chicken settings take_health_potion=0.92 -take_mana_potion=0.8 +take_mana_potion=0.40 take_rejuv_potion_health=0.75 -take_rejuv_potion_mana=0.45 +take_rejuv_potion_mana=0.10 heal_merc=0.7 heal_rejuv_merc=0.3 chicken=0.5 @@ -207,13 +207,16 @@ concentration=f5 ; ========================== ; Currently no Trav implementaiton! [trapsin] -burst_of_speed= -death_sentry= -fade= -lightning_sentry= -shadow_warrior= +; We assume fire blast or shock-web is left-click skill which is optional hotkey if pre-assigned. skill_left= +; Assume all other skills are right-click and hotkey required! +burst_of_speed=f1 +death_sentry=f2 +fade= +lightning_sentry=f3 +shadow_warrior=f4 +mind_blast=f5 ; =========================== ; ==== Builds: Barbarian ==== @@ -338,7 +341,7 @@ message_body_template={{"content": "{msg}"}} message_headers= 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="can_teleport_natively" +override_capabilities= pathing_delay_factor=4 ;If you want to control Hyper-V window from host use 0,51 here window_client_area_offset=0,0 diff --git a/src/char/trapsin.py b/src/char/trapsin.py index f711705..a334a66 100644 --- a/src/char/trapsin.py +++ b/src/char/trapsin.py @@ -1,4 +1,5 @@ import keyboard +from ui import skills from utils.custom_mouse import mouse from char import IChar from pather import Pather @@ -6,6 +7,7 @@ from logger import Logger from screen import convert_abs_to_monitor, convert_screen_to_abs, grab from config import Config from utils.misc import wait, rotate_vec, unit_vector +import time import random from pather import Location, Pather import numpy as np @@ -25,16 +27,11 @@ class Trapsin(IChar): wait(0.1, 0.13) mouse.click(button="right") wait(self._cast_duration) - if self._skill_hotkeys["shadow_warrior"]: - keyboard.send(self._skill_hotkeys["shadow_warrior"]) - wait(0.1, 0.13) - mouse.click(button="right") - wait(self._cast_duration) + + def pre_move(self, wait_tp: bool = False): + super().pre_move(wait_tp=wait_tp) if self._skill_hotkeys["burst_of_speed"]: - keyboard.send(self._skill_hotkeys["burst_of_speed"]) - wait(0.1, 0.13) - mouse.click(button="right") - wait(self._cast_duration) + self._cast_burst_of_speed() def _left_attack(self, cast_pos_abs: tuple[float, float], spray: int = 10): keyboard.send(Config().char["stand_still"], do_release=False) @@ -50,7 +47,6 @@ class Trapsin(IChar): mouse.release(button="left") keyboard.send(Config().char["stand_still"], do_press=False) - def _right_attack(self, cast_pos_abs: tuple[float, float], spray: float = 10): keyboard.send(self._skill_hotkeys["lightning_sentry"]) x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) @@ -66,35 +62,139 @@ class Trapsin(IChar): atk(4) keyboard.send(self._skill_hotkeys["death_sentry"]) atk(1) + + def _cast_shadow_warrior(self, cast_pos_abs: tuple[float, float], spray: float = 10): + if self._skill_hotkeys["shadow_warrior"]: + keyboard.send(self._skill_hotkeys["shadow_warrior"]) + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + mouse.press(button="right") + wait(0.06, 0.08) + mouse.release(button="right") + wait(self._cast_duration-0.06) + + def _cast_mind_blast(self, cast_pos_abs: tuple[float, float], spray: float = 10): + if self._skill_hotkeys["mind_blast"]: + keyboard.send(self._skill_hotkeys["mind_blast"]) + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + + mouse.press(button="right") + wait(0.06, 0.08) + mouse.release(button="right") + wait(self._cast_duration-0.06) + + def _cast_burst_of_speed(self): + if self._skill_hotkeys["burst_of_speed"]: + keyboard.send(self._skill_hotkeys["burst_of_speed"]) + + mouse.press(button="right") + wait(0.06, 0.08) + mouse.release(button="right") + wait(self._cast_duration-0.06) + + def _cast_fire_blast(self, cast_pos_abs: tuple[float, float], spray: float = 10, duration: float = 0.3): + keyboard.send(Config().char["stand_still"], do_release=False) + + #Get mouse in initial target logcation + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + + #We just hold down left mouse button to ensure we get full attack speed. + mouse.press(button="left") + now = start = time.time() + while (now - start) < duration: + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + wait(0.06, 0.08) + now = time.time() + mouse.release(button="left") + + keyboard.send(Config().char["stand_still"], do_press=False) + wait(self._attack_duration-0.06) + + def _cast_lightning_sentry(self, cast_pos_abs: tuple[float, float], spray: float = 10): + if not self._skill_hotkeys["lightning_sentry"]: + Logger.error("Lightning sentry hotkey not assigned. Required for trapsin!") + keyboard.send(self._skill_hotkeys["lightning_sentry"]) + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + + mouse.press(button="right") + wait(0.06, 0.08) + mouse.release(button="right") + wait(self._attack_duration-0.06) + + def _cast_death_sentry(self, cast_pos_abs: tuple[float, float], spray: float = 10): + if not self._skill_hotkeys["death_sentry"]: + Logger.error("Death sentry hotkey not assigned. Required for trapsin!") + keyboard.send(self._skill_hotkeys["death_sentry"]) + x = cast_pos_abs[0] + (random.random() * 2 * spray - spray) + y = cast_pos_abs[1] + (random.random() * 2 * spray - spray) + cast_pos_monitor = convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) + + mouse.press(button="right") + wait(0.06, 0.08) + mouse.release(button="right") + wait(self._attack_duration-0.06) + + def kill_pindle(self) -> bool: - atk_len = max(1, int(Config().char["atk_len_pindle"] / 2)) pindle_pos_abs = convert_screen_to_abs(Config().path["pindle_end"][0]) - cast_pos_abs = [pindle_pos_abs[0] * 0.9, pindle_pos_abs[1] * 0.9] - for _ in range(atk_len): - self._right_attack(cast_pos_abs, 11) - self._left_attack(cast_pos_abs, 11) - # Move to items - wait(self._cast_duration, self._cast_duration + 0.2) + cast_pos_abs = [pindle_pos_abs[0] * 1.2, pindle_pos_abs[1] * 1.2] + trap_cast_pos_abs = [pindle_pos_abs[0] * 0.45, pindle_pos_abs[1] * 0.40] + start = time.time() + self._cast_mind_blast(cast_pos_abs=cast_pos_abs) + cast_pos_abs = [pindle_pos_abs[0] * 0.8, pindle_pos_abs[1] * 0.8] + self._cast_shadow_warrior(cast_pos_abs=cast_pos_abs) + for _ in range(4): + self._cast_lightning_sentry(cast_pos_abs=trap_cast_pos_abs, spray=5.0) + self._cast_death_sentry(cast_pos_abs=trap_cast_pos_abs, spray=5.0) + + + trap_cast_pos_abs = [pindle_pos_abs[0] * 0.50, pindle_pos_abs[1] * 0.50] + time_remaining = Config().char["atk_len_pindle"] - (time.time() - start) + self._cast_fire_blast(cast_pos_abs=cast_pos_abs, duration=time_remaining) + if self.capabilities.can_teleport_natively: self._pather.traverse_nodes_fixed("pindle_end", self) else: - self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, force_tp=True) - return True + #Walk just close enough to pindle's position to see items. That way we leave sooner if there is nothing to grab + x_m, y_m = convert_abs_to_monitor([pindle_pos_abs[0] * 0.65, pindle_pos_abs[1] * 0.65]) + self.move((x_m, y_m), force_move=True) + return False def kill_eldritch(self) -> bool: - atk_len = max(1, int(Config().char["atk_len_eldritch"] / 2)) - eld_pos_abs = convert_screen_to_abs(Config().path["eldritch_end"][0]) - cast_pos_abs = [eld_pos_abs[0] * 0.9, eld_pos_abs[1] * 0.9] - for _ in range(atk_len): - self._right_attack(cast_pos_abs, 90) - self._left_attack(cast_pos_abs, 90) - # Move to items - wait(self._cast_duration, self._cast_duration + 0.2) + eldritch_pos_abs = convert_screen_to_abs(Config().path["eldritch_end"][0]) + cast_pos_abs = [eldritch_pos_abs[0] * 1.5, eldritch_pos_abs[1] * 1.5] + trap_cast_pos_abs = [eldritch_pos_abs[0] * 0.70, eldritch_pos_abs[1] * 0.70] + + start = time.time() + self._cast_shadow_warrior(cast_pos_abs=cast_pos_abs) + for _ in range(4): + self._cast_lightning_sentry(cast_pos_abs=trap_cast_pos_abs) + self._cast_death_sentry(cast_pos_abs=trap_cast_pos_abs) + + time_remaining = Config().char["atk_len_eldritch"] - (time.time() - start) + self._cast_fire_blast(cast_pos_abs=cast_pos_abs, duration=time_remaining) + if self.capabilities.can_teleport_natively: self._pather.traverse_nodes_fixed("eldritch_end", self) else: - self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=0.6, force_tp=True) + x_m, y_m = convert_abs_to_monitor([eldritch_pos_abs[0] * 1.0, eldritch_pos_abs[1] * 1.0]) + self.move((x_m, y_m), force_move=True) return True def kill_shenk(self) -> bool: