import time import keyboard import config import vision from capture import ScreenCapture from input_sender import press_action_key class Bot: def __init__(self): self.capture = ScreenCapture() self.enabled = False self.bar_bbox = None self.was_in_zone = False self.miss_count = 0 self.last_locate = 0.0 self.last_track_time = 0.0 self.prev_cursor_x = None def toggle(self): self.enabled = not self.enabled print(f"[Bot] {'ВКЛЮЧЕН' if self.enabled else 'выключен'}") if not self.enabled: self.bar_bbox = None self.was_in_zone = False self.prev_cursor_x = None def run(self): keyboard.add_hotkey(config.TOGGLE_HOTKEY, self.toggle) print(f"Захват экрана: {self.capture.backend}") print(f"{config.TOGGLE_HOTKEY.upper()} - включить/выключить бота. Ctrl+C - выход.") frame_interval = 1.0 / config.TRACK_FPS try: while True: t0 = time.time() if not self.enabled: time.sleep(0.05) continue if self.bar_bbox is None: self._try_locate() else: self._track() elapsed = time.time() - t0 if elapsed < frame_interval: time.sleep(frame_interval - elapsed) except KeyboardInterrupt: pass def _try_locate(self): now = time.time() if now - self.last_locate < config.LOCATE_RETRY_INTERVAL: time.sleep(0.01) return self.last_locate = now frame = self.capture.grab() if frame is None: return bbox = vision.locate_bar(frame) if bbox: self.bar_bbox = bbox self.miss_count = 0 print(f"[Bot] Шкала найдена: {bbox}") def _track(self): now = time.time() loop_dt = now - self.last_track_time if self.last_track_time else 0.0 self.last_track_time = now bx, by, bw, bh = self.bar_bbox screen_w, screen_h = self.capture.screen_size() margin_x = bw // 10 region_height = bh + int(bh * config.CURSOR_SEARCH_HEIGHT_RATIO) region = ( max(0, bx - margin_x), by, min(screen_w, bx + bw + margin_x), min(screen_h, by + region_height), ) frame = self.capture.grab(region=region) if frame is None: return local_bbox = (bx - region[0], 0, bw, bh) zone = vision.find_target_zone(frame, local_bbox) cursor_x = vision.locate_cursor(frame, local_bbox) if zone is None or cursor_x is None: self.miss_count += 1 if self.miss_count > config.MAX_TRACK_MISSES: print("[Bot] Шкала пропала с экрана, ищу заново...") self.bar_bbox = None self.was_in_zone = False self.prev_cursor_x = None return self.miss_count = 0 # Предсказываем, где курсор окажется через CURSOR_LEAD_TIME секунд, # чтобы скомпенсировать задержку захвата/обработки/нажатия - иначе # к моменту срабатывания клавиши курсор уже покидает узкую зону. velocity = 0.0 if self.prev_cursor_x is not None and loop_dt > 0: velocity = (cursor_x - self.prev_cursor_x) / loop_dt self.prev_cursor_x = cursor_x predicted_x = cursor_x + velocity * config.CURSOR_LEAD_TIME x_min, x_max = zone in_zone = x_min <= predicted_x <= x_max if in_zone and not self.was_in_zone: press_action_key() print( f"[Bot] E нажата | cursor_x={cursor_x:.1f} predicted_x={predicted_x:.1f} " f"zone=({x_min},{x_max}) | задержка цикла={loop_dt * 1000:.1f} мс" ) self.was_in_zone = in_zone if __name__ == "__main__": Bot().run()