Оглавление Сообщение об ошибках Ваше мнение о проекте E-Mail автору

Интерпретатор скриптовых команд (ScE)

 

Этот раздел содержит информацию о том как в общем устроен интерпретатор скриптовых команд и описываются принципы его работы.

 

v    Многозадачность

 

Чтобы двигаться дальше - нам необходимо более подробно разобраться с устройством SсE.

Когда вы открываете встроенный в игру редактор скриптов и нажимаете PgDn, перед вами появляется длинный список скриптов - в нем присутствуют все скрипты, хотя бы один раз исполнявшиеся с начала игры. Их имена - в первой колонке списка.

Во второй и третьей колонке вы видите некие числа. Они показывают - сколько экземпляров этого скрипта исполняется в данный момент.

Последняя колонка - число стартов скрипта за все время игры.


Во второй и третьей колонке напротив некоторых скриптов вы можете увидеть довольно большие числа - сотни и тысячи экземпляров скрипта, одновременно присутствующих в памяти SсE. Это наблюдение может натолкнуть вас на ошибочный вывод по поводу многозадачности SсE.

Вынужден разочаровать - подлинная многозадачность в SсE отсутствует.

Присутствует ее подобие - так называемая НЕВЫТЕСНЯЮЩАЯ многозадачность.

 

Что такое «невытесняющая многозадачность»?

