CLEO: Урок 3 - Циклы и отрицательные опкоды

Что такое цикл в скриптинге? Цикл - это часть кода которая повторяется указанное количество раз, либо повторяется пока верно указанное условие. Циклы являются очень важной частью в скриптинге, поэтому вам стоит о них знать.

Виды циклов

Цикл while

while 0256:   player $PLAYER_CHAR defined
wait 0
0826: enable_hud 0
end

Данный цикл будет повторять код до тех пор, пока игрок существует (0256: player $PLAYER_CHAR defined). В данном случае он убирает HUD. Когда игрок перестанет существовать, то скрипт продолжит движение вниз по коду.

В цикле while обязательно нужно ставить задержку wait 0 так как он будет повторяться неопределенное количество раз.

Но что делать если мы хотим, чтобы HUD убирался когда игрок НЕ существует? Для этого нужно использовать отрицательные опкоды. Чтобы написать отрицательный опкод, нужно взять обычный опкод, заменить 0 в начале на 8 и добавить not перед его описанием

Из такого опкода:

0256:   player $PLAYER_CHAR defined

У нас получится такой:

8256:   not player $PLAYER_CHAR defined

Цикл while true

while true
wait 0
0826: enable_hud 0
end

Данный цикл будет повторяться бесконечно, пока мы вручную его не остановим с помощью опкода break. О нем поговорим чуть позже.

Цикл for

for 0@ = 0 to 100
0826: enable_hud 0
end

Этот цикл повторяет код указанное количество раз, в нашем случае 101 раз (от 0 до 100). Переменная 0@ (можно поставить любую другую) в данном цикле нужна, чтобы хранить количество повторений, без нее ничего не будет работать и при компиляции скрипта вы получите ошибку.

Как вы могли заметить, тут я не написал wait 0. Почему? Потому что данный цикл будет повторяться известное нам количество раз - 101, и отсутствие задержки не приведет к зависанию игры. Конечно, без wait 0 код будет выполняться очень быстро и hud пропадет всего на какие-то доли секунды, поэтому в данном случае лучше добавить задержку.

Прерывание цикла

Прервать цикл можно опкодом break

while 0256:   player $PLAYER_CHAR defined
wait 0
0826: enable_hud 0
   if
   08FE:   text_box_displayed
   then
   break
   end
end

Как вы заметили, внутри цикла я добавил условие появился ли на экране текст бокс. Теперь цикл прервется в двух случаях: 1) если игрок не существует 2) если показан текст-бокс на экране. Опкод break можно использовать как в циклах while, так и в циклах for.

P.S. Отступы как у меня в коде делать не обязательно, Sanny Builder не чувствителен к ним. Отступы делаются для удобства и улучшения читабельности кода.

И так, теперь немного практики. Давайте напишем небольшой скрипт, который будет отнимать жизни у игрока в том случае, если он не удерживает клавишу W.

{$CLEO .cs}
0000:

:HEALTH
wait 0
while 8AB0:   not key_pressed 87
wait 0
    if 
    0256:   player $PLAYER_CHAR defined
    then
        wait 300
        0226: 0@ = actor $PLAYER_ACTOR health
        0@ -= 1
        0223: set_actor $PLAYER_ACTOR health_to 0@
    end
end
jump @HEALTH

Разберем код:

1) По классике создаем метку и пишем wait 0
2) Создаем цикл while с условием "если не нажата клавиша 87"
Каждая клавиша на клавиатуре имеет свой номер, в данном случае 87 - это клавиша W. Номера всех клавиш можно посмотреть тут.

3) Пишем wait 0 в начале цикла, чтобы игра не зависла
4) Создаем внутри цикла условие "существует ли игрок". Это нужно для того, чтобы здоровье не отнималось когда игрок умер или арестован.
5) Если игрок существует, добавляем задержку 300 миллисекунд, иначе здоровье будет отниматься слишком быстро
6) Записываем здоровье игрока в переменную 0@
7) Отнимаем от переменной 0@ 1 единицу
8) Устанавливаем игроку количество здоровья в виде переменной 0@
9) Возвращаемся на метку HEALTH, если цикл прерван

Таким образом мы создали скрипт, который будет отнимать 1 единицу здоровья игрока каждые 300 миллисекунд, если не нажата клавиша W.
Получается, с помощью скрипта мы заставим игрока постоянно двигаться. Если он остановиться, то начнет умирать. Чем-то напоминает фильм "Адреналин", да?

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

Чтобы это предотвратить, нужно добавить еще 2 условия:

{$CLEO .cs}
0000:

:HEALTH
wait 0
while 8AB0:   not key_pressed 87
wait 0
    if and
    $ONMISSION == 1 //если запущена миссия
    09E7:   player $PLAYER_CHAR not_frozen //если игрок не заморожен игрой 
    0256:   player $PLAYER_CHAR defined 
    then
        wait 300
        0226: 0@ = actor $PLAYER_ACTOR health
        0@ -= 1
        0223: set_actor $PLAYER_ACTOR health_to 0@
    end
end
jump @HEALTH

Что мы добавили в код:
1)$ONMISSION == 1 проверка запущена ли миссия
2) 09E7: player $PLAYER_CHAR not_frozen проверка не обездвижен ли игрок.
Как мы знаем, во время катсцен игрок обездвиживается, поэтому здоровье не будет отниматься в этом случае.

Теперь скрипт будет работать как надо, и здоровье будет отниматься только тогда, когда мы можем управлять игроком во время миcсии.

Вот и всё, теперь вы научились использовать циклы в CLEO скриптах, поздравляю!

К следующему уроку ->


Полезные ссылки

Номера клавиш в CLEO
Урок "Циклы - Часть 1" от wmysterio
Урок "Циклы - Часть 2" от wmysterio