Интересное в скриптинге

Всем привет! С Вами wmysterio, и это очередной и долгожданный урок по скриптингу GTA San Andreas. Как уже говорилось на форуме, я планирую сделать глобальную модификацию мейна, и я прошел игру заново, чтобы установить сюжет. Во время прохождения я захотел узнать как делаются некоторые вещи, и заглянул в оригинальный мейн.

Что же я нашел интересненького? Очень много любопытного. Например, узнал как сделать маркер в ввиде капельки:

У меня спрашивали очень много людей, и я наконец нашел ответ. Как оказалось, за это отвечает опкод:

075B: zoom_radar 0

Меня это слегка удивило, так как я, по-локиге, искал опкоды, связанные с маркерами, но как оказалось за маркеры ввиде капель отвечает радар. Чтобы достичь такого эффекта, нужно установить масштаб радара в число, отличное от нуля, например:

075B: zoom_radar 1

Удивительно, но это работает!

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

Что-бы заставить актёра патрулировать возле дома CJ, достаточно этого кода:

{$CLEO}

0615: define_AS_pack_begin 2@
05D3: AS_actor -1 goto_point 2500.376 -1685.9846 13.472 mode 4 time -1 ms
05D3: AS_actor -1 goto_point 2485.1157 -1686.6415 13.5085 mode 4 time -1 ms 
0643: set_AS_pack 2@ loop 1
0616: define_AS_pack_end 2@

