SKYRIM и перемещение по координатам

Dewize

Известный
Автор темы
447
93
Всем привет, недавно меня подцепил снова Skyrim и решил для него написать скриптики на Python. Для начала решил написать скрипт, который будет бегать за меня из точки А в точку Б. Пол дня проебал, дабы выяснить как работать с памятью, искал сами адреса памяти для скайрима через Cheat Engine, потом выяснил, что после перезахода всё адреса памяти меняются. Крч не суть.

Написав, что то адекватное ( как мне казалось ) я решил протестить, но кнч столкнулся с багами. Выставив координаты по которым нужно бежать персонажу, запустив скрипт, понял, что он просто не добегает до нужной точки. Думал-думал как пофиксить и крч никак не получилось. Кто в целом сможет мне помочь буду сильно Вам благодарен.

Код:
# ================================================
# ИМПООООООРТ
# ================================================

import math
import time
import threading
from threading import Thread
from typing import Optional, Tuple

import cv2
import keyboard
import numpy as np
import pydirectinput
import pymem
import pygetwindow as gw
import win32gui
from mss import mss

# ================================================
# КОНФИГУРАЦИЯ (Можно вообще добавить темку, что он сам дедектит из оффсета нужный адрес памяти)
# ================================================
MEMORY_ADDRESSES = {
    'coord_x': 0x248C1A7D4E4,
    'coord_y': 0x248C1A7D4E8,
    'coord_z': 0x248C1A7D4EC,
    # TODO: Добавить адрес для угла камеры
}

CONFIG = {
    'position_epsilon': 2.0,      # Радиус достижения цели
    'stuck_threshold': 0.1,       # Порог обнаружения застревания
    'stuck_attempts': 10,         # Количество проверок для обнаружения застревания
    'polling_interval': 0.05,     # Интервал обновления управления (сек)
    'turn_speed': 0.25,           # Чувствительность поворота
    'status_update_interval': 1   # Интервал обновления координат (сек)
}

# ================================================
# КЛАСС ДЛЯ РАБОТЫ С ПАМЯТЬЮ
# ================================================
class MemoryReader:
    def __init__(self):
        self._process_handler = None
        self._connect()
        
    def _connect(self) -> bool:
        """Подключение к процессу игры"""
        try:
            self._process_handler = pymem.Pymem("SkyrimSE.exe")
            print("Успешное подключение к процессу")
            return True
        except pymem.exception.ProcessNotFound:
            print("Ошибка: Процесс SkyrimSE.exe не найден")
            return False
        except Exception as e:
            print(f"Критическая ошибка подключения: {str(e)}")
            return False
    
    def read_position(self) -> Optional[Tuple[float, float, float]]:
        """Чтение координат из памяти игры"""
        if not self._process_handler:
            return None

        try:
            return (
                self._process_handler.read_float(MEMORY_ADDRESSES['coord_x']),
                self._process_handler.read_float(MEMORY_ADDRESSES['coord_y']),
                self._process_handler.read_float(MEMORY_ADDRESSES['coord_z'])
            )
        except pymem.exception.MemoryReadError:
            print("Ошибка чтения памяти: возможно, неверные адреса")
            return None
        except Exception as e:
            print(f"Неизвестная ошибка при чтении памяти: {str(e)}")
            return None

