Просмотры
Трюки с метками
Материал из GTAModding.ru
Общепринятое обращение с метками весьма узко: jump на метку и всё. Но реальные возможности для работы с метками гораздо шире. Сейчас я вам покажу вам несколько трюков с метками.
Трюк первый: хранение метки в переменной и динамический jump
Sanny Builder позволяет присваивать переменным метку. Например:
0@ = @MyLabel
и потом можно прыгнуть на эту сохранённую в переменной метку:
jump 0@Вот пример кода:
:Begin0@ = @MyLabel
jump 0@:MyLabel//...
Для чего это нужно? Ну например для динамического изменения кода скрипта по ходу игры. Или если какая-то проверка или часть кода не нужны по ходу игры, то можно их убрать.
Трюк второй: условный Gosub
Все знают, что gosub - это команда безусловного перехода на метку с последующим возвратом по команде return. Но не все знают, что gosub можно использовать как условие. Это бывает очень полезно, когда, например, в цикле вам нужно одновременно проверить несколько разнородных условий, а объединить их нельзя. Тогда на помощь приходит gosub. Seemann использовал этот трюк ранее, например в первой версии CLEO, и он неплохо себя зарекомендовал.
ifgosub @CheckDriverIsMalejf @gosubFalse
В чем подвох, спросите вы? Дело в том, что такой gosub потребует особого оформления результата при выходе. Чтобы gosub вернул True (и проверка сработала) или False (и проверка не сработала) нужно добавить перед return соответственно опкод 0485: return_true или 059A: return_false.
Пример:
:CheckDriverIsMale046C: 3@ = car 9@ driver
if3@ <> -1
jf @ReturnFalseif
03A3: actor 3@ malejf @ReturnFalse0485: return_true
return
:ReturnFalse059A: return_false
return
Таким образом gosub @CheckDriverIsMale вернет True, если за рулем машины 9@ сидит мужчина, иначе он вернет False. Бывает удобно использовать такой метод, когда вам нужно несколько раз проверять условие, требующее нескольких опкодов (например, вы в цикле в разных местах проверяете водителя-мужчину).
Трюк третий: прыжок на метку в зависимости от переменной
Можно высчитать значение метки и прыгнуть на неё в зависимости от значения переменной. Так как переменная может быть больше или меньше, чем предусмотрено, то при прыжке при непредусмотренном значении переменной игра может наткнуться на середину опкода, и зависнуть. Поэтому нужно в начале проверить границы переменной.
Для начала в Sanny Builder 3 нажмите кнопку "Отладочные опции" на панели инструментов, и выберите пункт CODE_OFFSETS. Тогда при декомпиляции Cleo-скрипта на каждой строчке будет показываться его оффсет (смещение) с начала файла.
Создайте новый файл и наберите код:
{$CLEO}jump @Labeljump @Labeljump @Labeljump @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-скриптах используется глобальное смещение, оно всегда неотрицательно.
Вот сам код:
var0@: Integer1@: Integer2@: Integerend{...}if and
0@>=0
0@<=3
then1@ = @Label
2@ = 0@
2@ *= -7 // Если не Cleo-скрипт, то минус убрать!
1@ += 2@
jump 1@:continueend
{...}0A93: end_custom_thread
:Labeljump @Case0jump @Case1jump @Case2jump @Case3:Case0{Действия}jump @continue:Case1{Действия}jump @continue:Case2{Действия}jump @continue:Case3{Действия}jump @continue
или другой пример:
var0@: Integer1@: Integer2@: Integerend{...}if and
0@>=0
0@<=3
then1@ = @Label
2@ = 0@
2@ *= -9
1@ += 2@
gosub 1@end{...}0A93: end_custom_thread
:Labelgosub @Case0return
gosub @Case1return
gosub @Case2return
gosub @Case3return
:Case0{Действия}return
:Case1{Действия}return
:Case2{Действия}return
:Case3{Действия}return
Конечно, в San Andreas в 95% случаев рекомендуется пользоваться Jump tables, а не этот способ. Этот способ впервые был применён в Vice City в GTA Mission Assembler.


