Общепринятое обращение с метками весьма узко: jump на метку и все. Но реальные возможности для работы с метками гораздо шире. Сейчас я вам покажу вам несколько трюков с метками.
Sanny Builder позволяет присваивать переменным метку. Например:
и потом можно прыгнуть на эту сохраненную в переменной метку:
Вот пример кода:
Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать.
Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub.
Seemann использовал этот трюк ранее, например в первой версии CLEO, и он неплохо себя зарекомендовал.
В чем подвох, спросите вы? Дело в том, что такой gosub потребует особого оформления результата при выходе. Чтобы gosub вернул True (и проверка сработала) или False (и проверка не сработала) нужно добавить перед return соответственно опкод 0485: return_true или 059A: return_false.
Пример:
Таким образом gosub @CheckDriverIsMale вернет True, если за рулем машины 9@ сидит мужчина, иначе он вернет False. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких оп кодов (например, вы в цикле в разных местах проверяете водителя-мужчину).
Можно высчитать значение метки и прыгнуть на нее в зависимости от значения переменной. Так как переменная может быть больше или меньше, чем предусмотрено, то при прыжке при непредусмотренном значении переменной игра может наткнуться на середину оп кода, и зависнуть. Поэтому нужно в начале проверить границы переменной.
Для начала в Sanny Builder 3 нажмите кнопку "Отладочные опции" на панели инструментов, и выберите пункт CODE_OFFSETS. Тогда при де компиляции CLEO-скрипта на каждой строчке будет показываться его оффсет (смещение) с начала файла.
Создайте новый файл и наберите код:
А теперь нажмите клавишу F6 чтобы ком пилить скрипт не копируя его в папку, и де компилируйте его. Появится код:
число в фигурных скобках - смещение опкода от начала файла в байтах. Разные опкоды могут иметь разную длину, более того, один и тот же опкод может иметь разную длину если у него есть параметры-числа или строки. Опкоды jump, gosub, return имеют всегда одинаковую длину (если параметр типа @label), поэтому легко высчитать смещение от начала файла, и прыгнуть на них.
Посмотрим на код. jump имеет длину семь байтов, так как смещение каждый раз изменяется на семь.
В CLEO, External, Mission скриптах используется локальное смещение, оно имеет всегда отрицательное значение. В Main-скриптах используется глобальное смещение, оно всегда неотрицательно.
Вот сам код:
или другой пример:
Конечно, в San Andreas в 95% случаев рекомендуется пользоваться Jump tables, а не этот способ. Этот способ впервые был применен в Vice City в GTA Mission Assembler.
Трюк первый: хранение метки в переменной и динамический jump
Sanny Builder позволяет присваивать переменным метку. Например:
- Code:
0@ = @MyLabel
и потом можно прыгнуть на эту сохраненную в переменной метку:
- Code:
jump 0@
Вот пример кода:
- Code:
:Begin
0@ = @MyLabel
jump 0@
:MyLabel
//...
Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать.
Трюк второй: условный Gosub
Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub.
Seemann использовал этот трюк ранее, например в первой версии CLEO, и он неплохо себя зарекомендовал.
- Code:
if
gosub @CheckDriverIsMale
jf @gosubFalse
В чем подвох, спросите вы? Дело в том, что такой gosub потребует особого оформления результата при выходе. Чтобы gosub вернул True (и проверка сработала) или False (и проверка не сработала) нужно добавить перед return соответственно опкод 0485: return_true или 059A: return_false.
Пример:
- Code:
: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-скрипта на каждой строчке будет показываться его оффсет (смещение) с начала файла.
Создайте новый файл и наберите код:
- Code:
{$CLEO}
jump @Label
jump @Label
jump @Label
jump @Label
А теперь нажмите клавишу F6 чтобы ком пилить скрипт не копируя его в папку, и де компилируйте его. Появится код:
- Code:
{$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-скриптах используется глобальное смещение, оно всегда неотрицательно.
Вот сам код:
- Code:
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
или другой пример:
- Code:
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.