# ================================================
# ОСНОВНОЙ КЛАСС АВТОПЕРЕДВИЖЕНИЯ
# ================================================
class AutoWalker:
    def __init__(self):
        self.memory = MemoryReader()
        self.target_position = (0.0, 0.0, 0.0)
        self.is_running = False
        self._exit_flag = False
        self._movement_thread = None
        self._current_camera_angle = 0.0  # TODO: Реализовать чтение угла камеры

    def _calculate_target_angle(self, current_pos: Tuple[float, float]) -> float:
        """Вычисление угла до цели в градусах"""
        dx = self.target_position[0] - current_pos[0]
        dy = self.target_position[1] - current_pos[1]
        return math.degrees(math.atan2(dy, dx))
    
    def _handle_stuck(self):
        """Алгоритм выхода из застревания"""
        pydirectinput.keyUp('w')
        # Прыжок + кратковременное движение назад
        pydirectinput.press('space')
        pydirectinput.keyDown('s')
        time.sleep(0.3)
        pydirectinput.keyUp('s')
        time.sleep(0.2)
        pydirectinput.keyDown('w')

    def _movement_loop(self):
        """Основной цикл управления движением"""
        last_position = (0.0, 0.0, 0.0)
        stuck_counter = 0

        while self.is_running and not self._exit_flag:
            # Получение текущих координат
            current_pos = self.memory.read_position()
            if not current_pos or None in current_pos:
                time.sleep(1)
                continue

            # Обнаружение застревания
            if math.dist(current_pos, last_position) < CONFIG['stuck_threshold']:
                stuck_counter += 1
                if stuck_counter > CONFIG['stuck_attempts']:
                    self._handle_stuck()
                    stuck_counter = 0
            else:
                stuck_counter = 0

            last_position = current_pos

            # Вычисление углов
            target_angle = self._calculate_target_angle(current_pos[:2])
            angle_diff = (target_angle - self._current_camera_angle + 180) % 360 - 180

            # Поворот камеры
            if abs(angle_diff) > 2:
                mouse_move = int(angle_diff * CONFIG['turn_speed'])
                pydirectinput.moveRel(mouse_move, 0, relative=True)

            # Движение вперед
            pydirectinput.keyDown('w')

            # Проверка достижения цели
            if math.dist(current_pos[:2], self.target_position[:2]) < CONFIG['position_epsilon']:
                pydirectinput.keyUp('w')
                print("\nЦель достигнута!")
                self.is_running = False

            time.sleep(CONFIG['polling_interval'])

    def toggle_movement(self):
        """Переключение режима автоперемещения"""
        self.is_running = not self.is_running

        if self.is_running:
            if not self._movement_thread or not self._movement_thread.is_alive():
                self._movement_thread = Thread(
                    target=self._movement_loop,
                    daemon=True
                )
                self._movement_thread.start()
            print("\nАвтоперемещение активировано")
        else:
            pydirectinput.keyUp('w')
            print("\nАвтоперемещение остановлено")

# ================================================
# УПРАВЛЕНИЕ ПРИЛОЖЕНИЕМ
# ================================================
def main():
    walker = AutoWalker()

    # Функции обработчики горячих клавиш
    def set_target_position():
        if position := walker.memory.read_position():
            walker.target_position = position
            print(f"\nНовая цель установлена: X:{position[0]:.2f} Y:{position[1]:.2f} Z:{position[2]:.2f}")

    def emergency_stop():
        walker._exit_flag = True
        walker.is_running = False
        pydirectinput.keyUp('w')
        print("\nАварийная остановка!")
        time.sleep(1)
        exit()

    # Настройка горячих клавиш
    keyboard.add_hotkey('F1', set_target_position)
    keyboard.add_hotkey('F2', walker.toggle_movement)
    keyboard.add_hotkey('Q', emergency_stop)

    # Информационное табло
    print("=" * 50)
    print("Skyrim AutoWalker v1.0")
    print("Управление:")
    print("[F1] - Установить целевую точку")
    print("[F2] - Старт/Стоп автоперемещения")
    print("[Q] - Аварийный выход")
    print("=" * 50)

    # Основной цикл обновления информации
    try:
        while not walker._exit_flag:
            position = walker.memory.read_position()
            status = "Активно" if walker.is_running else "Остановлено"
            print(
                f"\rСтатус: {status.ljust(8)} | "
                f"X: {position[0]:.2f} | "
                f"Y: {position[1]:.2f} | "
                f"Z: {position[2]:.2f}",
                end="",
                flush=True
            )
            time.sleep(CONFIG['status_update_interval'])
    except KeyboardInterrupt:
        emergency_stop()

if __name__ == "__main__":
    main()


Видео работы:
 

Похожие темы