Автор: HexBit

Создание миссий для GTA Vice City.

Я был удивлён, но в рунете очень мало материала по созданию миссий для GTA,
а точнее его совсем нет. Это объясняется тем что для создания миссий нужны хотя бы начальные знания программирования, так что эта статья в своём роде экслюзив (перепечатка без разрешения автора запрещена). В этой статье вы прочтете как создать простенькую миссию для GTA Vice City.В игре используется свой скриптовый язык на котором собственно и написаны все миссии. Все миссии скомпилированы в один файл main.scm (script mission), он находится в папке Data каталога игры. На сегодняшний день лучшая программа для создания миссий для Vice City - это Vice Mission Builder от кодера под ником Barton Waterduck (смотри раздел файлы).

Для начала немного теории. Процесс написании миссий похож на создание конфигов для MS-DOS, для тех кто помнит еще что это такое. Есть метки, есть некое условие, если условие выполняется то переходим на одну метку если не выполняется то на другую метку. Например, если игрок в машине то переходим на одну метку, если не в машине то на другую. Все метки в Mission Builder начинаются со знака двоеточия – : Как и в любой
среде разработки Mission Builder поддерживает комментарии. Все комментарии начинаются со знака точка с запятой - ; комментарии очень удобны, в любом месте кода можно вставить свои пояснения., что впоследствии помогает ориентироваться в коде. Естественно что комментарии игнорируется компилятором.

Структура скрипта миссий.
Условно весь код поделен на 4 блока
1. Блок памяти это всего одна строчка - DEFINE MEMORY а дальше идет количество зарезервированной памяти в байтах.
2. Блок определения 3D-объектов, начинается со строчки DEFINE OBJECTS 204 – 204 это общее кол-во объектов. Далее идет определение самих объектов - DEFINE OBJECT и имя объекта.
Зачем определять объекты в коде ? что бы управлять ими, например ворота на остров StarFish изначально закрыты, а потом в процессе прохождения игры они открываются.
3. Блок определения миссий, DEFINE MISSIONS – общее кол-во миссий и дальше список миссий и указатели на метку с началом миссии. DEFINE MISSION 1 AT ЈЈMission1 – это строка определяет миссию с номером 1 на метке :Mission1.
4. Блок кода собственно в этом блоке уже пишется сам код миссий.

Теперь о структуре команд.
03CB: set camera $X $Y $Z - эта команда установит камеру на заданных координатах.
03СB: - опкод.
set camera – команда, игнорируются компилятором.
$X $Y $Z – параметры, в данной случае это координаты камеры.
Как написано выше сама команда игнорируется компилятором, то есть для компилятора 03CB: set camera $X $Y $Z и 03CB: $X $Y $Z одно и тоже. Зачем же тогда нужны сами команды, да очень просто для удобства, ведь гораздо проще запомнить команду чем опкод. Пишем set camera, нажимаем F1 и опкод и параметры дописываются сами.

Переменные.
Во всех языках программирования высокового уровня используются переменные
и Mission Builder не исключение. Переменные в нём обозначаются знаком $. Зачем нужны переменные опять же для удобства написания кода. Например выше ты видел такую строчку 03CB: set camera $X $Y $Z - здесь координаты камеры определены в переменных $X $Y $Z, можно было написать так - 03CB: set camera 3.0 1.0 4.0 ,но если ты и далее будешь использовать эти координаты, то проще заключить их в переменные:
0005: $X= 3.0 , эта команда устанавливает что переменная $X будет равна 3.0
0005: $Y= 1.0 , эта команда устанавливает что переменная $Y будет равна 1.0 и т.д.
Кроме того все математические операции, сложение, деление могут вестись только с переменными. В переменные заключатся не только числа но вообще любые объекты,
009A: $ACTOR1 = create_actor 4 #COP at 1.0 1.0 1.0 - этой командой мы создали копа с переменной $ACTOR1.

Математические операции.
С переменными разобрались теперь об математических операциях.