Это механизм, от которого даже многоуважаемая фирма Майкрософт отказалась вот уже девять лет тому назад (с выходом в свет Windows'95). Суть механизма заключается в том, что программа (в нашем случае - скрипт) должна сама себя приостанавливать и таким образом давать возможность выполняться остальным программам.

Чтобы лучше понять систему, представьте себе телефон-автомат. И очередь желающих позвонить. Не вытесняющая многозадачность в таком случае будет выглядеть так: каждый человек звонит, говорит сколько ему позволяет его совесть, вешает трубку и переходит в конец очереди - рассчитывая на то, что у всех в очереди совесть таки имеется.

Если при этом в очереди попадается бессовестный человек (программа, не позволяющая системе прервать себя и дать возможность выполняться другим программам) - очередь замирает и ждет, пока этот негодяй не закончит трепаться (на это время система повисает). В Х2 это может выглядеть следующим образом:

001           while [TRUE]

002           end

Запуск этого скрипта, неминуемо подвесит игру.

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

В SсE жандарма нет. Поэтому скрипты не должны надолго "занимать телефон", иначе игра просто повиснет. Чтобы такого не происходило, в SсE есть ряд команд, при исполнении которых скрипт может быть прерван. Насколько большое значение этому придается показывает хотя бы то, что такие команды имеют специальную отметку - знак '@' перед именем.

Соответственно, программист должен позаботиться о том, чтобы в его скриптах такие команды встречались почаще. Например:

001           while [TRUE]

002           @ wait 10 ms

003           end

Теперь этот скрипт не вызовет зависания игры, а будет исполняться прерываясь в строке 002, давая поработать другим скриптам и задачам.

Рассмотрим имеющиеся в ScE прерываемые команды «@».

 

Семейство wait

 

В него входят две команды:

1.                  wait <длительность паузы> ms

2.                  wait randomly from <минимальная длительность паузы> to <максимальная длительность паузы> ms

 

Команды этого семейства приостанавливают выполнение скрипта на определенное время. Вполне логично, что эту паузу заполняют собой другие скрипты. Показатель ms приблизительный. Так как минимальное время ожидания не может быть меньше времени прогонки одного фрейма, то есть чтобы вычислить минимальную задержку нужно взять текущее количество фреймов в секунду и разделить на 1000, получим количество ms за один фрейм это будет минимальное время задержки.

 

Семейство команд fly

 

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

Вот их перечень:

1.fly to homebase - корабль отправляется в порт приписки.

2. fly to station <станция> - корабль отправляется на указанную станцию.

3. fly to sector <сектор> - корабль отправляется в указанный сектор.

4. turn turret <номер турели> to <объект:цель> : timeout=<время на выполнение команды> ms - корабль пытается навести указанную турель на цель в течение некоторого времени. Команда возвращает специальный код, по которому можно узнать - удалось ли прицелиться. Если код равен FLRET_FIREFREE - прицелиться удалось и можно стрелять. Если код равен FLRET_TIMEOUT - турель не успела навестись за указанное время. Впрочем, подробный разговор о кодах FLRET у нас далеко-далеко впереди. (Возвращаемые коды FLRET будут рассмотрены далее в Разделе Особенности команд- Команды FLY)

5. attack run on target <объект:цель> : timeout=<время на выполнение команды> ms - корабль совершает атакующий маневр в направлении цели.

6. defensive move : type=<число:тип маневра>, intensity= <число: интенсивность маневра>,  timeout=<время на выполнение маневра> ms, avoid object=<объект, вокруг которого строится маневр> - корабль выполняет маневр уклонения от указанного объекта.

7. move to ware object <объект:цель для захвата в трюм> for collecting : timeout=<время на выполнение команды> ms - корабль приближается к объекту на расстояние захвата в трюм.

8. move around <длительность полета> ms - корабль просто некоторое время слоняется в космосе без дела.

9. escort ship <корабль-цель> - корабль начинает сопровождать выбранную цель, не включаясь однако с ней в один строй (formation).

10. escort ship <корабль-цель> : timeout=<длительность сопровождения> ms - то же, что и предыдущая команда, но с ограничением по времени.

11. follow object <объект> with precision <дистанция следования> m - корабль строго следует за объектом, соблюдая дистанцию.

12. follow <объект> with precision <дистанция> m : timeout=<время следования> ms - то же, что и предыдущая команда, но с ограничением по времени.

13. move to position : x=<координата x>, y=<координата y>, z=<координата z>, with precision <погрешность> m - корабль летит куда-нибудь в район указанной точки, останавливаясь от нее не дальше, чем допускает указанная погрешность.

 

 

Семейство script calls

 

На сегодня это семейство включает одну команду :

call script. -  Команда вызывает другой скрипт. Может быть вызвана с префиксом START, в этом случае скрипт в котором находится команда call script не дожидаясь окончания вызываемого им скрипта, продолжит работу. Прерываемость данной команды условна и подразумевает наличие в вызываемом скрипте команд «@», в противном случае вызываемый скрипт не прервать.

 

В дополнение к выше сказанному, есть еще один момент.  Есть такой пожалуй наиболее неприятный баг: если какой-то непрерывно выполняющийся кусок скрипта (т.е. между двумя ближайшими прерываемыми командами @) выполняется на протяжении более 200 мс, то игра необратимо повиснет. Это одна из основных причин повисания казалось бы идеально написанного скрипта. Причем, если на быстром процессоре скрипт может отмолотить за 50 мс, это не гарантирует, что на компьютере с более слабым процессором этот же скрипт не подвесит игру. Но это еще не самое страшное. Как известно, весь движок игры есть один единственный процесс, где графика, экономика, скрипты и многое другое выполняется поочередно Итог - стоит дать сбой анимации в интенсивном бою и это незамедлительно отражается на скорости исполнения скриптов. Не вместились в окно 200 мс - и игра повисла. И этот баг не отловишь, т.к. он может иметь место 1 раз за 20 часов игры.

Так что не экономьте на командах wait! Даже "wait 1 ms" здорово помогает избегать подобных инцидентов. Т.е. обязательно внутри любого цикла должен быть "wait 1 ms", если только этот цикл не велик и вы абсолютно уверены, что он завершится при любых обстоятельствах.

 

v    Локальные скрипты, глобальные скрипты

Раньше мы уже обсуждали тот факт, что скрипты бывают локальные и глобальные. С глобальными скриптами мы столкнемся при изучении текста и концепции StationManager. (см. «Описание скриптов пилотов» )

Локальные скрипты отличаются от глобальных тем, что они привязаны к какому-либо объекту. Что значит "привязаны"? Это значит, что в момент уничтожения объекта все эти скрипты будут остановлены. Также это значит, что внутри этих скриптов обсуждавшаяся ранее константа [THIS] будет указывать на объект, к которому скрипты привязаны.

Еще одно отличие локальных скриптов заключается в том, что каждому локальному скрипту при запуске назначается номер задачи (Task ID) и приоритет (prio). Когда в игре активирован редактор скриптов, вы можете просмотреть информацию обо всех задачах, присутствующих на каком-либо объекте, через меню информации об объекте (кнопка I). Просто нажимайте PgDn - и ниже общих свойств объекта вы увидите список стеков задач. Вы увидите общую информацию по каждому стеку и список находящихся в стеке скриптов.

 

v    Задачи, стеки, приоритеты

 

Каждый объект в игре можно образно представить себе в виде библиотечной картотеки. Картотека - это набор ящичков с буковками алфавита. В каждом ящичке лежит стопочка карточек с названиями книг, начинающимися на ту самую букву алфавита что написана на ящичке. Карточки в ящичке тоже отсортированы - но уже по второй букве названия (у всех карточек из одного ящичка первая буква названия одинаковая).

Так вот, в нашем случае картотека - это объект. Ящички маркированы не буквами, а числами. Эти числа называются номерами задач, а ящички - стеками задач. Карточки - это скрипты. Номер задачи скрипта показывает, в каком стеке скрипт будет исполняться (в какой ящичек ляжет карточка).

Если для запуска локального скрипта мы используем команду start call script <скрипт> или start task <число:номер задачи> with script <скрипт> and prio <число:приоритет>, из ящичка будут безжалостно вытряхнуты все находящиеся в нем карточки (будут уничтожены все скрипты в стеке задачи) и вместо них в ящичек будет помещена одна-единственная карточка (будет запущен указанный скрипт). Однако имеет место одна оговорка, команда @ START [THIS]->call script ничего не вытеснит и не запустит, а просто подвесит текущий скрипт, если он работает в стеке Task 0. Если же запустить call script без префикса START и на отличном от текущего объекте, то она тоже вытеснит все из стека Task 0 этого объекта.

Если для запуска локального скрипта мы используем команду interrupt task <число:номер задачи> with script <скрипт> and prio <число:приоритет>, то скриптовый движок сначала сравнит приоритеты нового скрипта и того, который в данный момент исполняется в указанном стеке. Если приоритет нового скрипта больше (причем строго больше) приоритета уже исполняющегося - исполняющийся скрипт будет приостановлен и "поверх" него запустится новый скрипт. Старый скрипт при этом никуда не денется и будет смирно ждать пока не завершится новый скрипт, с более высоким приоритетом (или пока весь стек не будет "зачищен" командой start task). Иными словами, приоритет - это вторая буква в названии книги на карточке. Если в ящичке уже лежит стопка карточек, то мы сможем добавить в него только такую карточку, название которой идет по алфавиту следующим за названием с верхней карточки в стопке.

Таким образом, в стеке задачи может лежать несколько скриптов. Активным (работающим) будет только самый последний, остальные скрипты в стеке задачи будут "заморожены".

Это очень удобный механизм для моделирования реакций на окружающую обстановку.

Например, предположим, на объекте (транспортном корабле), в стеке с номером 0, исполняется скрипт, ведущий корабль на торговую станцию за товаром. И в процессе полета возникает необходимость отбиться от пиратов.

Если бы не было стекового механизма, нам следовало бы сначала запомнить что мы делали (куда и зачем летели), где-то сохранить эту информацию, затем остановить текущий скрипт полета, затем запустить скрипт обороны от нападения, затем вновь вспомнить чем мы занимались до нападения (куда и зачем летели) и снова запустить скрипт полета к цели.

Стековый механизм позволяет нам просто запустить в том же стеке (с номером 0) скрипт обороны с приоритетом большим, чем приоритет скрипта полета к цели. Тогда скрипт полета будет автоматически "заморожен" и автоматически же "разморожен" в момент окончания "боевого" скрипта.

 

v    "Волшебные" номера задач

 

Номера ящичков-задач могут идти не подряд и не с начала, то есть на объекте могут существовать, к примеру, задачи с номерами 2, 5 и 999999.

Почти все задачи равноправны, но есть несколько специальных задач с особыми номерами и особым назначением исполняющихся в них скриптов.

Задача 0 (Task 0) на объектах-кораблях служит для запуска скриптов автопилота. Все действия, которые вы назначаете кораблю через основную командную консоль, отрабатываются скриптами в нулевом стеке. Все поступающие кораблю сигналы также отрабатываются в этом стеке (о сигналах поговорим чуть позже).

Задачи 1-6 (Task 1-Task 6) на объектах-кораблях служат для запуска скриптов управления турелями больших кораблей. Номер задачи соответствует номеру турели в консоли управления турелями.

Задачи 10 и 11 (Task 10, Task 11) на объектах-кораблях соответствуют двум введенным в версии 1.4 дополнительным слотам для команд. Команды для первого слота отрабатываются в десятом стеке, команды для второго слота - в одиннадцатом.

Задачи 10-19 (Task 10 - Task 19) на объектах-станциях соответствуют десяти введенным в версии 1.4 командным слотам станций игрока. Команды для первого слота отрабатываются с десятом стеке, команды для второго слота - в одиннадцатом стеке и так далее.

 

v    Прерывания.

Иногда в определенной игровой ситуации, может потребоваться чтобы  ваш скрипт запущенный на объекте и выполняющий какую либо задачу прервался и на некоторое время передал управление другому скрипту, который в свою очередь отработав, вернул управление прерванному скрипту. Например, ситуация когда сектор патрулирует несколько кораблей объеденных в группу, т.е. подчиненные корабли сопровождают корабль лидер. Если лидер совершает прыжок в другой сектор, то сопровождающие корабли должны временно прервать выполнение сопровождения и совершить прыжок в след за лидером, а после прыжка вернуться к сопровождению. В ScE для выполнения подобных действий предусмотрены две команды. Первая из них это call script эта команда безусловно прервет выполнение текущего скрипта до тех пор, пока вызываемый скрипт не закончит работу. Эта команда удобна в случае необходимости прервать выполнение текущего скрипта в не зависимости от выполняемых в данный момент задач в частности их приоритетов. Вторая команда interrupt with script позволяет прервать текущий скрипт в случае если приоритет вызываемого скрипта выше приоритета вызывающего скрипта (более подробно использование приоритетов будет рассмотрено в следующей главе «Сигналы»). Кроме того, эта команда не прерывает текущий скрипт сразу, а лишь помещает его в очередь на выполнение и прерывание будет произведено на первой встреченной команде со знаком «@».

Для демонстрации используем два небольших скрипта. В первом рассмотрим команду call script :

Скрипт CallScript.xml

001   write to log file #1  append=[TRUE]  value='CALLSCRIPT string 001'
002 @ = [THIS] -> call script 'temp' : 
003   write to log file #1  append=[TRUE]  value='CALLSCRIPT string 003'
004   return null

 

Вспомогательный скрипт temp.xml

001   write to log file #1  append=[TRUE]  value='TEMP string 001'
002 @ wait 10 ms
003   write to log file #1  append=[TRUE]  value='TEMP string 003'
004   return null

 

Выполнив скрипт CallScript смотрим порядок выполнения в лог файле log0001.txt:

 

CALLSCRIPT string 001

TEMP string 001

TEMP string 003

CALLSCRIPT string 003

 

Из примера видно, что CallScript был прерван в строке 002, далее полностью отработал скрипт temp и после завершения управление было возвращено CallScript в стоку 003. Теперь давайте рассмотрим команду interrupt with script:

 

Скрипт INTERRUPT

001   write to log file #1  append=[TRUE]  value='INTERRUPT str 001'
002   [THIS] ->  interrupt with script 'temp' and prio 1000:  arg1=null arg2=null arg3=null arg4=null
003   write to log file #1  append=[TRUE]  value='INTERRUPT str 003'
004 @ wait 1 ms
005   write to log file #1  append=[TRUE]  value='INTERRUPT str 005'
006   return null

 

В итоге мы получаем запись в логе:

 

INTERRUPT str 001

INTERRUPT str 003

TEMP string 001

TEMP string 003

INTERRUPT str 005

 

Что говорит о том, что скрипт INTERRUPT начав работать выполнил соку 001, во второй стоке в очередь на выполнение был поставлен скрипт temp, но его выполнение не началось, а было продолжено выполнение скрипта INTERRUPT строка 003. В стоке 004 встречена команда со знаком «@», здесь выполнение скрипта INTERRUPT прерывается и полностью выполняется скрипт temp. По окончании работы скрипт temp возвращает управление INTERRUPT в строку 005. Следует заметить, что прерывание произошло т.к. приоритет скрипта temp мы установили как 1000, в то время как приоритет INTERRUPT равен 0, но если приоритет вызывающего скрипта был бы выше, то прерывание не произошло бы. В ситуации если приоритет вызывающего скрипта был повышен после того как вызываемый скрипт был помещен в очередь, т.е. после команды interrupt with , то прерывание все равно произойдет и вызываемый скрипт будет полностью выполнен. Так же при использовании данной команды следует учитывать тот факт, что если после команды interrupt with нет ни одной команды со знаком «@» то прерывания скрипта не произойдет.

v    Сигналы.

В Х2 имеется специальная группа прерываний называемая сигналами. Понятие  сигнал в ScE подразумевает, прерывание выполняемого скрипта специальным прерыванием сформированным в случае возникновения в игре определенных условий, самим ScE. Например, если корабль атакован другим кораблем, то для атакованного корабля ScE сформирует сигнал SIGNAL_ATTACKED. Данный сигнал, прервет скрипт выполняемый в нулевом стеке на атакуемом корабле в данный момент и передаст управление специальному скрипту обработчику сигнала, который в свою очередь будет выполняться в этом же стеке. Прерывание произойдет только при условии, что приоритет скрипта обработчика выше приоритета скрипта выполняемого в данный момент на объекте, в противном случае сигнал будет проигнорирован. После обработки сигнала, скрипт обработчик вернет управление скрипту, который выполнялся на корабле до прерывания. Сам процесс прерывания происходит по принципу «не вытесняющей многозадачности», т.е. выполняемый скрипт будет прерван не мгновенно по поступлению сигнала, а на первой же команде со значком «@». Это еще один аргумент в пользу размещения в ваших скриптах  команд задержек.

Сигналы в Х2 делятся на две группы, это синхронные сигналы и асинхронные. Разделение на группы происходит в зависимости от момента генерации сигнала. Различия заключаются в следующем:

Синхронные сигналы – это прерывания генерируемые ScE, непосредственно в момент наступления определенных условий. Точнее можно сказать, это прерывание формируемое синхронно наступлению определенных условий в игре. Например, в момент гибели корабля ScE сформирует сигнал SIGNAL_KILLED, но в момент возникновения данного сигнала, корабль еще существует и ScE  будет ожидать пока не закончит свою работу скрипт-обработчик данного сигнала, хотя все игровые показатели данного объекта будут говорить о том что он уничтожен (нулевой щит и корпус). И только после того как обработчик вернет управление (return null), ScE продолжит выполнять процедуры связанные с уничтожением объекта, как то, анимационный эффект взрыва, удаление из памяти ссылок на уничтоженный объект и удаление самого объекта из галактики Х. Все эти процессы проходят за считанные миллисекунды и не заметны играющему, но скриптописатель должен учитывать данные нюансы при создании собственных скриптов изменяющих стандартные скрипты-обработчики сигналов. В Х2 очень легко нарушить некий баланс имеющийся в игре, что не минуемо отразится на играбельности в целом.

Асинхронный сигнал – это прерывание сформированное ScE по прошествии определенных игровых условий, т.е этот сигнал будет передан после того как событие выполнившие заданные условия произошло. Примером асинхронного сигнала может послужить сигнал SIGNAL_ATTACKED. Данный сигнал формируется после того как объект был атакован, то есть после того как выстрелы атакующего объекта достигли цели и ScE уже произвел соответствующее снятие щита и корпуса у атакованного объекта и воспроизвел все анимационные эффекты. Соответственно, ScE не приостанавливается при обработке асинхронных сигналов для ожидания момента когда скрипт-обработчик сигнала вернет управление как это происходит при генерации и обработке синхронных сигналов. А выполнив все необходимые процедуры, формирует сам сигнал, который в свою очередь запускает соответствующий скрипт обработчик.

Теперь рассмотрев виды сигналов, следует оговорить еще одну особенность их формирования движком Х2. Для этого мы используем понятие «автоаргумент». Под этим понятием я подразумеваю следующее:

Автоаргумент – это набор данных сформированный самим ScE при генерации определенного сигнала. Эти аргументы будут переданы скрипту–обработчику сигнала автоматически и в определенном порядке без какого-либо дополнительного участия самого программиста.

Какие данные передавать скрипту, прописано самом движке Х2 и изменить их стандартными путями невозможно. Но можно воспользоваться теми данными что уже есть. Для этого необходимо при написании скрипта-обработчика в полях аргументов заготовить места под них, а ScE при формировании сигнала сам сгенерирует нужные данные. Данные будут переданы в определенном порядке, соответственно и аргументы должны быть расположены в таком же порядке. Стандартные автоаргументы формируемые для сигналов и их порядок описан в таблице.

 Всего ScE Х2 формирует семь видов сигналов, для каждого из них назначен  стандартный скрипт-обработчик, который задается при запуске игры или загрузке сейва. Изначально, в игре за назначение обработчиков сигналов, отвечает служебный скрипт !init.ship.globalscriptmap.std.xml, находящийся в папке \scripts. !init.ship.globalscriptmap.std.xml вызывается из основного инициализирующего скрипта !init.ship.globalscriptmap.xml, это основной инициализирующий скрипт осуществляющий инициализацию всех установок заданных по умолчанию. Далее, в процессе игры, пользователь может при помощи собственных скриптов сам переназначить скрипт обработчик. В таблице указаны все имеющиеся в игре сигналы и заданные им в !init.ship.globalscriptmap.std.xml, скрипты обработчики.

 

Сигнал

Тип сигнала

Синх \

Асинх

Условие возникновения

Автоаргументы

Стандартный скрипт-обработчик

Приоритет по умолчанию

SIGNAL_ATTACKED

Асинх.

Объект атакован. Выстрелы атакующего достигли цели.

1. The attacker – тип данных <Ship\Station>, нападающий объект.

 

2. action – тип данных <var\number> возвращается код текущей операции выполняемой объектом. В стандартном обработчике используются коды [ACTION_LAND_IN_STATION] посадка на станцию и [ACTION_ENTER_GATE] входит во врата.

!ship.signal.attacked (для кораблей класса «Корабль»)

!ship.signal.attacked.ts (для корабле класса «легкий транспорт TS»)

!station.signal.attacked.pl (для станций)

 

 

100

SIGNAL_COLLISIONWARN

 

 

 

Данный сигнал не работает.

 

SIGNAL_LEADERNEEDSHELP

Асинх.

Сигнал сопровождающему кораблю – лидер атакован

1. <Var\Ship> указатель на корабль требующий помощи.

2. <Value> атакующий объект

!ship.signal.leaderneedshelp

150

SIGNAL_FOLLOWERNEEDSHELP

Асинх.

Сигнал лидеру – сопровождающий атакован

1. <Var\Ship> указатель на корабль требующий помощи.

2. <Value> атакующий объект

!ship.signal.followerneedshelp

150

SIGNAL_

FORMATIONLEADERCHANGED

Асинх.

Сигнал для всей группы  сопровождающих кораблей – лидер изменился (например: предыдущий лидер убит)

1. <Var\Ship> Новый лидер формации.

!ship.signal.formationleaderchg (скрипт -пустышка) сигнал обрабатывается на уровне движка.

200

SIGNAL_CAPTURED

Синхр.

Объект захвачен

 

!ship.signal.captured

300

SIGNAL_KILLED

Синхр.

Объект уничтожен

1. <Value> объект убийца.

2. <ObjectCommand> текущая команда объекта

3. <value> Основная цель команды

4. <value> Вторая цель команды

5. <value> Первый параметр выполнявшейся команды

6. <value> Второй  параметр выполнявшейся команды

 

!ship.signal.killed – для всех объектов.

10000

 

Для того, чтобы понять как это работает, давайте попробуем создать небольшой скрипт наглядно демонстрирующий работу сигналов. Рассматривать будем работу сигнала SIGNAL_ATTACKED (его легче всего получить, достаточно обстрелять объект не принадлежащий игроку. Обратите внимание на то, что данный сигнал не формируется при обстреле объектов той же расы что и нападающий). И так, суть нашего скрипта, это эмуляция некого парализующего эффекта длящегося одну минуту, у обстрелянного корабля относящегося к классу «легкий транспорт TS» и принадлежащего расе аргон, а также вывод названия  атаковавшего объекта в бортовой журнал.

Создаем свой скрипт обработчик, назовем его signal.ATTACKED

Script signal.ATTACKED

Version: 0
for Script Engine Version: 25

Description

New associated script SIGNAL ATTACKED

Arguments

1: Attacker, Var/Ship/Station, ‘the attacker’

Source Text


001   $cont = 3
002   while $cont
003 @  wait 100 ms
004    play sample 1114
005    dec $cont = 
006   end
007   write to player logbook $Attacker
008 @ wait 60000 ms
009   return null

 

 

001-006 организуем воспроизведение звукового сигнала похожего на бряканье ложки в пустой кружке, это сообщит нам о том, что произошло прерывание вызванное поступлением сигнала SIGNAL_ATTACKED и в данный момент работает созданный нами обработчик этого сигнала.

007 запишем в бортовой журнал переменную $Attacker которая в данный момент содержит указатель на атакующий объект, т.е. на наш корабль. Значение в эту переменную было внесено самим движком, когда был сформирован сигнал. Это пример использования автоаргументов.

008 собственно и есть, сама эмуляция одноминутного паралича у корабля получившего сигнал.

009 возвращаем управление прерванному сигналом скрипту.

 

Для того, чтобы наш скрипт назначить обработчиком, нам потребуется еще один скрипт который переназначит стандартный скрипт обработчик на нужный нам. Назовем его, init.ATTACKED.signal :

Script init.ATTACKED.signal

Version: 0
for Script Engine Version: 25

Description

Init associated script SIGNAL ATTACKED

Arguments

Source Text


001   Назначаем новый обработчик сигнала SIGNAL_ATTACKED 
002   global script map: set: key=SIGNAL_ATTACKED, class=Лёгкий транспорт TS, race=Аргон, script='signal.ATTACKED', prio=100
003   return null

 

Теперь перезагружаем сохраненку или воспользуемся командой из главного меню редактора «Reinit Script Caches», т.к. наш инициализирующий файл имеет имя формата init.*.*, то он загрузится в память автоматически при использовании любого из вышеуказанных способов. Теперь, вылетаем в космос на любом корабле имеющем хоть одну пушку и ищем любой аргонский транспорт класса TS. Подлетаем к нему на расстояние выстрела, смотрим информацию о нем. Нас интересует служебная информация о скриптах выполняемых на этом корабле в данный момент:

Как мы видим, в стеке нашего подопытного объекта, находиться три скрипта (на вашем объекте может выполняться другой набор скриптов, в нашем случае это не имеет особого значения). Фактически, это процесс выполнения команды «Купить товар». Теперь, обстреляйте корабль (не убейте, а только обстреляйте), стреляйте одиночными выстрелами пока не услышите звуковой сигнал издаваемый нашим скриптом обработчиком. Как только это произошло, вы увидите, что транспорт остановился и завис в космосе. Открываем информационный экран:

Как мы видим, в стек добавился еще один скрипт. Это назначенный нами обработчик сигнала SIGNAL_ATTACKED и т.к. приоритет для нашего скрипта обработчика, мы назначили как 100 ( в init.ATTACKED.signal, строка 002), а все выполняемые на объекте в нулевом стеке имеют приоритет 0, то наш скрипт обработчик signal.ATTACKED будет добавлен в конец нулевого стека и ему будет передано управление. Так как, практически после каждого попадания по объекту ScE будет генерировать сигнал SIGNAL_ATTACKED, то установка приоритета для скрипта обработчика является своеобразной блокировкой от прерывания уже выполняемого  в данный момент обработчика. Прерывание будет возможно только в том случае, если выполняемый в данный момент скрипт обработчик сам понизил свой приоритет или обработчик поступившего сигнала имеет более высокий приоритет.

            Наличие сигналов-прерываний в ScE и возможность изменения скриптом собственного приоритета, предоставляет возможность гибкого управления реакциями объекта на внешние условия, используя для выполнения разных действий сам скрипт обработчик. Эта возможность наглядно реализована в стандартном скрипте обработчике сигнала SIGNAL_ATTACKED для кораблей класса «легкий транспорт TS» - скрипт !ship.signal.attacked.ts. Давайте разберемся как работает этот скрипт. Не буду описывать сам скрипт, а лишь логику построения процедур.

001   
002   * front mounted lasers
003   $frontlasers = [THIS] -> get max. number of lasers in turret 0
004   $maxlaserst = [THIS] -> get maximum laser strength
005   $frontmaxlaserst = [THIS] -> get max laser strength in turret 0
006   
007   $random = random value from 0 to 100 - 1
008   
009   if [THIS] != [PLAYERSHIP] AND ( $random < 30 OR [OWNER] == Player )
010    $r = random value from 5 to 11 - 1
011    $r = [THIS] -> launch $r fight drones: protect me or attack target=null
012   end
013   
014   if $action == [ACTION_LAND_IN_STATION] OR $action == [ACTION_ENTER_GATE]
015    return null
016   end

Проверяем некоторые условия. Если текущий объект не корабль игрока и еще кое-какие условия с элементом случайности, то запускаем Дронов для защиты. Проверяем не запущена ли в данный момент процедура стыковки или входа во врата, если запущена возвращаем управление скрипту, прерванному поступлением сигнала. Если ни какие условия не совпали, продолжается выполнение скрипта.

017   
018   * try to attack
019   $sobj = [THIS] -> get SectorObject ID
020   if not is script with prio 50 on stack
021    if ( [OWNER] == Player AND $sobj == 0 ) OR ( $random < 20 AND $frontlasers AND $frontmaxlaserst )
022 @   skip if not [THIS] -> call script '!ship.signal.attacked' :  the attacker=$attacker  action=$action
023      return null
024    end
025   end
026   
027   if $sobj
028    $speed = [THIS] -> get max speed
029    $attackerspeed = $attacker -> get max speed
030    if $speed <= $attackerspeed OR $random > 80
031     $r = random value from 40 to 90 - 1
032     $timeout = random value from 20000 to 40000 - 1
033 @   $r = [THIS] -> defensive move: type=null, intensity=$r, timeout=$timeoutms, avoid object=$attacker
034    end
035   end

Проверяем не убегает ли в данный момент наш объект от агрессора (наличие в нулевом стеке скрипта с приоритетом 50, откуда он мог взяться будет понятно далее по рассмотрению скрипта). Если «нет», то в зависимости от условий (наличие пушек, принадлежность, элемент случайности), либо запускаем скрипт для атаки объекта-агрессора, либо выполняем маневр уклонения. Если ни какие условия не совпали, продолжается выполнение скрипта.

036   
037   * try to fly to nearest station only if not already done...
038   if not is script with prio 50 on stack
039    if not is script with prio 99 on stack
040     if $random < 40 AND [OWNER] != Player
041      $flags = [Find.Friend] | [Find.Neutral] | [Find.Nearest]
042      $station =  find station: sector=[SECTOR] class or type=null race=null flags=$flags refobj=[THIS] maxdist=null maxnum=null refpos=null

Еще раз в строках 038 и 039 осуществляется проверка не убегает ли объект от агрессора или не атакует ли его. Все эти проверки осуществляются при помощи работы с приоритетами, которые выставляют скрипты выполняющие соответствующие задачи. Если нет, учитываем некую случайность (переменная $random) и если данный объект не принадлежит игроку, ищем ближайшую дружественную или нейтральную станцию.

043      if $station
044   * run the flee to station part with prio 50
045       set script priority to 50
046 @     = [THIS] -> call script '!ship.sh.attack.ts.flee' :  the attacker=$attacker  station to dock at=$station
047       return null
048      end
049     end
050    end
051   end
052   
053   return null

Если станция найдена, то понижаем приоритет скрипта со 100 до 50 и запускаем механизм бегства на ближайшую найденную станцию, используя вспомогательный скрипт !ship.sh.attack.ts.flee с указанием атакующего объекта (автоаргумент) и выбранную для бегства станцию. Теперь, если во время полета к станции объект снова получит сигнал SIGNAL_ATTACKED, то он не прекратит бегства к станции, а выполнив маневр уклонения продолжит полет к ней. Точно также, при совпадении условий в сегменте 018-025, будет запущен скрипт атаки, который в свою очередь установит свой приоритет как 99 и в случае поступления сигнала  SIGNAL_ATTACKED обработчик учтет наличие в стеке скрипта выполняющего атаку.

Для полного понимания этого материала, попробуйте разобрать работу других скриптов обработчиков, это скрипты с именами !ship.signal.*. И поэкспериментируйте в игре, создавая для подопытных объектов условия в которых для них будут генерироваться те или иные сигналы. Просматривая какие скрипты находятся в нулевом стеке и как у них изменяются приоритеты, разобраться не составит труда.

 

 

 

 

 

 


Оглавление Сообщение об ошибках Ваше мнение о проекте E-Mail автору
Сайт создан в системе uCoz