Оглавление | Сообщение об ошибках | Ваше мнение о проекте | E-Mail автору |
Этот раздел содержит информацию о том как в общем устроен интерпретатор скриптовых команд и описываются принципы его работы.
Чтобы двигаться дальше - нам необходимо более подробно разобраться с устройством 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 за один фрейм это будет минимальное время задержки.
В это семейство входят команды "протяженного действия" из раздела навигационных команд, когда кораблю отдается приказ, исполнение которого может занять какое-то время (и не всегда известно точно - какое). Но во время выполнения этих команд система не будет приостановлена.
Вот их перечень:
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 - и ниже общих свойств объекта вы увидите список стеков задач. Вы увидите общую информацию по каждому стеку и список находящихся в стеке скриптов.
Каждый объект в игре можно образно представить себе в виде библиотечной картотеки. Картотека - это набор ящичков с буковками алфавита. В каждом ящичке лежит стопочка карточек с названиями книг, начинающимися на ту самую букву алфавита что написана на ящичке. Карточки в ящичке тоже отсортированы - но уже по второй букве названия (у всех карточек из одного ящичка первая буква названия одинаковая).
Так вот, в нашем случае картотека - это объект. Ящички маркированы не буквами, а числами. Эти числа называются номерами задач, а ящички - стеками задач. Карточки - это скрипты. Номер задачи скрипта показывает, в каком стеке скрипт будет исполняться (в какой ящичек ляжет карточка).
Если для запуска локального скрипта мы используем команду 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) скрипт обороны с приоритетом большим, чем приоритет скрипта полета к цели. Тогда скрипт полета будет автоматически "заморожен" и автоматически же "разморожен" в момент окончания "боевого" скрипта.
Номера ящичков-задач могут идти не подряд и не с начала, то есть на объекте могут существовать, к примеру, задачи с номерами 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 командным слотам станций игрока. Команды для первого слота отрабатываются с десятом стеке, команды для второго слота - в одиннадцатом стеке и так далее.
Иногда в определенной игровой ситуации, может потребоваться чтобы ваш скрипт запущенный на объекте и выполняющий какую либо задачу прервался и на некоторое время передал управление другому скрипту, который в свою очередь отработав, вернул управление прерванному скрипту. Например, ситуация когда сектор патрулирует несколько кораблей объеденных в группу, т.е. подчиненные корабли сопровождают корабль лидер. Если лидер совершает прыжок в другой сектор, то сопровождающие корабли должны временно прервать выполнение сопровождения и совершить прыжок в след за лидером, а после прыжка вернуться к сопровождению. В ScE для выполнения подобных действий предусмотрены две команды. Первая из них это call script эта команда безусловно прервет выполнение текущего скрипта до тех пор, пока вызываемый скрипт не закончит работу. Эта команда удобна в случае необходимости прервать выполнение текущего скрипта в не зависимости от выполняемых в данный момент задач в частности их приоритетов. Вторая команда interrupt with script позволяет прервать текущий скрипт в случае если приоритет вызываемого скрипта выше приоритета вызывающего скрипта (более подробно использование приоритетов будет рассмотрено в следующей главе «Сигналы»). Кроме того, эта команда не прерывает текущий скрипт сразу, а лишь помещает его в очередь на выполнение и прерывание будет произведено на первой встреченной команде со знаком «@».
Для демонстрации используем два небольших скрипта. В первом рассмотрим команду call script :
Скрипт CallScript.xml
|
Вспомогательный скрипт temp.xml
|
Выполнив скрипт CallScript смотрим порядок выполнения в лог файле log0001.txt:
CALLSCRIPT string 001
TEMP string 001
TEMP string 003
CALLSCRIPT string 003
Из примера видно, что CallScript был прерван в строке 002, далее полностью отработал скрипт temp и после завершения управление было возвращено CallScript в стоку 003. Теперь давайте рассмотрим команду interrupt with script:
Скрипт INTERRUPT
|
В итоге мы получаем запись в логе:
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 нет ни одной команды со знаком «@» то прерывания скрипта не произойдет.
В Х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 Description New associated script SIGNAL ATTACKED Arguments 1: Attacker, Var/Ship/Station, ‘the attacker’ Source Text
|
001-006 организуем воспроизведение звукового сигнала похожего на бряканье ложки в пустой кружке, это сообщит нам о том, что произошло прерывание вызванное поступлением сигнала SIGNAL_ATTACKED и в данный момент работает созданный нами обработчик этого сигнала.
007 запишем в бортовой журнал переменную $Attacker которая в данный момент содержит указатель на атакующий объект, т.е. на наш корабль. Значение в эту переменную было внесено самим движком, когда был сформирован сигнал. Это пример использования автоаргументов.
008 собственно и есть, сама эмуляция одноминутного паралича у корабля получившего сигнал.
009 возвращаем управление прерванному сигналом скрипту.
Для того, чтобы наш скрипт назначить обработчиком, нам потребуется еще один скрипт который переназначит стандартный скрипт обработчик на нужный нам. Назовем его, init.ATTACKED.signal :
Script init.ATTACKED.signal
Version: 0 Description Init associated script SIGNAL ATTACKED Arguments Source Text
|
Теперь перезагружаем сохраненку или воспользуемся командой из главного меню редактора «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. Давайте разберемся как работает этот скрипт. Не буду описывать сам скрипт, а лишь логику построения процедур.
|
Проверяем некоторые условия. Если текущий объект не корабль игрока и еще кое-какие условия с элементом случайности, то запускаем Дронов для защиты. Проверяем не запущена ли в данный момент процедура стыковки или входа во врата, если запущена возвращаем управление скрипту, прерванному поступлением сигнала. Если ни какие условия не совпали, продолжается выполнение скрипта.
|
Проверяем не убегает ли в данный момент наш объект от агрессора (наличие в нулевом стеке скрипта с приоритетом 50, откуда он мог взяться будет понятно далее по рассмотрению скрипта). Если «нет», то в зависимости от условий (наличие пушек, принадлежность, элемент случайности), либо запускаем скрипт для атаки объекта-агрессора, либо выполняем маневр уклонения. Если ни какие условия не совпали, продолжается выполнение скрипта.
|
Еще раз в строках 038 и 039 осуществляется проверка не убегает ли объект от агрессора или не атакует ли его. Все эти проверки осуществляются при помощи работы с приоритетами, которые выставляют скрипты выполняющие соответствующие задачи. Если нет, учитываем некую случайность (переменная $random) и если данный объект не принадлежит игроку, ищем ближайшую дружественную или нейтральную станцию.
|
Если станция найдена, то понижаем приоритет скрипта со 100 до 50 и запускаем механизм бегства на ближайшую найденную станцию, используя вспомогательный скрипт !ship.sh.attack.ts.flee с указанием атакующего объекта (автоаргумент) и выбранную для бегства станцию. Теперь, если во время полета к станции объект снова получит сигнал SIGNAL_ATTACKED, то он не прекратит бегства к станции, а выполнив маневр уклонения продолжит полет к ней. Точно также, при совпадении условий в сегменте 018-025, будет запущен скрипт атаки, который в свою очередь установит свой приоритет как 99 и в случае поступления сигнала SIGNAL_ATTACKED обработчик учтет наличие в стеке скрипта выполняющего атаку.
Для полного понимания этого материала, попробуйте разобрать работу других скриптов обработчиков, это скрипты с именами !ship.signal.*. И поэкспериментируйте в игре, создавая для подопытных объектов условия в которых для них будут генерироваться те или иные сигналы. Просматривая какие скрипты находятся в нулевом стеке и как у них изменяются приоритеты, разобраться не составит труда.
Оглавление | Сообщение об ошибках | Ваше мнение о проекте | E-Mail автору |