В Mission Builder'е повсеместно используются координаты. Создать игрока вводите координаты, отправить тачку что бы ехала туда-то опять координаты. Утилита Player Pos, сохраняет по нажатию кнопки F4 текущее положение игрока. скачать


В MB используются два типа чисел:
Integer - целые числа , 100 целое число.
Float – число с плавающей запятой, 3.0.
Опкоды для работы с разными типами чисел разные.
0004: $X= 10, определяем что переменная $X равна 10.
0008: $X += 5, слагаем к переменной 5 итого 15.
0010: $X *= 2, умножаем на два итого 30.
000C: $X -= 10, вычитаем 10 итого 20.
0014: $X /= 4, делим на 4 итого 5.
Теперь опкоды для чисел с плавающей запятой.
0009: - Сложение.
000D: - Вычитание.
0011: - Умножение.
0015: - Деление.

Типы используемые в MB.
Здесь рассмотрим типы обозначений которые используются в MB.
Один тип мы уже знаем, знаком $ обозначаются переменные.
Используются еще один тип переменных, обозначается он знаком @. Используются он для 16-битных значений в основном во внутренних таймерах. Имя переменной может быть только числовое то есть никаких CAR@ и PED@, только числа.
0006: 2@ = 100, устанавливаем переменную 2@ со значение 100.
Тип JJ - так обозначается переход на метку в любом месте кода.
0002: jump ЈЈLabel1, переход на метку :Label1
Тип J – обозначает переход на метку только внутри определенного блока.
Знаком # обозначаются машины, педы, оружие.
Со знаком перехода на метки – J, очень много геммороя, это связано с кодировкой Windows, мало того в Windows 9x он вообще отображается по другому, лучше этот знак копировать из предыдущих строк или не получится скомпилировать код.

Теперь когда ты имеешь общее понятие о типах, переменных мы напишем свою простенькую миссию. Миссия будет заключается в том что нужно будет обогнать оборзевщего копа в гонке.
Также гонку можно будет выиграть, разбив машину оппонента. Гонка будет происходить на острове StarFish. Запускаем VC Mission Builder, если ты запускаешь его в первый раз то надо указать папку с Vice City.Чтобы не писать весь код с нуля, мы возьмем за основу файл в котором вырезаны все сюжетные миссии, нажимаем Ctrl+O и открываем файл Stripped_complete.txt, который находится в папке MissionBuilder\Stripped scripts. Для начала нам надо добавить нашу миссию в блок миссий. В меню Edit выбери Find (Поиск) и в открывшемся окне напиши DEFINE MISSIONS, теперь мы нашли место где определены миссии. Изменяем общее кол-во миссий на DEFINE MISSIONS 2 и после DEFINE MISSION 0 добавляем строку с нашей миссией, DEFINE MISSION 1 AT ЈЈMyMission. Так мы указали что наша миссия будет находится на метке : MyMission.
Переходим в самый конец файла, и перед строкой 004E: end_thread, добавляем всего одну строчку - 0417: start mission 1, так мы стартуем нашу миссию. А теперь самое интересное, сам код нашей миссии.
Пишем :MyMission – стартовая метка миссии. Создаем поток миссии с именем MISSION, 03A4: name_thread "MISSION", создавать поток обязательно поскольку при его создании выделяется необходимая память. Делаем небольшую задержку в 1000мс - 0001: wait 1000 ms , потом переносим игрока к началу гонки 0055: put_player $PLAYER_CHAR at -273.507 -489.287 10.063. $PLAYER_CHAR – как раз обозначает игрока а дальше идут координаты по осям X,Y,Z.Теперь надо создать машину для нас и оппонента и самого оппонента, оборзевщего копа :).Делаем запрос на модели
:RequestModels ; метка
0247: request model #DELUXO ; запрашиваем модель машины Deluxo
0247: request model #COP ; запрашиваем модель копа
далее надо проверить существуют ли такие модели вообще.
:CheckCars
00D6: if 21 ; если
8248: NOT model #DELUXO available ; модель Deluxo несуществует
8248: NOT model #COP available ; и модель Сop несуществует
004D: jump_if_false ЈCreateCars ; если существуют
0001: wait 0 ms ; задержка
0002: jump ЈCheckCars ; переходим на метку :CheckCars
Разберем этот блок поподробней если модели тачки и копа существуют то переходим на следующую метку, если не существуют то переходим опять на начало блока и все повторяется снова. Если ты заметил в if cтоит 21 это потому что мы проверяем сразу 2 условия. Далее мы создаем машины и копа.
:CreateCars
Очищаем территорию где будут создаваться тачки.

