В скриптах для GTA San Andreas есть возможность использовать таблицы переходов. Опкод имеет громадный размер, и постоянно редактировать метки, добавление или удаление блоков было очень нудным и хлопотным делом. Генератор скриптов существенно упрощает написание подобного рода код. Вот так можно написать код таблицы переходов:
publicclassTEST:Thread {Int anyInt;publicoverridevoidSTART( LabelJump label ) {anyInt.random( 0,10 );jump_table( anyInt, jt => { // тут будут создаваться метки для переходов } );end_thread(); }}
Здесь jt — это объект, который позволит нам создавать переходы и ассоциировать их с числом. Ассоциация происходит с помощью метода label. Пример использования:
Если количество блоков превысит максимальный размер, то автоматически будет создано продолжение таблицы переходов, как это показано ниже, в результате:
//------------- THREAD TEST ---------------
:TEST
03A4: name_thread 'TEST'
0209: 0@ = random_int_in_ranges 0 10
0871: init_jump_table 0@ total_jumps 11 default_jump 0 @TEST_SWITCH_0_CASE_END jumps 0 @TEST_SWITCH_0_CASE_0 1 @TEST_SWITCH_0_CASE_1 2 @TEST_SWITCH_0_CASE_2 3 @TEST_SWITCH_0_CASE_3 4 @TEST_SWITCH_0_CASE_4 5 @TEST_SWITCH_0_CASE_5 6 @TEST_SWITCH_0_CASE_6
0872: jump_table_jumps 7 @TEST_SWITCH_0_CASE_7 8 @TEST_SWITCH_0_CASE_8 9 @TEST_SWITCH_0_CASE_9 10 @TEST_SWITCH_0_CASE_10 -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END
:TEST_SWITCH_0_CASE_0
// case #0
:TEST_SWITCH_0_CASE_1
// case #1
:TEST_SWITCH_0_CASE_2
// case #2
:TEST_SWITCH_0_CASE_3
// case #3
:TEST_SWITCH_0_CASE_4
// case #4
:TEST_SWITCH_0_CASE_5
// case #5
:TEST_SWITCH_0_CASE_6
// case #6
:TEST_SWITCH_0_CASE_7
// case #7
:TEST_SWITCH_0_CASE_8
// case #8
:TEST_SWITCH_0_CASE_9
// case #9
:TEST_SWITCH_0_CASE_10
// case #10
:TEST_SWITCH_0_CASE_END
004E: end_thread
В примере я использовал делегат. Он нужен в том случае, если нет необходимости делать переходы на метки, которые генерируются методом label. Если эту метку нужно использовать (например: при условиях), то пользуемся анонимным методом с сигнатурой label => вместо delegate. Объект jt также имеет свойство EndLabel, которое хранит метку выхода с таблицы. Она генерируется в конце всех блоков. Пример:
publicclassTEST:Thread {Int anyInt;publicoverridevoidSTART( LabelJump label ) {anyInt.random( 0,10 );jump_table( anyInt, jt => {jt.label( 0, label => {wait( 0 );and( label,PlayerChar.is_defined() ); // переход на текущую CASE-метку, если условие не выполняется Comment ="case #0";jump( jt.EndLabel ); // переход в конец таблицы } );jt.label( 1, label => {wait( 0 ); Comment ="case #1";jump( label ); // переход на текущую CASE-метку } );jt.label( 2,delegate { Comment ="case #2";jump( jt.EndLabel ); // переход в конец таблицы } ); } ); // здесь будет метка выхода с таблицыwait( 2500 );end_thread(); }}
Метку конца таблицы также можно получить с объекта label в данном примере:
//...jt.label( 0, label => { Comment ="case #0";jump( label.EndLabel ); // переход в конец таблицы} );//...
Результат:
//------------- THREAD TEST ---------------
:TEST
03A4: name_thread 'TEST'
0209: 0@ = random_int_in_ranges 0 10
0871: init_jump_table 0@ total_jumps 3 default_jump 0 @TEST_SWITCH_0_CASE_END jumps 0 @TEST_SWITCH_0_CASE_0 1 @TEST_SWITCH_0_CASE_1 2 @TEST_SWITCH_0_CASE_2 -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END -1 @TEST_SWITCH_0_CASE_END
:TEST_SWITCH_0_CASE_0
0001: wait 0 ms
00D6: if
0256: player $2 defined
004D: jump_if_false @TEST_SWITCH_0_CASE_0
// case #0
0002: jump @TEST_SWITCH_0_CASE_END
:TEST_SWITCH_0_CASE_1
0001: wait 0 ms
// case #1
0002: jump @TEST_SWITCH_0_CASE_1
:TEST_SWITCH_0_CASE_2
// case #2
0002: jump @TEST_SWITCH_0_CASE_END
:TEST_SWITCH_0_CASE_END
0001: wait 2500 ms
004E: end_thread
Если значения "кейсов" возрастают от нуля каждый раз на единицу, то можно воспользоваться событием Auto. Вместо анонимных функций более компактный вид имеет следующий код:
publicclassTEST:Thread {Int anyInt;publicoverridevoidSTART( LabelJump label ) {anyInt.random( 0,10 );jump_table( anyInt, jt => { // В столбик добавляем новый обработчик каждого кейсаjt.Auto+= case_0; // anyInt == 0jt.Auto+= case_1; // anyInt == 1jt.Auto+= case_2; // anyInt == 2jt.Auto+= case_3; // anyInt == 3voidcase_0( LabelCase label ) { // локальная функция внутри метода "jump_table" Comment ="case #0";jump( jt.EndLabel ); }voidcase_1( LabelCase label ) { // локальная функция внутри метода "jump_table" Comment ="case #1";jump( jt.EndLabel ); }voidcase_2( LabelCase label ) { // локальная функция внутри метода "jump_table" Comment ="case #2";jump( jt.EndLabel ); }voidcase_3( LabelCase label ) { // локальная функция внутри метода "jump_table" Comment ="case #3";jump( jt.EndLabel ); } } ); // здесь будет метка выхода с таблицыwait( 2500 );end_thread(); }}
Так, как у нас нет возможности использовать объект jt вне метода START (в этом примере), то все функции нужно сделать локальными. Также они позволяют не разбрасывать код по всему файлу. Вот результат: