Загрузка данных


import keyboard
import time
import threading
import pyautogui
from pynput.mouse import Controller
import json
import os
import traceback
import colorsys

try:
    import win32api
    import win32con
    DIRECT_INPUT = True
except ImportError:
    DIRECT_INPUT = False

class ParkourBot:
    def __init__(self):
        self.running = False
        self.paused = False
        self.current_pitch = 0
        self.min_pitch = 15
        self.step_down = 2
        
        self.mouse = Controller()
        
        # Настройки времени
        self.timings = {
            # Прыжок
            'jump_delay': 0.1,
            'jump_hold': 0.15,
            'double_jump_delay': 0.05,
            
            # Ускорение (двойное W)
            'first_w_time': 0.15,
            'w_press_gap': 0.05,
            'second_w_time': 0.1,
            
            # Движение
            'forward_time': 0.3,
            'post_jump_forward': 0.2,
            
            # Камера
            'camera_reset_delay': 0.2,
            'camera_up_angle': 5,
            'camera_up_delay': 0.1,
            
            # Стабилизация
            'landing_delay': 0.5,
            'stabilization_time': 0.1,
            
            # Сканирование
            'block_check_delay': 0.05,
            'scan_rotation_speed': 0.02,
            'scan_angle_step': 10,
            
            # Паузы
            'resume_delay': 0.3,
            
            # Цвета
            'color_tolerance': 15,  # Допуск цвета в процентах (0-100)
        }
        
        # Базовые цвета блоков (красные оттенки)
        self.base_colors = [
            (255, 23, 23),   # Ярко-красный
            (255, 29, 30),   # Светло-красный
            (188, 17, 18),   # Тёмно-красный
            (196, 17, 18),   # Бордовый
        ]
        
        # Генерируем расширенную палитру
        self.generate_color_palette()
        
        self.load_settings()
    
    def generate_color_palette(self):
        """Создаёт расширенную палитру цветов на основе базовых"""
        self.block_colors = []
        
        for base in self.base_colors:
            self.block_colors.append(base)
            
            # Генерируем близкие оттенки
            r, g, b = base
            
            # Конвертируем в HSV для генерации оттенков
            h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
            
            # Добавляем вариации яркости
            for brightness in [0.8, 0.9, 1.1, 1.2]:
                new_v = min(1.0, max(0.0, v * brightness))
                new_r, new_g, new_b = colorsys.hsv_to_rgb(h, s, new_v)
                color = (int(new_r * 255), int(new_g * 255), int(new_b * 255))
                if color not in self.block_colors:
                    self.block_colors.append(color)
            
            # Добавляем вариации насыщенности
            for saturation in [0.8, 0.9, 1.1, 1.2]:
                new_s = min(1.0, max(0.0, s * saturation))
                new_r, new_g, new_b = colorsys.hsv_to_rgb(h, new_s, v)
                color = (int(new_r * 255), int(new_g * 255), int(new_b * 255))
                if color not in self.block_colors:
                    self.block_colors.append(color)
        
        print(f"✓ Сгенерировано {len(self.block_colors)} оттенков красного")
    
    def load_settings(self):
        """Загружает настройки из файла"""
        try:
            if os.path.exists('parkour_settings.json'):
                with open('parkour_settings.json', 'r', encoding='utf-8') as f:
                    saved = json.load(f)
                    
                    # Загружаем только те настройки, которые есть в файле
                    for key in self.timings:
                        if key in saved:
                            self.timings[key] = saved[key]
                    
                    # Загружаем базовые цвета если есть
                    if 'base_colors' in saved:
                        self.base_colors = [tuple(c) for c in saved['base_colors']]
                        self.generate_color_palette()
                    
                    print("✓ Настройки загружены из parkour_settings.json")
                    return True
            else:
                print("⚠ Файл настроек не найден, используются стандартные")
        except Exception as e:
            print(f"⚠ Ошибка загрузки настроек: {e}")
        return False
    
    def save_settings(self):
        """Сохраняет настройки в файл"""
        try:
            # Создаём словарь для сохранения
            save_data = {
                **self.timings,
                'base_colors': self.base_colors
            }
            
            with open('parkour_settings.json', 'w', encoding='utf-8') as f:
                json.dump(save_data, f, indent=4, ensure_ascii=False)
            
            print("✓ Настройки сохранены в parkour_settings.json")
            return True
        except Exception as e:
            print(f"⚠ Ошибка сохранения: {e}")
            return False
    
    def release_keys(self):
        """Отпускает все клавиши"""
        try:
            keyboard.release('w')
            keyboard.release('space')
        except:
            pass
    
    def move_mouse(self, dx, dy):
        """Движение мыши"""
        try:
            if DIRECT_INPUT:
                win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(dx), int(dy), 0, 0)
            else:
                pos = self.mouse.position
                self.mouse.position = (pos[0] + dx, pos[1] + dy)
        except:
            pass
    
    def color_match_percent(self, color1, color2):
        """
        Проверяет схожесть цветов в процентах
        Возвращает True если цвета похожи больше чем на tolerance%
        """
        try:
            tolerance = self.timings['color_tolerance']
            
            # Вычисляем разницу по каждому каналу
            dr = abs(color1[0] - color2[0])
            dg = abs(color1[1] - color2[1])
            db = abs(color1[2] - color2[2])
            
            # Максимальная разница (100%)
            max_diff = 255 * 3
            
            # Фактическая разница
            actual_diff = dr + dg + db
            
            # Процент схожести
            similarity = 100 - (actual_diff / max_diff * 100)
            
            # Проверяем, достаточно ли похожи
            return similarity >= (100 - tolerance)
            
        except:
            return False
    
    def find_block(self):
        """Поиск блока в центре экрана"""
        try:
            screenshot = pyautogui.screenshot()
            w, h = screenshot.size
            cx, cy = w // 2, h // 2
            
            # Проверяем центр
            pixel = screenshot.getpixel((cx, cy))
            
            # Проверяем совпадение с любым цветом из палитры
            for color in self.block_colors:
                if self.color_match_percent(pixel[:3], color):
                    return True, (cx, cy)
            
            # Если не нашли в центре, проверяем соседние пиксели
            for dx in [-5, 0, 5]:
                for dy in [-5, 0, 5]:
                    if dx == 0 and dy == 0:
                        continue
                    try:
                        pixel = screenshot.getpixel((cx + dx, cy + dy))
                        for color in self.block_colors:
                            if self.color_match_percent(pixel[:3], color):
                                return True, (cx + dx, cy + dy)
                    except:
                        continue
            
            return False, None
        except Exception as e:
            print(f"Ошибка поиска: {e}")
            return False, None
    
    def reset_camera(self):
        """Выпрямляет камеру"""
        if self.current_pitch > 0:
            steps = 5
            step = self.current_pitch / steps
            for _ in range(steps):
                if not self.running or self.paused:
                    break
                self.move_mouse(0, -step)
                time.sleep(self.timings['camera_reset_delay'] / steps)
            self.current_pitch = 0
    
    def camera_up(self):
        """Поднимает камеру для прыжка"""
        angle = self.timings['camera_up_angle']
        if angle > 0:
            self.move_mouse(0, -angle)
            time.sleep(self.timings['camera_up_delay'])
    
    def camera_down(self):
        """Опускает камеру обратно"""
        angle = self.timings['camera_up_angle']
        if angle > 0:
            self.move_mouse(0, angle)
            time.sleep(self.timings['camera_up_delay'])
    
    def rotate_scan(self):
        """Сканирование поворотом"""
        for _ in range(2):
            if not self.running or self.paused:
                break
            
            for _ in range(0, 180, self.timings['scan_angle_step']):
                if not self.running or self.paused:
                    break
                
                self.move_mouse(self.timings['scan_angle_step'], 0)
                time.sleep(self.timings['scan_rotation_speed'])
                
                found, pos = self.find_block()
                if found:
                    return True, pos
        
        return False, None
    
    def look_down(self):
        """Опускает голову при поиске"""
        if self.current_pitch < self.min_pitch:
            self.move_mouse(0, self.step_down)
            self.current_pitch += self.step_down
    
    def do_sprint(self):
        """Ускорение - двойное нажатие W"""
        print("  Ускорение (W W)...")
        
        # Первое нажатие W
        keyboard.press('w')
        time.sleep(self.timings['first_w_time'])
        keyboard.release('w')
        
        # Небольшая пауза
        time.sleep(self.timings['w_press_gap'])
        
        # Второе нажатие W (удерживаем)
        keyboard.press('w')
        time.sleep(self.timings['second_w_time'])
    
    def do_jump(self):
        """Выполнение прыжка"""
        print("\n" + "="*40)
        print("ПРЫЖОК")
        print("="*40)
        
        # Выпрямляем камеру
        self.reset_camera()
        time.sleep(self.timings['jump_delay'])
        
        # Ускорение (двойное W)
        self.do_sprint()
        
        # Поднимаем камеру
        self.camera_up()
        
        # Прыжок
        print("  Прыжок!")
        keyboard.press('space')
        time.sleep(self.timings['jump_hold'])
        keyboard.release('space')
        
        # Опускаем камеру
        self.camera_down()
        
        # Движение после прыжка
        time.sleep(self.timings['post_jump_forward'])
        
        # Отпускаем W
        keyboard.release('w')
        
        # Приземление
        print("  Приземление...")
        time.sleep(self.timings['landing_delay'])
        time.sleep(self.timings['stabilization_time'])
        
        print("✓ Готово\n")
    
    def test_color_detection(self):
        """Тестирует определение цветов"""
        print("\n" + "="*50)
        print("ТЕСТ ОПРЕДЕЛЕНИЯ ЦВЕТОВ")
        print("="*50)
        print(f"Допуск цвета: {self.timings['color_tolerance']}%")
        print("\nНаведитесь на блок и нажмите Enter")
        input("> ")
        
        try:
            screenshot = pyautogui.screenshot()
            w, h = screenshot.size
            cx, cy = w // 2, h // 2
            pixel = screenshot.getpixel((cx, cy))
            
            print(f"\nЦвет под прицелом: RGB{pixel[:3]}")
            
            # Проверяем совпадение
            best_match = None
            best_similarity = 0
            
            for color in self.block_colors:
                dr = abs(pixel[0] - color[0])
                dg = abs(pixel[1] - color[1])
                db = abs(pixel[2] - color[2])
                similarity = 100 - ((dr + dg + db) / (255 * 3) * 100)
                
                if similarity > best_similarity:
                    best_similarity = similarity
                    best_match = color
            
            if best_match:
                print(f"Лучшее совпадение: RGB{best_match}")
                print(f"Схожесть: {best_similarity:.1f}%")
                
                if best_similarity >= (100 - self.timings['color_tolerance']):
                    print("✓ Блок ОБНАРУЖЕН!")
                else:
                    print("✗ Блок НЕ обнаружен (недостаточная схожесть)")
                    print(f"  Требуется: {100 - self.timings['color_tolerance']}%")
                    print(f"  Текущая: {best_similarity:.1f}%")
                    print("  Совет: увеличьте допуск цвета в настройках")
            else:
                print("✗ Нет совпадений с палитрой")
                
        except Exception as e:
            print(f"Ошибка теста: {e}")
    
    def main_loop(self):
        """Основной цикл"""
        print("Запуск...")
        
        while self.running:
            if self.paused:
                time.sleep(0.1)
                continue
            
            try:
                found, _ = self.find_block()
                
                if found:
                    print("✓ Блок найден!")
                    self.do_jump()
                    time.sleep(self.timings['resume_delay'])
                else:
                    found_scan, _ = self.rotate_scan()
                    
                    if found_scan:
                        self.do_jump()
                    else:
                        self.look_down()
                        if self.current_pitch >= self.min_pitch:
                            self.reset_camera()
                    
                    time.sleep(self.timings['block_check_delay'])
                    
            except Exception as e:
                print(f"Ошибка: {e}")
                self.release_keys()
                time.sleep(0.5)
    
    def setup_menu(self):
        """Меню настройки"""
        print("\n" + "="*50)
        print("НАСТРОЙКА БОТА")
        print("="*50)
        
        if os.path.exists('parkour_settings.json'):
            use = input("\nЗагрузить сохранённые настройки? (Y/n): ").strip().lower()
            if use == 'n':
                print("Настройка заново...")
            else:
                print("✓ Загружены сохранённые настройки")
                self.show_settings()
                return
        
        print("\nВводите значения или Enter (пропустить)")
        print("Введите 0 для сброса на стандартное\n")
        
        settings = {
            "ЦВЕТА": [
                ('color_tolerance', 'Допуск цвета (%)', 5, 50, '%'),
            ],
            "ПРЫЖОК": [
                ('jump_delay', 'Задержка перед прыжком', 0.05, 0.5, 'сек'),
                ('jump_hold', 'Длительность прыжка', 0.05, 0.5, 'сек'),
                ('double_jump_delay', 'Пауза между двойным прыжком', 0.01, 0.2, 'сек'),
            ],
            "УСКОРЕНИЕ (ДВОЙНОЕ W)": [
                ('first_w_time', 'Первое нажатие W (разгон)', 0.05, 0.3, 'сек'),
                ('w_press_gap', 'Пауза между W', 0.01, 0.2, 'сек'),
                ('second_w_time', 'Второе нажатие W (перед прыжком)', 0.05, 0.3, 'сек'),
            ],
            "КАМЕРА": [
                ('camera_up_angle', 'Подъём камеры (пиксели)', 0, 20, 'пикс'),
                ('camera_up_delay', 'Задержка подъёма', 0.05, 0.3, 'сек'),
            ],
            "СТАБИЛИЗАЦИЯ": [
                ('landing_delay', 'Ожидание приземления', 0.1, 1.0, 'сек'),
                ('stabilization_time', 'Стабилизация', 0.05, 0.5, 'сек'),
            ],
        }
        
        defaults = self.timings.copy()
        
        for group, params in settings.items():
            print(f"\n--- {group} ---")
            for item in params:
                key, desc, min_v, max_v, unit = item
                cur = self.timings[key]
                print(f"\n{desc}")
                print(f"Текущее: {cur} {unit} (от {min_v} до {max_v})")
                
                inp = input("> ").strip()
                
                if inp == "":
                    print(f"✓ {cur} {unit}")
                elif inp == "0":
                    self.timings[key] = defaults[key]
                    print(f"✓ Сброшено: {defaults[key]} {unit}")
                else:
                    try:
                        val = float(inp)
                        if min_v <= val <= max_v:
                            self.timings[key] = val
                            print(f"✓ {val} {unit}")
                        else:
                            print(f"⚠ От {min_v} до {max_v}")
                    except:
                        print("⚠ Число!")
        
        # Тест цветов
        print("\n" + "="*50)
        print("Хотите протестировать определение цветов?")
        test = input("(Y/n): ").strip().lower()
        if test != 'n':
            self.test_color_detection()
        
        self.save_settings()
        
        # Тест движений
        print("\nТест ускорения через 3 сек...")
        print("Переключитесь на Minecraft!")
        for i in range(3, 0, -1):
            print(f"{i}...")
            time.sleep(1)
        
        print("Тест W W!")
        keyboard.press('w')
        time.sleep(self.timings['first_w_time'])
        keyboard.release('w')
        time.sleep(self.timings['w_press_gap'])
        keyboard.press('w')
        time.sleep(0.2)
        keyboard.release('w')
        
        print("✓ Готово\n")
    
    def show_settings(self):
        """Показывает текущие настройки"""
        print("\nТекущие настройки:")
        print(f"  Допуск цвета: {self.timings['color_tolerance']}%")
        print(f"  Базовых цветов: {len(self.base_colors)}")
        print(f"  Всего оттенков: {len(self.block_colors)}")
    
    def start(self):
        """Запуск"""
        self.setup_menu()
        
        print("="*50)
        print("БОТ ЗАПУЩЕН!")
        print("F8 - Пауза | F9 - Стоп | F10 - Тест цвета")
        print("="*50)
        
        self.running = True
        self.paused = False
        
        # Запуск цикла в потоке
        thread = threading.Thread(target=self.main_loop)
        thread.daemon = True
        thread.start()
        
        # Управление
        f8_state = False
        f10_state = False
        
        while self.running:
            try:
                # F8 - пауза
                if keyboard.is_pressed('F8'):
                    if not f8_state:
                        f8_state = True
                        self.paused = not self.paused
                        if self.paused:
                            print("\n⏸ ПАУЗА")
                            self.release_keys()
                        else:
                            print("\n▶ ПРОДОЛЖЕНИЕ")
                            time.sleep(0.3)
                else:
                    f8_state = False
                
                # F10 - тест цвета
                if keyboard.is_pressed('F10'):
                    if not f10_state:
                        f10_state = True
                        was_paused = self.paused
                        self.paused = True
                        self.test_color_detection()
                        self.paused = was_paused
                else:
                    f10_state = False
                
                # F9 - стоп
                if keyboard.is_pressed('F9'):
                    self.running = False
                    print("\n✓ СТОП")
                    break
                
                time.sleep(0.05)
                
            except KeyboardInterrupt:
                break
        
        self.release_keys()
        print("Выход")

# ============= ЗАПУСК =============
if __name__ == "__main__":
    print("="*50)
    print("БОТ ПАРКУРА MINECRAFT v4.0")
    print("="*50)
    print("\nТребования:")
    print("  • Запуск от администратора")
    print("  • Чувствительность мыши = 0%")
    print("  • Оконный режим")
    print("\nЦвета блоков: красные оттенки")
    print(f"  •