-
Notifications
You must be signed in to change notification settings - Fork 0
API UO Follow
ClassicUO / Basic IDE
Оглавление · Алфавитный указатель
Включает фоновое следование за загруженным персонажем/NPC либо движение к точке X, Y, Z. Во время движения клиент проверяет переданные условия остановки; скрипт продолжает выполнение сразу после вызова.
UO.Follow() -> Integer
UO.Follow(target:Any) -> Integer
UO.Follow(target:Any, distance:Any) -> Integer
UO.Follow(target:Any, distance:Any, running:Any) -> Integer
UO.Follow(target:Any, distance:Any, running:Any, stopConditions:Any) -> Integer
UO.Follow(x:Any, y:Any, z:Any, distance:Any, running:Any) -> Integer
UO.Follow(x:Any, y:Any, z:Any, distance:Any, running:Any, stopConditions:Any) -> Integer
Выберите одну из зарегистрированных форм. Параметры передаются позиционно. Any означает значение BASIC с преобразованием внутри команды; Unit — отсутствие возвращаемого значения.
-
target— Serial персонажа/NPC: Integer, hex/десятичная строка, lasttarget либо имя, ранее сохранённое через AddObject. Это не graphic/type и не координаты. Нельзя следовать за собой или предметом. 0 останавливает следование. Без аргументов Follow() также останавливает его; прицел не появляется. -
distance— Допуск по X/Y в клетках: максимум |dx| и |dy|. Для персонажа Integer 1..18, по умолчанию 1; разница Z до 16 допустима. Для точки Integer 0..18, аргумент обязателен: 0 требует точного X/Y, 2 допускает остановку в пределах двух клеток; Z должен совпасть точно. Недопустимое значение не заменяет прежний режим. -
running— Boolean-совместимое значение: true/1 — бег, false/0 — ходьба; по умолчанию true. Серверные ограничения скорости, выносливость, паралич и препятствия сохраняются. Флаг не ускоряет серверные шаги. -
stopConditions— Необязательный массив условий остановки: четвёртый аргумент для персонажа, шестой для координат. Каждый элемент — массив ровно из трёх значений [field, comparison, threshold]. field и comparison — строки, threshold — Integer/Decimal; для флагов подходят true/false (1/0). Срабатывание ЛЮБОГО условия прекращает Follow (OR). Пустой массив не добавляет условий. Число элементов не ограничено фиксированными 3/5/10 пунктами, но ограничено памятью; большие списки проверяются порциями. Разрешённые поля и единицы перечислены ниже; неизвестное поле, оператор или неправильная форма массива вызывают ошибку до замены прежнего Follow. -
x / y— Только формы с 5/6 аргументами: целые мировые координаты на текущей карте, 0..65535 и внутри загруженных границ карты. Это не координаты окна/gump и не serial. Переход на другую карту не выполняется. -
z— Только формы с 5/6 аргументами: целая высота мира -128..127. Для завершения требуется именно эта высота, поэтому указывайте Z доступной поверхности нужного этажа. Отдельных значений «любой Z» или автоматического выбора высоты нет.
Integer 1 (true) — режим следования принят клиентом, либо выполнен запрос остановки; Integer 0 (false) — нет подходящего загруженного персонажа, игрок недоступен/мёртв, цель вне обзора, неверна дистанция или клиент не поддерживает команду. Это не результат прибытия, не serial и не число шагов. Разрешены IF result = 1 и IF result = true. Ошибки типов/неизвестного имени объекта могут вызвать ошибку скрипта. Для координат 0 также означает недопустимые X/Y/Z или допуск. 1 означает только принятие фоновой задачи; прибытие и будущая проходимость ещё не подтверждены.
- Ровно один режим Follow на клиента: персонаж или координаты. Повторный успешный вызов заменяет цель и параметры. Неуспешный вызов не отменяет прежнюю цель. Для следования за персонажем он должен уже находиться в загруженном списке Mobiles в пределах ClientViewRange; координатная точка может находиться дальше обзора.
- При достижении distance возле персонажа ходьба прекращается, но режим остаётся включённым и возобновляет движение, когда цель отходит. Возле координатной точки задача полностью завершается при соблюдении допуска X/Y и точного Z. Команда не сообщает о гарантированной достижимости маршрута при запуске.
- Follow(), Follow(0), MoverStop(), ручное перемещение мышью/стрелками, новый скриптовый маршрут или стандартное следование клиента прекращают этот режим. TerminateAll/Terminate("all") также его останавливают. Отмена ещё выполняющегося скрипта-владельца останавливает его Follow.
- Оба режима прекращаются при смене карты или персонажа, смерти игрока и разрыве соединения. Следование за персонажем дополнительно прекращается при исчезновении цели из памяти/обзора. Появление цели позднее не запускает режим автоматически: нужен новый Follow.
- Закрытие IDE не останавливает режим. Нормальное завершение запустившего скрипта оставляет Follow включённым. Пауза скрипта сама по себе не является паузой этого клиентского режима; перед паузой используйте Follow(0), когда движение нужно прекратить.
- Расчёт выполняет обычный Pathfinder порциями на игровом потоке. При следовании за персонажем новый маршрут запрашивается не чаще одного раза за 500 мс; уже выполняющийся поиск не перезапускается. После отказа повторная попытка не чаще раза за 1000 мс. Такой режим может оставаться активным без движения — используйте таймаут. Координатная задача при отказе пути завершается, как описано ниже.
- Follow не запускает поиск отсутствующей цели, не телепортирует и не открывает двери сам. Уже отправленные серверу шаги могут завершиться после команды остановки. В варианте Classic без Basic IDE скриптовая команда недоступна.
- Поля расстояния/времени: distance — текущая дистанция до цели в клетках max(abs(dx),abs(dy)); height — абсолютная разность Z в единицах мира; elapsed — миллисекунды с принятия этого Follow. distance > 12 останавливает при удалении дальше 12 клеток, elapsed >= 30000 — после 30 секунд. Повторный успешный Follow начинает отсчёт заново.
- Состояние своего персонажа: self.hp / self.mana / self.stam — текущие значения; self.hppercent / self.manapercent / self.stampercent — проценты 0..100 от соответствующего максимума. Неизвестный максимум (<=0) означает неизвестное значение: условие по этой характеристике не срабатывает, а не получает выдуманный 0.
- Логические поля своего персонажа: self.poisoned (отравлен), self.paralyzed (парализован), self.hidden (скрыт). Значения 1/0. Например self.paralyzed = true прекращает следование при параличе.
- Поля цели: target.dead — мёртвый mobile, target.hidden — скрыт, target.visible — видим клиенту (не CheckLOS), target.hp / target.hppercent — загруженные HP/проценты. Если HP max неизвестен, условие HP не срабатывает. Сервер может прислать относительную шкалу HP чужого персонажа, поэтому для неё удобнее hppercent.
- Дополнительные поля цели: target.notoriety — статус 0..7 (0 неизвестный, 1 Innocent, 2 Ally, 3 Gray, 4 Criminal, 5 Enemy, 6 Murderer, 7 Invulnerable); target.body — числовой graphic тела. Это не type одежды. Например target.notoriety <> 2 прекращает сопровождение при потере статуса Ally.
- Операторы: "=", "<>", "<", "<=", ">", ">=". Равенство точное; для процентов обычно нужен порог <= или >=. Поля нечувствительны к регистру. Условия являются данными, не исполняемыми строками BASIC: произвольные команды или имена функций в field не вызываются.
- Копия условий фиксируется при вызове, включая числовые пороги. Изменение исходного массива/переменной не обновляет режим; повторите Follow с новым массивом. Отменённый режим сам не возобновляется, когда условие перестанет выполняться.
- Условия проверяются и во время движения, и когда персонаж уже стоит возле цели. Обычный список до 64 элементов проверяется за один игровой кадр, следующая проверка не раньше чем через 250 мс. Больший список — по 64 элемента за кадр, затем пауза 250 мс. Поле elapsed может остановить позже порога на время проверки/кадра: это не точный таймер.
- До окончания первой проверки всего списка новый маршрут не начинается. Если условие уже выполнено, Follow вернёт 1 за принятый запрос, затем остановится на обновлении без первого шага. Пакеты шагов от прежнего движения уже могли быть отправлены.
- Новые условия не отключают обязательные причины остановки: потерю цели/игрока/карты/соединения, смерть игрока, отмену владельца, Terminate all и ручное/другое движение. Скриптовый код не выполняется на игровом потоке; проверяются только перечисленные локальные значения.
- Координатные формы: Follow(x,y,z,distance,running) и Follow(x,y,z,distance,running,stopConditions). Первые пять аргументов обязательны. Follow(x,y,z) не является короткой записью координат: три аргумента по-прежнему означают target,distance,running.
- В координатном режиме distance в условии означает оставшееся расстояние по X/Y до точки, height — абсолютную разницу с её Z; elapsed и все self.* работают как обычно. Поля target.* недопустимы: у точки нет HP, тела или статуса. Попытка передать их вызывает ошибку скрипта до замены прежнего режима.
- Условия проверяются до первого маршрута, во время его расчёта и пока персонаж движется. Срабатывание любого условия снимает задачу и отменяет принадлежащий ей путь. Частота и обработка больших списков такие же, как при следовании за персонажем; уже отправленные серверу шаги могут завершиться.
- Координатная задача завершается при достижении точки либо отказе/окончании поиска и движения без достижения. Бесконечные повторы к недоступной точке не запускаются. Обычные внутренние перерасчёты Pathfinder при препятствиях сохраняются. После завершения уход с места не возобновляет задачу.
- Если Pathfinder уже отправил последние шаги и закончил путь, но эти шаги ещё выполняются/ожидают подтверждения, задача продолжает проверять условия. Сама очередь отправленных шагов не стирается: её должен обработать обычный механизм движения и подтверждений сервера.
- Список можно собрать через List.Add в DO … LOOP или FOR, а затем один раз передать checks.ToArray() последним аргументом соответствующей формы. Не нужно вызывать Follow заново на каждой итерации: это заменяет режим и сбрасывает elapsed. Количество строк не ограничено небольшим числом, но набор поддерживаемых field ограничен перечисленными выше полями.
- Для собственных вычислений и сочетаний И/ИЛИ используйте обычный BASIC-цикл: LOOP UNTIL (условие1) OrElse (условие2), а для одновременного выполнения — AndAlso и скобки. LOOP завершает только цикл, поэтому после него явно вызовите UO.Follow(0), предпочтительно в Finally. Такой цикл и его проверки существуют, пока работает скрипт; массив stopConditions проверяет сам клиент и после нормального завершения скрипта.
Ниже описана реализация этого движка. Имена внутренних методов не являются дополнительными командами BASIC.
Конструктор регистрирует семь форм UO.Follow: 0..4 аргумента для персонажа/остановки, 5/6 — для точки. В формах 4/6 проверяет массив условий.
Через IApiFollowBridge / IApiConditionalFollowBridge / IApiCoordinateFollowBridge передаёт запрос клиенту и возвращает Integer 1/0.
Исходник проекта: external/InjectionScript/src/InjectionScript/Runtime/InjectionApiUO.cs; функция InjectionApiUO.
На игровом потоке проверяет доступность Basic и запускает контроллер.
Отключает прежнее стандартное следование после принятия новой цели.
Исходник проекта: src/ClassicUO.Client/Game/Managers/ClassicUOInjectionApiBridge.cs; функция Follow.
Проверяет цель и дистанцию, запоминает карту, игрока и владельца.
Заменяет прежний режим только после успешной проверки.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция Start.
Проверяет отмену, потерю цели, карту и дистанцию. При необходимости запускает incremental WalkTo с ограничением частоты.
Возле цели останавливает путь и ждёт её следующего перемещения, сохраняя режим.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция Update.
Вызывает StopOwnedPath и очищает цель/владельца.
Номер маршрута защищает новый чужой путь от отмены старым владельцем.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция Stop.
На потоке скрипта проверяет массив строк, имена полей, операторы и числовые пороги.
Создаёт неизменяемые условия; ошибка не заменяет прежний режим.
Исходник проекта: external/InjectionScript/src/InjectionScript/Runtime/FollowStopCondition.cs; функция Parse.
До планирования движения проверяет не больше 64 условий за кадр; после полного прохода ждёт 250 мс.
Первое совпадение вызывает Stop; большие списки не обрабатываются целиком за один кадр.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция CheckStopConditions.
Проверяет координаты, допуск, состояние игрока и совместимость условий; сохраняет точку текущей карты.
Возвращает признак принятия задачи; это ещё не прибытие к точке.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция StartAt.
Проверяет условия перед планированием и во время активного пути. Передаёт Pathfinder точный Z и допуск X/Y.
При достижении, условии остановки или отказе пути очищает задачу; номер пути защищает новое движение.
Исходник проекта: src/ClassicUO.Client/Game/Managers/BasicFollowController.cs; функция Update.
Новые потоки или циклы ожидания на каждый Follow не создаются. Контроллер обновляется вместе с менеджером скриптов, независимо от открытого окна IDE.
SUB Main()
# Сначала выберите персонажа/NPC обычным игровым таргетом.
VAR leader = UO.GetSerial("lasttarget")
# Только target: distance=1, running=true.
VAR accepted = UO.Follow(leader)
IF accepted = true THEN
UO.Print("Following is enabled; script continues")
ELSE
UO.Print("Select a loaded nearby character")
END IF
END SUBРазбор параметров и выполнения:
- leader — serial последней цели. Единица подтверждает принятие режима, а не прибытие. После END SUB режим продолжает работать до остановки/потери цели.
SUB Main()
VAR leader = UO.GetSerial("lasttarget")
# target=leader, distance=3 клетки, running=false.
IF UO.Follow(leader, 3, false) = 1 THEN
# Скрипт свободен выполнять другую работу. Здесь ждём 10 секунд.
WAIT(10000)
UO.Follow(0)
UO.Print("Following stopped")
END IF
END SUBРазбор параметров и выполнения:
- WAIT измеряется в миллисекундах. Follow(0) выключает общий режим следования; последующие перемещения цели больше не вызывают движение.
SUB Main()
# Имя leader предварительно сохранено через AddObject.
VAR leader = UO.GetSerial("leader")
IF UO.Follow(leader, 2, true) = 0 THEN
UO.Print("Cannot start following")
RETURN
END IF
TRY
VAR attempts = 0
WHILE attempts < 20 AND UO.Exists(leader)
# Не переиздаём Follow в цикле — режим уже наблюдает цель.
WAIT(500)
attempts = attempts + 1
WEND
FINALLY
# Без аргументов — остановка. Это не выбор новой цели.
UO.Follow()
END TRY
END SUBРазбор параметров и выполнения:
- distance=2, running=true; максимум 20 ожиданий по 500 мс. Exists проверяет наличие объекта, не достижимость пути. FINALLY останавливает режим при обычном выходе/ошибке; аварийная отмена владельца дополнительно обрабатывается клиентом.
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
VAR leader = UO.GetSerial("lasttarget")
DIM conditions[2] # Три условия: индексы 0, 1, 2.
conditions[0] = StopWhen("distance", ">", 12)
conditions[1] = StopWhen("self.hppercent", "<=", 40)
conditions[2] = StopWhen("elapsed", ">=", 30000)
# distance=2 здесь — дистанция сопровождения, не предел 12 выше.
VAR accepted = UO.Follow(leader, 2, true, conditions)
IF accepted = 0 THEN
UO.Print("Cannot follow this target")
END IF
# Скрипт заканчивается, клиент продолжает сопровождение и проверки.
END SUBРазбор параметров и выполнения:
- Остановка по ЛЮБОМУ условию: цель дальше 12 клеток ИЛИ наши HP не больше 40% ИЛИ прошло 30 секунд. Это не AND. StopWhen приведена полностью, её можно вынести в Include. Единица означает принятие, не прибытие.
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
VAR checks = List()
checks.Add(StopWhen("target.hidden", "=", true))
checks.Add(StopWhen("target.dead", "=", true))
checks.Add(StopWhen("target.notoriety", "<>", 2))
checks.Add(StopWhen("self.stam", "<=", 5))
checks.Add(StopWhen("height", ">", 16))
# Можно дописывать checks.Add(...) для других условий/порогов.
VAR leader = UO.GetSerial("lasttarget")
VAR conditions = checks.ToArray()
UO.Follow(leader, 2, false, conditions)
END SUBРазбор параметров и выполнения:
- Останавливаемся, если цель скрылась/умерла/перестала быть Ally, осталось не больше 5 выносливости либо перепад Z превысил 16. false означает идти пешком. ToArray передаёт список строк условий; размер заранее не задаётся. Если цель уже не Ally, движение не начнётся.
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
DIM conditions[1]
conditions[0] = StopWhen("distance", "<=", 3)
conditions[1] = StopWhen("elapsed", ">=", 15000)
# Обычный Follow возле цели остаётся активным. Условие distance<=3
# полностью выключает режим: за дальнейшим уходом цели уже не идём.
UO.Follow(UO.GetSerial("lasttarget"), 1, true, conditions)
END SUBРазбор параметров и выполнения:
- Поддерживаемая дистанция 1, условие остановки <=3; проверка каждые 250 мс может заметить достижение с задержкой. Таймаут 15000 мс не позволяет сопровождать бесконечно. Возврат Follow всё ещё означает принятие, не достижение трёх клеток.
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
# Редактируйте список: здесь два статуса, но можно добавить другие.
VAR statuses = List()
statuses.Add(5) # Enemy.
statuses.Add(6) # Murderer.
VAR checks = List()
VAR index = 0
DO WHILE index < statuses.Count()
checks.Add(StopWhen("target.notoriety", "=", statuses[index]))
index += 1
LOOP
# К построенному списку добавляем условия другого вида.
checks.Add(StopWhen("self.hppercent", "<=", 40))
checks.Add(StopWhen("elapsed", ">=", 30000))
# Один вызов устанавливает весь список, а не только последнюю строку.
VAR accepted = UO.Follow(UO.GetSerial("lasttarget"), 2, true, checks.ToArray())
IF accepted = 0 THEN
UO.Print("Select a loaded nearby character")
END IF
END SUBРазбор параметров и выполнения:
- DO WHILE собирает отдельное условие для каждого элемента statuses. Остановка при статусе 5 ИЛИ 6, своих HP <=40% ИЛИ через 30000 мс. Пустой statuses пропустит цикл, но два следующих условия останутся. Новые строки добавляйте до Follow; последующее изменение List не изменяет уже установленную копию. Для background-проверок тело цикла не требуется выполнять повторно.
FUNCTION StopEscort(leader, startedAt)
# Функция скрипта, не строка поля stopConditions.
# HP здесь в единицах здоровья, не в процентах.
RETURN (UO.GetDistance(leader) > 12) OrElse (UO.GetHP("self") <= 30) OrElse (Now() - startedAt >= 30000)
END FUNCTION
SUB Main()
VAR leader = UO.GetSerial("lasttarget")
VAR startedAt = Now() # Миллисекунды; не UO.Timer().
# Проверяем также до первого шага/ожидания.
IF leader = 0 OrElse StopEscort(leader, startedAt) THEN
RETURN
END IF
IF UO.Follow(leader, 2, true) = 0 THEN
RETURN
END IF
TRY
DO
UO.Wait(250) # Не опрашиваем игру непрерывно без ожидания.
LOOP UNTIL StopEscort(leader, startedAt)
FINALLY
# Выход из LOOP сам по себе не выключает фоновое следование.
UO.Follow(0)
END TRY
END SUBРазбор параметров и выполнения:
- StopEscort(leader, startedAt) возвращает Boolean-совместимые 1/0: цель дальше 12 клеток ИЛИ свои HP <=30 ИЛИ прошло 30 секунд. Добавляйте OrElse (ещё_условие); для обязательного сочетания двух проверок используйте (условиеA AndAlso условиеB). Неизвестные HP могут читаться как 0; пример тогда отказывается начинать. Пока скрипт выполняется, он проверяет выражения примерно каждые 250 мс; пауза скрипта приостанавливает эти проверки. Finally отправляет остановку при обычном выходе/ошибке; отмену владельца дополнительно отслеживает клиент. Если цель исчезла и клиент уже остановил Follow, этот цикл завершится по своим условиям, не позже проверки таймаута. Для работы после END SUB используйте четвёртый аргумент, как в предыдущих примерах.
SUB Main()
# Замените координаты и Z на доступную точку своей карты.
# distance=0: точный X/Y; running=true: бег.
VAR accepted = UO.Follow(1445, 1690, 0, 0, true)
IF accepted = 0 THEN
UO.Print("Coordinate task was not accepted")
END IF
END SUBРазбор параметров и выполнения:
- Пять аргументов: X=1445, Y=1690, Z=0, допуск X/Y=0, бег. Возврат 1 означает принятие, а не уже выполненное прибытие. На месте задача выключится полностью. Для досрочной ручной остановки вызовите UO.Follow(0).
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
VAR checks = List()
checks.Add(StopWhen("self.hppercent", "<=", 40))
checks.Add(StopWhen("self.paralyzed", "=", true))
checks.Add(StopWhen("elapsed", ">=", 30000))
# Один вызов: точка, допуск две клетки, бег, весь массив условий.
UO.Follow(1445, 1690, 0, 2, true, checks.ToArray())
# END SUB не выключает клиентские проверки во время пути.
END SUBРазбор параметров и выполнения:
- При HP <=40%, параличе или после 30 секунд клиент прекращает текущий путь. Иначе задача завершится в пределах двух клеток X/Y от (1445,1690), строго на Z=0. Проверки продолжаются и при поиске пути, и в движении; это не разовая проверка перед стартом. target.* здесь не применяется.
FUNCTION StopWhen(field, comparison, threshold)
# Пользовательская функция: создаёт одну строку условия.
DIM rule[2]
rule[0] = field
rule[1] = comparison
rule[2] = threshold
RETURN rule
END FUNCTION
SUB Main()
VAR checks = List()
checks.Add(StopWhen("distance", "<=", 3))
checks.Add(StopWhen("height", ">", 16))
checks.Add(StopWhen("elapsed", ">=", 20000))
# До точки десять клеток на восток от текущего положения.
VAR x = UO.GetX("self") + 10
VAR y = UO.GetY("self")
VAR z = UO.GetZ("self")
UO.Follow(x, y, z, 0, false, checks.ToArray())
END SUBРазбор параметров и выполнения:
- distance=0 в вызове задаёт точное прибытие, но условие distance<=3 прекращает задачу раньше, когда очередная проверка заметит такое расстояние. false означает ходьбу. Перепад Z>16 или 20000 мс тоже останавливают задачу. Направление не гарантирует проходимости; замените смещение на точку своего маршрута.
ClassicUO
Bad Newbie & Basic IDE
Найти в справочнике
Клиент и скрипты
Каталоги значений
Разработка и помощь