0395: clear area 1 at -274.309 -486.170 range 10.0 10.0
собственно создаем сами тачки
00A5: $PLAYER_CAR = create_car #DELUXO at -274.309 -486.170 10.071
00A5: $OPP_CAR = create_car #DELUXO at -272.837 -476.868 10.095
Мы так мы создали две тачки Deluxo, в переменной $PLAYER_CAR – машина игрока, а в $OPP_CAR - машина оппонента. #DELUXO – определяет что создавать надо именно тачку Deluxo а не еще что-то. В принципе можно заменить на #PCJ600 тогда гонки будут на мотоциклах, только не забудь в запросах и в проверке тоже #PCJ600 поставить.
Далее мы поворачиваем тачки на девяносто градусов.
0175: set_car $PLAYER_CAR z_angle_to 90.
0175: set_car $OPP_CAR z_angle_to 90.0
создаем копа за рулем тачки $OPP_CAR
0129: $OPP_COP = create_actor 4 #COP in_car $OPP_CAR driverseat
далее устанавливаем свойства тачки.
Устанавливаем максимальную скорость тачки.
00AD: set car $OPP_CAR max speed to 50.0
Ставим чтоб машина оппонента игнорировала светофоры
00AE: unknown_set_car $OPP_CAR to_ignore_traffic_lights 2
делаем водилу психом :)
00A8: set car $OPP_CAR to psycho driver
сбрасываем флаги копа
01ED: reset actor $OPP_COP flags
Есть один баг, сразу после создания в тачке водилы он все время пытается куда –то уехать, причем даже если мы ему это не разрешали. Поэтому пишем такую строчку
02C2: car $OPP_CAR drive to point -272.837 -476.868 10.095
так мы указываем куда ехать тачке $OPP_CAR. Но координаты на которые надо ехать, это и есть координаты где мы создали тачку. Таким образом тачка никуда не едет, но от бага мы избавились.
Далее когда все машины созданы, пора начинать гонку.
Но не сразу а как только игрок сядет в тачку.
:CheckInCar
0001: wait 0 ms ; задержка
00D6: if 0 ; Если
80DC: NOT player $PLAYER_CHAR driving $PLAYER_CAR ; Игрок не в машине
004D: jump_if_false ЈBackCount ; Если в машине переходим на метку
0001: wait 0 ms ; задержка
0002: jump ЈCheckInCar ; Переходим на метку :CheckInCar
В это коде мы проверяем, если игрок ( $PLAYER_CHAR ) в машине ( $PLAYER_CAR ) то мы переходим на следующую метку, если нет то переходим на начало блока. Таким образом пока игрок не сядет в машину гонка не начнется. Тебе интересно зачем нужно вставлять задержки 0001: wait 0 ms , а затем что этот кусок кода - цикл. И если этот цикл будет повторяться без задержки то игра просто зависнет.
Далее начинаем считать 3, 2 ,1 после чего стартуем гонку.
Сначала замораживаем игрока, то есть им на время нельзя будет управлять.
01B4: set player $PLAYER_CHAR frozen state 0
Говорим копам твердое нет :)
01F7: set_player $PLAYER_CHAR ignored_by_cops_state_to 1
и начинаем считать
00BA: text_styled "RACE2" 1500 ms 4 ; выводим на экран сообщение 3
018C: play_sound 7 at 0.0 0.0 0.0 ; проигрываем звук
0001: wait 1500 ms ; ожидаем 1500 мс
0395: clear area 1 at -274.309 -486.170 range 11.0 1.0 ; очищаем площадку
так повторяем это код 3 раза изменяя лишь выводимый на экран текст.
После чего выводим сообщение о начале гонки и размораживаем игрока.
00BA: text_styled "RACE1" 1200 ms 4 ; выводим сообщение о начале гонки
01B4: set player $PLAYER_CHAR frozen state 1 ; "размораживаем игрока"
ну и наконец начинаем гонку.
0001: wait 0 ms ; Ожидаем
0186: $OPP_MRK = create marker above car $OPP_CAR ; создаем маркер на радаре вокруг машины оппонента
02C2: car $OPP_CAR drive to point -696.092 -489.462 9.890 ; указываем чтобы машина оппонента ехала на координаты завершения миссии
018A: $F_CHECK = create checkpoint at -696.092 -489.462 9.890 ; создаем чекпоинт на радаре он обозначает место финала гонки
далее проверяем условия выигрыша гонки, если оппонент мертв или игрок первый приехал к финишу.
:CheckWin ;
00D6: if 0 ; если
8118: NOT actor $OPP_COP dead ; оппонент коп жив
004D: jump_if_false ЈEndMission ; если мертв то переходим на метку завершения миссии
00D6: if 0 ; если
; игрок в машине не на координатах проехав которые мы выиграете миссию с радиусом X Y Z
80F7: NOT player $PLAYER_CHAR 0 near point in car -696.092 -489.462 9.890 radius 6.0 6.0 6.0
004D: jump_if_false ЈEndMission ; если на координатах то переходм на завершения гонки
0001: wait 0 ms ; задержка
проверяем условия проигрыша, если оппонент первый приехал к финишу.
:CheckFailed
00D6: if 0 ; если
; машина оппонента не на координатах завершения гонки
81AF: NOT car $OPP_CAR 0 ()near point -696.092 -489.462 9.890 radius 6.0 6.0 4.0
004D: jump_if_false ЈMissFailed ; если на них то мы проиграли миссию и переходим на метку проигрыша
0001: wait 0 ms ; задержка
0002: jump ЈCheckWin ; переходим на метку :CheckWin
далее метки, игрок выиграл или проиграл
; Проиграли
:MissFailed
00BA: text_styled "M_FAIL" 5000 ms 1 ; Выводим сообщении о пройгрыше миссии
0002: jump ЈEnd ; Переходим на метку :End

