Урок 2. Поиск и использование объектов.
В ближайших двух уроках (2 и 3) мы будем разбираться с принципами работы с различными объектами УО. Это очень важная тема, очень мало скриптов в которых в той или иной степени не задействована работа с объектами. Для удобства я разбил эту, в принципе, почти необъятную на два этапа изучения - вводный и более сложный - именно поэтому и сделано разделение на
2 урока. Внимательно читайте и изучайте этот урок, помня при этом, что это только "цветочки"
Прежде чем начинать разбираться с инструментами инжекта по работе с объектами, надо разобраться в устройстве мира Ультимы и терминологии. Весь мир УО состоит из двух типов - динамические объекты и статитические объекты. Статические объекты (обычно называют статикой) - это фоновое изображение, в основном неизменная на различных шардах. Это земля, вода, деревья, скалы, пещеры, города (здания). Статика тоже может быть подразделена на типы, но об этом позже. Динамические объекты (или просто объекты, как их обычно называют) - это (обычно) то, что привнесено в мир администрацией и игроками. Это оружие, ресурсы, контейнеры, двери, ворота, заборы, украшения, животные, сами игроки в конце концов (игровые чары). Инжект предоставляет удобный инструмент определения статик или динамик - наберите команду ,hide и ткните прицелом в объект. Если он исчезнет (вернуть вы его можете выполнив команду ,resend) - это динамический объект. Если не исчезнет или вообще прицел не нажимается - статика.
Каждый объект в мире имеет уникальный номер (обычно называется сериалом или ID), по которому вы можете работать с этим объектом. Номер обычно представлен в виде длинного (8 знаков) шестнадцатиричного числа, например 0x4010AF44. Практически всегда в инжекте эти числа используются в строковом виде, то есть "0x4010AF44". Обратиться к объекту можно тремя способами: использовав его сериал, использовав имя, присвоенное этому объекту на закладке Objects, либо использовав указатель на этот объект. Об указателях мы поговорим позже.
Пока же дальше о устройстве мира. Каждый объект мира УО имеет ряд свойств. Это номер его графического предствления (то есть номер картинки, которой этот объект в мире представлен), это его цвет, это количество в стопке (если объект предствляет из себя стопку или кучку чего-либо), это его координаты в мире в данное время и так далее. Наберите в игру команду ,info и укажите прицелом на объект. Вы получите полные сведения об этом объекте и о способе предствления этих данных.
Что еще важно? Объект получает от сервера сериал при создании и обычно сохраняет этот сериал до разрушения. Но есть и исключения. Во-первых, нельзя путаться - стопка (кучка) из, например, 4 штучек руды - это один объект, уберите 1 штучку руды и вы получите новый объект - стопку из 3 штучек руды. Соответственно, новый сериал. Во-вторых, обычно такие стопки (кучки) изменяют свой сериал даже при простом перекладывании. Понятно, что в этом случае нет никакого смысла запоминать сериал таких объектов. В подобных случаях, а также в массе других случаев важным инструментом работы с объектами становится его тип.
Тип объекта - это номер его графического представления в игре. Именно поэтому тип объекта иногда называют его графикой. Тип объекта представляется в виде четырёхзначного шестнадцатеричного числа, например 0x1234. Типы объектов вы тоже практически всегда будете использовать как строки, то есть "0x1234". В отличие от сериала типы нескольких одинаковых по графическому представлению предметов будут совпадать. Так у всех даггеров один тип, у всех хитер-щитов тоже один тип. Аналогично другие объекты. В некоторых случаях один предмет может иметь два типа, в зависимости от ориентации (например кирки и хатчеты, поэкспериментируйте, берите в руку и кладите обратно - кирка будет поворачиваться и у нее будет изменяться тип).
Опять же, повторю: тип объекта - это его графическое представление, то есть фактически иконка. Причем без привязки к цвету. Так у рега black pearl, у жемчуга (pearl) - один тип, они отличаются только цветом. Тоже самое касается ингов разных металлов - это все один тип. Если вы снова взглянете на тот результат, который вы получили командой ,info (см. выше) вы увидите, что тип объекта и цвет объекта - это разные параметры.
Ну и напоследок. О указателях, то есть о третьем способе указания на объект. Указатель - это некая переменная инжекта (уже существующая, вам не надо ее создавать), которая хранит в себе ссылку на какой-то объект УО. Здесь важно понимать, что указатель - это не сериал объекта, а только ссылка на этот предмет, которую можно использовать в скриптах вместо сериала объекта. Но если вы захотите получить сериал этого объекта - вам надо будет использовать специальную функцию "взятия" этого сериала с указателя. Подробнее о указателях мы будем говорить в следующей главе, но определение мне надо было дать сейчас, так как есть один указатель, который мы будем использовать практически сразу - это 'finditem'. Сразу хочу обратить ваше внимание, что все имена указателей в скриптах всегда должны писаться в кавычках!
Ну, думаю, с теорией разобрались. Теперь ближе к инжекту.
Если у вас есть сериал объекта, который вы хотите использовать - тут все просто. Вставляйте его в соответствующую команду и все. Самая распространенная ошибка - это пытаться искать объект по сериалу. Этого делать не нужно. Игра (сервер, клиент, инжект) сама знает где лежит объект с таким сериалом. Если же он для вас недоступен (находится далеко) или вообще не существует - вы просто получите соответствующее сообщение. Но в инжекте есть возможность проверить местоположение конкретного объекта по сериалу, это команда ContainerOf.
UO.ContainerOf( object ) - возвращает параметр объекта object (по умолчанию - персонажа игрока) - контейнер, в котором находится этот объект.
То есть команда ContainerOf вернет вам сериал контейнера в котором находится ваш объект. Тут важно учитывать один аспект. Инжект и клиент "не помнят" что находится в закрытом контейнере, например, сундуке, если он с момента последнего запуска игры ни разу не открывался. И команда ContainerOf в этом случае вернет нам '0xFFFFFFFF' - что означает: на земле или неизвестно где (если предмет находится на земле, мы всегда сможем понять где он командами определения дистанции до объекта, об этом мы поговорим позже).
Давайте рассмотрим пример:
Code:
sub TestContOf()
VAR ser1 = '0x12345678', ser2
ser2 = UO.ContainerOf ( ser1 )
If ser2 <> '0xFFFFFFFF' Then
UO.Print( 'Сериал контейнера: ' + ser2 )
Endif
endsub
В данном примере в переменную ser1 у нас занесен сериал какого-то определенного предмета. Этот скрипт запросит сериал контейнера в котором находится наш объект (это может быть сундук, мешок, бекпак персонажа, банковский бокс), потом проверит, что вернула нам команда UO.ContainerOf - если объект недоступен или находится на земле - скрипт закончится, иначе - выведет сериал этого контейнера.
Теперь мы приходим к наиболее распространенной ситуации - когда нам неизвестен сериал объекта, который мы хотим использовать. Мы знаем только его тип и (необязательно) цвет. Тогда нам надо найти этот объект. Для такого поиска служит команда Findtype.
UO.FindType( type, [ color, containerobject/ground/my ] ) - ищет обьект указаного типа type и цвета color в указаном контейнере и помещает найденый обьект в системный указатель finditem.Сразу отметим синтаксис указания команд. В круглых скобках после названия указываются параметры команды (я об этом уже писал), в квадратных - необязательные параметры, то есть параметры, которые можно не указывать. В данном случае единственным обязательным параметром является параметр type - то есть тип объекта для поиска. Результат поиска - то есть ссылка на конкретный найденный объект будет помещен в указатель finditem.
Теперь о параметрах:
Code:
Если тип type указан '-1' или '0xffff' то ищутся обьекты любого типа.
Если цвет color не указан, указан '-1' или '0xffff' то ищутся обьекты любого цвета.
Если контейнер object указан, то ищется в контейнере. Если не указан, указан '-1' или '0xffff', или указан ошибочно то ищутся обьекты в бекпаке персонажа.
указан 'ground' или '1' то обьект ищется на земле, в радиусе устанавливаемом ,set finddistance.
Если указан 'my' или '2' то ищется в бекпаке и всех подсумках.
Нельзя указывать контейнер для поиска не указав цвет color.
Например, UO.Findtype( '0x1BEF') будет искать инги (указан тип ингов) в паке персонажа (остальные параметры опущены, соответственно по умолчанию ищутся объекты любого цвета в паке персонажа). UO.Findtype( '0x1BEF', '0x0000' ) будет искать инги указанного цвета в паке персонажа. UO.Findtype( '0x1BEF', '0x0000', '0x12345678' ) будет искать инги указанного цвета в контейнере с сериалом '0x12345678'.
Не забывайте контролировать, что указанный контейнер хоть раз открывался за текущий сеанс игры. Если контейнер не открывался - инжект "не помнит" его содержимого и не сможет искать в нем (как открыть контейнер из скрипта я расскажу ниже).
Поиск остановится при нахождении первого найденного объекта, соответствующего условиям. Что же делать если нам нужен не он, а какой-то другой? Для этого используется команда Ignore.
UO.Ignore( object, [ on/off ] ) - указывает что обьект должен игнорироваться (или не игнорироваться, с параметром off) при последующих командах поиска, например findtype. Разрешить поиск всех обьектов снова можно командой ignorereset.Я лично ни разу не пользовался переключателями on/off в этой команде, вполне достаточно что по умолчанию идет параметр on.
Обычно в скрипте идет поиск объекта по типу, потом проверяется - нужный ли мы объект нашли, если нет - найденное игнорируется и поиск повторяется. Для примера предположим что в паке лежат две кучки ингов, в одной 10 штук, в другой 100. Надо найту ту, в которой 100 ингов.
Code:
UO.Findtype( '0x1BEF' )
If UO.GetQuantity( 'finditem' ) == 10 Then
UO.Ignore( 'finditem' )
UO.Findtype( '0x1BEF' )
Endif
В данном примере мы ищем инги, потом проверяем командой GetQuantity (она считает количество объектов в указанной стопке), количество ингов в найденной стопке. Если количество равно 10 - игнорируем результат поиска и ищем снова.
Обратите внимание - мы используем указатель 'finditem' как объект. Именно так и есть - в этом указателе содержится ссылка на конкретный найденный объект последним поиском. Если же нам нужен (для каких-нибудь целей) сериал этого объекта, то нам нужно воспользоваться командой GetSerial:
VAR MySerial = UO.GetSerial( 'finditem' )После такого "считывания" сериала найденного объекта мы можем использовать как MySerial, так и 'finditem' совершенно одинаково, только 'finditem' изменит свое содержимое при следующем поиске.
После находения нужного нам объекта (если мы использовали Ignore) желательно очистить список игнорируемых объектов командой IgnoreReset (чтобы не было проблем при последующих поисках).
UO.IgnoreReset() - Включает в поиск все обьекты, которые были указаны как игнорируемые командой Ignore.Теперь можно приведенный выше код оформить отдельным скриптом:
Code:
sub FindIngs()
UO.Findtype( '0x1BEF' )
If UO.GetQuantity( 'finditem' ) == 10 Then
UO.Ignore( 'finditem' )
UO.Findtype( '0x1BEF' )
Endif
UO.Print( 'Сериал найденного объекта: ' + UO.GetSerial( 'finditem' ) )
UO.IgnoreReset()
endsub
Но этот скрипт работает только в данном конкретном случае, когда мы знаем что у нас в паке две кучки ингов, в одной из них 10 штук (ищем другую). А если их у нас много?
На самом деле поиск находит сразу все подходящие под условия объекты, но устанавливает указатель "finditem' на первый из них (первый с точки зрения инжекта, вы не можете узнать в каком порядке он их считает). Но вы всегда в скрипте можете узнать сколько подходящих объектов поиск нашел. Для этого служит команда FindCount.
UO.FindCount() - Возвращает количество обьектов, найденых последней командой FindType.Как видите из синтаксиса, команда возвращает число - количество найденных объектов. Обычное применение этой команды - проверка найдено ли что-то подходящее под заданные условия. Например, вам надо в скрипте использовать инги в паке (скажем, для БСа). Вот пример:
Code:
UO.Findtype( '0x1BEF' )
If UO.FindCount() > 0 Then
.... ; используем найденный объект через указатель 'finditem')
Else
UO.Print( 'Нету ингов!' )
Endif
Теперь рассмотрим подробно один практический скрипт, хорошо иллюстрирующий вышеописанные команды. Давайте напишем скрипт, который будет искать вокруг нас разных чаров и выдавать нам их сериалы. Именно так устроены скрипты на поиск ПК. Для удобства (это все-таки пример) ограничимся поиском только мужских чаров, то есть чаров с типом '0x0190'. Как будет устроен такой скрипт? Скрипт будет в цикле искать вокруг вашего чара других чаров по типу, если находит - брать его сериал, определять расстояние до этого чара, выводить на экран сообщение об этом. После этого игнорировать этого чара и искать следующего. Напишем сначала общий цикл:
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
Repeat
UO.FindType( '0x0190', '-1', '1' )
.........
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
Основным циклом я выбрал Repeat...Until потому что во-первых, мне надо чтобы хоть раз поиск производился, во-вторых, так как команда UO.FindCount() количество найденных чаров будет возвращать только после первого поиска, а поиск у нас производится уже внутри цикла. В самом деле, нельзя написать:
Code:
While UO.FindCount() > 0
UO.FindType(.....)
...........
Wend
так как на момент первой проверки в операторе While условия ни одного поиска произведено не было, а значит и UO.FindCount() быстрее всего вернет ошибку или случайное число.
В конце скрипта я сразу поставил команду UO.IgnoreReset(), так как внутри цикла мы точно будем использовать UO.Ignore(). Конечно, мы будем ее использовать только в случае успешности поиска, а если никого вокруг чара нету - цикл прервется после первого поиска и никаких чаров в игнор-список занесено не будет. Но это не страшно. UO.IgnoreReset() в этом случае просто ничего не сделает.
Также я завел четыре переменные: SerChar, ColChar, NamChar, DistChar. Для чего они - узнаете позже.
Продолжаем. Дополним наш скрипт проверкой результатов поиска:
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
...........
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
Итак, внутри цикла в скрипте у нас появился оператор If...Endif, в котором мы проверяем, нашел ли последний поиск какого-либо чара. Если нашел - тут мы дальше будем писать какие-то действия, если не нашел - тут мы ставим небольшую паузу, чтобы избежать ошибки пустого цикла (см. урок 1). На самом деле у нас не будет пустого цикла, даже если мы не поставим паузы - у нас есть действие в цикле (это поиск), ну лучше перестраховаться. Идем дальше:
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
SerChar = UO.GetSerial( 'finditem' )
UO.Print( 'Нашли чара! Его сериал: ' + SerChar )
UO.Ignore( 'finditem' )
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
Ну вот. В случае успешного поиска мы записываем в переменную SerChar сериал этого чара, потом выводим на экран сообщение с этим сериалом и даем команду игнорировать этого чара при следующих поисках. Что теперь происходит в скрипте? При запуске скрипт производит первый поиск, если никого рядом нету - ждет полсекунды и выходит из скрипта. Если же кто-то найден - берется его сериал, выводится сообщение, этот чар игнорируется при следующих поисках. Так как кто-то был найден - цикл будет продолжен (условие в until не выполняется). И так далее.
Теперь один важный момент. Когда я описывал параметры команды FindType, там упоминалась установка дистанции поиска на земле. Все дело в том, что инжект позволяет изменять расстояние, на котором мы будем искать необходимое нам на земле (то есть вокруг чара). Минимум - это 1 тайл (клетка), максимум - зависит от шарда, обычно это 12-14 тайлов. Это очень удобно. В самом деле - не всегда нужен поиск на максимальной дистанции, часто нужно поискать что-то совсем рядом с чаром, а на те объекты, что находятся вне пределов возможностей чара для использования лучше не обращать внимания. Эта установка производится через команду Set (через эту команду производится достаточно много разных установок, мы еще будем возвращаться к этой команде). Пока нас интересует параметр finddistance.
UO.Set( 'finddistance', '6' ) - устанавливает параметр finddistance в 6 тайлов (клеток).Обратите внимание на синтаксис команды - и параметр, и значение пишутся в кавычках. Добавим эту команду в наш скрипт:
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
UO.Set( 'finddistance', '6' )
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
SerChar = UO.GetSerial( 'finditem' )
UO.Print( 'Нашли чара! Его сериал: ' + SerChar )
UO.Ignore( 'finditem' )
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
Надо понимать - мы установили дистанцию для поиска командой FindType для всех скриптов, которые вы будете запускать в течение текущего сеанса игры. После перезапуска инжекта дистанция сбросится на дистанцию по умолчанию (вы можете ее посмотреть если наберете в клиенте команду ,set finddistance без значения).
Получился вполне рабочий скрипт. Единственное - информации о найденном чаре мало. Хочется больше о нем узнать. Нет проблем. Давайте расширять возможности скрипта. Сначала добавим определение расстояния до этого чара. Для этого используется команда UO.GetDistance (эта команда возвращает расстояние до объекта):
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
UO.Set( 'finddistance', '6' )
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
SerChar = UO.GetSerial( 'finditem' )
DistChar = UO.GetDistance( 'finditem' )
UO.Print( 'Нашли чара! Его сериал: ' + SerChar )
UO.Print( 'Расстояние до этого чара: ' + str( DistChar ) )
UO.Ignore( 'finditem' )
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
У нас в скрипте появилась новая команда: str. Она написана в нестандартном синтаксисе, то есть без префикса UO. Эта команда (на самом деле тут у нас очередная небольшая путаница в терминологии, большинство команд, называемых мною, на самом деле является встроенными функциями, но выбор терминологии - право автора учебника
) относится к командам, встроенным в скриптовый модуль инжекта, их не так много, но мы в ближайшем будущем будем использовать только три, причем одну из них мы уже хорошо знаем:
str( число) - преобразует число в строку
int( строка) - преобразует строку в число
wait( число) - ну это вы уже знаете...Другие команды, встроенные в скриптовый модуль (они все пишутся без префикса UO) мы будем рассматривать по мере необходимости.
Итак, теперь наш скрипт определяет расстояние до найденного чара и выводит о нем информацию. Продолжим. Еще было бы неплохо, определить - ПК или АПК этот чар. Инжект нам это позволяет сделать. Для этого нам потребуется команда GetNotoriety.
UO.GetNotoriety( object ) - возвращает параметр обьекта object (по умолчанию - персонажа игрока) - злобность (цвет). Значения такие:
1 - Innocent (синий), 2 - Friend (зелёный), 3 - Gray (серый), 4 - Criminal (серый), 5 - Enemy (рыжий), 6 - Murderer (красный).Не на всех шардах есть все эти значения, поэтому мы не будем встраивать в скрипт преобразование числа в название цвета, добавим просто определение злобности и вывод номера:
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
UO.Set( 'finddistance', '6' )
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
SerChar = UO.GetSerial( 'finditem' )
DistChar = UO.GetDistance( 'finditem' )
ColChar = UO.GetNotoriety( 'finditem' )
UO.Print( 'Нашли чара! Его сериал: ' + SerChar )
UO.Print( 'Расстояние до этого чара: ' + str( DistChar ) )
UO.Print( 'Уровень злобности этого чара: ' + str( ColChar ) )
UO.Ignore( 'finditem' )
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
Ну вот. Осталась только одна характеристика найденного чара, которую мы еще не выводим, это его имя. Добавим в скрипт и это, только сначала обращу ваше внимание на одну тонкость. Дело в том, что до тех пор, пока вы не кликнули по нему - инжект не знает имени этого чара и не может его предоставить нам. Учтем это. Заодно запомните очередную команду: Click.
UO.Click( object ) - эмулирует клик левой клавишей мышки по указанному объекту.Ну и заодно:
UO.GetName( object ) - Возвращает параметр обьекта object (по умолчанию - персонажа игрока) - имя обьекта.Добавляем. Сразу только укажу - после клика надо поставить какую-то паузу, чтобы дождаться реакции сервера на одиночный клик по чару (должно высветиться в игре имя чара). Вполне вероятно, что указанной мною паузы в полсекунды вам не хватит - тогда увеличьте ее.
Code:
sub FindMen()
VAR SerChar, ColChar, NamChar, DistChar
UO.Set( 'finddistance', '6' )
Repeat
UO.FindType( '0x0190', '-1', '1' )
If UO.FindCount() > 0 Then
SerChar = UO.GetSerial( 'finditem' )
DistChar = UO.GetDistance( 'finditem' )
ColChar = UO.GetNotoriety( 'finditem' )
UO.Click( 'finditem' )
wait( 500 )
NamChar = UO.GetName( 'finditem' )
UO.Print( 'Нашли чара! Его сериал: ' + SerChar )
UO.Print( 'Расстояние до этого чара: ' + str( DistChar ) )
UO.Print( 'Уровень злобности этого чара: ' + str( ColChar ) )
UO.Print( 'Имя этого чара: ' + NamChar )
UO.Ignore( 'finditem' )
Else
wait( 500 )
Endif
Until UO.FindCount() == 0
UO.IgnoreReset()
endsub
На этом второй урок закончен. В третьем уроке мы продолжим изучать работу с объектами в скриптах, а также изучим оператор for...next и массивы.