model.Load(#WMYDRUG)
038B: load_requested_models

0@ = Actor.Create(Mission2, #WMYDRUG, 2485.1157, -1686.6415, 12.5085)9.4457
0618: assign_actor 0@ to_AS_pack 2@
061B: remove_references_to_AS_pack 2@

Model.Destroy(#WMYDRUG)

0A93: end_custom_thread

Обратите внимание на опкод 0643. Он заставляет проигрывать содержимое экшн-пака с начала по окончанию действия. Здесь важно то, что этот опкод должен находится между началом и кончом AS-пака. В противном случае повторения не произойдёт. Такой подход отлично подойдёт для мейна, где можно с помощью глобальных переменных устанавливать разные маршруты движения, имея всего 4 строки кода. Это очень важно для оптимизации кода, и мы заставляем игру больше рабоать на нас, а не на оборот.

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

{$CLEO}

model.Load(#WMYDRUG)
Model.Load(#TEC9)
038B: load_requested_models

07E5: copy_decision_maker 65542 to 3@

0@ = Actor.Create(Mission2, #WMYDRUG, 2485.1157, -1686.6415, 12.5085)
Actor.GiveWeaponAndAmmo(0@, 32, 5000)
060B: set_actor 0@ decision_maker_to 3@
0631: put_actor 0@ in_group $PLAYER_GROUP

Model.Destroy(#WMYDRUG)
Model.Destroy(#TEC9)

0A93: end_custom_thread

Эта модель поведения мне очень понравилась, так как актёр никак не реагируте на внешние факторы со стороны игрока. Одним словом Ван не нужно больше зрботиться о том, что ваш спутник вдруг начнёт у Вас стрелять или убегать куда-то от Вас.

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

07DD: set_actor 0@ attack_rate 50

Число 50 как раз таки и отвечает за частоту стрельбы. Насколько я понял, это значение указывается в процентах, но если поставить -1 или 100, то достигается максимум. Следущий скрипт сделает актёра машиной для убиства:

{$CLEO}

model.Load(#WMYDRUG)
Model.Load(#TEC9)
038B: load_requested_models

0@ = Actor.Create(Mission2, #WMYDRUG, 2485.1157, -1686.6415, 12.5085)
Actor.GiveWeaponAndAmmo(0@, 32, 5000)

02E2: set_actor 0@ weapon_accuracy_to -1
07DD: set_actor 0@ attack_rate -1
05E2: AS_actor 0@ kill_actor $PLAYER_ACTOR

Model.Destroy(#WMYDRUG)
Model.Destroy(#TEC9)

0A93: end_custom_thread

Если Вы начали играть в близи от дома CJ, то думаю вы ощутите как теперь стреляет этот дружок.

Далее рассмотрим ещё некоторые поведения актёров. Опкод:

0588: disable_actor 0@ validate_position 0

Заставляет актёра атаковать врага на месте. Полезно, когда стрелка нужно разместить где-то на крыше и не нужно чтобы он пытался с неё спрыгнуть. Актёр начинает атаку, когда игрок находится в его поле видимости.

Опкод:

0816: set_actor 0@ dont_chase_victim 1

Заставляет актёра не бегать за жетрвой а стрелять только когда она находится в радиусе действия его оружия. В отличии от предыдущего опкода, актёр может передвигаться по его усмотрению. Используя эти скрипты, можем сделать скрипт охранника, который будет атаковать CJ при обнаружении. Для этого в место опкода 07E5 используем более так сказать "зловещий" опкод 060A, а также установим отношение актёра к игроку на ненависть. Вот что у меня получилось:

{$CLEO}

0615: define_AS_pack_begin 2@
05D3: AS_actor -1 goto_point 2500.376 -1685.9846 13.472 mode 4 time -1 ms
05C9: AS_actor -1 on_guard 3000 ms 
05D3: AS_actor -1 goto_point 2485.1157 -1686.6415 13.5085 mode 4 time -1 ms 
05C9: AS_actor -1 on_guard 3000 ms 
0643: set_AS_pack 2@ loop 1
0616: define_AS_pack_end 2@

060A: create_decision_maker_type 4 store_to 3@

model.Load(#WMYDRUG)
Model.Load(#TEC9)
038B: load_requested_models

0@ = Actor.Create(Mission2, #WMYDRUG, 2485.1157, -1686.6415, 12.5085)
Actor.Health(0@) = 1000
Actor.GiveWeaponAndAmmo(0@, 32, 5000)
077A: set_actor 0@ acquaintance 4 to_actors_pedtype 0 // see ped.dat
0350: set_actor 0@ maintain_position_when_attacked 1 
02E2: set_actor 0@ weapon_accuracy_to -1
07DD: set_actor 0@ attack_rate -1
0618: assign_actor 0@ to_AS_pack 2@
061B: remove_references_to_AS_pack 2@
060B: set_actor 0@ decision_maker_to 3@

Model.Destroy(#WMYDRUG)
Model.Destroy(#TEC9)

0A93: end_custom_thread

Результат оправдал себя: противник атакует только когда увидит CJ, при этом соблюдаюстя все нормы, например охранник не видит CJ, если тот спрятался за за забором в двух милиметрах от него.

Следующая "вкусняшка" - опкод 0677. Он заставляет актёра идти к другому актёру, стать напротив него и общаться. Вот этот код:

{$CLEO}

wait 2000
model.Load(#WMYDRUG)
038B: load_requested_models

0@ = Actor.Create(Mission2, #WMYDRUG, 2480.7903, -1675.9349, 12.3378)
Model.Destroy(#WMYDRUG)
0677: AS_actor 0@ chat_with_actor $PLAYER_ACTOR lead_speaker_flag 1 unknown_flag 1 
0677: AS_actor $PLAYER_ACTOR chat_with_actor 0@ lead_speaker_flag 0 unknown_flag 1

0A93: end_custom_thread

Стоит расмотреть детали параметров. Значение "lead_speaker_flag" указывает кто первым будет жестикулировать руками. Если поставить эти флаги одновременно в 1, то оба персонажа начнут махать руками. Если оба параметра установлены в 0, то ничего не произойдёт. Другой параметр пока неизвестный мне. Мои тесты показали, что если числа будут разными, то актёры подойдут, но никаких действий больше не подадут. Если оба параметра установлены в 0 или 1, то всё работает.

Я буду продолжать исследовать main, и как только найду что-то новенькое и интересное - сразу сообщу Вам. С Вами был wmysterio, пока!

Last updated