Трюки с метками — различия между версиями
Материал из GTAModding.ru
VcSaJen (обсуждение | вклад) (Новая: Общепринятое обращение с метками весьма узко: jump на метку и всё. Но реальные возможности для работы ...) |
Seemann (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | Общепринятое обращение с метками весьма узко: [[jump]] на метку и всё. Но реальные возможности для работы с метками гораздо шире. Сейчас я вам покажу вам несколько трюков с метками. | + | Общепринятое обращение с метками весьма узко: [[0002|jump]] на метку и всё. Но реальные возможности для работы с метками гораздо шире. Сейчас я вам покажу вам несколько трюков с метками. |
− | + | ==Трюк первый: хранение метки в переменной и динамический jump'''== | |
− | + | [[Sanny Builder]] позволяет присваивать переменным метку. Например: | |
− | Sanny Builder позволяет присваивать переменным метку. Например: | + | |
<source lang="scm">0@ = @MyLabel</source> | <source lang="scm">0@ = @MyLabel</source> | ||
Строка 20: | Строка 19: | ||
Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать. | Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать. | ||
− | |||
<!-- Примера пока нет --> | <!-- Примера пока нет --> | ||
<!-- Добавьте тут полезный пример --> | <!-- Добавьте тут полезный пример --> | ||
− | + | ==Трюк второй: условный Gosub== | |
− | + | Все знают, что [[0050|gosub]] - это команда безусловного перехода на метку с последующим возвратом по команде [[0051|return]]. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub. [[User:Seemann|Seemann]] использовал этот трюк ранее, например в первой версии [[CLEO]], и он неплохо себя зарекомендовал. | |
− | + | ||
− | Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub. [[Seemann]] использовал этот трюк ранее, например в первой версии CLEO, и он неплохо себя зарекомендовал. | + | |
<source lang="scm" line> | <source lang="scm" line> | ||
Строка 60: | Строка 56: | ||
Таким образом gosub @CheckDriverIsMale вернет True, если за рулем машины 9@ сидит мужчина, иначе он вернет False. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких опкодов (например, вы в цикле в разных местах проверяете водителя-мужчину). | Таким образом gosub @CheckDriverIsMale вернет True, если за рулем машины 9@ сидит мужчина, иначе он вернет False. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких опкодов (например, вы в цикле в разных местах проверяете водителя-мужчину). | ||
− | + | ==Трюк третий: прыжок на метку в зависимости от переменной== | |
− | + | ||
Можно высчитать значение метки и прыгнуть на неё в зависимости от значения переменной. Так как переменная может быть больше или меньше, чем предусмотрено, то при прыжке при непредусмотренном значении переменной игра может наткнуться на середину опкода, и зависнуть. Поэтому нужно в начале проверить границы переменной. | Можно высчитать значение метки и прыгнуть на неё в зависимости от значения переменной. Так как переменная может быть больше или меньше, чем предусмотрено, то при прыжке при непредусмотренном значении переменной игра может наткнуться на середину опкода, и зависнуть. Поэтому нужно в начале проверить границы переменной. | ||
Строка 188: | Строка 183: | ||
</source> | </source> | ||
− | Конечно, в San Andreas в 95% случаев рекомендуется пользоваться | + | Конечно, в San Andreas в 95% случаев рекомендуется пользоваться Jump tables, а не этот способ. Этот способ впервые был применён в Vice City в [[GTA Mission Assembler]]. |
− | [[Категория:GTA SA]] | + | [[Категория:GTA SA]][[Категория:Скриптинг]] |
− | [[Категория:Скриптинг]] | + |
Текущая версия на 07:48, 4 января 2009
Общепринятое обращение с метками весьма узко: jump на метку и всё. Но реальные возможности для работы с метками гораздо шире. Сейчас я вам покажу вам несколько трюков с метками.
Трюк первый: хранение метки в переменной и динамический jump
Sanny Builder позволяет присваивать переменным метку. Например:
0@ = @MyLabel
и потом можно прыгнуть на эту сохранённую в переменной метку:
jump 0@
Вот пример кода:
:Begin
0@ = @MyLabel
jump 0@
:MyLabel
//...
Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать.
Трюк второй: условный Gosub
Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub. Seemann использовал этот трюк ранее, например в первой версии 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. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких опкодов (например, вы в цикле в разных местах проверяете водителя-мужчину).
Трюк третий: прыжок на метку в зависимости от переменной
Можно высчитать значение метки и прыгнуть на неё в зависимости от значения переменной. Так как переменная может быть больше или меньше, чем предусмотрено, то при прыжке при непредусмотренном значении переменной игра может наткнуться на середину опкода, и зависнуть. Поэтому нужно в начале проверить границы переменной.
Для начала в Sanny Builder 3 нажмите кнопку "Отладочные опции" на панели инструментов, и выберите пункт CODE_OFFSETS. Тогда при декомпиляции Cleo-скрипта на каждой строчке будет показываться его оффсет (смещение) с начала файла.
Создайте новый файл и наберите код:
{$CLEO}
jump @Label
jump @Label
jump @Label
jump @Label
:Label
А теперь нажмите клавишу F6 чтобы компилить скрипт не копируя его в папку, и декомпилируйте его. Появится код:
// This file was decompiled using sascm.ini published by Seemann (http://sannybuilder.com/files/SASCM.rar) on 13.10.2007
{$VERSION 3.1.0027}
{$CLEO .cs}
//-------------MAIN---------------
{0} jump @Noname_28
{7} jump @Noname_28
{14} jump @Noname_28
{21} jump @Noname_28
:Noname_28 // Note: a jump to this label will crash the game
число в фигурных скобках - смещение опкода от начала файла в байтах. Разные опкоды могут иметь разную длину, более того, один и тот же опкод может иметь разную длину если у него есть параметры-числа или строки. Опкоды jump, gosub, return имеют всегда одинаковую длину (если параметр типа @label), поэтому легко высчитать смещение от начала файла, и прыгнуть на них.
Посмотрим на код. jump имеет длину семь байтов, так как смещение каждый раз изменяется на семь.
В Cleo, External, Mission скриптах используется локальное смещение, оно имеет всегда отрицательное значение. В Main-скриптах используется глобальное смещение, оно всегда неотрицательно.
Вот сам код:
var
0@: Integer
1@: Integer
2@: Integer
end
{...}
if and
0@>=0
0@<=3
then
1@ = @Label
2@ = 0@
2@ *= -7 // Если не Cleo-скрипт, то минус убрать!
1@ += 2@
jump 1@
:continue
end
{...}
0A93: end_custom_thread
:Label
jump @Case0
jump @Case1
jump @Case2
jump @Case3
:Case0
{Дестия}
jump @continue
:Case1
{Дестия}
jump @continue
:Case2
{Дестия}
jump @continue
:Case3
{Дестия}
jump @continue
или другой пример:
var
0@: Integer
1@: Integer
2@: Integer
end
{...}
if and
0@>=0
0@<=3
then
1@ = @Label
2@ = 0@
2@ *= -9
1@ += 2@
gosub 1@
end
{...}
0A93: end_custom_thread
:Label
gosub @Case0
return
gosub @Case1
return
gosub @Case2
return
gosub @Case3
return
:Case0
{Дестия}
return
:Case1
{Дестия}
return
:Case2
{Дестия}
return
:Case3
{Дестия}
return
Конечно, в San Andreas в 95% случаев рекомендуется пользоваться Jump tables, а не этот способ. Этот способ впервые был применён в Vice City в GTA Mission Assembler.