Hey guys, I created this aimbot and decided to make it available. It works, or used to work, for GGH. Maybe it needs an update now. It's open source, and I've made it available on my git. If anyone makes improvements, it would be interesting to share them with everyone <3
Code:
"""
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: ******_window['title']}\n"
f" Resolu��o: ******_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://******.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()