; Выиграли
:EndMission
0394: play_music 1 ;проигрываем музыку
01E3: text_1number_styled "M_PASS" 3000 5000 ms 1 ;Выводим сообщении о завершении миссии

и завершаем .
:End
034F: destroy actor with fade $OPP_COP ; уничтожаем актера $OPP_COP, нашего оппонента
00A6: destroy car $OPP_CAR ; уничтожаем машину оппонента
0164: disable_marker $F_CHECK ; отключаем маркер на радаре
01F7: set player $PLAYER_CHAR ignored by cops state to 0 (true) ;
004E: end_thread ;Завершаем поток
Теперь осталось скомпилировать наш код, в scm - файл, нажимаем F9 и Mission Builder создаст main.scm скопирует его в папку Vice City и запустит игру.
Вот и конец статьи у меня уже руки устали, стучать по клаве:).
Не так давно вышел GTA Сан-Андреас и хочу сказать что кодинг миссий для него,
ничем не отличается от написания миссий для Vice city. Ну и конечно на этом тема создания миссий не закрыта и в скором времени я опубликую статью по написании более сложных миссий. А еще есть мыслишка сделать онлайновую базу данных опкодов с их описания и параметрами но это все в будущем …Исходный код миссии можешь скачать отсюда. Если у тебя возникли какие-то вопросы по Mission Builder’у пиши мне на hexbit@mail.ru , но не стоит заваливать меня спамом с просьбами типа “а как написать мод по мотивам фильма Бригада” такие письма я сразу кидаю в трэш. Я могу помочь только по конкретному вопросу.

Hosted by uCoz