Загрузка данных
import mss
import numpy as np
import win32api
import win32con
import ctypes
import threading
import time
import tkinter as tk
from tkinter import ttk, messagebox
import uuid
import random
import sys
import math
import cv2
import queue
cv2.setUseOptimized(True)
cv2.setNumThreads(max(1, cv2.getNumberOfCPUs() - 1))
cv2.ocl.setUseOpenCL(False)
try:
from makcu import create_controller, MouseButton
MAKCU_SDK_AVAILABLE = True
except ImportError:
MAKCU_SDK_AVAILABLE = False
class MouseButton:
LEFT = 1; RIGHT = 2; MIDDLE = 3; MOUSE4 = 4; MOUSE5 = 5
try:
from pygrabber.dshow_graph import FilterGraph
PYGRABBER_AVAILABLE = True
except ImportError:
PYGRABBER_AVAILABLE = False
try:
from PIL import Image, ImageTk
PILLOW_AVAILABLE = True
except ImportError:
PILLOW_AVAILABLE = False
try:
ctypes.windll.winmm.timeBeginPeriod(1)
ctypes.windll.user32.SetProcessDPIAware()
except:
pass
MOUSEEVENTF_MOVE = 0x0001
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
def get_vk_name(vk_code):
special_keys = {
0x01: "Makcu Left", 0x02: "Makcu Right", 0x04: "Makcu Middle",
0x05: "Makcu Mouse 4", 0x06: "Makcu Mouse 5", 0x08: "Backspace",
0x09: "Tab", 0x10: "Shift", 0x11: "Ctrl", 0x12: "Alt",
0x14: "Caps Lock", 0x1B: "Esc", 0x20: "Space"
}
if vk_code in special_keys: return special_keys[vk_code]
if 0x30 <= vk_code <= 0x5A: return chr(vk_code)
return f"Key [0x{vk_code:02X}]"
def set_random_console_title():
try: ctypes.windll.kernel32.SetConsoleTitleW(str(uuid.uuid4())[:8])
except: pass
set_random_console_title()
class CaptureManager:
def __init__(self):
self.capture = None
self.current_frame = None
self.lock = threading.Lock()
self.running = False
self.thread = None
self.device_index = 0
def list_capture_devices(self):
if PYGRABBER_AVAILABLE:
try:
graph = FilterGraph()
devices = graph.get_input_devices()
if devices: return [f"{i}: {name}" for i, name in enumerate(devices)]
except: pass
devices = []
for index in range(5):
cap = cv2.VideoCapture(index, cv2.CAP_DSHOW)
if cap.isOpened():
devices.append(f"Device {index} (Generic)")
cap.release()
return devices
def start_capture(self, device_index, width=1920, height=1080):
self.stop_capture()
self.device_index = device_index
self.capture = cv2.VideoCapture(device_index, cv2.CAP_DSHOW)
if not self.capture.isOpened():
raise Exception(f"Не удалось открыть устройство {device_index}")
self.capture.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)
self.capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G'))
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.capture.set(cv2.CAP_PROP_FPS, 60)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 1)
self.running = True
self.thread = threading.Thread(target=self._capture_loop, daemon=True)
self.thread.start()
def _capture_loop(self):
empty_frames = 0
while self.running and self.capture and self.capture.isOpened():
self.capture.grab()
ret, frame = self.capture.retrieve()
if ret:
empty_frames = 0
with self.lock:
self.current_frame = frame
else:
empty_frames += 1
if empty_frames > 500: break
time.sleep(0.005)
self.running = False
def get_frame(self):
with self.lock:
if self.current_frame is not None:
return self.current_frame.copy()
return None
def stop_capture(self):
self.running = False
if self.thread and self.thread.is_alive():
self.thread.join(timeout=1)
if self.capture:
self.capture.release()
self.capture = None
self.current_frame = None
def is_active(self):
return self.running and self.capture is not None and self.capture.isOpened()
class MakcuManager:
def __init__(self):
self.controller = None
self.connected = False
self.button_mapping = {
0x01: MouseButton.LEFT, 0x02: MouseButton.RIGHT,
0x04: MouseButton.MIDDLE, 0x05: MouseButton.MOUSE4,
0x06: MouseButton.MOUSE5
}
def auto_connect(self):
if not MAKCU_SDK_AVAILABLE: return False
try:
self.controller = create_controller(debug=False, auto_reconnect=True)
self.connected = self.controller.is_connected()
if self.connected:
self.controller.enable_button_monitoring(True)
return self.connected
except:
self.connected = False
return False
def disconnect(self):
if self.controller and self.connected:
try: self.controller.disconnect()
except: pass
self.connected = False
self.controller = None
def is_button_pressed(self, vk_code):
makcu_btn = self.button_mapping.get(vk_code)
if self.connected and self.controller and makcu_btn:
try:
if self.controller.is_pressed(makcu_btn): return True
except: pass
return (ctypes.windll.user32.GetAsyncKeyState(vk_code) & 0x8000) != 0
def send_mouse_move(self, dx, dy):
if not self.connected or not self.controller: return False
dx = max(-127, min(127, int(dx)))
dy = max(-127, min(127, int(dy)))
if dx == 0 and dy == 0: return True
try:
self.controller.move(dx, dy)
return True
except: return False
def send_mouse_click(self, vk_code=0x01, down=True):
if not self.connected or not self.controller: return False
makcu_btn = self.button_mapping.get(vk_code)
if not makcu_btn: return False
try:
if down: self.controller.press(makcu_btn)
else: self.controller.release(makcu_btn)
return True
except: return False
class DynamicKinematics:
def __init__(self):
self.aim_speed = 5.0
self.humanization = 5.0
# Интеграторы для ПИД
self.integral_x = 0.0
self.integral_y = 0.0
# EMA (Экспоненциальная скользящая средняя) для устранения джиттера
self.ema_target_x = None
self.ema_target_y = None
self.prev_error_x = 0.0
self.prev_error_y = 0.0
self.max_integral = 30.0
def reset(self):
self.integral_x = 0.0; self.integral_y = 0.0
self.ema_target_x = None; self.ema_target_y = None
self.prev_error_x = 0.0; self.prev_error_y = 0.0
def update(self, raw_target_x, raw_target_y, center_x, center_y, dt):
if dt <= 0.0001: return 0.0, 0.0
# 1. EMA ФИЛЬТР КООРДИНАТ (Устраняет дерганье прицела из-за микро-движений противника)
# Чем выше хуманизация, тем сильнее сглаживаются резкие скачки пикселей.
alpha = max(0.15, 1.0 - (self.humanization * 0.08))
if self.ema_target_x is None:
self.ema_target_x, self.ema_target_y = raw_target_x, raw_target_y
else:
self.ema_target_x = alpha * raw_target_x + (1.0 - alpha) * self.ema_target_x
self.ema_target_y = alpha * raw_target_y + (1.0 - alpha) * self.ema_target_y
error_x = self.ema_target_x - center_x
error_y = self.ema_target_y - center_y
distance = math.hypot(error_x, error_y)
# 2. ДИНАМИЧЕСКИЙ ГЕЙН (Устраняет "слишком медленную наводку")
# Базовая тяга пропорциональна ползунку скорости
base_Kp = self.aim_speed * 1.5
if distance > 25.0:
Kp = base_Kp * 1.2 # Резкий флик, если враг далеко
else:
# Параболическое торможение при входе в хитбокс
Kp = base_Kp * max(0.2, (distance / 25.0))
Kd = self.humanization * 0.1 # Тормозящий эффект
Ki = 0.02
self.integral_x = max(-self.max_integral, min(self.max_integral, self.integral_x + error_x * dt))
self.integral_y = max(-self.max_integral, min(self.max_integral, self.integral_y + error_y * dt))
derivative_x = (error_x - self.prev_error_x) / dt
derivative_y = (error_y - self.prev_error_y) / dt
self.prev_error_x = error_x; self.prev_error_y = error_y
out_x = (Kp * error_x) + (Ki * self.integral_x) + (Kd * derivative_x)
out_y = (Kp * error_y) + (Ki * self.integral_y) + (Kd * derivative_y)
return out_x, out_y
class StealthColorBot:
def __init__(self):
self.root = tk.Tk()
self.root.overrideredirect(True)
self.root.geometry("440x740")
self.root.configure(bg="#050505")
self.root.attributes("-topmost", True)
self.root.attributes("-alpha", 0.98)
try:
hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id())
style = ctypes.windll.user32.GetWindowLongW(hwnd, -20)
ctypes.windll.user32.SetWindowLongW(hwnd, -20, style | 0x00000080 | 0x00000008)
except: pass
self.running = True
self.active = False
self.visible = True
self.capture_manager = CaptureManager()
self.makcu = MakcuManager()
self.kinematics = DynamicKinematics()
self.mouse_lock = threading.Lock()
self.target_velocity_x = 0.0
self.target_velocity_y = 0.0
self.remainder_x = 0.0
self.remainder_y = 0.0
self.tracking_enabled = tk.BooleanVar(value=True)
self.tracking_key = 0x06
self.target_color = (250, 100, 250)
self.target_color_bgr = (250, 100, 250)
self.fov = 120
self.tolerance = 15
self.target_bone_var = tk.StringVar(value="HEAD")
self.deadzone = 2.0
self.max_speed = 1200.0 # Повышен лимит скорости для фликов
self.trigger_enabled = tk.BooleanVar(value=False)
self.trigger_key = 0x05
self.trigger_zone = 5
self.trigger_tolerance = 15
self.is_clicking = False
self.trigger_queue = queue.Queue()
self.min_reaction = 0.03
self.max_reaction = 0.08
self.trigger_density = 0.25
self.color_amp = 1
self.min_area = 10
self.show_fov_overlay = tk.BooleanVar(value=True)
self.show_preview_window = tk.BooleanVar(value=False)
self._latest_preview_frame = None
self.preview_tk_window = None
self.preview_tk_label = None
self.scan_interval = 0.001
self.mouse_send_rate = 500
self.mouse_send_interval = 1.0 / self.mouse_send_rate
self.last_mouse_send_time = time.perf_counter()
self.last_aim_time = time.perf_counter()
self.screen_width = win32api.GetSystemMetrics(0)
self.screen_height = win32api.GetSystemMetrics(1)
self.local_center_x = self.screen_width // 2
self.local_center_y = self.screen_height // 2
self.capture_source_var = tk.StringVar(value="capture")
self.capture_device_list = []
self.capture_device_index = 0
self.capture_status_text = tk.StringVar(value="Not connected")
self.cap_res_width = tk.IntVar(value=1920)
self.cap_res_height = tk.IntVar(value=1080)
self.aim_bounds = []
self.trig_bounds = []
self.last_raw_target_x = None
self.last_raw_target_y = None
self.target_lost_frames = 0
self.setup_ui()
self.setup_overlay()
self._update_hsv_cache()
threading.Thread(target=self.logic_loop, daemon=True).start()
threading.Thread(target=self.mouse_movement_loop, daemon=True).start()
threading.Thread(target=self.key_listener, daemon=True).start()
threading.Thread(target=self.trigger_worker, daemon=True).start()
threading.Thread(target=self._auto_connect_makcu, daemon=True).start()
self.root.after(30, self.update_preview_ui_loop)
def _auto_connect_makcu(self):
attempts = 0
while self.running and not self.makcu.connected:
try:
if self.makcu.auto_connect():
self.root.after(0, lambda: self.makcu_status_label.config(text="Status: Connected (SDK)", fg="#00ff00"))
break
else:
time.sleep(3)
attempts += 1
if attempts > 10: break
except: time.sleep(2)
def _get_hsv_bounds(self, bgr_color, tol):
bgr = np.uint8([[bgr_color]])
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)[0][0]
h, s, v = int(hsv[0]), int(hsv[1]), int(hsv[2])
lower_s, upper_s = max(0, s - tol), min(255, s + tol)
lower_v, upper_v = max(0, v - tol), min(255, v + tol)
h_low, h_high = h - tol, h + tol
bounds = []
if h_low < 0:
bounds.append((np.array([0, lower_s, lower_v], dtype=np.uint8), np.array([h_high, upper_s, upper_v], dtype=np.uint8)))
bounds.append((np.array([179 + h_low, lower_s, lower_v], dtype=np.uint8), np.array([179, upper_s, upper_v], dtype=np.uint8)))
elif h_high > 179:
bounds.append((np.array([h_low, lower_s, lower_v], dtype=np.uint8), np.array([179, upper_s, upper_v], dtype=np.uint8)))
bounds.append((np.array([0, lower_s, lower_v], dtype=np.uint8), np.array([h_high - 179, upper_s, upper_v], dtype=np.uint8)))
else:
bounds.append((np.array([h_low, lower_s, lower_v], dtype=np.uint8), np.array([h_high, upper_s, upper_v], dtype=np.uint8)))
return bounds
def _update_hsv_cache(self):
self.aim_bounds = self._get_hsv_bounds(self.target_color_bgr, self.tolerance)
self.trig_bounds = self._get_hsv_bounds(self.target_color_bgr, self.trigger_tolerance)
def apply_mask(self, hsv_img, bounds, amplification):
final_mask = None
for lower, upper in bounds:
m = cv2.inRange(hsv_img, lower, upper)
if final_mask is None: final_mask = m
else: final_mask = cv2.bitwise_or(final_mask, m)
if amplification > 0 and final_mask is not None:
kernel = np.ones((2, 2), np.uint8)
final_mask = cv2.dilate(final_mask, kernel, iterations=int(amplification))
return final_mask
def setup_ui(self):
header = tk.Frame(self.root, bg="#12001f", height=30)
header.pack(fill="x")
self.make_draggable(header)
tk.Label(header, text="V-Assist Engine (Anti-Jitter)", bg="#12001f", fg="#b042ff", font=("Consolas", 11, "bold")).pack(side="left", padx=10)
tk.Button(header, text="X", bg="#12001f", fg="#b042ff", bd=0, activebackground="#36005c", activeforeground="#ffffff", command=self.exit_app, font=("Candara", 10, "bold")).pack(side="right", padx=5)
style = ttk.Style()
style.theme_use('default')
style.configure('TNotebook', background='#050505', borderwidth=0)
style.configure('TNotebook.Tab', background='#12001f', foreground='#e0e0e0', padding=[10, 2], font=("Candara", 9, "bold"))
style.map('TNotebook.Tab', background=[('selected', '#36005c')], foreground=[('selected', '#ffffff')])
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill="both", expand=True, padx=5, pady=5)
self.aim_frame = tk.Frame(self.notebook, bg="#050505")
self.trig_frame = tk.Frame(self.notebook, bg="#050505")
self.vis_frame = tk.Frame(self.notebook, bg="#050505")
self.capture_frame = tk.Frame(self.notebook, bg="#050505")
self.makcu_frame = tk.Frame(self.notebook, bg="#050505")
self.notebook.add(self.aim_frame, text="Aim")
self.notebook.add(self.trig_frame, text="Smart Trigger")
self.notebook.add(self.vis_frame, text="Vision")
self.notebook.add(self.capture_frame, text="Capture")
self.notebook.add(self.makcu_frame, text="Makcu")
tk.Checkbutton(self.aim_frame, text="Enable Target Acquisition", variable=self.tracking_enabled, bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a", activebackground="#050505", activeforeground="#b042ff", font=("Candara", 10)).pack(anchor="w")
tk.Checkbutton(self.aim_frame, text="Render FOV Overlay", variable=self.show_fov_overlay, bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a", activebackground="#050505", activeforeground="#b042ff", font=("Candara", 10)).pack(anchor="w")
bind_frame_aim = tk.Frame(self.aim_frame, bg="#050505")
bind_frame_aim.pack(fill="x", pady=2)
tk.Label(bind_frame_aim, text="Aim Bind:", bg="#050505", fg="#e0e0e0").pack(side="left")
self.bind_btn_aim = tk.Button(bind_frame_aim, text=f"Bound: {get_vk_name(self.tracking_key)}", bg="#1a1a1a", fg="#b042ff", bd=0, command=lambda: self.start_keybind('aim'))
self.bind_btn_aim.pack(side="right", padx=5)
self.create_slider(self.aim_frame, "FOV Size (Radius px)", 10, 800, self.fov, "fov")
bone_frame = tk.Frame(self.aim_frame, bg="#050505")
bone_frame.pack(fill="x", pady=2)
tk.Label(bone_frame, text="Target Bone:", bg="#050505", fg="#e0e0e0").pack(side="left")
tk.Radiobutton(bone_frame, text="HEAD", variable=self.target_bone_var, value="HEAD", bg="#050505", fg="#00ff00", selectcolor="#1a1a1a").pack(side="left")
tk.Radiobutton(bone_frame, text="CENTER", variable=self.target_bone_var, value="CENTER", bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a").pack(side="left")
tk.Label(self.aim_frame, text="- Intuitive Control Parameters -", bg="#050505", fg="#5A5A5A", font=("Candara", 9, "bold")).pack(pady=(5, 0))
self.create_slider(self.aim_frame, "Aim Speed (Velocity & Snappiness)", 1.0, 10.0, self.kinematics.aim_speed, "aim_speed", res=0.1)
self.create_slider(self.aim_frame, "Humanization (Smoothness & Wobble)", 1.0, 10.0, self.kinematics.humanization, "humanization", res=0.1)
self.create_slider(self.aim_frame, "Deadzone (Stop jitter near target px)", 0.0, 10.0, self.deadzone, "deadzone", res=0.5)
tk.Checkbutton(self.trig_frame, text="Enable Smart Triggerbot", variable=self.trigger_enabled, bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a", activebackground="#050505", activeforeground="#b042ff", font=("Candara", 10)).pack(anchor="w")
bind_frame_trig = tk.Frame(self.trig_frame, bg="#050505")
bind_frame_trig.pack(fill="x", pady=2)
tk.Label(bind_frame_trig, text="Trigger Bind:", bg="#050505", fg="#e0e0e0").pack(side="left")
self.bind_btn_trig = tk.Button(bind_frame_trig, text=f"Bound: {get_vk_name(self.trigger_key)}", bg="#1a1a1a", fg="#b042ff", bd=0, command=lambda: self.start_keybind('trig'))
self.bind_btn_trig.pack(side="right", padx=5)
self.create_slider(self.trig_frame, "Trigger Hitbox Size (Px)", 1, 20, self.trigger_zone, "trigger_zone", res=1)
self.create_slider(self.trig_frame, "Min Color Density % (Anti-Fake)", 0.05, 1.0, self.trigger_density, "trigger_density", res=0.05)
tk.Label(self.trig_frame, text="- Humanized Reaction Delays -", bg="#050505", fg="#5A5A5A", font=("Candara", 9)).pack(pady=(5, 0))
self.create_slider(self.trig_frame, "Min Reaction (s)", 0.0, 0.15, self.min_reaction, "min_reaction", res=0.01)
self.create_slider(self.trig_frame, "Max Reaction (s)", 0.0, 0.25, self.max_reaction, "max_reaction", res=0.01)
tk.Label(self.vis_frame, text="Computer Vision Constraints", bg="#050505", fg="#e0e0e0", font=("Candara", 10, "bold")).pack(anchor="w", pady=(2,0))
preset_frame = tk.Frame(self.vis_frame, bg="#050505")
preset_frame.pack(fill="x", pady=2)
tk.Label(preset_frame, text="Engine Preset:", bg="#050505", fg="#e0e0e0").pack(side="left")
self.preset_combo = ttk.Combobox(preset_frame, state="readonly", values=["Purple (Tritanopia)", "Red (Default)", "Yellow"])
self.preset_combo.current(0)
self.preset_combo.pack(side="right", padx=5)
self.preset_combo.bind("<<ComboboxSelected>>", self.apply_color_preset)
self.create_inline_color_picker(self.vis_frame, "Target Hex/RGB", "target_color")
self.create_slider(self.vis_frame, "HSV Tolerance Threshold", 0, 100, self.tolerance, "tolerance")
self.create_slider(self.vis_frame, "Morphological Amp (Thickness)", 0, 10, self.color_amp, "color_amp", res=1)
self.create_slider(self.vis_frame, "Min Body Area (Noise Suppress)", 1, 50, self.min_area, "min_area", res=1)
tk.Label(self.capture_frame, text="Data Stream Source", bg="#050505", fg="#e0e0e0", font=("Candara", 10, "bold")).pack(anchor="w", pady=(2,0))
tk.Radiobutton(self.capture_frame, text="Internal Screen (mss) - 1PC", variable=self.capture_source_var, value="screen", bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a").pack(anchor="w")
tk.Radiobutton(self.capture_frame, text="Hardware Capture (PCIe/USB)", variable=self.capture_source_var, value="capture", bg="#050505", fg="#e0e0e0", selectcolor="#1a1a1a").pack(anchor="w")
list_frame = tk.Frame(self.capture_frame, bg="#050505")
list_frame.pack(fill="x", pady=2)
self.device_combo = ttk.Combobox(list_frame, state="readonly", width=35)
self.device_combo.pack(side="left", padx=2)
self.device_combo.bind("<<ComboboxSelected>>", self.on_device_selected)
tk.Button(list_frame, text="↻", bg="#1a1a1a", fg="#b042ff", bd=0, command=self.refresh_devices, width=3).pack(side="left", padx=2)
tk.Checkbutton(self.capture_frame, text="Enable Diagnostic Preview UI", variable=self.show_preview_window, bg="#050505", fg="#00ff00", selectcolor="#1a1a1a", activebackground="#050505", activeforeground="#00ff00", font=("Candara", 10, "bold")).pack(anchor="w", pady=(5,0))
tk.Button(self.capture_frame, text="Initialize Video Pipeline", bg="#1a1a1a", fg="#b042ff", bd=0, command=self.start_capture_card).pack(fill="x", pady=5)
self.capture_status_label = tk.Label(self.capture_frame, textvariable=self.capture_status_text, bg="#050505", fg="#a0a0a0", font=("Candara", 9))
self.capture_status_label.pack(anchor="w")
self.refresh_devices()
tk.Label(self.makcu_frame, text="Hardware HID Interface", bg="#050505", fg="#e0e0e0", font=("Candara", 10, "bold")).pack(anchor="w", pady=(2,0))
self.makcu_status_label = tk.Label(self.makcu_frame, text="Status: Disconnected", bg="#050505", fg="#a0a0a0", font=("Candara", 9))
self.makcu_status_label.pack(anchor="w")
tk.Button(self.makcu_frame, text="Establish Serial Handshake", bg="#1a1a1a", fg="#b042ff", bd=0, command=self.auto_connect_makcu).pack(fill="x", pady=5)
tk.Button(self.makcu_frame, text="Terminate HID Connection", bg="#1a1a1a", fg="#b042ff", bd=0, command=self.disconnect_makcu).pack(fill="x", pady=2)
bottom_frame = tk.Frame(self.root, bg="#050505")
bottom_frame.pack(fill="x", side="bottom", padx=15, pady=5)
self.toggle_btn = tk.Button(bottom_frame, text="START ENGINE", bg="#1a1a1a", fg="#b042ff", font=("Arial", 10, "bold"), bd=0, activebackground="#2a2a2a", activeforeground="#ffffff", command=self.toggle_active, height=2, cursor="hand2")
self.toggle_btn.pack(fill="x")
def make_draggable(self, widget):
widget.bind("<Button-1>", self.on_drag_start)
widget.bind("<B1-Motion>", self.on_drag_motion)
def on_drag_start(self, event):
self.root._drag_data = {"x": event.x, "y": event.y}
def on_drag_motion(self, event):
x = self.root.winfo_x() + (event.x - self.root._drag_data["x"])
y = self.root.winfo_y() + (event.y - self.root._drag_data["y"])
self.root.geometry(f"+{x}+{y}")
def apply_color_preset(self, event=None):
val = self.preset_combo.get()
if "Purple" in val:
self.r_var.set(250); self.g_var.set(100); self.b_var.set(250); self.update_attr("tolerance", 15)
elif "Red" in val:
self.r_var.set(255); self.g_var.set(50); self.b_var.set(50); self.update_attr("tolerance", 15)
elif "Yellow" in val:
self.r_var.set(255); self.g_var.set(255); self.b_var.set(0); self.update_attr("tolerance", 15)
self._sync_color_picker()
def refresh_devices(self):
threading.Thread(target=lambda: self.root.after(0, self._update_device_combo_task), daemon=True).start()
def _update_device_combo_task(self):
devices = self.capture_manager.list_capture_devices()
self.capture_device_list = devices
if self.capture_device_list:
self.device_combo['values'] = self.capture_device_list
self.device_combo.current(0)
self._extract_device_index(self.capture_device_list[0])
else:
self.device_combo['values'] = []
self.capture_status_text.set("No devices found")
def on_device_selected(self, event):
selection_idx = self.device_combo.current()
if 0 <= selection_idx < len(self.capture_device_list):
self._extract_device_index(self.capture_device_list[selection_idx])
def _extract_device_index(self, selected_str):
if ":" in selected_str:
try: self.capture_device_index = int(selected_str.split(":")[0])
except: pass
elif "Device" in selected_str:
try: self.capture_device_index = int(selected_str.split(" ")[1])
except: pass
def start_capture_card(self):
def _start():
if self.capture_source_var.get() == "screen":
self.root.after(0, lambda: self.capture_status_text.set("Ready: Local Screen (mss)"))
else:
w = self.cap_res_width.get(); h = self.cap_res_height.get()
self.root.after(0, lambda: self.capture_status_text.set(f"Connecting ({w}x{h}). Please wait..."))
try:
self.capture_manager.start_capture(self.capture_device_index, width=w, height=h)
time.sleep(2.5)
frame = self.capture_manager.get_frame()
if frame is not None:
real_h, real_w = frame.shape[:2]
self.root.after(0, lambda: self.capture_status_text.set(f"Stream Active: {real_w}x{real_h}"))
else:
self.capture_manager.stop_capture()
self.root.after(0, lambda: self.capture_status_text.set("Stream Error"))
except Exception as e:
self.root.after(0, lambda: self.capture_status_text.set(f"Error: {e}"))
threading.Thread(target=_start, daemon=True).start()
def auto_connect_makcu(self):
def _auto():
try:
if self.makcu.auto_connect():
self.root.after(0, lambda: self.makcu_status_label.config(text="Status: Connected (SDK)", fg="#00ff00"))
else:
self.root.after(0, lambda: messagebox.showerror("Makcu", "Подключение не удалось."))
except Exception as e:
self.root.after(0, lambda: messagebox.showerror("Makcu", str(e)))
threading.Thread(target=_auto, daemon=True).start()
def disconnect_makcu(self):
self.makcu.disconnect()
self.makcu_status_label.config(text="Status: Not connected", fg="#a0a0a0")
def create_inline_color_picker(self, parent, label_text, target_attr):
frame = tk.Frame(parent, bg="#0a0a0a", highlightthickness=1, highlightbackground="#1a1a1a")
frame.pack(fill="x", pady=2)
top_frame = tk.Frame(frame, bg="#0a0a0a")
top_frame.pack(fill="x", padx=5, pady=2)
tk.Label(top_frame, text=label_text, bg="#0a0a0a", fg="#e0e0e0", font=("Candara", 9, "bold")).pack(side="left")
self.preview_canvas = tk.Canvas(top_frame, width=24, height=24, bg="#ffffff", highlightthickness=1, highlightbackground="#36005c", cursor="hand2")
self.preview_canvas.pack(side="right")
self.color_label = tk.Label(top_frame, text="", bg="#0a0a0a", fg="#a0a0a0", font=("Consolas", 8), cursor="hand2")
self.color_label.pack(side="right", padx=10)
sliders_frame = tk.Frame(frame, bg="#0a0a0a")
sliders_frame.pack(fill="x", padx=5, pady=0)
current_rgb = getattr(self, target_attr)
self.r_var, self.g_var, self.b_var = tk.IntVar(value=current_rgb[0]), tk.IntVar(value=current_rgb[1]), tk.IntVar(value=current_rgb[2])
self.target_attr_ref = target_attr
tk.Scale(sliders_frame, from_=0, to=255, variable=self.r_var, orient="horizontal", bg="#0a0a0a", fg="#ff4d4d", troughcolor="#1a0000", highlightthickness=0, bd=0, activebackground="#330000", command=self._on_color_slide, showvalue=0).pack(fill="x")
tk.Scale(sliders_frame, from_=0, to=255, variable=self.g_var, orient="horizontal", bg="#0a0a0a", fg="#4dff4d", troughcolor="#001a00", highlightthickness=0, bd=0, activebackground="#003300", command=self._on_color_slide, showvalue=0).pack(fill="x")
tk.Scale(sliders_frame, from_=0, to=255, variable=self.b_var, orient="horizontal", bg="#0a0a0a", fg="#4d4dff", troughcolor="#00001a", highlightthickness=0, bd=0, activebackground="#000033", command=self._on_color_slide, showvalue=0).pack(fill="x")
self._sync_color_picker()
def _on_color_slide(self, *args):
self._sync_color_picker()
def _sync_color_picker(self):
r, g, b = self.r_var.get(), self.g_var.get(), self.b_var.get()
setattr(self, self.target_attr_ref, (r, g, b))
setattr(self, self.target_attr_ref + '_bgr', (b, g, r))
self._update_hsv_cache()
hex_color = '#%02x%02x%02x' % (r, g, b)
self.color_label.config(text=f"({r},{g},{b}) {hex_color.upper()}")
self.preview_canvas.config(bg=hex_color)
def create_slider(self, parent, label_text, min_val, max_val, default_val, attr_name, res=1):
frame = tk.Frame(parent, bg="#050505")
frame.pack(fill="x", pady=0)
tk.Label(frame, text=label_text, bg="#050505", fg="#b5b5b5", font=("Candara", 9)).pack(anchor="w")
slider = tk.Scale(frame, from_=min_val, to=max_val, resolution=res, orient="horizontal", bg="#050505", fg="#b042ff", troughcolor="#1a1a1a", highlightthickness=0, bd=0, activebackground="#36005c")
if attr_name == "tolerance": self.slider_tol = slider
slider.set(default_val)
slider.pack(fill="x")
slider.config(command=lambda val, a=attr_name: self.update_attr(a, val))
def update_attr(self, attr_name, val):
if attr_name in ['aim_speed', 'humanization']:
setattr(self.kinematics, attr_name, float(val))
else:
setattr(self, attr_name, float(val) if '.' in str(val) else int(float(val)))
if attr_name in ('tolerance', 'trigger_tolerance'):
if hasattr(self, 'slider_tol') and attr_name == "tolerance": self.slider_tol.set(self.tolerance)
self._update_hsv_cache()
def start_keybind(self, target='aim'):
if getattr(self, f'_binding_{target}', False): return
setattr(self, f'_binding_{target}', True)
btn = self.bind_btn_aim if target == 'aim' else self.bind_btn_trig
btn.config(text="Listening...")
threading.Thread(target=self._wait_for_key, args=(target,), daemon=True).start()
def _wait_for_key(self, target):
time.sleep(0.4)
while self.running:
if self.makcu.connected and self.makcu.controller:
for vk_code, makcu_btn in self.makcu.button_mapping.items():
try:
if self.makcu.controller.is_pressed(makcu_btn):
self._apply_bind(target, vk_code)
return
except: pass
for vk in range(1, 256):
if ctypes.windll.user32.GetAsyncKeyState(vk) & 0x8000:
if vk != 0x1B:
self._apply_bind(target, vk)
return
time.sleep(0.01)
def _apply_bind(self, target, vk_code):
if target == 'aim': self.tracking_key = vk_code
else: self.trigger_key = vk_code
btn = self.bind_btn_aim if target == 'aim' else self.bind_btn_trig
val = self.tracking_key if target == 'aim' else self.trigger_key
self.root.after(0, lambda b=btn, v=val: b.config(text=f"Bound: {get_vk_name(v)}"))
setattr(self, f'_binding_{target}', False)
def toggle_active(self):
self.active = not self.active
if self.active:
self.toggle_btn.config(text="HALT ENGINE", fg="#ff3333", bg="#260000", activebackground="#400000")
self.kinematics.reset()
else:
self.toggle_btn.config(text="START ENGINE", fg="#b042ff", bg="#1a1a1a", activebackground="#2a2a2a")
with self.mouse_lock:
self.target_velocity_x = 0.0
self.target_velocity_y = 0.0
def exit_app(self):
self.running = False
self.capture_manager.stop_capture()
self.makcu.disconnect()
self.root.destroy()
sys.exit()
def key_listener(self):
while self.running:
if win32api.GetAsyncKeyState(win32con.VK_END) & 1:
if self.visible: self.root.after(0, self.root.withdraw)
else:
self.root.after(0, self.root.deiconify)
self.root.after(0, self.root.lift)
self.root.after(0, self.root.focus_force)
self.visible = not self.visible
time.sleep(0.1)
def setup_overlay(self):
self.overlay = tk.Toplevel(self.root)
self.overlay.overrideredirect(True)
self.overlay.attributes("-topmost", True)
self.overlay.attributes("-transparentcolor", "black")
self.overlay.config(bg="black")
self.overlay.geometry(f"{self.screen_width}x{self.screen_height}+0+0")
hwnd = ctypes.windll.user32.GetParent(self.overlay.winfo_id())
style = ctypes.windll.user32.GetWindowLongW(hwnd, -20)
ctypes.windll.user32.SetWindowLongW(hwnd, -20, style | 0x00080000 | 0x00000020)
self.overlay_canvas = tk.Canvas(self.overlay, width=self.screen_width, height=self.screen_height, bg="black", highlightthickness=0)
self.overlay_canvas.pack()
self.overlay_circle = self.overlay_canvas.create_oval(0, 0, 0, 0, outline="white", width=1)
self.last_fov = self.fov; self.last_show = self.show_fov_overlay.get()
self.update_overlay_loop()
def update_overlay_loop(self):
current_show = self.show_fov_overlay.get()
current_fov = self.fov
if current_show != self.last_show or current_fov != self.last_fov:
if current_show:
self.overlay.deiconify()
r = int(current_fov) // 2
self.overlay_canvas.coords(self.overlay_circle, self.local_center_x - r, self.local_center_y - r, self.local_center_x + r, self.local_center_y + r)
else:
self.overlay.withdraw()
self.last_show = current_show; self.last_fov = current_fov
self.root.after(200, self.update_overlay_loop)
def update_preview_ui_loop(self):
if self.show_preview_window.get() and PILLOW_AVAILABLE:
if self.preview_tk_window is None or not self.preview_tk_window.winfo_exists():
self.preview_tk_window = tk.Toplevel(self.root)
self.preview_tk_window.title("Diagnostic Pipeline Preview")
self.preview_tk_window.attributes("-topmost", True)
self.preview_tk_window.geometry("300x300")
self.preview_tk_window.configure(bg="black")
self.preview_tk_label = tk.Label(self.preview_tk_window, bg="black")
self.preview_tk_label.pack(fill="both", expand=True)
if self._latest_preview_frame is not None:
rgb_img = cv2.cvtColor(self._latest_preview_frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(rgb_img)
imgtk = ImageTk.PhotoImage(image=img)
self.preview_tk_label.config(image=imgtk)
self.preview_tk_label.image = imgtk
else:
if self.preview_tk_window is not None and self.preview_tk_window.winfo_exists():
self.preview_tk_window.destroy()
self.preview_tk_window = None; self.preview_tk_label = None
self.root.after(30, self.update_preview_ui_loop)
def mouse_movement_loop(self):
while self.running:
if not self.active or not self.tracking_enabled.get():
time.sleep(0.01)
self.last_mouse_send_time = time.perf_counter()
continue
current_time = time.perf_counter()
dt = current_time - self.last_mouse_send_time
if dt >= self.mouse_send_interval:
with self.mouse_lock:
v_x = self.target_velocity_x
v_y = self.target_velocity_y
if v_x != 0.0 or v_y != 0.0:
move_px_x = v_x * dt
move_px_y = v_y * dt
step_x = int(round(move_px_x + self.remainder_x))
step_y = int(round(move_px_y + self.remainder_y))
self.remainder_x = (move_px_x + self.remainder_x) - step_x
self.remainder_y = (move_px_y + self.remainder_y) - step_y
if step_x != 0 or step_y != 0:
if self.makcu.connected:
self.makcu.send_mouse_move(step_x, step_y)
else:
ctypes.windll.user32.mouse_event(MOUSEEVENTF_MOVE, step_x, step_y, 0, 0)
else:
self.remainder_x = 0.0; self.remainder_y = 0.0
self.last_mouse_send_time = current_time
else:
time.sleep(0.001)
def trigger_worker(self):
while self.running:
try:
item = self.trigger_queue.get(timeout=0.1)
if item is None: continue
self._do_trigger()
self.trigger_queue.task_done()
except queue.Empty: pass
def _do_trigger(self):
mean_reaction = (self.min_reaction + self.max_reaction) / 2.0
if mean_reaction > 0:
reaction = random.lognormvariate(math.log(mean_reaction), 0.5)
reaction = max(self.min_reaction, min(self.max_reaction, reaction))
else:
reaction = 0.0
if reaction > 0.002: time.sleep(reaction)
shots = random.choices([1, 2, 3], weights=[70, 20, 10])[0]
for _ in range(shots):
if self.makcu.connected:
self.makcu.send_mouse_click(0x01, True)
time.sleep(0.03)
self.makcu.send_mouse_click(0x01, False)
else:
ctypes.windll.user32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
time.sleep(0.03)
ctypes.windll.user32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
time.sleep(random.uniform(0.06, 0.12))
self.is_clicking = False
def execute_trigger(self):
if self.is_clicking: return
self.is_clicking = True
self.trigger_queue.put(True)
def logic_loop(self):
with mss.mss() as sct:
current_roi_size = self.fov
while self.running:
if not self.active:
time.sleep(0.05)
self.last_aim_time = time.perf_counter()
continue
loop_start = time.perf_counter()
max_fov = int(self.fov)
if current_roi_size > max_fov: current_roi_size = max_fov
offset_x, offset_y = 0, 0
micro_roi_active = False
if self.capture_source_var.get() == "screen":
left = max(0, self.local_center_x - max_fov // 2)
top = max(0, self.local_center_y - max_fov // 2)
width = min(max_fov, self.screen_width - left)
height = min(max_fov, self.screen_height - top)
if width <= 0 or height <= 0: continue
region = {"left": left, "top": top, "width": width, "height": height}
try:
img_data = sct.grab(region)
img_bgra = np.frombuffer(img_data.bgra, dtype=np.uint8).reshape((height, width, 4))
full_bgr = img_bgra[:, :, :3]
except: continue
else:
if not self.capture_manager.is_active():
time.sleep(0.05)
continue
frame = self.capture_manager.get_frame()
if frame is None: continue
h, w = frame.shape[:2]
cx, cy = w // 2, h // 2
half_fov = max_fov // 2
x1 = max(0, cx - half_fov); y1 = max(0, cy - half_fov)
x2 = min(w, cx + half_fov); y2 = min(h, cy + half_fov)
full_bgr = frame[y1:y2, x1:x2]
if 'full_bgr' in locals() and full_bgr.size > 0:
act_h, act_w = full_bgr.shape[:2]
center_x, center_y = act_w // 2, act_h // 2
if self.tracking_enabled.get() and self.target_lost_frames < 10 and self.last_raw_target_x is not None:
target_x_int = int(self.last_raw_target_x)
target_y_int = int(self.last_raw_target_y)
half_roi = current_roi_size // 2
mx1 = max(0, target_x_int - half_roi); my1 = max(0, target_y_int - half_roi)
mx2 = min(act_w, target_x_int + half_roi); my2 = min(act_h, target_y_int + half_roi)
search_bgr = full_bgr[my1:my2, mx1:mx2]
offset_x, offset_y = mx1, my1
micro_roi_active = True
else:
search_bgr = full_bgr
hsv_search = cv2.cvtColor(search_bgr, cv2.COLOR_BGR2HSV)
debug_target_pos = None
if self.trigger_enabled.get() and self.makcu.is_button_pressed(self.trigger_key):
trigger_zone_size = int(self.trigger_zone)
local_cx = center_x - offset_x
local_cy = center_y - offset_y
if 0 <= local_cx <= search_bgr.shape[1] and 0 <= local_cy <= search_bgr.shape[0]:
y1_t = max(0, local_cy - trigger_zone_size)
y2_t = min(search_bgr.shape[0], local_cy + trigger_zone_size)
x1_t = max(0, local_cx - trigger_zone_size)
x2_t = min(search_bgr.shape[1], local_cx + trigger_zone_size)
hsv_area = hsv_search[y1_t:y2_t, x1_t:x2_t]
trig_mask = self.apply_mask(hsv_area, self.trig_bounds, self.color_amp)
if trig_mask is not None:
non_zero = cv2.countNonZero(trig_mask)
total_pixels = trig_mask.shape[0] * trig_mask.shape[1]
if total_pixels > 0 and (non_zero / total_pixels) >= self.trigger_density:
self.execute_trigger()
if self.tracking_enabled.get():
now = time.perf_counter()
dt_vision = now - self.last_aim_time
self.last_aim_time = now
if self.makcu.is_button_pressed(self.tracking_key):
mask = self.apply_mask(hsv_search, self.aim_bounds, self.color_amp)
target_found_this_frame = False
if mask is not None and cv2.countNonZero(mask) > 0:
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
best_score = float('inf')
best_contour = None
for c in contours:
x, y, w_box, h_box = cv2.boundingRect(c)
if (w_box * h_box) < self.min_area: continue
cx_box = x + w_box / 2 + offset_x
cy_box = y + h_box / 2 + offset_y
dist_to_center = math.hypot(cx_box - center_x, cy_box - center_y)
if dist_to_center > (max_fov / 2): continue # Игнорирование за пределами FOV
score = dist_to_center
# АБСОЛЮТНАЯ ПЕРСИСТЕНТНОСТЬ (Target Lock):
# Если враг уже взят на мушку, мы игнорируем любые другие цели рядом
if self.last_raw_target_x is not None:
dist_to_last = math.hypot(cx_box - self.last_raw_target_x, cy_box - self.last_raw_target_y)
if dist_to_last < 40:
score -= 10000.0
if score < best_score:
best_score = score
best_contour = c
if best_contour is not None:
target_found_this_frame = True
self.target_lost_frames = 0
x, y, w_box, h_box = cv2.boundingRect(best_contour)
if self.target_bone_var.get() == "HEAD":
try:
# Захват самой верхней точки и сдвиг вниз в область шеи/глаз
topmost = tuple(best_contour[best_contour[:, :, 1].argmin()][0])
dynamic_offset = int(h_box * 0.12)
target_x = topmost[0] + offset_x
target_y = topmost[1] + offset_y + dynamic_offset
except IndexError:
target_x = x + w_box / 2 + offset_x
target_y = y + h_box * 0.15 + offset_y
else:
M = cv2.moments(best_contour)
if M["m00"] != 0:
target_x = (M["m10"] / M["m00"]) + offset_x
target_y = (M["m01"] / M["m00"]) + offset_y
else:
target_x = x + w_box / 2 + offset_x
target_y = y + h_box / 2 + offset_y
debug_target_pos = (target_x, target_y)
vel_x, vel_y = self.kinematics.update(target_x, target_y, center_x, center_y, dt_vision)
speed = math.hypot(vel_x, vel_y)
if speed > self.max_speed:
vel_x = (vel_x / speed) * self.max_speed
vel_y = (vel_y / speed) * self.max_speed
with self.mouse_lock:
self.target_velocity_x = vel_x
self.target_velocity_y = vel_y
self.last_raw_target_x = target_x
self.last_raw_target_y = target_y
current_roi_size = max(80, int(max_fov * 0.4))
if not target_found_this_frame:
self.target_lost_frames += 1
with self.mouse_lock:
self.target_velocity_x = 0.0; self.target_velocity_y = 0.0
if self.target_lost_frames > 10:
self.kinematics.reset()
self.last_raw_target_x = None
current_roi_size = max_fov
else:
self.target_lost_frames += 1
with self.mouse_lock:
self.target_velocity_x = 0.0; self.target_velocity_y = 0.0
self.kinematics.reset()
self.last_raw_target_x = None
current_roi_size = max_fov
if self.show_preview_window.get():
m_rect = (offset_x, offset_y, search_bgr.shape[1], search_bgr.shape[0]) if micro_roi_active else None
self._prepare_preview(full_bgr, center_x, center_y, debug_target_pos, m_rect)
elapsed = time.perf_counter() - loop_start
sleep_time = self.scan_interval - elapsed
if sleep_time > 0: time.sleep(sleep_time)
def _prepare_preview(self, img_bgr, cx, cy, target_pos, micro_rect):
debug_frame = img_bgr.copy()
cv2.line(debug_frame, (cx, 0), (cx, debug_frame.shape[0]), (50, 50, 50), 1)
cv2.line(debug_frame, (0, cy), (debug_frame.shape[1], cy), (50, 50, 50), 1)
if micro_rect is not None:
mx, my, mw, mh = micro_rect
cv2.rectangle(debug_frame, (mx, my), (mx + mw, my + mh), (255, 100, 0), 1)
if target_pos is not None:
cv2.circle(debug_frame, (int(target_pos[0]), int(target_pos[1])), 4, (0, 255, 0), -1)
cv2.line(debug_frame, (cx, cy), (int(target_pos[0]), int(target_pos[1])), (0, 255, 0), 1)
scale = 3
self._latest_preview_frame = cv2.resize(debug_frame, (debug_frame.shape[1]*scale, debug_frame.shape[0]*scale), interpolation=cv2.INTER_NEAREST)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
bot = StealthColorBot()
bot.run()