Порт с UOPilot
Перенести последовательность еды из UOPilot/AHK в EyeAuras
Сохраним задачу исходного скрипта: активировать TL.exe, открыть меню клавишей F, найти Vegetarian.bmp (или запасной Vegetarian1.bmp), выбрать еду, закрыть меню и нажать правой кнопкой в сохранённой точке. Исходный AHK ниже оставлен для сравнения.
Выполнить последовательность в C# Script
Поместите весь C# блок ниже в верхнеуровневый файл действия C# Script EyeAuras и запустите Run. Окно игры должно быть открыто. Положите оба образца в C:\Scripts\Eat или измените imageDirectory. Настройте раскладку и размеры окна под координаты примера: 1005,1005 — экранная точка, область поиска и 1499,1004 — координаты относительно окна, не обещание клиентских координат без рамки. Сначала нажмите F5, чтобы сохранить экранную точку правого клика; F3 запросит один проход, F6 включит/выключит повторение. Остановка Run завершает цикл.
Все CV/ввод выполняются последовательно главным циклом, обработчики клавиш только меняют состояние под блокировкой. Пауза 100 мс идёт после прохода, а не задаёт точный период внешнего SetTimer: последовательность сама занимает время. GUI из AHK здесь заменён журналом, поведение таймеров и игровых средств ввода не заявляется эквивалентным.
Что переносим
Ниже исходный AHK-скрипт и адаптация его основной последовательности для EyeAuras. Статусные сообщения перенесены в лог; таймер и управление выполнением устроены иначе, как описано выше.
Альтернатива была бы использовать Toast API, OnScreenCanvas API или BlazorWindows, но просто для отображения строки текста это все кажется перебором.
UO Pilot
#NoEnv
SetWorkingDir %A_ScriptDir%
SetTitleMatchMode, Fast
; Переменные для хранения координат курсора
global CursorX := 0
global CursorY := 0
global Toggle := 0 ; Переключатель для включения/выключения
; Создаем GUI для вывода текста на экран
Gui, +AlwaysOnTop +ToolWindow -Caption +E0x20 ; Всегда поверх окон, без заголовка, кликабельно
Gui, Add, Text, vStatusText, Скрипт выключен ; Текст, который будет обновляться
Gui, Show, NoActivate X0 Y0, StatusWindow ; Показываем окно без активации в углу экрана
; Горячая клавиша F5: запись координат курсора
F5::
CoordMode, Mouse, Screen
MouseGetPos, CursorX, CursorY
GuiControl,, StatusText, Координаты сохранены: X=%CursorX%, Y=%CursorY%
Sleep, 2000 ; Показываем сообщение 2 секунды
GuiControl,, StatusText, Скрипт выключен
return
; Горячая клавиша F3: однократное выполнение скрипта
F3::
Gosub, MainScript
return
; Горячая клавиша F6: запуск/остановка цикла
F6::
Toggle := !Toggle ; Переключаем состояние
if (Toggle) {
GuiControl,, StatusText, Скрипт включен
SetTimer, RunMainScript, 100 ; Запускаем таймер каждые 100 мс
} else {
GuiControl,, StatusText, Скрипт выключен
SetTimer, RunMainScript, Off ; Останавливаем таймер
}
return
; Таймер для выполнения основного скрипта
RunMainScript:
Gosub, MainScript
return
; Перемещение GUI по экрану
~LButton::
MouseGetPos, StartMouseX, StartMouseY ; Сохраняем начальные координаты мыши
Gui, +LastFound ; Получаем ID последнего созданного GUI
WinGetPos, GuiStartX, GuiStartY, GuiW, GuiH ; Сохраняем начальные координаты окна
; Проверяем, что клик произошел внутри окна
if (StartMouseX >= GuiStartX && StartMouseX <= GuiStartX + GuiW && StartMouseY >= GuiStartY && StartMouseY <= GuiStartY + GuiH) {
While GetKeyState("LButton", "P") { ; Пока зажата ЛКМ
MouseGetPos, CurrentMouseX, CurrentMouseY ; Получаем текущие координаты мыши
; Вычисляем смещение
OffsetX := CurrentMouseX - StartMouseX
OffsetY := CurrentMouseY - StartMouseY
; Новые координаты окна
NewGuiX := GuiStartX + OffsetX
NewGuiY := GuiStartY + OffsetY
Gui, Show, x%NewGuiX% y%NewGuiY% NoActivate ; Перемещаем окно
Sleep, 20 ; Небольшая задержка для снижения нагрузки
}
}
return
; Основной код скрипта
MainScript:
WinActivate, TL.exe
Sleep, 2500
SendInput, {f}
Sleep, 300
CoordMode, Mouse, Screen
Click, 1005, 1005
CoordMode, Mouse, Window
Sleep, 200
if !MyImageSearch(FoundX, FoundY, 640, 186, 1237, 939, "Vegetarian.bmp")
{
Sleep, 1000
if !MyImageSearch(FoundX, FoundY, 640, 186, 1237, 939, "Vegetarian.bmp")
{
if !MyImageSearch(FoundX, FoundY, 640, 186, 1237, 939, "Vegetarian1.bmp")
{
GuiControl,, StatusText, Объект "Vegetarian" не найден!
Sleep, 2000
GuiControl,, StatusText, Скрипт выключен
return
}
}
}
Click, %FoundX%, %FoundY%
Sleep, 200
Click, 1499, 1004
Sleep, 200
SendInput, {Esc}
Sleep, 200
Sleep, 600
CoordMode, Mouse, Screen
Click, right, %CursorX%, %CursorY%
CoordMode, Mouse, Window
return
; Функция поиска изображения
MyImageSearch(ByRef FoundX, ByRef FoundY, X1, Y1, X2, Y2, ImageFile)
{
CoordMode, Pixel, Window
Loop, 10
{
ImageSearch, FoundX, FoundY, %X1%, %Y1%, %X2%, %Y2%, %A_ScriptDir%\%ImageFile%
if (ErrorLevel = 0)
return true
Sleep, 100
}
return false
}
EyeAuras C# Script
using System.Drawing;
using System.IO;
using System.Windows.Input;
using EyeAuras.Shared.Services;
using EyeAuras.OpenCVAuras.Scripting;
using EyeAuras.Roxy.Api;
using EyeAuras.Scripting.Api;
using PoeShared.Scaffolding;
private object stateGate { get; } = new object();
private bool enabled { get; set; }
private bool runOnce { get; set; }
private bool captureCursor { get; set; }
{
var imageDirectory = @"C:\Scripts\Eat";
lock (stateGate)
{
enabled = false;
runOnce = false;
captureCursor = false;
}
Point savedCursor = Point.Empty;
var SendInput = GetService<ISendInputScriptingApi>();
var SendInputWindow = GetService<ISendInputScriptingApi>();
SendInput.TargetWindow = null;
var window = GetService<IWindowSelector>().GetWindow("TL.exe");
SendInputWindow.TargetWindow = window;
using var cv = GetService<IComputerVisionExperimentalScriptingApi>().ForWindow(window).EnableOsd(10);
while (!cancellationToken.IsCancellationRequested)
{
bool run, capture;
lock (stateGate)
{
capture = captureCursor;
captureCursor = false;
run = enabled || runOnce;
runOnce = false;
}
if (capture)
{
savedCursor = SendInput.CursorPosition;
Log.Info($"Точка правого клика: {savedCursor}");
}
if (run) MainScript();
Sleep(100);
}
void MainScript()
{
if (savedCursor.IsEmpty)
{
Log.Warn("Сначала сохраните точку клавишей F5");
return;
}
window.Activate();
Sleep(2500);
SendInput.KeyPress(Key.F);
Sleep(300);
SendInput.MouseClick(1005, 1005);
Sleep(200);
int FoundX, FoundY;
if (!MyImageSearch(out FoundX, out FoundY, 640, 186, 1237, 939, "Vegetarian.bmp"))
{
Sleep(1000);
if (!MyImageSearch(out FoundX, out FoundY, 640, 186, 1237, 939, "Vegetarian.bmp"))
{
if (!MyImageSearch(out FoundX, out FoundY, 640, 186, 1237, 939, "Vegetarian1.bmp"))
{
Log.Warn($"Объект 'Vegetarian' не найден!");
Sleep(2000);
Log.Warn($"Скрипт выключен");
return;
}
}
}
SendInputWindow.MouseClick(FoundX, FoundY);
Sleep(200);
SendInputWindow.MouseClick(1499, 1004);
Sleep(200);
SendInputWindow.KeyPress(Key.Escape);
Sleep(200);
Sleep(600);
SendInput.MouseRightClick(savedCursor.X, savedCursor.Y);
}
bool MyImageSearch(out int FoundX, out int FoundY, int X1, int Y1, int X2, int Y2, string ImageFile)
{
Rectangle result;
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
result = cv.ImageSearch(Path.Combine(imageDirectory, ImageFile),
similarityThreshold: 95, region: Rectangle.FromLTRB(X1, Y1, X2, Y2));
if (!result.IsEmpty)
{
//found the image
FoundX = result.X;
FoundY = result.Y;
return true;
}
Sleep(100);
}
FoundX = FoundY = 0;
return false;
}
}
[Keybind(Hotkey = "F3")]
public void RunOnce() { lock (stateGate) runOnce = true; }
[Keybind(Hotkey = "F5")]
public void SaveCursor() { lock (stateGate) captureCursor = true; }
[Keybind(Hotkey = "F6")]
public void ToggleScript() { lock (stateGate) enabled = !enabled; }
Атрибутированные обработчики F3/F5/F6 объявлены методами объекта скрипта за пределами исполняемого блока; локальные функции не обнаруживаются механизмом Keybind. Общие флаги хранятся в свойствах скрипта под одним lock и сбрасываются в начале запуска. Точка курсора и CV остаются локальными данными основного цикла.
При успешном поиске выбирается левый верхний угол найденного образца. ImageSearch для ForWindow уже возвращает координаты окна; их передаём в отдельный SendInputWindow с тем же TargetWindow. Для экранных точек используем SendInput.TargetWindow = null, чтобы стандартный контроллер не преобразовал их повторно. При отсутствии обоих образцов выводится предупреждение и проход заканчивается.
Rectangle.FromLTRB переводит правую/нижнюю границу исходной области в ширину/высоту C# прямоугольника. Sleep и проверки между поисками учитывают отмену, синхронные CV/ввод не гарантируют немедленного прерывания. Симулятор должен быть доступен; Windows/игровой запуск здесь не проверен. Карта CV и карта ввода ведут к контрактам SDK.