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


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.total_look_down = 0  # Сколько всего опущена голова
        
        self.mouse = Controller()
        
        self.timings = {
            # Задержка перед запуском
            'start_delay': 5,
            
            # Подъём головы (после центрирования)
            'head_raise_angle': 40,
            'head_raise_steps': 5,
            
            # Поиск блока (вращение с постепенным опусканием)
            'scan_rotation_speed': 0.02,
            'scan_angle_step': 10,
            'scan_look_down_step': 2,     # На сколько опускать голову за шаг
            'scan_look_down_max': 60,     # Максимальное опускание при поиске
            'scan_rotations': 2,          # Количество полных оборотов
            
            # Прыжок
            'camera_up_angle': 30,
            'camera_up_delay': 0.05,
            'first_w_time': 0.08,
            'w_gap': 0.03,
            'second_w_time': 0.05,
            'jump_hold': 0.1,
            'post_jump_forward': 0.15,
            
            # Шифт после прыжка
            'shift_delay': 0.2,
            'shift_hold': 0.3,
            
            # Центрирование на блоке (голова вниз, ходьба вперёд-назад)
            'center_look_down_angle': 80,  # Опускание головы для центрирования
            'center_move_speed': 0.1,
            'center_move_repeat': 3,
            'center_find_delay': 0.3,     # Пауза после нахождения точки
            
            # Приземление
            'landing_delay': 0.3,
            'stabilization_time': 0.1,
            
            # Паузы
            'cycle_delay': 0.3,
            'color_tolerance': 15,
        }
        
        self.base_colors = [
            (255, 23, 23),
            (255, 29, 30),
            (188, 17, 18),
            (196, 17, 18),
        ]
        
        self.generate_color_palette()
        
        self.settings_file = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 
            'parkour_settings.json'
        )
        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
            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("Сгенерировано оттенков красного: " + str(len(self.block_colors)))
    
    def load_settings(self):
        try:
            if os.path.exists(self.settings_file):
                with open(self.settings_file, '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("Настройки загружены")
        except Exception as e:
            print("Стандартные настройки: " + str(e))
    
    def save_settings(self):
        try:
            save_data = {}
            for key, value in self.timings.items():
                save_data[key] = value
            save_data['base_colors'] = self.base_colors
            
            try:
                with open(self.settings_file, 'w', encoding='utf-8') as f:
                    json.dump(save_data, f, indent=4, ensure_ascii=False)
                print("Настройки сохранены")
            except PermissionError:
                home_dir = os.path.expanduser('~')
                backup_file = os.path.join(home_dir, 'parkour_settings.json')
                with open(backup_file, 'w', encoding='utf-8') as f:
                    json.dump(save_data, f, indent=4, ensure_ascii=False)
                print("Настройки сохранены в " + backup_file)
        except Exception as e:
            print("Ошибка сохранения: " + str(e))
    
    def release_keys(self):
        try:
            keyboard.release('w')
            keyboard.release('a')
            keyboard.release('s')
            keyboard.release('d')
            keyboard.release('space')
            keyboard.release('shift')
        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):
        try:
            tolerance = self.timings['color_tolerance']
            dr = abs(color1[0] - color2[0])
            dg = abs(color1[1] - color2[1])
            db = abs(color1[2] - color2[2])
            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
            return False
        except:
            return False
    
    def raise_head(self):
        """Подъём головы (после центрирования)"""
        print("  [ПОДЪЁМ ГОЛОВЫ]")
        angle = self.timings['head_raise_angle']
        steps = self.timings['head_raise_steps']
        
        for _ in range(steps):
            if not self.running or self.paused:
                break
            self.move_mouse(0, -angle)
            time.sleep(0.02)
        
        self.total_look_down = max(0, self.total_look_down - angle * steps)
        time.sleep(0.1)
        print("  Голова поднята")
    
    def scan_for_block(self):
        """Вращение с постепенным опусканием головы для поиска блока"""
        print("  [ПОИСК БЛОКА] Вращение + опускание головы...")
        
        look_down_current = 0
        look_down_max = self.timings['scan_look_down_max']
        look_down_step = self.timings['scan_look_down_step']
        scan_step = self.timings['scan_angle_step']
        rotations = self.timings['scan_rotations']
        
        # Считаем общее количество шагов
        total_steps = (360 // scan_step) * rotations
        look_down_every_n_steps = max(1, total_steps // (look_down_max // look_down_step))
        
        step_count = 0
        
        for rotation in range(rotations):
            if not self.running or self.paused:
                break
            
            for _ in range(0, 360, scan_step):
                if not self.running or self.paused:
                    break
                
                # Вращение
                self.move_mouse(scan_step, 0)
                time.sleep(self.timings['scan_rotation_speed'])
                
                # Постепенное опускание головы
                step_count += 1
                if step_count % look_down_every_n_steps == 0 and look_down_current < look_down_max:
                    self.move_mouse(0, look_down_step)
                    look_down_current += look_down_step
                
                # Проверяем блок
                if self.find_block():
                    print("  БЛОК НАЙДЕН!")
                    self.total_look_down += look_down_current
                    return True
        
        self.total_look_down += look_down_current
        print("  Блок не найден")
        return False
    
    def do_jump(self):
        """Прыжок с разгоном"""
        print("=" * 40)
        print("[ПРЫЖОК]")
        print("=" * 40)
        
        # Поднимаем камеру для прыжка
        print("  Подъём камеры...")
        angle = self.timings['camera_up_angle']
        for _ in range(5):
            self.move_mouse(0, -angle)
            time.sleep(0.005)
        time.sleep(self.timings['camera_up_delay'])
        
        # Разгон W W
        print("  Разгон W W...")
        keyboard.press('w')
        time.sleep(self.timings['first_w_time'])
        keyboard.release('w')
        time.sleep(self.timings['w_gap'])
        keyboard.press('w')
        time.sleep(self.timings['second_w_time'])
        
        # Прыжок
        print("  ПРЫЖОК!")
        keyboard.press('space')
        time.sleep(self.timings['jump_hold'])
        keyboard.release('space')
        
        jump_time = time.time()
        
        # Полёт
        time.sleep(self.timings['post_jump_forward'])
        keyboard.release('w')
        
        # Приземление
        print("  Приземление...")
        time.sleep(self.timings['landing_delay'])
        
        # Опускаем камеру обратно
        for _ in range(5):
            self.move_mouse(0, angle)
            time.sleep(0.005)
        
        # Шифт
        elapsed = time.time() - jump_time
        shift_wait = max(0, self.timings['shift_delay'] - elapsed)
        if shift_wait > 0:
            time.sleep(shift_wait)
        
        print("  Шифт!")
        keyboard.press('shift')
        time.sleep(self.timings['shift_hold'])
        keyboard.release('shift')
        
        time.sleep(self.timings['stabilization_time'])
        print("Прыжок завершён")
        print("=" * 40)
    
    def center_on_block(self):
        """Центрирование: голова вниз, ходьба вперёд-назад, поиск красной точки"""
        print("=" * 40)
        print("[ЦЕНТРИРОВАНИЕ]")
        print("=" * 40)
        
        # Опускаем голову вниз для центрирования
        print("  Опускание головы...")
        angle = self.timings['center_look_down_angle']
        for _ in range(5):
            self.move_mouse(0, angle)
            time.sleep(0.01)
        self.total_look_down += angle * 5
        
        # Ходим вперёд-назад и ищем красную точку
        found_center = False
        
        for i in range(self.timings['center_move_repeat']):
            if not self.running or self.paused:
                break
            
            # Вперёд
            print("  Вперёд...")
            keyboard.press('w')
            start_time = time.time()
            while time.time() - start_time < self.timings['center_move_speed']:
                if not self.running or self.paused:
                    break
                if self.find_block():
                    print("  ТОЧКА НАЙДЕНА!")
                    found_center = True
                    break
                time.sleep(0.02)
            keyboard.release('w')
            
            if found_center:
                break
            
            time.sleep(0.05)
            
            # Назад
            print("  Назад...")
            keyboard.press('s')
            start_time = time.time()
            while time.time() - start_time < self.timings['center_move_speed']:
                if not self.running or self.paused:
                    break
                if self.find_block():
                    print("  ТОЧКА НАЙДЕНА!")
                    found_center = True
                    break
                time.sleep(0.02)
            keyboard.release('s')
            
            if found_center:
                break
            
            time.sleep(0.05)
        
        if found_center:
            print("  Центрирование успешно!")
            time.sleep(self.timings['center_find_delay'])
        else:
            print("  Точка не найдена при центрировании")
        
        print("=" * 40)
        return found_center
    
    def main_loop(self):
        """Основной цикл"""
        print("\n=== ЗАПУСК ЦИКЛА ===\n")
        
        while self.running:
            if self.paused:
                time.sleep(0.1)
                continue
            
            try:
                # ШАГ 1: Подъём головы
                self.raise_head()
                
                # ШАГ 2: Поиск блока (вращение + опускание)
                found = self.scan_for_block()
                
                if found:
                    # ШАГ 3: Прыжок
                    self.do_jump()
                    
                    # ШАГ 4: Центрирование (голова вниз, ходьба, поиск точки)
                    self.center_on_block()
                    
                    # Цикл повторяется: снова подъём головы, поиск, прыжок...
                else:
                    print("  Пропуск цикла (блок не найден)")
                    time.sleep(self.timings['cycle_delay'])
                    
            except Exception as e:
                print("Ошибка: " + str(e))
                traceback.print_exc()
                self.release_keys()
                time.sleep(0.5)
    
    def setup_menu(self):
        print("=" * 50)
        print("НАСТРОЙКА БОТА")
        print("=" * 50)
        
        if os.path.exists(self.settings_file):
            use = input("Загрузить сохранённые настройки? (Y/n): ").strip().lower()
            if use == 'n':
                print("Настройка заново...")
            else:
                print("Загружены сохранённые настройки")
                self.show_settings()
                return
        
        print("Вводите значения или Enter (пропустить)")
        print("Введите 0 для сброса на стандартное")
        print("Максимальные значения до 200 пикс!\n")
        
        settings = {
            "ЗАДЕРЖКА СТАРТА": [
                ('start_delay', 'Задержка перед запуском', 1, 15, 'сек'),
            ],
            "ЦВЕТА": [
                ('color_tolerance', 'Допуск цвета (%)', 5, 50, '%'),
            ],
            "1. ПОДЪЁМ ГОЛОВЫ": [
                ('head_raise_angle', 'Угол подъёма', 10, 200, 'пикс'),
                ('head_raise_steps', 'Шагов подъёма', 2, 10, 'шагов'),
            ],
            "2. ПОИСК БЛОКА (вращение + опускание)": [
                ('scan_rotation_speed', 'Скорость вращения', 0.01, 0.1, 'сек'),
                ('scan_angle_step', 'Шаг вращения', 5, 30, 'градусов'),
                ('scan_look_down_step', 'Шаг опускания головы', 1, 10, 'пикс'),
                ('scan_look_down_max', 'Максимальное опускание', 10, 200, 'пикс'),
                ('scan_rotations', 'Количество оборотов', 1, 5, 'оборотов'),
            ],
            "3. ПРЫЖОК": [
                ('camera_up_angle', 'Подъём камеры', 10, 200, 'пикс'),
                ('first_w_time', 'Первое W', 0.03, 0.2, 'сек'),
                ('w_gap', 'Пауза между W', 0.01, 0.1, 'сек'),
                ('second_w_time', 'Второе W', 0.03, 0.2, 'сек'),
                ('jump_hold', 'Длительность прыжка', 0.05, 0.5, 'сек'),
                ('post_jump_forward', 'Полёт вперёд', 0.05, 0.5, 'сек'),
            ],
            "4. ШИФТ": [
                ('shift_delay', 'Задержка перед шифтом', 0.1, 1.0, 'сек'),
                ('shift_hold', 'Длительность шифта', 0.1, 0.8, 'сек'),
            ],
            "5. ЦЕНТРИРОВАНИЕ (голова вниз + ходьба)": [
                ('center_look_down_angle', 'Опускание головы', 10, 200, 'пикс'),
                ('center_move_speed', 'Длительность шага', 0.05, 0.5, 'сек'),
                ('center_move_repeat', 'Повторений вперёд-назад', 1, 8, 'раз'),
                ('center_find_delay', 'Пауза после нахождения', 0.1, 0.5, 'сек'),
            ],
            "ПРИЗЕМЛЕНИЕ": [
                ('landing_delay', 'Ожидание приземления', 0.1, 1.0, 'сек'),
                ('stabilization_time', 'Стабилизация', 0.05, 0.3, 'сек'),
            ],
        }
        
        defaults = self.timings.copy()
        
        for group, params in settings.items():
            print("--- " + group + " ---")
            for item in params:
                key, desc, min_v, max_v, unit = item
                cur = self.timings[key]
                print(desc)
                print("Текущее: " + str(cur) + " " + unit + " (от " + str(min_v) + " до " + str(max_v) + ")")
                
                inp = input("> ").strip()
                
                if inp == "":
                    print("Оставлено: " + str(cur) + " " + unit)
                elif inp == "0":
                    self.timings[key] = defaults[key]
                    print("Сброшено: " + str(defaults[key]) + " " + unit)
                else:
                    try:
                        val = float(inp)
                        if min_v <= val <= max_v:
                            self.timings[key] = val
                            print("Установлено: " + str(val) + " " + unit)
                        else:
                            print("Значение должно быть от " + str(min_v) + " до " + str(max_v))
                    except:
                        print("Введите число!")
        
        self.save_settings()
        print("Настройка завершена!\n")
    
    def show_settings(self):
        print("\nТекущие настройки:")
        print("  Задержка старта: " + str(self.timings['start_delay']) + " сек")
        print("  Подъём головы: " + str(self.timings['head_raise_angle']) + " пикс")
        print("  Опускание при поиске: до " + str(self.timings['scan_look_down_max']) + " пикс")
        print("  Прыжок камера: " + str(self.timings['camera_up_angle']) + " пикс")
        print("  Шифт через: " + str(self.timings['shift_delay']) + " сек")
        print("  Центрирование шагов: " + str(self.timings['center_move_repeat']))
        print("  Допуск цвета: " + str(self.timings['color_tolerance']) + "%\n")
    
    def start(self):
        self.setup_menu()
        
        print("=" * 50)
        print("БОТ ЗАПУСКАЕТСЯ...")
        print("=" * 50)
        
        delay = self.timings['start_delay']
        print("Задержка: " + str(delay) + " сек. Переключитесь на Minecraft!")
        for i in range(int(delay), 0, -1):
            print("  " + str(i) + "...")
            time.sleep(1)
        
        print("СТАРТ!")
        print("=" * 50)
        print("ЦИКЛ:")
        print("  1. Подъём головы")
        print("  2. Вращение + опускание (поиск блока)")
        print("  3. Прыжок с W W")
        print("  4. Шифт")
        print("  5. Голова вниз + ходьба (центрирование)")
        print("  6. Точка найдена -> возврат к шагу 1")
        print("F8 - Пауза | F9 - Стоп")
        print("=" * 50)
        
        self.running = True
        self.paused = False
        
        thread = threading.Thread(target=self.main_loop)
        thread.daemon = True
        thread.start()
        
        f8_state = False
        
        while self.running:
            try:
                if keyboard.is_pressed('F8'):
                    if not f8_state:
                        f8_state = True
                        self.paused = not self.paused
                        if self.paused: