Загрузка данных
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: