Генератор позволяет делать метки и совершать прыжки на них, как это реализовано в Sanny Builder. Чтобы сделать метку, нужно использовать свойство "Label". Для прыжка используется команда "jump":
[Thread]
public void TEST() {
label = 1;
jump( 1 );
label = 2;
jump( 2 );
end_thread();
}
Мы можем совершать прыжки только на существующие метки! Здесь вместо имени используется номер. Будет сгенерирован следующий код:
//------------- TEST --------------- :TEST 03A4: name_thread 'TEST' :TEST_1 0002: jump @TEST_1 :TEST_2 0002: jump @TEST_2 004E: end_thread
Если нам нужно сделать прыжок на первую метку, то используем метод "jump" без параметров.
[Thread]
public void TEST() {
jump();
end_thread();
}
Это будет сгенерировано так:
//------------- TEST --------------- :TEST 03A4: name_thread 'TEST' 0002: jump @TEST 004E: end_thread
Если поток не должен завершать свою работу, то команду "end_thread()" использовать не надо.
Также мы можем запускать потоки, как это делается в Sanny Builder. Для этого есть команды "create_thread" и "create_thread_wb":
[Thread]
public void MAIN() {
create_thread( TEST1 ); // без параметров
create_thread( TEST2, 10, 20, -1, 0.0 ); // с параметрами
create_thread_wb( TEST3 );
end_thread();
}
[Thread]
public void TEST1() {
end_thread();
}
[Thread]
public void TEST2() {
end_thread();
}
[Thread]
public void TEST3() {
end_thread();
}
Это приводит к такому результату:
//------------- MAIN --------------- :MAIN 03A4: name_thread 'MAIN' 0180: set_on_mission_flag_to $409 // Note: your missions have to use the variable defined here 0004: $14 = 250 // $ = ? (int) 004F: create_thread @TEST1 004F: create_thread @TEST2 10 20 -1 0.0 00D7: create_thread_wb @TEST3 004E: end_thread //------------- TEST1 --------------- :TEST1 03A4: name_thread 'TEST1' 004E: end_thread //------------- TEST2 --------------- :TEST2 03A4: name_thread 'TEST2' 004E: end_thread //------------- TEST3 --------------- :TEST3 03A4: name_thread 'TEST3' 004E: end_thread
Обратите внимание, что в эти команды передаются не имена потоков, как таковых. Мы указываем ссылку на метод, который помечен атрибутом, не вызывая его через скобки. Из параметра генератор возьмёт имя и сделает всё за нас.
Кроме прыжков, мы можем делать переходы с последующем возвратом. Это - gosub. У нас есть возможность использовать эту конструкцию двумя способами. Классический способ:
[Thread]
public void TEST() {
gosub( 1 );
end_thread();
label = 1;
comment = "this is a comment...";
@return();
}
Метод "return()" должен иметь префикс "@", так как в C# есть встроенное ключевое слово с таким же именем. Также я добавил возможность встраивать комментарии в код через свойство "comment". В итоге мы будем иметь следующий код:
:TEST 03A4: name_thread 'TEST' 0050: gosub @TEST_1 004E: end_thread :TEST_1 /* this is a comment... */ 0051: return
Второй способ позволяет делать встроенные "gosub-ы", который будет автоматически генерировать метки и делать возвраты. Он позволяет писать конструкцию на ходу. В качестве параметра команды используется анонимная функция, которую в этом случае лучше использовать через делегат:
[Thread]
public void TEST() {
gosub( delegate {
comment = "gosub #1";
gosub( delegate {
comment = "gosub #1.1";
gosub( delegate {
comment = "gosub #1.1.1";
} );
} );
comment = "gosub #2";
gosub( delegate {
comment = "gosub #2.1";
} );
} );
end_thread();
}
Эта громадная конструкция делает следующее:
//------------- TEST --------------- :TEST 03A4: name_thread 'TEST' 0050: gosub @TEST_AUTO_GOSUB_0 004E: end_thread :TEST_AUTO_GOSUB_0 /* gosub #1 */ 0050: gosub @TEST_AUTO_GOSUB_1 /* gosub #2 */ 0050: gosub @TEST_AUTO_GOSUB_2 0051: return :TEST_AUTO_GOSUB_1 /* gosub #1.1 */ 0050: gosub @TEST_AUTO_GOSUB_3 0051: return :TEST_AUTO_GOSUB_3 /* gosub #1.1.1 */ 0051: return :TEST_AUTO_GOSUB_2 /* gosub #2.1 */ 0051: return
Естественно без практики и в пустом виде понять как это работает сложно новичку. Со временем с этим проблем возникнуть не должно.
Далее научимся запускать миссию. Для это используется команда "start_mission"
[Thread]
public void MAIN() {
start_mission( MISSION );
// start_mission( TEST1 ); // запуск потока как миссию скомпилировано не будет
// create_thread( MISSION ); // запуск миссии как потока скомпилировано не будет
end_thread();
}
[Thread]
public void TEST1() {
end_thread();
}
[Mission( false )]
public void MISSION() {
end_thread();
}
Номер миссии будет взят автоматически. Нам достаточно передать в "start_mission" ссылку на метод, помеченным атрибутом "Mission". Код выше будет сгенерирован вот так:
//------------- MAIN --------------- :MAIN 03A4: name_thread 'MAIN' 0180: set_on_mission_flag_to $409 // Note: your missions have to use the variable defined here 0004: $14 = 250 // $ = ? (int) 0417: start_mission 0 004E: end_thread //------------- TEST1 --------------- :TEST1 03A4: name_thread 'TEST1' 004E: end_thread //------------- Mission: MISSION (0) --------------- :MISSION 03A4: name_thread 'MISSION' 004E: end_thread
Как запускать внешние скрипты я расскажу после того, как познакомимся с условиями и циклами. Сейчас я расскажу про опкоды. Их, как таких, нету в генераторе. На их месте, как уже многие догадались, используются методы ( команды ). Некоторые из них вы уже узнали, если пользовались Sanny Builder.
Генератор в качестве параметров команд в большинстве случаев использует динамическую типизацию. То есть в метод мы можем передавать типы не по назначению. К примеру вот так:
wait( "bla-bla" );
В этом случае генератор будет вызывать ошибку и требовать указать нужный тип. Возникает вопрос: как узнать какие типы требует команда и как узнать за что те параметры должны отвечать? Для этого я использовал нотацию в формате "ИМЯ_ТИП". Если ввести команду, то в подсказках будет видно что и зачем нужно:

Кроме команд, генератор использует дополнительные типы данных, которые имеют свой набор команд. О них я напишу в другой главе.