Пишем миссию на CLEO

27 ноября 2013
GTA_Masters19


Статья выйдет довольно-таки большая, поэтому ниже вы видите содержание:
1. Введение в CLEO-миссии и принцип их работы.
2. Нужный инструментарий и базовые знания, необходимые для работы.
3. Пишем нашу самую простую миссию и разбираем код.
4. Советы и подсказки по CLEO и Sanny Builder
5. Пишем стартер для миссии.
6. Небольшая статья о том, в чем различия между CLEO и main.scm, и почему миссии лучше писать на последнем.
Как мы видим, наши планы грандиозны, уместить все-все-все в одну статью не получится, но приготовьтесь к тому, что она будет довольно-таки большой и не каждый сможет дочитать до конца. Но ведь, чтобы писать скрипты и миссии, нужно упорство (не упоротость!) и желание. Именно для таких людей я и написал статью. Поехали!
Пишем миссию на CLEO

Введение в CLEO-миссии и принцип их работы
Вообще, миссии в игре - не более чем простые скрипты, но более сложно устроенные и имеющие свою иерархию. В играх серии GTA, начиная с третьей части и заканчивая San Andreas используется особый скриптовый язык, имя которого нам даже и неизвестно Но суть не в этом. Несмотря на неофициальное название данных миссий "CLEO-миссии", они вовсе не обязательно могут иметь директиву {$CLEO} в заголовке, и могут встраиваться внутрь scm-файла, но об этом я расскажу в конце статьи, пока что мы поучимся писать миссии на CLEO, чтобы было проще для начала. С технической точки зрения, миссия - это особый поток, для работы которого игра выделяет особое место в памяти. Особенность миссий состоит в том, что благодаря переменной $ONMISSION во время выполнения текущей миссии невозможно начать другую, сохраниться, и т.д. Если миссия будет провалена, либо выполнена (обязательное условие любой миссии), то переменная $ONMISSION обнуляется скриптером. Вот и все, что нужно знать о миссиях для начала. Теперь перейдем к базовым знаниям и инструментарию.


Нужный инструментарий и базовые знания, необходимые для работы
Для того, чтобы писать миссии на scm-коде, вам понадобится программа Sanny Builder, хотя, мало для кого это покажется сверхъестетственным. Добавлю от себя: не ленитесь пользоваться справкой SB даже в отношении этих самых миссий. Также вам может понадобится программа Ped Editor для просмотра всех моделей пешеходов в игре. В итоге получается вот что:
* Sanny Builder (кодинг)
* Ped Editor (просмотр педов)
...а также любые дополнительные программы для просмотра авто, редактор карт для поиска объектов (на сайте в этом же разделе, где лежит эта статья есть несколько уроков, посвященных основам SB - даже я иногда пользуюсь ими, в этом ничего ужасного нет), и т.д. Все зависит от того, какой сложности вы хотите создать миссию и насколько она будет проработанной.
Также вам понадобятся базовые знания по работе с программой Sanny Builder и опыт работы хотя бы с одним скриптом, хотя, надо бы и больше - вы должны понимать, как работают массивы и циклы, как создавать и работать с переменными, и т.д. Без знания всего этого создать свою миссию на scm можно, но вряд ли дело пойдет дальше "убей чувака и садись в машину". Тем не менее, с помощью scm можно создать поистине интересную и захватывающую миссию, о которой DYOM-ерам можно только мечтать.


