You are not logged in.
Pages: 1
Открыл эту тему для того, чтобы собрать разные секреты, трюки, которые существуют при написании скриптов имогут существенно облегчить решение той или иной задачи. Самый большой трюк - это конечно сам Sanny Builder, но и в использовании обычных опкодов так же есть неочевидные возможности.
Внимание! Обсуждение ведется в >>отдельной теме<<
Кто хочет поделиться своими фирменными секретами, милости прошу в эту тему.
Итак, трюк первый: условный Gosub
Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub. Я использовал этот трюк ранее, например в первой версии CLEO, и он неплохо себя зарекомендовал.
if gosub @CheckDriverIsMale jf @gosubFalse
В чем подвох, спросите вы? Дело в том, что такой gosub потребует особого оформления результата при выходе. Чтобы gosub вернул True (и проверка сработала) или False (и проверка не сработала) нужно добавить перед return соответственно опкод 0485: return_true или 059A: return_false.
Пример:
:CheckDriverIsMale 046C: 3@ = car 9@ driver if 3@ <> -1 jf @ReturnFalse if 03A3: actor 3@ male jf @ReturnFalse 0485: return_true return :ReturnFalse 059A: return_false return
Таким образом gosub @CheckDriverIsMale вернет True, если за рулем машины 9@ сидит мужчина, иначе он вернет False. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких опкодов (например, вы в цикле в разных местах проверяете водителя-мужчину).
Я не проверял как gosub будет вести себя в группе условий (if and, if or), возможно там такой трюк срабатывать не будет.
Last edited by Seemann (11-06-2007 02:59)
Offline
Трюк второй: глобальные массивы
Трюк касается возможности глобальных массивов выходить далеко за пределы main.scm и читать/писать значения в любой адрес памяти. На этом трюке основаны метод инжектов на ассемблере, а так же некоторые моды.
Трюк описан здесь.
Offline
Несколько простых советов по оптимизации кода и уменьшения его размера
Числа 0 и 0.0 одно и то же, однако первый вариант занимает на 3 байта меньше при компиляции. Т.е. везде, где стоит 0.0, можно писать 0.
Опкод 00D6: 0 только занимает место. Реальной пользы от него нет, поэтому можете не писать его. Это не распространяется на IF с несколькими условиями (IF AND, IF OR).
Инициализировать переменные в ноль необязательно. Все глобальные переменные при старте игры равны нулю, локальные при каждом создании потока также очищаются.
Если из исходника удалить блок DEFINE OBJECTS (полностью все объекты), Sanny Builder сам составит список объектов на основе компилируемого кода. Как правило, 5-10 объектов никогда не используется, что даст выигрыш в 20 байтов на каждой бесполезной модели.
И, наконец: опкод 004F и 00D7 это одно и то же, за тем исключением, что 004F может дополнительно передавать параметры в новый поток. Никакой wasted_busted проверки в 00D7 нет - это миф. Они оба создают одинаковые по своей сути потоки. Так что когда создаете поток и не передаете в него параметры, используйте 00D7, он на 1 байт короче при компиляции.
Offline
Скриптовый движок SA имеет один неприятный баг, связанный с метками. Вы не можете осуществить переход из тела внешнего скрипта к его началу, т.е. сделать jump на самую первую метку.
DEFINE SCRIPT TEST_SCR AT @TEST_SCR ... //-------------External script (TEST_SCR)--------------- :TEST_SCR wait 0 jump @TEST_SCR // этот опкод вызовет ошибку
Поэтому, если внешний скрипт представляет собой сплошной цикл, то нужно вставить какой-то опкод в начале, а затем метку для перехода:
DEFINE SCRIPT TEST_SCR AT @TEST_SCR ... //-------------External script (TEST_SCR)--------------- :TEST_SCR 0000: // можно вставить 03A4 :TEST_SCR_1 wait 0 jump @TEST_SCR_1 // этот опкод будет работать.
Тот же баг проявляет себя и в миссиях, но там переход к самому началу почти никогда не требуется.
Offline
Модели актеров #NULL и #MALE01 не требуют предварительной загрузки. Но есть еще одна особенность у этих моделей. Если создавать актера с типом актера 6 (полицейский)
009A: 4@ = create_actor_pedtype 6 model #MALE01 at 2482.72 -1658.665 13.3372
то создается полицейский, тот что встречается в сельской местности (#CSHER).
009A: 4@ = create_actor_pedtype 6 model #NULL at 2482.72 -1658.665 13.3372
создается полицейский(#LAPD1)
Если установить pedtype 6 для модели актера, например, #BMOST
009A: 4@ = create_actor_pedtype 6 model #BMOST at 2482.72 -1658.665 13.3372
то стоит только ударить этого актера появятся звезда розыска, а также у актера есть оружие (#nitestick и #colt45).
P.S. При pedtype 0 и pedtype 1 – игра вылетает (для актеров). Остальные работают нормально, в том числе и pedtype 3.
Last edited by yelmi (30-06-2007 17:43)
Offline
FXT-файлы от GXTHook можно использовать для быстрого перевода текстов в игре.
Например, чтобы изменить название машины ELEGANT на другое, не надо редактировать american.gxt. Достаточно создать новый файл, например perevod.fxt, добавить в него строку
ELEGANT ELEGANT Cupe
и сохранить этот файл в папку игра\CLEO\CLEO_TEXT.
Теперь в игре машина будет называться ELEGANT Cupe.
Нужное название строки GXT (ELEGANT, в примере) можно найти в файле GTASA.text, который находится в папке SB3\help\GXT Strings.
Можно сделать и русский перевод, если правильно сконвертировать строку.
PS. Разумеется, CLEO3 и GXTHook должны быть установлены.
Offline
[large]О передаче строк в функции и потоки.[/large]
Для начала несколько слов о том, как в скриптах хранятся строки. Для сохранения строки используется два вида переменных: одни для коротких строк ('...'), другой - для длинных ("..."). Переменные первого типа обозначаются символом s рядом с названием (1@s, s$str). Переменные второго - символом v (2@v, v$str1).
Теперь о том, как хранятся сами символы строк. В других языках программирования, чаще всего, переменная типа string не хранит в себе весь набор букв. Такая переменная хранит в себе адрес памяти, где эти буквы располагаются. Стринговая переменная представляет собой по сути указатель на первый символ строки, и занимает, как и любой указатель, 4 байта. Не важно какой длины строка - 1 буква или 500, стринговая переменная всегда в длину 4 байта (в большинстве случаев).
В SA строки хранятся в самих переменных. Как известно, существует 2 вида переменных - локальные и глобальные. Для каждого из них в памяти зарезервировано определенная область памяти ограниченного размера. Например, для каждого потока выделятся 32 переменные по 4 байта и, соответственно, буфер памяти для хранения значений этих переменных в 128 байтов. Аналогичная ситуация с буфером глобальных переменных (его размер может меняться в разных майнах при компиляции).
Строки хранятся внутри данного буфера переменных. Это означает, что в стринговую переменную 0@s записывается не указатель на первую букву слова MAIN, а сами эти буквы (в ASCII-кодах).
Вернемся теперь к двум видам строк - коротким и длинным (строкам с переменной длиной).
Короткие строки всегда занимают в памяти 8 байтов. В такую переменную можно записать до 7 символов включительно, остаток заполняется нулями. Не важно, 1 буква в строке или 7, размер такой строки всегда 8 байтов. Таким образом, если вы записываете в переменную 1@s строку 'STRING' она займет 8 байтов. Учитывая, что буквы строк хранятся в самих переменных (см. выше), а одна переменная может хранить только 4 байта (1 буква - 1 байт), то часть строки будет записана в следующую по порядку переменную: 2@.
Получается: 1@ хранит 'STRI', 2@ хранит 'NG'.
С длинными строками ситуация аналогичная, с единственной разницей, что они занимают 4 переменные (16 байтов).
1@v = "LONG_STRING" => 1@ = "LONG", 2@ = "_STR", 3@ "ING", 4@ - хранит остаток (нулевые байты).
Обратите внимание, 1@v = "MAIN" хоть и хранит только 4 символа, но запись такой переменной затрет значения не только 1@ но и 2@, 3@ и 4@
16 байтов является пределом для стринговых переменных. Нельзя записать в переменную 1@v строку длиной 20 символов, например.
Если подытожить, строки занимают в памяти либо 8 либо 16 байтов (зависит от типа строки). Для их хранения требуется 2 или 4 переменные, идущие подряд (0@, 1@; $1, $2 и т п.).
Теперь перейдем к главному. В новые потоки, создаваемые опкодом 004F, можно передавать числовые параметры, значения которых записываются по порядку в локальные переменные нового потока.
create_thread @new 100 $ONMISSION 1@
в потоке @new переменная 0@ будет равна 100, 1@ равно значению $ONMISSION, 2@ равно значению 1@ потока, из которого был вызван опкод 004F.
Те же правила действуют при вызове SCM-функций:
0AB1: call_scm_func @Recalc 1 -1.0
в функции Recalc переменная 0@ будет равна -1.0.
Но стандартными способами передать не число, а строку в поток/функцию нельзя.
0AB1: call_scm_func @endT 1 'BENZIN' // так нельзя
Однако есть другой способ. Вспомним, что строка хранится в переменных. Строка - это набор букв, которые по сути являются числами. Мы можем сохранить строку в стринговую переменную и передать ее по частям:
0@s = 'BENZIN' 0AB1: call_scm_func @endT 2 0@ 1@ // передаем обе части короткой строки
а в функции используем строку также через стринговую переменную:
:endT 0459: end_thread_named 0@s // 'BENZIN' 0AB2: ret 0
Для длинных строк правило аналогичное, но требуется передать значения 4-х переменных:
0@v = "SNEAKERBINCBLK" // 0@..3@ 4@v = "SNEAKER" // 4@..7@ 8@ = 3 0AB1: call_scm_func @clothes 9 0@ 1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ // передаем две строки и числовой параметр
:clothes 087B: set_player $PLAYER_CHAR clothes_texture 0@v model 4@v body_part 8@ 0AB2: ret 0
[---]
Возможно, для полного понимания данного текста вам потребуется немного воображения. Важно понять принципы хранения строк в памяти и все станет ясно.
Last edited by Seemann (24-01-2009 09:12)
Offline
У опкода
00A1: put_actor 0@ at 0.0 0.0 0.0
(
Actor.PutAt(0@, 0.0, 0.0, 0.0)
) есть один неприятный эффект: игра при телепорте подстраховывается и увеличивает координату Z, что очень критично при скриптовых роликах на улице и особенно в интерьерах.
Опкод
09BC: put_actor 0@ at 0.0 0.0 0.0
лишён этого недостатка, и вам не придётся пересчитывать координату Z.
[small][/small]
Offline
Опкод
07DD: set_actor 0@ temper_to 80
фактически означает длину очереди (100 - в очередь весь магазин).
Так что при [c]07DD: set_actor 0@ temper_to 100[/c] обстрел почти непрерывный.
@Capushon -
P. S. Удалите это сообщение если не подходит.
[small][/small]
Offline
У некоторых актеров (в частности #LAPDM1) внешний вид в игре, зависит не от модели,а от типа его самого.
Тоесть если создать актера:
009A: 666@ = create_actor 6 #LAPDM1 at 10@ 11@ 12@ - и присвоить ему тип 6 (полицейский) то в игре будет отображаться обычный полицейский (без шлема)
но если написать вот так:
009A: 666@ = create_actor 23 #LAPDM1 at 10@ 11@ 12@ - где тип 23 - то в игре будет отображаться именно LAPDM1 (полицейский с шлемом) - правда теперь ему не присвоенно поведения полицейского и после смерти этот актер не "отдаст" дубинку и пистолет.
Да и анимация стрельбы у него будет не как у полицейского,а как у обычного актера.
Offline
Часто задают вопрос : как сделать так, чтобы парковочный генератор всегда создавал транспортное средство при приближении игрока ? Так вот, за это в опкоде 014B отвечает 5-й параметр: 1 - транспорт появляется всегда, 0 - транспорт может появиться, а может и не появиться (определяется случайным образом). Пример: рядом с гаражом СФ в ряд всегда стоят 7 припаркованных автомобилей
{$CLEO} 0A95: 014B: 0@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 105.7468 27.5391 angle 90.0 014C: set_parked_car_generator 0@ cars_to_generate_to 101 014B: 1@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 115.7468 27.5391 angle 90.0 014C: set_parked_car_generator 1@ cars_to_generate_to 101 014B: 2@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 125.7468 27.5391 angle 90.0 014C: set_parked_car_generator 2@ cars_to_generate_to 101 014B: 3@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 135.7468 27.5391 angle 90.0 014C: set_parked_car_generator 3@ cars_to_generate_to 101 014B: 4@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 145.7468 27.5391 angle 90.0 014C: set_parked_car_generator 4@ cars_to_generate_to 101 014B: 5@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 155.7468 27.5391 angle 90.0 014C: set_parked_car_generator 5@ cars_to_generate_to 101 014B: 6@ = init_parked_car_generator 528 -1 -1 1 alarm 50 door_lock 0 0 40000 at -1991.1958 165.7468 27.5391 angle 90.0 014C: set_parked_car_generator 6@ cars_to_generate_to 101 0A93:
Чтобы генератор всегда создавал транспорт, необходимо соблюдение дистанции между соседними парковочными генераторами, а также между парковочным генераторами и статическими объектами.
Last edited by Den_spb (26-04-2010 14:25)
Offline
Цвет маркера можно задать в виде RGBA в 16-ричном виде, например,
0165: set_marker 0@ color_to 0xff00ffff
Это работает на всей третьей серии игр GTA.
Однако значения 0-8 зарезервированы как табличные, что и используется в стандартном main'е.
I know everything and nothing...
Offline
Создаем качественно анимированную птицу, которой можно управлять.
Конечно, для начала надо написать несколько разных анимаций, я написал всего три - спокойный полет, резкий подъем и пикирование. Взял самолет, сделал его прозрачным, и посадил внутрь персонажа. Дальше идет захват и проверка ускорения по вертикали, дополнительно - проверка на полет в нужную точку.
Я использовал такую систему для создания птерозавра, вот что получилось: http://vk.com/video163942910_171723788
Offline
Создаем качественно анимированную птицу, которой можно управлять.
Конечно, для начала надо написать несколько разных анимаций, я написал всего три - спокойный полет, резкий подъем и пикирование. Взял самолет, сделал его прозрачным, и посадил внутрь персонажа. Дальше идет захват и проверка ускорения по вертикали, дополнительно - проверка на полет в нужную точку.
https://pp.vk.me/c633516/v633516874/10a … Ko-7VM.jpg
Я использовал такую систему для создания птерозавра, вот что получилось: http://vk.com/video163942910_171723788
моно избавится от лишних wait 0 и скрипт будет работать немного быстрее
Offline
Создаем "текстуру боли". В некоторых играх при уроне, например, по углам высвечиваются покраснения, в гта это можно сделать за 10 минут. Для начала, конечно, нужна сама текстура, тут минутное дело в фш, вопросов не будет. Вот сам поток создания:
:PAIN
thread 'PAIN'
wait 0
:PAIN_1
$PAIN1 = Actor.Health ($PLAYER_ACTOR)
wait 10
$PAIN2 = Actor.Health ($PLAYER_ACTOR)
if
0024: $PAIN1 > $PAIN2 // если игрок теряет здоровье
jf @PAIN_1
:PAIN_2
0390: load_txd_dictionary ..XXX
038F: load_texture ... as 1 //архив и текстуры загружаю так поздно, потому что у меня на интерфейсе используются другие текстуры, и при более ранней загрузке начинаются путаницы текстур и глюки. тормозов игры не замечено.
wait 0
038D: draw_texture 1 position 320.0 223.0 size 650.0 455.0 RGBA 220 0 0 100
wait 0
038D: draw_texture 1 position 320.0 223.0 size 650.0 455.0 RGBA 220 0 0 220
wait 0
038D: draw_texture 1 position 320.0 223.0 size 650.0 455.0 RGBA 220 0 0 170
wait 0
038D: draw_texture 1 position 320.0 223.0 size 650.0 455.0 RGBA 220 0 0 100
jump @PAIN_1
//размер и позицию текстур уже подогнал на весь экран, во всех разрешениях все ровно, не сползает.
Результат: http://vk.com/video114114874_171671840
Last edited by DromeoStalker (27-02-2016 19:57)
Offline
Pages: 1