Миссии в деталях

В этой справке уже есть статья о том, как добавить миссию. Если используем её автоматическую генерацию, то у нас есть некоторые "события", которые можно использовать в отдельных участках кода.

Всего таких событий три: "код победы", "код провала" и "код очистки". Первый блок выполняется когда миссия завершилась удачно ( по-умолчанию ). Второй блок будет выполнен тогда, миссия была провалена. И третий - когда нужно очистить игру от созданных элементов в миссии: актёров, маркеров и т.п.

Для вызова этих "событий" нужно воспользоваться атрибутом Mission и указать ему анонимную функцию для выполнения:

[Mission]
public void MISSION() {

    var index = Int.local( 0 ); // 0@
    var counter = Int.local( 1 ); // 1@
    var x = Float.local( 2 ); // 2@
    var enemyArray = Array<Actor>.local( 50, 9 ); // 50@ ... 59@
    var enemyMarkerArray = Array<Marker>.local( 60, 9 ); // 60@ ... 69@

    x.value = 0.0;
    enemyArray.each( index, actor => {
        actor.create_random( x, 1400.0, 13.0 );
        enemyMarkerArray[ index ].create_above_actor( actor );
        x += 1.2;
    } );

    cycle( delegate {
        wait( 0 );
        counter.value = 0;
        enemyArray.each( index, actor => {
            and( actor.is_dead(), delegate {
                counter += 1;
                and( enemyMarkerArray[ index ].is_enabled(), delegate {
                    enemyMarkerArray[ index ].disable();
                } );
            } );
        } );
        and( counter >= enemyArray.count, delegate {
            jump( 0 );
        } );
    } );

    label = 0;

    // далее пишем события:

    Mission.OnPassed = delegate {
        show_text_styled( "M_PASSD", 5000, 1 );
    };

    Mission.OnFailed = delegate {
        show_text_styled( "M_FAIL", 5000, 1 );
    };

    Mission.OnClear = delegate {
        comment = "clear all markers and actors";
        to( index, 0, enemyMarkerArray.count, delegate {
            and( enemyMarkerArray[ index ].is_enabled(), delegate {
                enemyMarkerArray[ index ].disable();
            } );
            and( enemyArray[ index ].is_defined(), delegate {
                enemyArray[ index ].remove_references().destroy();
            } );
        } );
    };

}

События можно писать в любом месте кода миссии. Я рекомендую это делать в самом конце. Вот такой код получится в итоге:

//------------- Mission: MISSION (0) ---------------

:MISSION
03A4: name_thread 'MISSION'
0050: gosub @MISSION_START
00D6: if
0112:     wasted_or_busted // mission only
004D: jump_if_false @MISSION_END
0050: gosub @MISSION_FAILED

:MISSION_END
0004: $409 = 0 // $ = ? (int)
00D8: mission_cleanup
004E: end_thread

:MISSION_START
0317: increment_mission_attempts
0004: $409 = 1 // $ = ? (int)
0007: 2@ = 0.0 // @ = ? (float)

for 0@ = 0 to 9
0376: 50@(0@,9i) = create_random_actor_at 2@ 1400.0 13.0
0187: 60@(0@,9i) = create_marker_above_actor 50@(0@,9i)
000B: 2@ += 1.2 // @ += ? (float)
end


:MISSION_LOOP_0
0001: wait 0 ms
0006: 1@ = 0 // @ = ? (int)

for 0@ = 0 to 9
00D6: if
0118:     actor 50@(0@,9i) dead
then
000A: 1@ += 1 // @ += ? (int)
00D6: if
075C:     marker 60@(0@,9i) enabled
then
0164: disable_marker 60@(0@,9i)
end
end
end

00D6: if
0029:     1@ >= 9 // @ >= ? (int)
then
0002: jump @MISSION_0
end
0002: jump @MISSION_LOOP_0


:MISSION_0

:MISSION_PASSED
0050: gosub @MISSION_CLEAR
00BA: show_text_styled GXT 'M_PASSD' time 5000 style 1
0051: return

:MISSION_FAILED
0050: gosub @MISSION_CLEAR
00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1
0051: return

:MISSION_CLEAR
/* clear all markers and actors */

int 0@
for 0@ = 0 to 9
00D6: if
075C:     marker 60@(0@,9i) enabled
then
0164: disable_marker 60@(0@,9i)
end
00D6: if
056D:     actor 50@(0@,9i) defined
then
01C2: remove_references_to_actor 50@(0@,9i) // Like turning an actor into a random pedestrian
009B: destroy_actor 50@(0@,9i)
end
end

0051: return

Часто будут возникать ситуации, когда нужно написать собственный код провала. В помощь нам придут команды прыжков на метку провала и победы, а также переход с возвратом на метку чистки.

Использовать эти команды можно только в миссиях с автоматической генерацией. Вот пример:

[Mission]
public void MISSION() {

    var checkpoint = Checkpoint.local( 0 ).create( 140.0, 1440.0, 13.0 );

    jf( 0, PlayerActor.is_near_point_3d_on_foot( 1, 140.0, 1440.0, 13.0, 3.0, 3.0, 3.0 ) );

    and( !PlayerChar.is_money_greater( 4000 ), delegate {
        jump_failed(); // прыжок на метку провала
    } );

    and( PlayerActor.is_current_weapon( WeaponNumber.AK47 ), delegate {
        jump( 1 );
    } );

    jump_passed(); // прыжок на метку победы

    label = 1;
    gosub_clear(); // переход на метку чистки с возвратом
    show_text_highpriority( "GXTNAME", 5000, 1 );
    show_text_styled( "M_FAIL", 5000, 1 );
    @return();

    Mission.OnPassed = delegate {
        show_text_styled( "M_PASSD", 5000, 1 );
    };

    Mission.OnFailed = delegate {
        show_text_styled( "M_FAIL", 5000, 1 );
    };

    Mission.OnClear = delegate {
        checkpoint.disable();
    };

}

Если использовать персональные блоки для провала или успеха, то перед этими метками команду "jump_passed" использовать обязательно! Также обязательным условием является завершать свои блоки командой "@return". Вот такой результат получится:

//------------- Mission: MISSION (0) ---------------

:MISSION
03A4: name_thread 'MISSION'
0050: gosub @MISSION_START
00D6: if
0112:     wasted_or_busted // mission only
004D: jump_if_false @MISSION_END
0050: gosub @MISSION_FAILED

:MISSION_END
0004: $409 = 0 // $ = ? (int)
00D8: mission_cleanup
004E: end_thread

:MISSION_START
0317: increment_mission_attempts
0004: $409 = 1 // $ = ? (int)
018A: 0@ = create_checkpoint_at 140.0 1440.0 13.0

:MISSION_AUTO_LABEL_0
0001: wait 0 ms
00D6: if
00FE:     actor $3 sphere 1 in_sphere 140.0 1440.0 13.0 radius 3.0 3.0 3.0
004D: jump_if_false @MISSION_AUTO_LABEL_0
00D6: if
810A: not player $2 money > 4000
then
0002: jump @MISSION_FAILED
end
00D6: if
02D8:     actor $3 current_weapon == 30
then
0002: jump @MISSION_1
end
0002: jump @MISSION_PASSED

:MISSION_1
0050: gosub @MISSION_CLEAR
00BC: show_text_highpriority GXT 'GXTNAME' time 5000 flag 1
00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1
0051: return

:MISSION_PASSED
0050: gosub @MISSION_CLEAR
00BA: show_text_styled GXT 'M_PASSD' time 5000 style 1
0051: return

:MISSION_FAILED
0050: gosub @MISSION_CLEAR
00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1
0051: return

:MISSION_CLEAR
0164: disable_marker 0@
0051: return