Пишем нашу самую простую миссию и разбираем код
Итак, вы ознакомились с текстом выше и готовы к созданию scm-миссий. Открываем Sanny Builder и видим обычное пустое окно редактора. Кликните по кнопке "Создать" и мы можем заполнять рабочее поле командами и опкодами. Напоминаю, что мы пишем нашу миссию на CLEO, так что в первой строке прописываем директиву {$CLEO}. Но: хочу сразу предупредить: если вы так и оставите эту директиву, то ваш файл скомпилируется в .cs и миссия будет работать, конечно, но официально .cs не является форматом для CLEO-миссий. Чтобы файл скомпилировался как миссия, необходимо вместо обычной фразы CLEO написать вот такую команду: {$CLEO .cm}. Пробел между CLEO и точкой обязателен, cm - тоже. Этой командой мы указываем компилятору, что файл должен быть собран в формат CLEO-миссии, то есть, .cm.
Теперь необходимо заполнить рабочее поле небольшой заготовкой, сейчас мы ее вместе и напишем. После директивы на всякий случай прописываем команду 0000:, в любой момент может выскочить ошибка "Переход на нулевой оффсет". Насколько мы помним, данное сочетание называется опкодом. Теперь объявляем метку. Вы можете назвать ее как хотите, это будет наш заголовок CLEO-миссии:
:HEADthread 'MYMIS'gosub @MISSION_STARTifwasted_or_bustedjf @MISSION_CLEANUPgosub @MISSION_FAILED

Итак, разберем эти строчки. Команда thread 'MYMIS' задает всем меткам название MYMIS, MYMIS_1, MYMIS_2, и т.д. Это необходимо чисто для удобства после декомпиляции, по умолчанию, при отсутствии этой команды все метки именуются как NONAME, NONAME_1... gosub - переход с последующим возвратом на метку, откуда он был вызван и с переходом на следующую строку. Сначала мы идем на старт миссии. Затем идет простое условие (одиночное if), в котором мы проверяем, не умер ли наш игрок и не арестован ли он, коротенькой командой wasted_or_busted. Если условие выполнится, то игра перейдет на метку провала миссии @MISSION_FAILED, а если нет - то пойдет на метку @MISSION_CLEANUP. Учтите, что данные проверки должны быть в каждой миссии, а также учите то, что команда wasted_or_busted используется только в миссиях, конечно, ничего плохого не случится, если вы используете ее в обычном скрипте, но опытные люди могут довольно сильно наругать за ее использование. Для CLEO есть свои опкоды проверки на "арестованность" игрока, а также на то, что он мертв. Идем дальше.
:MISSION_STARTgosub @MISSION_PASSEDreturn

Команда return обязательна при использовании gosub, я вам говорил, что при использовании gosub игра возвращается на метку, где объявляется gosub, а затем идет дальше по коду, так вот, эта команда return и дает игре знать, где надо возвратиться на предыдущий gosub. В этой части кода нет ничего особенного и сложного, это, можно сказать, часть заголовка миссии. Идем далее:
:MISSIONincrement_mission_attemptsfade 0 1000wait 1000$ONMISSION = 1//Здесь размещается код миссииreturn

Собственно, здесь мы и будем писать команды для нашей миссии, но разберем, что же идет выше. Строка increment_mission_attempts увеличивает число попыток прохождения миссий в статистике на 1, строчка не обязательна, особенно, если миссия тестовая. Обычно все миссии в GTA: San Andreas (но: к миссиям также относятся две служебные @INITIAL и @INITIAL2, а также дополнительные миссии, так что будем говорить про сюжетные задания) начинаются с затемнения экрана, эта команда заставляет экран затемняться (первый параметр), второй параметр задает время затемнения. Небольшое примечание с использованием эффекта fade: игра, как только дошла до команды fade, сразу же идет дальше, просто экран только начнет затемняться, а игра-то уже ушла дальше, поэтому перед вами может внезапно появиться из ниоткуда машина или актер, а экран еще не затемнился! Поэтому я ставлю после fade 1 строку wait, и ее параметр устанавливаю такой же, как и второй параметр fade, чтобы игра начала затемнять экран, подождала секунду, и только потом шла дальше по коду. Параметр wait заставляет игру подождать определенный промежуток времени, этот промежуток измеряется в миллисекундах. Затем мы ставим переменную $ONMISSION равной одному. Бояться тут нечего, эта глобльная переменная может использоваться абсолютно в любом скрипте. Также не забудьте, что одинарное равно (=) - во многих языках программирования - оператор присваивания. Оператор равенства записывается как двойное равно (==), не путайте!
Далее мы размещаем, собственно, сам код миссии и выполняем return на последний gosub. Затем идет блок провала, прохождения миссии и блок остановки миссии. Вот они:
:MISSION_PASSED01E3: text_1number_styled 'M_PASSD' 0 5000 ms 1018C: play_sound 1return

