- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 104
- Reaction score
- 7
Had to dump this manually again to keep it clean for the community. I have been messing around with this GunBound aimbot project and finally got a stable build that isn't crashing every five minutes. It is a Python-based external using PyQt5 for the UI and Tesseract for OCR-based screen reading.
Tech Overview:
This is an external approach (Ring 3/RPM) that relies on screen capturing to detect game elements like wind, angle, and power values. It processes the coordinates, calculates the trajectory, and automates inputs via Windows API.
Compile & Setup:
You will need to ensure all the v25_*_ULTRA_FINAL modules are in your local directory. If you are planning to package this for distribution, I recommend using Nuitka over PyInstaller to handle the obfuscation better, as standard scripts get flagged by simple heuristics.
Repository:
Dev Note:
The logic currently uses a 200ms debounce on hotkeys to prevent input spam. The trajectory calculations are handled via a physics wrapper. I have included a setup wizard to handle the initial window detection, so it should be relatively plug-and-play if you are running at standard resolutions.
I’m currently looking into refining the OCR fallback logic since it can be inconsistent during high-intensity matches. If any of you coders here have ideas on how to optimize the `detect_enemy_position` loop to reduce the frame-time or have a better way to hook the game memory without triggering detection, drop your fixes below.
Has anyone tested this on the current server version? Let me know if you run into any weird behavior with the auto-shoot timing.
Tech Overview:
This is an external approach (Ring 3/RPM) that relies on screen capturing to detect game elements like wind, angle, and power values. It processes the coordinates, calculates the trajectory, and automates inputs via Windows API.
- Architecture: Python + PyQt5 interface with a threaded worker for low-latency processing.
- Performance: Integrated daemon flag for proper cleanup, state saving on close, and event-based sleep to avoid high CPU usage.
- Features: Manual targeting mode, auto-shoot with randomized delay for humanization, and a smoothed detection logic (70/30 weight).
- Dependencies: Requires Tesseract-OCR properly mapped in your PATH.
Compile & Setup:
You will need to ensure all the v25_*_ULTRA_FINAL modules are in your local directory. If you are planning to package this for distribution, I recommend using Nuitka over PyInstaller to handle the obfuscation better, as standard scripts get flagged by simple heuristics.
Code:
# Basic dependency check
pip install PyQt5 pyautogui pytesseract
Python:
"""
GunBound Aimbot v25.0 - GUI FINAL
FIXES IN THIS VERSION:
✅ 1. Worker with daemon flag (terminates correctly)
✅ 2. Protection against multiple activations
✅ 3. Sleep with event (more efficient)
✅ 4. State saving on closure
COMPLETE FEATURES:
✅ COMPLETE worker thread with all fixes
✅ Debounce on all hotkeys (200ms)
✅ Integrated ManualTargetingMode
✅ Integrated overlay WITH ALL fixes
✅ Complete validations
✅ Complete thread-safety
✅ Value fallback
✅ Human variation
✅ Setup wizard
✅ Professional interface
✅ Dark mode
✅ Tray icon
"""
import sys
import threading
import time
import random
import pyautogui
from pathlib import Path
from typing import Optional, Dict
from datetime import datetime
import os
import json
# PyQt5
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QSlider, QComboBox, QCheckBox, QSpinBox,
QTextEdit, QTabWidget, QTableWidget, QTableWidgetItem, QProgressBar,
QSystemTrayIcon, QMenu, QDialog, QFileDialog, QMessageBox,
QSplitter, QGroupBox, QGridLayout, QFrame, QShortcut
)
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QThread, QSize, QPoint
from PyQt5.QtGui import QIcon, QColor, QFont, QPixmap, QImage, QKeySequence
# ✅ Imports atualizados para versões ULTRA FINAL
try:
from v25_config_ULTRA_FINAL import Config, detect_game_window, auto_calibrate, load_config, save_config
from v25_capture_ULTRA_FINAL import (
capture_screen,
recognize_wind_from_image,
recognize_angle_from_image,
recognize_power_from_image,
detect_enemy_position,
detect_player_position,
validate_ocr_result
)
from v25_overlay_ULTRA_ULTRA_FINAL import OverlayManager
from v25_physics_wrapper import PhysicsEngine, calculate_trajectory
from v25_automation_WIN_API_ULTRA_FINAL import WindowsAPIAutomation
from v25_utils import setup_logger
except ImportError as e:
print(f"⚠️ ERRO DE IMPORT: {e}")
print("Certifique-se que todos os arquivos v25_*_ULTRA_FINAL.py estão presentes!")
sys.exit(1)
# ═══════════════════════════════════════════════════════════════
# ✅ DETECÇÃO DINÂMICA DE TESSERACT (corrigido!)
# ═══════════════════════════════════════════════════════════════
def find_tesseract() -> bool:
"""Detecta Tesseract dinamicamente"""
import pytesseract
possible_paths = [
r'C:\Program Files\Tesseract-OCR\tesseract.exe',
r'C:\Arquivos de Programas\Tesseract-OCR\tesseract.exe',
r'C:\Program Files (x86)\Tesseract-OCR\tesseract.exe',
r'C:\Users\{}\AppData\Local\Tesseract-OCR\tesseract.exe'.format(
os.getenv('USERNAME', 'User')
),
'/usr/bin/tesseract',
'/usr/local/bin/tesseract',
]
for path in possible_paths:
if os.path.exists(path):
pytesseract.pytesseract.tesseract_cmd = path
print(f"✅ Tesseract encontrado: {path}")
return True
try:
import subprocess
result = subprocess.run(['tesseract', '--version'], capture_output=True, timeout=2)
if result.returncode == 0:
print("✅ Tesseract encontrado no PATH")
return True
except:
pass
return False
# ═══════════════════════════════════════════════════════════════
# MANUAL TARGETING MODE
# ═══════════════════════════════════════════════════════════════
class ManualTargetingMode:
"""Modo de mira manual (F2)"""
def __init__(self):
self.manual_target = None
self.locked = False
def set_target(self, x: int, y: int):
self.manual_target = (x, y)
self.locked = True
print(f"🎯 Alvo manual travado: ({x}, {y})")
def clear_target(self):
self.manual_target = None
self.locked = False
print("🔓 Alvo manual desbloqueado")
def get_target(self):
return self.manual_target if self.locked else None
# ═══════════════════════════════════════════════════════════════
# ✅ WORKER THREAD COM TODAS AS CORREÇÕES
# ═══════════════════════════════════════════════════════════════
class AimbotWorker(QThread):
"""✅ Worker COM TODAS AS CORREÇÕES APLICADAS!"""
# Signals
performance_updated = pyqtSignal(dict)
detection_updated = pyqtSignal(dict)
shot_info_updated = pyqtSignal(dict)
log_message = pyqtSignal(str)
def __init__(self, config: Config, game_window: dict, overlay_manager):
super().__init__()
# ✅ QThread não precisa de setDaemon - usa quit() e wait()
self.config = config
self.game_window = game_window
self.overlay_manager = overlay_manager
# Estado
self.running = True
self.enabled = False
# ✅ CORREÇÃO 3: EVENTO PARA CONTROLE EFICIENTE!
self.resume_event = threading.Event()
# Physics
self.physics = PhysicsEngine()
# Automation
self.automation = WindowsAPIAutomation()
# Manual mode
self.manual_mode = ManualTargetingMode()
# Auto-shoot
self.auto_shoot_enabled = False
self.last_shot_time = 0
# Fallback values
self.last_valid_wind = (0, "none")
self.last_valid_angle = 45
self.last_valid_power = 50
# Smoothing
self.player_positions = []
self.enemy_positions = []
# Performance
self.frame_count = 0
self.loop_times = []
def enable(self):
"""Habilita worker"""
self.enabled = True
self.resume_event.set() # ✅ Acorda thread
self.log_message.emit("✅ Aimbot ATIVADO")
def disable(self):
"""Desabilita worker"""
self.enabled = False
self.resume_event.clear()
self.log_message.emit("⏸️ Aimbot PAUSADO")
def toggle_auto_shoot(self):
"""Toggle auto-shoot"""
self.auto_shoot_enabled = not self.auto_shoot_enabled
status = "ON" if self.auto_shoot_enabled else "OFF"
self.log_message.emit(f"🎯 Auto-shoot: {status}")
def stop(self):
"""Para worker"""
self.running = False
self.resume_event.set() # ✅ Desbloqueia se estiver esperando
self.wait()
def run(self):
"""✅ Loop principal COM EVENTO (mais eficiente)"""
self.log_message.emit("🚀 Worker iniciado")
while self.running:
loop_start = time.time()
# ✅ CORREÇÃO 3: ESPERA COM EVENTO (não sleep fixo!)
if not self.enabled:
self.resume_event.wait(timeout=0.1) # ✅ Mais eficiente!
continue
try:
# Capturar tela
hwnd = self.game_window['hwnd']
screenshot = capture_screen(hwnd)
if screenshot is None:
self.log_message.emit("⚠️ Falha ao capturar tela")
time.sleep(0.1)
continue
# ✅ OCR com FALLBACK
wind_result = recognize_wind_from_image(screenshot, self.config.WIND_ROI)
if validate_ocr_result(wind_result, 'wind'):
self.last_valid_wind = wind_result
wind_speed, wind_dir = self.last_valid_wind
angle_result = recognize_angle_from_image(screenshot, self.config.PLAYER_ANGLE_ROI)
if validate_ocr_result(angle_result, 'angle'):
self.last_valid_angle = angle_result
current_angle = self.last_valid_angle
power_result = recognize_power_from_image(screenshot, self.config.POWER_ROI)
if validate_ocr_result(power_result, 'power'):
self.last_valid_power = power_result
current_power = self.last_valid_power
# ✅ Detecção com SMOOTHING (70/30)
player_pos = detect_player_position(screenshot, self.config.GAME_AREA_ROI)
if player_pos:
self.player_positions.append(player_pos)
if len(self.player_positions) > 5:
self.player_positions.pop(0)
enemy_pos = detect_enemy_position(
screenshot,
self.config.GAME_AREA_ROI,
frame_count=self.frame_count # ✅ Sem global!
)
if enemy_pos:
self.enemy_positions.append(enemy_pos)
if len(self.enemy_positions) > 5:
self.enemy_positions.pop(0)
# Smoothed positions
smoothed_player = self._get_smoothed_position(self.player_positions)
smoothed_enemy = self._get_smoothed_position(self.enemy_positions)
# ✅ Manual target mode
target_pos = self.manual_mode.get_target() or smoothed_enemy
# Calcular shot
if smoothed_player and target_pos:
shot_angle, shot_power, trajectory = calculate_trajectory(
smoothed_player,
target_pos,
wind_speed,
wind_dir,
self.physics
)
# ✅ Verificar se encontrou solução
if shot_angle is None or shot_power is None:
# Não encontrou solução - pular este frame
time.sleep(0.05)
continue
# ✅ Validar ranges
shot_angle = max(0, min(90, shot_angle))
shot_power = max(0, min(100, shot_power))
# ✅ Update overlay (com verificação None!)
if self.overlay_manager is not None:
self.overlay_manager.update_trajectory(trajectory)
self.overlay_manager.update_player(*smoothed_player)
self.overlay_manager.update_target(*target_pos)
self.overlay_manager.update_wind(wind_speed, wind_dir)
self.overlay_manager.update_shot_info(shot_angle, shot_power)
self.overlay_manager.update_player_power(current_power)
# Status
if self.manual_mode.locked:
status = "Aimbot: ON (ALVO TRAVADO)"
else:
status = "Aimbot: ON"
self.overlay_manager.set_status(status)
# Emit signals
self.shot_info_updated.emit({
'angle': shot_angle,
'power': shot_power,
'wind_speed': wind_speed,
'wind_direction': wind_dir
})
self.detection_updated.emit({
'player': smoothed_player,
'enemy': target_pos,
'manual_locked': self.manual_mode.locked
})
# ✅ Auto-shoot com VARIAÇÃO HUMANA
if self.auto_shoot_enabled:
now = time.time()
cooldown = random.uniform(2.5, 3.5) # ✅ Variação!
if now - self.last_shot_time >= cooldown:
success = self.automation.perform_shot(
hwnd,
current_angle,
shot_angle,
shot_power
)
if success:
self.last_shot_time = now
self.log_message.emit(f"🎯 TIRO! Ângulo: {shot_angle}°, Força: {shot_power}%")
# Performance
loop_time = time.time() - loop_start
self.loop_times.append(loop_time)
if len(self.loop_times) > 30:
self.loop_times.pop(0)
avg_loop_time = sum(self.loop_times) / len(self.loop_times)
fps = 1.0 / avg_loop_time if avg_loop_time > 0 else 0
self.performance_updated.emit({
'fps': fps,
'loop_time': loop_time * 1000 # ms
})
self.frame_count += 1
# ✅ Sleep mínimo
time.sleep(0.05)
except Exception as e:
self.log_message.emit(f"❌ Erro no loop: {e}")
import traceback
traceback.print_exc()
time.sleep(0.1)
self.log_message.emit("🛑 Worker parado")
def _get_smoothed_position(self, positions):
"""Smoothing 70/30"""
if not positions:
return None
if len(positions) == 1:
return positions[0]
# 70% último, 30% anterior
last = positions[-1]
prev = positions[-2]
smoothed_x = int(last[0] * 0.7 + prev[0] * 0.3)
smoothed_y = int(last[1] * 0.7 + prev[1] * 0.3)
return (smoothed_x, smoothed_y)
# ═══════════════════════════════════════════════════════════════
# FIRST RUN DIALOG
# ═══════════════════════════════════════════════════════════════
class FirstRunDialog(QDialog):
"""Dialog de primeira execução"""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("GunBound Aimbot - Primeira Execução")
self.setModal(True)
self.setMinimumSize(600, 400)
self.game_window = None
self.config = None
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout()
# Header
header = QLabel("🎮 BEM-VINDO AO GUNBOUND AIMBOT v25.0 ULTRA FINAL")
header.setStyleSheet("font-size: 16px; font-weight: bold; color: #4CAF50;")
header.setAlignment(Qt.AlignCenter)
layout.addWidget(header)
# Info
info = QLabel(
"Este assistente irá configurar o aimbot automaticamente.\n\n"
"1. Abra o GunBound\n"
"2. Entre em uma partida\n"
"3. Clique em 'Detectar Janela'"
)
info.setWordWrap(True)
layout.addWidget(info)
# Status
self.status_label = QLabel("Status: Aguardando...")
self.status_label.setStyleSheet("color: #FFA726;")
layout.addWidget(self.status_label)
# Buttons
btn_layout = QHBoxLayout()
self.btn_detect = QPushButton("🔍 Detectar Janela")
self.btn_detect.clicked.connect(self.detect_window)
btn_layout.addWidget(self.btn_detect)
self.btn_ok = QPushButton("✅ Continuar")
self.btn_ok.clicked.connect(self.accept)
self.btn_ok.setEnabled(False)
btn_layout.addWidget(self.btn_ok)
layout.addLayout(btn_layout)
self.setLayout(layout)
def detect_window(self):
self.status_label.setText("🔍 Procurando janela do GunBound...")
QApplication.processEvents()
time.sleep(0.5)
game_window = detect_game_window()
if game_window:
self.game_window = game_window
self.config = auto_calibrate(game_window)
self.status_label.setText(
f"✅ Janela detectada: {game_window['title']}\n"
f" Resolução: {game_window['rect'][2]}x{game_window['rect'][3]}"
)
self.status_label.setStyleSheet("color: #4CAF50;")
self.btn_ok.setEnabled(True)
else:
self.status_label.setText(
"❌ Janela não encontrada!\n"
" Certifique-se que o GunBound está aberto e em uma partida."
)
self.status_label.setStyleSheet("color: #F44336;")
# ═══════════════════════════════════════════════════════════════
# ✅ DASHBOARD PRINCIPAL COM TODAS AS CORREÇÕES
# ═══════════════════════════════════════════════════════════════
class AimbotDashboard(QMainWindow):
"""✅ Dashboard COM TODAS AS CORREÇÕES!"""
def __init__(self):
super().__init__()
# State
self.config = None
self.game_window = None
self.overlay_manager = None
self.worker = None
# Debounce
self.last_f1_time = 0
self.last_f2_time = 0
self.last_f3_time = 0
# Setup
self.setWindowTitle("GunBound Aimbot v25.0 ULTRA FINAL")
self.setMinimumSize(1200, 800)
# ✅ First run (ANTES de criar overlay!)
if not self.first_run():
sys.exit(0)
# ✅ UI setup
self.setup_ui()
# ✅ CORREÇÃO: Overlay DEPOIS de QApplication!
self.create_overlay()
# ✅ Hotkeys (QShortcut!)
self.setup_hotkeys()
# ✅ Tray icon
self.setup_tray()
# Logger
self.logger = setup_logger()
def first_run(self) -> bool:
"""Wizard de primeira execução"""
# Verificar Tesseract
if not find_tesseract():
QMessageBox.critical(
self,
"Tesseract não encontrado",
"Tesseract-OCR não foi encontrado!\n\n"
"Baixe e instale de:\n"
"https://github.com/UB-Mannheim/tesseract/wiki"
)
return False
# Carregar config
self.config = load_config()
if self.config is None or not self.config.is_calibrated():
# Primeira execução - wizard
dialog = FirstRunDialog(self)
if dialog.exec_() == QDialog.Accepted:
self.game_window = dialog.game_window
self.config = dialog.config
return True
else:
return False
else:
# Config já existe - detectar janela
self.game_window = detect_game_window()
if not self.game_window:
QMessageBox.warning(
self,
"Janela não encontrada",
"Abra o GunBound e entre em uma partida antes de iniciar o aimbot."
)
return False
# Recalibrar se necessário
current_rect = self.game_window['rect']
if current_rect != getattr(self, '_last_rect', None):
self.config.calibrate_from_window(self.game_window)
save_config(self.config)
self._last_rect = current_rect
return True
def create_overlay(self):
"""✅ Cria overlay DEPOIS de QApplication"""
try:
self.overlay_manager = OverlayManager(self.game_window)
self.overlay_manager.show()
self.log("✅ Overlay criado e ativado")
except Exception as e:
self.log(f"⚠️ Erro ao criar overlay: {e}")
self.overlay_manager = None
def setup_ui(self):
"""Setup da interface"""
central = QWidget()
self.setCentralWidget(central)
main_layout = QHBoxLayout()
# ═══ PAINEL ESQUERDO ═══
left_panel = QWidget()
left_layout = QVBoxLayout()
# Header
header = QLabel("🎮 GUNBOUND AIMBOT v25.0")
header.setStyleSheet(
"font-size: 20px; font-weight: bold; "
"color: #4CAF50; padding: 10px;"
)
header.setAlignment(Qt.AlignCenter)
left_layout.addWidget(header)
# Controls
controls_group = QGroupBox("⚙️ Controles")
controls_layout = QVBoxLayout()
self.btn_toggle = QPushButton("🎯 LIGAR AIMBOT (F1)")
self.btn_toggle.setCheckable(True)
self.btn_toggle.setMinimumHeight(50)
self.btn_toggle.setStyleSheet(self.get_button_style(False))
self.btn_toggle.clicked.connect(self.toggle_aimbot)
controls_layout.addWidget(self.btn_toggle)
self.btn_set_target = QPushButton("📍 Definir Alvo Manual (F2)")
self.btn_set_target.setMinimumHeight(40)
self.btn_set_target.clicked.connect(self.set_manual_target)
controls_layout.addWidget(self.btn_set_target)
self.btn_calculate = QPushButton("🧮 Calcular Tiro (F3)")
self.btn_calculate.setMinimumHeight(40)
self.btn_calculate.clicked.connect(self.calculate_shot)
controls_layout.addWidget(self.btn_calculate)
self.chk_auto_shoot = QCheckBox("🎯 Auto-Shoot (F4)")
self.chk_auto_shoot.stateChanged.connect(self.toggle_auto_shoot)
controls_layout.addWidget(self.chk_auto_shoot)
controls_group.setLayout(controls_layout)
left_layout.addWidget(controls_group)
# Status
status_group = QGroupBox("📊 Status")
status_layout = QVBoxLayout()
self.lbl_status = QLabel("Status: Desligado")
self.lbl_fps = QLabel("FPS: --")
self.lbl_loop_time = QLabel("Loop: -- ms")
status_layout.addWidget(self.lbl_status)
status_layout.addWidget(self.lbl_fps)
status_layout.addWidget(self.lbl_loop_time)
status_group.setLayout(status_layout)
left_layout.addWidget(status_group)
# Detection
detection_group = QGroupBox("🎯 Detecção")
detection_layout = QVBoxLayout()
self.lbl_player = QLabel("Jogador: --")
self.lbl_enemy = QLabel("Inimigo: --")
self.lbl_manual = QLabel("Manual: --")
detection_layout.addWidget(self.lbl_player)
detection_layout.addWidget(self.lbl_enemy)
detection_layout.addWidget(self.lbl_manual)
detection_group.setLayout(detection_layout)
left_layout.addWidget(detection_group)
# Shot Info
shot_group = QGroupBox("🎯 Tiro")
shot_layout = QVBoxLayout()
self.lbl_angle = QLabel("Ângulo: --")
self.lbl_power = QLabel("Força: --")
self.lbl_wind = QLabel("Vento: --")
shot_layout.addWidget(self.lbl_angle)
shot_layout.addWidget(self.lbl_power)
shot_layout.addWidget(self.lbl_wind)
shot_group.setLayout(shot_layout)
left_layout.addWidget(shot_group)
left_layout.addStretch()
left_panel.setLayout(left_layout)
left_panel.setMaximumWidth(350)
# ═══ PAINEL DIREITO (LOGS) ═══
right_panel = QWidget()
right_layout = QVBoxLayout()
log_header = QLabel("📋 Logs")
log_header.setStyleSheet("font-size: 14px; font-weight: bold;")
right_layout.addWidget(log_header)
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
self.log_text.setStyleSheet(
"background: #1E1E1E; color: #E0E0E0; "
"font-family: Consolas; font-size: 11px;"
)
right_layout.addWidget(self.log_text)
right_panel.setLayout(right_layout)
# Adicionar ao main
main_layout.addWidget(left_panel)
main_layout.addWidget(right_panel)
central.setLayout(main_layout)
# Dark theme
self.apply_dark_theme()
self.log("✅ Interface inicializada")
def setup_hotkeys(self):
"""✅ Setup com QShortcut (não keyboard library!)"""
# F1 - Toggle
QShortcut(QKeySequence("F1"), self).activated.connect(self.toggle_aimbot_hotkey)
# F2 - Set target
QShortcut(QKeySequence("F2"), self).activated.connect(self.set_manual_target_hotkey)
# F3 - Calculate
QShortcut(QKeySequence("F3"), self).activated.connect(self.calculate_shot_hotkey)
# F4 - Auto-shoot
QShortcut(QKeySequence("F4"), self).activated.connect(self.toggle_auto_shoot_hotkey)
self.log("✅ Hotkeys configurados (F1-F4)")
def setup_tray(self):
"""Setup tray icon"""
try:
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setToolTip("GunBound Aimbot v25.0")
tray_menu = QMenu()
show_action = tray_menu.addAction("Mostrar")
show_action.triggered.connect(self.show)
quit_action = tray_menu.addAction("Sair")
quit_action.triggered.connect(self.close)
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.show()
except:
pass
# ═══════════════════════════════════════════════════════════════
# ✅ CORREÇÃO 2: PROTEÇÃO CONTRA MÚLTIPLAS ATIVAÇÕES
# ═══════════════════════════════════════════════════════════════
def toggle_aimbot(self):
"""Toggle aimbot COM PROTEÇÃO"""
if self.btn_toggle.isChecked():
# ✅ VERIFICAR se worker já existe
if self.worker is not None:
self.log("⚠️ Worker já está rodando!")
self.btn_toggle.setChecked(False)
return
# Criar worker
self.worker = AimbotWorker(
self.config,
self.game_window,
self.overlay_manager
)
# Conectar signals
self.worker.performance_updated.connect(self.update_performance)
self.worker.detection_updated.connect(self.update_detection)
self.worker.shot_info_updated.connect(self.update_shot_info)
self.worker.log_message.connect(self.log)
# Iniciar
self.worker.start()
self.worker.enable()
# UI
self.btn_toggle.setText("🛑 DESLIGAR AIMBOT (F1)")
self.btn_toggle.setStyleSheet(self.get_button_style(True))
self.lbl_status.setText("Status: ✅ LIGADO")
self.log("✅ Aimbot LIGADO")
else:
# Parar
if self.worker:
self.worker.disable()
self.worker.stop()
self.worker = None
# UI
self.btn_toggle.setText("🎯 LIGAR AIMBOT (F1)")
self.btn_toggle.setStyleSheet(self.get_button_style(False))
self.lbl_status.setText("Status: ⏸️ DESLIGADO")
# ✅ Limpar overlay (com verificação None!)
if self.overlay_manager is not None:
self.overlay_manager.clear()
self.overlay_manager.set_status("Aimbot: OFF")
self.log("⏸️ Aimbot DESLIGADO")
def toggle_aimbot_hotkey(self):
"""✅ F1 com DEBOUNCE"""
now = time.time()
if now - self.last_f1_time < 0.2:
return
self.last_f1_time = now
self.btn_toggle.setChecked(not self.btn_toggle.isChecked())
self.toggle_aimbot()
def set_manual_target(self):
"""Set manual target"""
if not self.worker or not self.worker.enabled:
self.log("⚠️ Aimbot deve estar ligado!")
return
# Get mouse position
pos = pyautogui.position()
# Convert to game window coordinates
game_x = self.game_window['rect'][0]
game_y = self.game_window['rect'][1]
target_x = pos.x - game_x
target_y = pos.y - game_y
# Set target
self.worker.manual_mode.set_target(target_x, target_y)
self.log(f"📍 Alvo manual definido: ({target_x}, {target_y})")
def set_manual_target_hotkey(self):
"""✅ F2 com DEBOUNCE"""
now = time.time()
if now - self.last_f2_time < 0.2:
return
self.last_f2_time = now
self.set_manual_target()
def calculate_shot(self):
"""Calculate shot"""
self.log("🧮 Calculando tiro...")
def calculate_shot_hotkey(self):
"""✅ F3 com DEBOUNCE"""
now = time.time()
if now - self.last_f3_time < 0.2:
return
self.last_f3_time = now
self.calculate_shot()
def toggle_auto_shoot(self):
"""Toggle auto-shoot"""
if self.worker:
self.worker.toggle_auto_shoot()
def toggle_auto_shoot_hotkey(self):
"""F4 - toggle auto-shoot"""
self.chk_auto_shoot.setChecked(not self.chk_auto_shoot.isChecked())
# ═══════════════════════════════════════════════════════════════
# UPDATES
# ═══════════════════════════════════════════════════════════════
def update_performance(self, data):
"""Update performance"""
self.lbl_fps.setText(f"FPS: {data['fps']:.1f}")
self.lbl_loop_time.setText(f"Loop: {data['loop_time']:.1f} ms")
def update_detection(self, data):
"""Update detection"""
player = data.get('player')
enemy = data.get('enemy')
manual = data.get('manual_locked', False)
if player:
self.lbl_player.setText(f"Jogador: ({player[0]}, {player[1]})")
if enemy:
self.lbl_enemy.setText(f"Inimigo: ({enemy[0]}, {enemy[1]})")
if manual:
self.lbl_manual.setText("Manual: 🔒 TRAVADO")
else:
self.lbl_manual.setText("Manual: 🔓 Livre")
def update_shot_info(self, data):
"""Update shot info"""
self.lbl_angle.setText(f"Ângulo: {data['angle']}°")
self.lbl_power.setText(f"Força: {data['power']}%")
self.lbl_wind.setText(
f"Vento: {data['wind_speed']} {data['wind_direction']}"
)
def log(self, message: str):
"""Log message"""
timestamp = datetime.now().strftime("%H:%M:%S")
formatted = f"[{timestamp}] {message}"
self.log_text.append(formatted)
# Auto-scroll
scrollbar = self.log_text.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
# ═══════════════════════════════════════════════════════════════
# STYLES
# ═══════════════════════════════════════════════════════════════
def get_button_style(self, active: bool) -> str:
"""Get button style"""
if active:
return """
QPushButton {
background: #F44336;
color: white;
font-size: 14px;
font-weight: bold;
border-radius: 8px;
padding: 10px;
}
QPushButton:hover {
background: #D32F2F;
}
"""
else:
return """
QPushButton {
background: #4CAF50;
color: white;
font-size: 14px;
font-weight: bold;
border-radius: 8px;
padding: 10px;
}
QPushButton:hover {
background: #45A049;
}
"""
def apply_dark_theme(self):
"""Apply dark theme"""
self.setStyleSheet("""
QMainWindow {
background: #2B2B2B;
}
QWidget {
background: #2B2B2B;
color: #E0E0E0;
font-family: Segoe UI;
}
QGroupBox {
border: 2px solid #404040;
border-radius: 8px;
margin-top: 10px;
padding-top: 10px;
font-weight: bold;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px;
}
QPushButton {
background: #404040;
color: white;
border-radius: 6px;
padding: 8px;
font-size: 12px;
}
QPushButton:hover {
background: #505050;
}
QCheckBox {
spacing: 8px;
}
QLabel {
color: #E0E0E0;
}
""")
# ═══════════════════════════════════════════════════════════════
# ✅ CORREÇÃO 4: SALVAMENTO DE ESTADO AO FECHAR
# ═══════════════════════════════════════════════════════════════
def closeEvent(self, event):
"""✅ Close COM SALVAMENTO DE ESTADO!"""
print("\n👋 Encerrando...")
# ✅ CORREÇÃO 4: Salvar estado
if self.worker and self.worker.enabled:
try:
state = {
'last_target': self.worker.manual_mode.manual_target,
'last_wind': self.worker.last_valid_wind,
'last_angle': self.worker.last_valid_angle,
'last_power': self.worker.last_valid_power,
'auto_shoot_enabled': self.worker.auto_shoot_enabled
}
# Salvar em arquivo
state_path = Path.home() / '.gunbound_aimbot' / 'last_state.json'
with open(state_path, 'w') as f:
json.dump(state, f, indent=2)
print("✅ Estado salvo")
except Exception as e:
print(f"⚠️ Erro ao salvar estado: {e}")
# Parar worker
if self.worker:
self.worker.disable()
self.worker.stop()
print("✅ Worker parado")
# Esconder overlay
if self.overlay_manager is not None:
self.overlay_manager.hide()
print("✅ Overlay escondido")
# Aceitar close
event.accept()
print("✅ Encerrado!\n")
# ═══════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════
def main():
"""Main entry point"""
print("\n" + "="*70)
print("🎮 GUNBOUND AIMBOT v25.0 - ULTRA ULTRA FINAL")
print("="*70 + "\n")
# ✅ QApplication PRIMEIRO!
app = QApplication(sys.argv)
# Dashboard
dashboard = AimbotDashboard()
dashboard.show()
print("✅ Dashboard aberto!")
print("\n💡 HOTKEYS:")
print(" F1 - Ligar/Desligar aimbot")
print(" F2 - Definir alvo manual (posição do mouse)")
print(" F3 - Calcular tiro")
print(" F4 - Toggle auto-shoot")
print()
# Run
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Repository:
You cant view this link please login.
Dev Note:
The logic currently uses a 200ms debounce on hotkeys to prevent input spam. The trajectory calculations are handled via a physics wrapper. I have included a setup wizard to handle the initial window detection, so it should be relatively plug-and-play if you are running at standard resolutions.
I’m currently looking into refining the OCR fallback logic since it can be inconsistent during high-intensity matches. If any of you coders here have ideas on how to optimize the `detect_enemy_position` loop to reduce the frame-time or have a better way to hook the game memory without triggering detection, drop your fixes below.
Has anyone tested this on the current server version? Let me know if you run into any weird behavior with the auto-shoot timing.