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

Стаття вийде доволі велика, тому нижче ви бачите зміст:
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-місії:
thread 'MYMIS'
gosub @MISSION_START
if
wasted_or_busted
jf @MISSION_CLEANUP
gosub @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 є свої опкоди перевірки на "арештованість" гравця, а також на те, що він мертвий. Йдемо далі.
gosub @MISSION_PASSED
return
Команда return обов'язкова при використанні gosub, я вам казав, що при використанні gosub гра повертається на мітку, де оголошується gosub, а потім йде далі по коду, так от, ця команда return і дає грі знати, де треба повернутися на попередній gosub. У цій частині коду немає нічого особливого і складного, це, можна сказати, частина заголовка місії. Ідемо далі:
increment_mission_attempts
fade 0 1000
wait 1000
$ONMISSION = 1
//Тут розміщується код місії
return
Власне, тут ми і будемо писати команди для нашої місії, але розберемо, що ж іде вище. Рядок increment_mission_attempts збільшує число спроб проходження місій у статистиці на 1, рядок не обов'язковий, особливо, якщо місія тестова. Зазвичай всі місії в GTA: San Andreas (але: до місій також відносяться дві службові @INITIAL і @INITIAL2, а також додаткові місії, так що будемо говорити про сюжетні завдання) починаються з затемнення екрану, ця команда змушує екран затемнюватися (перший параметр), другий параметр задає час затемнення. Невелика примітка з використанням ефекту fade: гра, як тільки дійшла до команди fade, відразу ж йде далі, просто екран тільки почне затемнюватися, а гра-то вже пішла далі, тому перед вами може раптово з'явитися з нізвідки машина або актор, а екран ще не затемнився! Тому я ставлю після fade 1 рядок wait, і її параметр встановлюю таким же, як і другий параметр fade, щоб гра почала затемнювати екран, почекала секунду, і тільки потім йшла далі по коду. Параметр wait змушує гру почекати певний проміжок часу, цей проміжок вимірюється в мілісекундах. Потім ми ставимо змінну $ONMISSION рівною одному. Боятися тут нічого, ця глобальна змінна може використовуватися абсолютно в будь-якому скрипті. Також не забудьте, що одинарне дорівнює (=) - у багатьох мовах програмування - оператор присвоєння. Оператор рівності записується як подвійне дорівнює (==), не плутайте!
Далі ми розміщуємо, власне, сам код місії і виконуємо return на останній gosub. Потім йде блок провалу, проходження місії і блок зупинки місії. Ось вони:
01E3: text_1number_styled 'M_PASSD' 0 5000 ms 1
018C: play_sound 1
return
Це блок успішного проходження місії. Тут примітні тільки дві команди - перша виводить на екрані текст з написом "Місія пройдена" тривалістю 5 секунд (вгадали, де регулюється час виводу тексту на екран?). А друга - програє звук успішного завершення місії, він використовується у всіх місіях оригінальної гри. Потім йде повернення на другу мітку нашої місії, звідти - gosub на блок очищення місії. Зараз ми розглянемо блок провалу місії:
00BA: text_styled 'M_FAIL' 5000 ms 1
return
Тут все також просто і зрозуміло - виводимо текст провалу місії. В оригінальній грі немає звуку провалу місії, так що не раджу псувати атмосферу гри, але вам ніхто і не забороняє. І, нарешті, розглянемо блок очищення місії:
$ONMISSION = 0
mission_cleanup
return
Тут ми змінну $ONMISSION прирівнюємо до нуля (місія ж у нас вже закінчилась) і виконуємо очищення місії командою mission_cleanup. Вона вивантажує з пам'яті моделі, приводить до ладу змінні (наприклад, якщо ви вимкнули пішоходів, то ця команда поверне змінну, яку ви змінювали, в норму), і виконує безліч корисних операцій по звільненню пам'яті комп'ютера від залишків місії. Потім йде команда return. Якби ми тут написали end_thread, то місію вже було б неможливо почати заново, тільки примусово зробити jump (або gosub) на мітку початку цієї місії. Ось так в фіналі повинна виглядати заготовка вашої CLEO-місії:
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
Тепер цю заготовку можна заповнювати, але я рекомендую призначити на неї макрос, так буде легше при подальшому створенні місій.
Тепер ви можете завантажувати моделі, анімації, катсцени, робити все, що хочете у вашій місії. Тільки не забувайте про перевірку на завантаженість! На це досить часто спотикаються новачки, через що в їхніх місіях закрадаються нелепі і банальні помилки, у вигляді вильотів і т.д. Ось як правильно реалізувати завантаження моделі:
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, в іншому випадку компілятор видасть помилку. Але як же реалізувати послідовну перевірку на завантаженість десяти моделей підряд? Щоб не створювати купу міток, можна скористатися наступним рецептом:
if and
Model.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_1
Model.Available(#AMBULAN)
Model.Available(#AMBULAN)
jf @MODEL_1
І ця конструкція буде вважатися абсолютно правильною, оскільки "якщо перші вісім моделей завантажені, то йдемо далі, а якщо ні, то на початок мітки", так що не бійтеся її використовувати. А тепер розглянемо приклад простої місії, в якій нам потрібно вбити з'явившогося балласа:
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-скрипт, який запускає місію на основі певних успішно виконаних умов. Пишеться стартер просто, розглянемо його код:
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'ом.
Висновок
Як ви бачите, писати CLEO-місії дуже просто, а можливостей у них в сотні разів більше, ніж у DYOM. Сподіваюся, вам допомогла ця стаття зробити свій перший крок у світ CLEO-місій. Якщо виникають якісь проблеми - пишіть у коментарі, я та інші знаючі користувачі постараємося вам допомогти. Єдине, що я хотів би вам сказати наприкінці - ніколи не бійтеся, експериментуйте так, як ви хочете, і найголовніше - частіше користуйтеся довідкою Sanny Builder'а
GTA_Masters19, 2013, Libertycity.ru ©