Это блок успешного прохождения миссии. Здесь примечательны только две команды - первая выводит на экране текст с надписью "Миссия пройдена" длительностью 5 секунд (догадались, где регулируется время вывода текст на экран?). А вторая - проигрывает звук успешного завершения миссии, он используется во всех миссиях оригинальной игры. Затем идет возврат на вторую метку нашей миссии, оттуда - gosub на блок очистки миссии. Сейчас мы рассмотрим блок провала миссии:
:MISSION_FAILED00BA: text_styled 'M_FAIL' 5000 ms 1return

Здесь все также просто и понятно - выводим текст провала миссии. В оригинальной игре нет звука провала миссии, так что не советую портить атмосферу игры, но вам никто и не запрещает. И, наконец, рассмотрим блок очистки миссии:
:MISSION_CLEANUP$ONMISSION = 0mission_cleanupreturn

Здесь мы переменную $ONMISSION приравниваем к нулю (миссия-то у нас уже закончилась) и выполняем очистку миссии командой mission_cleanup. Она выгружает из памяти модели, приводит в порядок переменные (к примеру, если вы отключили пешеходов, то эта команда приведет переменную, которую вы меняли в норму), и выполняет множество полезных операций по освобождению памяти компьютера от остатков миссии. Затем идет команда return. Если бы мы здесь написали end_thread, то мисиию бы уже невозможно было бы начать заново, только насильно сделать jump (или gosub) на метку начала этой миссии. Вот так в финале должна выглядеть заготовка вашей CLEO-миссии:
{$CLEO .cs}
0000:
:HEAD
thread 'MYMIS'
gosub @MISSION_START
if
wasted_or_busted
jf @MISSION_CLEANUP
gosub @MISSION_FAILED

:MISSION_START
gosub @MISSION_PASSED
return

:MISSION
increment_mission_attempts
fade 0 1000
wait 1000
$ONMISSION = 1
//Здесь размещается код миссии
return

:MISSION_PASSED
01E3: text_1number_styled 'M_PASSD' 0 5000 ms 1
018C: play_sound 1
return

:MISSION_FAILED
00BA: text_styled 'M_FAIL' 5000 ms 1
return

:MISSION_CLEANUP
$ONMISSION = 0
mission_cleanup
return


