Метки, переходы, опкоды

Генератор позволяет делать метки и совершать прыжки на них, как это реализовано в 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" );

В этом случае генератор будет вызывать ошибку и требовать указать нужный тип. Возникает вопрос: как узнать какие типы требует команда и как узнать за что те параметры должны отвечать? Для этого я использовал нотацию в формате "ИМЯ_ТИП". Если ввести команду, то в подсказках будет видно что и зачем нужно:

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