Теперь эту заготовку можно заполнять, но я рекомендую назначить на нее макрос, так будет легче при последующем вашем создании миссий.
Теперь вы можете загружать модели, анимации, катсцены, делать все, что хотите в вашей миссии. Только не забывайте про проверку на загурженность! На это довольно часто спотыкаются новички, из-за чего в их миссии закрадываются нелепые и банальные ошибки, в виде вылетов и т.д. Вот как правильно реализовать загрузку модели:
:MODEL
wait 0
Model.Load(#AMBULAN)
038B: load_requested_models

:MODEL_1
wait 0
if
Model.Available(#AMBULAN)
jf @MODEL_1

:MODEL_2
//Здесь вы используете свою модель
wait 0
Model.Destroy(#AMBULAN)


Но, как я уже и говорил, благодаря mission_cleanup удалять модели из памяти необязательно. Правда, если вы загружаете больше одной модели, то в проверке на загруженность вместо простого if должно стоять if and, что будет означать "условие выполнится только в том случае, если все его подусловия будут иметь истинные значения", соответственно, if or можно трактовать как "условие выполнится только в том случае, если хотя бы одно из его подусловий будет иметь истинное значение", но такой вариант не подходит для проверки на загруженность моделей. Оговорка: если вы используете if and либо if or, то максимальное число подусловий - 8, в противном случае компилятор будет выдавать ошибку. Но как же реализовать последовательную проверку на загруженность десяти моделей подряд? Чтобы не создавать кучу меток, можно воспользоваться следующим рецептом:
:MODEL_1if andModel.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)Model.Available(#AMBULAN)jf @MODEL_1Model.Available(#AMBULAN)Model.Available(#AMBULAN)jf @MODEL_1

И данная кострукция будет считаться абсолютно правильной, так как "если первые восемь моделей загружены, то идем дальше, а если нет, то на начало метки", так что не бойтесь ее использовать. А теперь рассмотрим пример простой миссии, в которой нам надо убить появившегося балласа:
{$CLEO .cs}
0000:
:HEAD
thread 'MYMIS'
gosub @MISSION_START
if
wasted_or_busted
jf @MISSION_CLEANUP
gosub @MISSION_FAILED

:MISSION_START
gosub @MISSION_PASSED
return

:MISSION
increment_mission_attempts
fade 0 1000
wait 1000
$ONMISSION = 1
Model.Load(#BALLAS2)
Model.Load(#AK47)

:MODELS_CHECK
if and
Model.Available(#BALLAS2)
Model.Available(#AK47)
jf @MODELS_CHECK
Actor.Create(0@, 7, #BALLAS2, 0.0, 0.0, 0.0)
01B2: give_actor 0@ weapon 31 ammo 2000
05E2: AS_actor 0@ kill_actor $PLAYER_ACTOR
Marker.CreateAboveActor(1@, 0@)

:BALLAS_KILLED
wait 0
if
Actor.Dead(0@)
jf @BALLAS_KILLED
Marker.Destroy(1@)
jump @MISSION_PASSED
return

:MISSION_PASSED
01E3: text_1number_styled 'M_PASSD' 0 5000 ms 1
018C: play_sound 1
return

:MISSION_FAILED
00BA: text_styled 'M_FAIL' 5000 ms 1
return

:MISSION_CLEANUP
$ONMISSION = 0
mission_cleanup
return


В этом примере мы создаем актера, даем ему переменную 0@, даем ему оружие - автомат Калашникова (перед тем, как дать актеру оружие, загрузите модель!) номера оружия можно посмотреть в справке SB, собственно, ею я часто пользуюсь. Также мы заставляем актера 0@ атаковать актера $PLAYER_ACTOR (игрок) и создаем над балласом маркер. Далее идет проверка на то, что баллас убит. Если это так, то удаляем маркер (не забудьте это сделать, а то он будет висеть на актером до тех пор, пока игра не уберет труп из памяти) и переходим на блок успешного прохождения. Не забывайте что оттуда идет переход на вторую метку в миссии, а оттуда - на метку очистки миссии, а там все подчистит команда mission_cleanup. Вот такая простенькая миссия, бесполезная, да, но тем не менее, это ваша первая миссия на CLEO.


Советы и подсказки по CLEO и Sanny Builder
Дам несколько советов и подсказок, которые помогут вам при создании своей собственной миссии на CLEO:
1. Не рекомендую писать миссии на CLEO, так как папка CLEO забивается. Лучше вставлять их в код main.scm, либо в ваш Mission Pack (файл scr.scm)
2. Чаще пользуйтесь справкой SB. Там содержится очень много полезной информации.
3. Координаты вычисляются очень просто. Просто встаньте на место, откуда вы хотите считать координаты, затем перейдите в Sanny Builder нажмите сочетание клавиш Ctrl + Shift + C. Чтобы вставить угол разворота игрка, нажмите Ctrl + Shift + E.
4. Обычно миссии начинаются с катсцен. Можно реализовать свою, но получится у вас не лучше, чем в DYOM.
5. Как и в CLEO, использование глобальных переменных осуждается.
6. В CLEO-миссиях подняты лимиты, так что за них не бойтесь.
7. Вы можете создать небольшой ролик, например, как в Bully, когда камера смотрит на главного персонажа, и показывается текст прохождения миссии. Не забудьте записать в переменные координаты игрока, а потом сдвигать камеру отностиельно них.
8. Вы можете в CLEO-миссиях использовать свои звуки, как в DYOM! Для этого переместите .mp3 файлы в директорию CLE0, и в поиске опкодов найдите слово "audiostream". Вы найдете команды для загрузки звука, для проверки на его загруженность и команду для его выгрузки. Собственно, в самом опкоде указывается путь к файлу, так что данный финт можно использовать не только с CLEO.
9. Записывать координаты в переменные можно не только с главным игроком, но и вообще с любым объектом, который вы создали в течении миссии.
10. Если вы исопльзуете ролики, то не забудьте включить режим широкого экрана опкодом 02A3: toggle_widescreen 1, чтобы было гармоничнее.
11. Не бойтесь меток. Не забывайте про проверку на загруженность модели, анимации и всего, что вы загружаете, это поможет избежать вылета игры в неподходящий момент.
12. То же самое касается wait 0.


Пишем стартер для миссии
Сама по себе миссия - это, конечно, хорошо, но только с теоритической точки зрения. Неплохо было бы протестировать нашу миссию на практике. Для этого необходимо написать стартер. Стартер - это небольшой CLEO-скрипт, который запускает миссию на основании неких успешно выполнившихся условий. Пишется стратер просто, рассмотрим его код:
{$CLEO}
0000:
:STARTER
thread 'STARTER'
wait 0
Marker.CreateIconAndSphere(2@, 1, 0.0, 0.0, 0.0)

:MISSION_START
wait 0
if
00FF: actor $PLAYER_ACTOR sphere 0 in_sphere 0.0 0.0 0.0 radius 1.2 1.2 2.0 on_foot
jf @MISSION_START
Marker.Disable(2@)
00BA: show_text_styled GXT 'SWEET_1' time 1000 style 24
0A94: start_custom_mission "MISSION"
jump @STARTER


Итак, в этом примере мы создали иконку и сферу начала миссии, затем проверили, что игрок находится в заданны координатах на ногах, выключили маркер, показали текст, который символизирует название миссии и начали миссию с названием MISSION. В этом опкоде, как вы уже, наверное, догадались, мы пишем реальное скомпилированное название миссии, но без расширения. Стартер мы компилируем как обычный CLEO-скрипт и кидаем в папку CLEO. Теперь ваша миссия будет запускаться и вы сможете ее протестровать.


В чем различия между CLEO и main.scm, и почему миссии лучше писать на последнем
Конечно, CLEO - хорошая основа не только для скриптов, но и для миссий, но у данного способа есть небольшой недостаток, и не один.
1. Для того, чтобы запустить миссию, необходим стартер, а это лишний скрипт в папке.
2. И хотя глобальные переменные запрещены, возможно совпадение глобальных переменных, если вы их использовали в нескольких миссиях одновременно.
Конечно, тут можно привести и больше недостатков, но это уже решать вам. Я лишь расскажу, как реализовать ту же самую миссию в main.scm.
Для начала в мейне или в вашем mpack'е в заголовке файла в строке DEFINE MISSIONS увеличьте число на 1. Затем ниже напишите DEFINE MISSION * AT @*, где первая звездочка - номер вашей миссий, а вторая звездочка - метка, где начинается ваша миссия.
Затем вставьте вашу миссию после ваших thread'ов после всех миссий, и добавьте thread, который будет выполнять функцию стартера миссии. Также рекомендую ввести переменную, которая будет считать количество пройденых миссий, чтобы не произошел какой-нибудь баг и вы не смогли пройти какую-либо мисси еще раз. Если вы делаете это в main.scm либо в вашем Mission Pack'е, то вы без боязни можете пользоваться глобальными переменными, хотя и здесь надо проявить осторожность, так как main грузится в память один, но в CLEO скриптах могут быть глобальные переменные, поэтому я их не рекомендую использовать там, а main - пожалуйста. Особенно, если вы делаете сюжет, рекомендую пользоваться Mission Pack'ом.


Заключение
Как вы видите, пишутся CLEО-мисси очень просто, а возможностей у них в сотни раз больше, чем в DYOM. Надеюсь, вам помогла эта статья сделать свой первый шаг в мир CLEO-миссий. Если возникают какие-либо проблемы - пишите в комментарии, я и другие знающие пользователи постараемся вам помочь. Единственное, что я хотел бы вам сказать напоследок - никогда не бойтесь, эксперементируйте так, как вы хотите и самое главное - почаще пользуйтесь справкой Sanny Builder'а
GTA_Masters19, 2013, Libertycity.ru ©
Рейтинг: 4 (4 голосов)
(признано в России экстремистской организацией)