ScreenLang - это язык описания графического интерфейса.
Интерфейс строится из набора компонентов, которые можно разделить на 2 группы: простые компоненты и контейнеры.

Указание любого свойства любого компонента можно сделать так:
`comp [main_param] param1 value1 param2 value2`

Так:

    comp [main_param] param1 value1:
        param2 value2

Или так:

    comp [main_param]:
        param1 value1
        param2 value2

А если поменять строки с указанием параметров местами, то абсолютно ничего не поменяется, кроме порядка вычисления параметров.
Ну и кол-во указываемых параметров, разумеется, не ограничено.

## Простые компоненты

## Python

`$`, `python` - используются обычно: также, как и в сценариях.

## Клавиатура

`key KEY action action_func`
Где

- `KEY` - строка, обозначающая клавишу на клавиатуре:
  - Например: проблел - `"SPACE"`, Enter - `"RETURN"`, стрелка влево - `"LEFT"`, F1 - `"F1"`, W - `"w"`,
  - Если присутствует префикс `"K_"`, то он удаляется автоматически, т. е. `"K_LEFT"` тоже будет работать (обратная совместимость).
- `action_func` - функция, которая будет выполнена при нажатии на клавишу (см. последний раздел).

Также есть настройка параметров `first_delay` (игнорирование нажатий после первого в течение некоторого времени) и `delay` (после второго и далее). Это время указывается в секундах, по умолчанию 0.333 и 0.010 соответственно.

Пример:
Где-то в init-блоке:

    python:
        def my_func(a = 123):
            print a

Теперь попробуем вызвать эту функцию при нажатии на Enter:
`key "RETURN" action my_func`
И действительно, при нажатии на Enter будет напечатано `123`.

Но часто бывает, что нам желательно бы передать этой функции параметры, это можно сделать так:
`key "RETURN" action my_func(5)`
Тут будет вызван результат выполнения функции `my_func`, а т. к. мы ничего из неё не возвращаем или, говоря иначе, возвращаем `None`, этот `None` будет проигнорирован.

Список клавиш можно посмотреть здесь (столбец "Key Name").

## Текст

`text TEXT`
Где `TEXT` - отображаемое значение (ожидается тип `str`).
У текста можно настроить ещё чисто-текстовые свойства:

- `font` - имя файла шрифта, который располагается в `resources/fonts/` (без расширения, примеры: `"Calibri"`, `"Arial"`),
- `color` - цвет в формате `0xRRGGBB`,
- `text_size` (не путать с `size`) - размер шрифта,
- `text_align` - выравнивание строк текста по горизонтали, допустимы нецелочисленные значения (`float`, обычно от 0 до 1), а также строки `"left"`, `"center"` и `"right"` (означают `0.0`, `0.5` и `1.0` соответственно),
- `text_valign` - выравнивание строк по вертикали (если точнее, расположение строк в текстовом поле), точно так же допустимы `float` и строки `"top"`, `"center"` и `"bottom"` для всё тех же значений `0.0`, `0.5` и `1.0` соответственно.

Группа свойств для настройки начертания текста (`True` - включить, `False` - выключить, по умолчанию `False`):

- `bold` - полужирность текста,
- `italic` - курсив,
- `underline` - подчёркивание,
- `strikethrough` - зачёркивание.

## Кнопка

С текстом или обычная:

    textbutton TEXT action action_func # с текстом
    button action action_func # без текста

Где

- `TEXT` - отображаемый текст,
- `action_func` - аналогично `key`.

Свойства для отображения:

- `ground` - фон кнопки в обычном состоянии,
- `hover` - фон кнопки, когда на неё наведена мышь.

Если мышь наведена на полностью прозрачный пиксель изображения, то она не считается наведённой. Для избегания такого поведения используйте около-нулевую непрозрачность вместо нулевой. Для пользователя такое не должно быть заметно.

Также для текстовой кнопки доступны все текстовые настройки. И ещё: вы вряд ли сможете это хоть как-то использовать, но обычная кнопка - это текстовая кнопка с пустым текстом.

Дополнительные свойства кнопок:

- `action` - действие, выполняемое при нажатии Левой Кнопки Мыши,
- `alternate` - действие, выполняемое при нажатии Правой Кнопки Мыши,
- `hovered` - действие, выполняемое при наведении на кнопку,
- `unhovered` - действие, выполняемое при убирании курсора с кнопки,
- `activate_sound` - звук, проигрываемый на канале `"button_click"` при клике Левой Кнопки Мыши,
- `hover_sound` - звук, проигрываемый на канале `"button_hover"` при наведении на кнопку,
- `mouse` - `True`/`False`, определяет,
  - нужно ли использовать на кнопке "наведённый" курсор мыши
  - и считать нажатие `<Space>` и `<Enter>` как клик ЛКМ
  - (при `False` не использует и не считает).

## Контейнеры

`if`, `elif`, `else`, `for`, `while` (внутри циклов попрежнему могут использоваться `break` и `continue`, `else` всё так же доступна после циклов).
Это может показаться странным, но всё только что перечисленное - контейнеры, хоть и несколько необычные. Они добавляют потомков не в себя, а в своего родителя (а родитель может добавить его в своего родителя, а не в себя, если тоже принадлежит к этой группе фейк-контейнеров).

При создании такого контейнера создаются все нужные для данного кадра потомки. Если в следующем кадре их количество увеличивается (например, в цикле `for i in xrange(my_var)` значение `my_var` больше, чем в предыдущем кадре, или условие стало истинным, а раньше всегда было ложным), то и недостающие потомки создаются. Если уменьшается - ненужные в данном кадре потомки отключаются (не происходит обновления свойств и отрисовки) и включатся только когда это понадобится. Т. е. всё работает так, как и ожидается интуитивно.

Но помнить о том, что это всё-таки не просто условия/циклы, а всё же контейнеры, может быть полезно. Например, если вы собираетесь делать 10 условий внутри цикла с тысячей итераций, то лучше всё же этого не делать и попытаться это дело оптимизировать. Допустим, у нас есть такой сильно упрощённый (и от этого даже несколько нелепый) пример:

    for i in xrange(1000):
        if i < 100:
            image 'images/some_dir/000+.png'
        elif i < 200:
            image 'images/some_dir/100+.png'
    #   ...
        else:
            image 'images/some_dir/900+.png'

Если задуматься, то тут создаётся 1000 * 10 = 10000 условий и 1000 изображений. Ну, 1000 изображений, допустим, нам реально необходимы (например, у нас игра с кучей зомби, или мы хотим устроить красивый снегопад). А вот избавиться от кучи условий вполне возможно (тем более, что их значительно больше, а потому в первую очередь избавляться нужно от них).

Конкретно в этом случае можно написать всё более кратко, но вряд ли часто будет нужно именно это, поэтому я опишу то, что будет работать для более общего случая:

    python:
        images = []
        for i in xrange(1000):
            if i < 100:
                path = 'images/some_dir/000+.png'
            elif i < 200:
                path = 'images/some_dir/100+.png'
    #       ...
            else:
                path = 'images/some_dir/900+.png'
            images.append(path)

    for path in images:
        image path

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

Это уже - оптимизация, влияющая на читаемость кода. Она может её как улучшить, так и ухудшить, поэтому пытайтесь сохранить в первую очередь её (читаемость), и если она страдает слишком сильно, и всё слишком запутывается, а вам нужно отрисовать лишь десяток объектов - не используйте оптимизацию.
Ещё раз повторю: если у вас производительность не страдает - ставьте лёгкость понимания кода на первое место. Кстати, это относится ко всему коду, а не только к ScreenLang.

## Изображение

`image IMAGE`
Где `IMAGE` - путь к изображению (применимо и то, что описывалось в главе с im-функциями).

Тут следует упомянуть вот о чём.
Допустим, у вас есть что-то типа такого (упрощённо):
`image im.scale('images/some_dir/some_name.png', 200, 100)`
Так вот, не стоит так делать, лучше будет (не всегда, но в большинстве случаев) переписать это следующим образом:

    image 'images/some_dir/some_name.png':
        size (200, 100)

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

## imagemap и hotspot

Можно лишь условно назвать `imagemap` контейнером, потому что единственный объект, который он может содержать - `hotspot`.
Он же, в свою очередь, может быть добавлен только в `imagemap` (а содержать вообще ничего не может).

Итак, `imagemap` виден как изображение, которое указано в его свойстве `ground`. При наведении курсора мыши на область (на непрозрачный пиксель), указанную в одном из `hotspot`'ов, поверх этой области рисуется изображение, указанное в свойстве `hover`. Свойства `ground` и `hover` очень похожи на соответствующие у кнопок.

Элемент `hotspot` имеет свою область в `imagemap` (`x`, `y`, `width`, `height`) и свойство `action` (а так же остальные кнопочные свойства).
Пример использования:

    imagemap:
        ground 'images/menu/ground.png'
        hover  'images/menu/hover.png'

        hotspot (50, 50, 200, 25) action Function(start_mod, 'main_game')
        hotspot (50, 75, 200, 25) action exit_from_game

Используются, в основном, для создания меню, в котором варианты действий нельзя (или сложно) представить в виде обычных кнопок.

## vbox и hbox

Это контейнеры, которые автоматически располагают элементы внутри себя (`vbox` - по вертикали, в столбец; `hbox` - по горизонтали, в линию).
Имеют свойство `spacing`, которое определяет расстояние между объектами (по умолчанию - 0). Пример:

    vbox:
        spacing 15

        textbutton "Играть"    action Function(start_mod, 'main_game')
        textbutton "Настройки" action ShowMenu('settings')
        textbutton "Выход"     action exit_from_game

## null

Ничто, пустота.
Имеет размеры: в отличие от Ren'Py, не `width` и `height`, а `xsize` и `ysize` (или сразу `size`, об этом позже). Имена этих свойств изменены для большей логичности и стандартизации. Может использоваться и в качестве контейнера.
Например, так можно сделать область в четверть экрана и расставить по её углам картинки:

    null:
        size (0.5, 0.5)

        image img align (0.0, 0.0)
        image img align (0.0, 1.0)
        image img align (1.0, 0.0)
        image img align (1.0, 1.0)

## use

Позволяет использовать указанный скрин в качестве элемента интерфейса.
Например, `use my_screen` - включить скрин my_screen в текущий скрин (без кавычек!).
Или с передачей параметров: `use my_screen(1, some_var + 2, 'some string')`.
Поддерживаются даже именованные параметры: `use my_screen(1, 2 + 3, prop = 10, other_prop = None)`.

Важно!
Рекурсивное включение скринов друг в друга недопустимо.
Т. е. нельзя включить скрин `A` в скрин `B`, если скрин `B` уже включает в себя скрин `A` (принципиально, без исключений).

## screen

Собственно, самый верхний узел во всей этой иерархии. Имеет свойства:

- `has` - позволяет превратить скрин в соответствующий авто-контейнер (`has vbox` или `has hbox`),
- `zorder` - отвечает за сортировку: меньше `zorder` - раньше отрисовывается, больше других скринов перекрывают текущий,
- `modal` - если есть хоть 1 скрин с `modal True`, то все скрины с `False` не реагируют на мышь и клавиатуру,
- `save` - нужно ли сохранять скрин, чтобы потом восстановить его после загрузки, по умолчанию `True` - сохранять, `False` - отключить сохранение (отключено, например, для скринов паузы и сохранения).

Кроме этих имеются и все остальные общие свойства (`alpha`, `rotate`, `pos`, `anchor` и прочие, благодаря чему можно, например, повернуть или сместить сразу весь скрин всего одной строчкой).

В отличие от Ren'Py, здесь весь python-код в скринах выполняется в глобальном пространстве имён (т. е. так, как это и ожидается по умолчанию).
С одной стороны, это вызывает некоторые неудобства, например, такой код может работать не так, как от него ожидается:

    label some_label:
        $ i = 100 + 23
        # В этом месте может начаться отрисовка скринов,
        # если какой-нибудь из них использует переменную i,
        # то её значение, разумеется, уже не будет равно 123.
        # Поэтому используйте более осмысленные имена переменных.
        np "100 + 23 = " + str(i)

С другой стороны это всё же лучше, чем чуть ли не в каждом блоке python-кода писать кучу `global`'ов.

Ну а раз скрины не имеют локальных пространств имён, то и приём параметров осуществляется не так, как в Ren'Py. Например, если параметры передаются так: `use some_screen('text', color = 0xFFFFFF)`, то чтение может быть таким:

    # определение имён параметров (и их значений по умолчанию) как в python функции
    screen some_screen(text_str, text_color, x = 100):
        text screen.text_str: # чтение - из объекта <screen> - либо через точку
            color screen['text_color'] # либо через строку в квадратных скобках
            $ prop_name = 'x'
            xpos screen[prop_name] # вместо прямого указания строки можно использовать любую строковую переменную

Ну и простой пример использования скрина:

    screen my_screen:
        image "images/my_image.png":
            size (400, 300)
            xalign time.time() % 1 # дробная часть (остаток от деления на 1) от кол-ва секунд от начала 1970 года
            yalign 0.5

Показать его можно так:
`show screen my_screen` - да, всё та же команда `show`, но со словом `screen`.

Или так:
`$ show_screen('my_screen')`
Или вот так (снова обратная совместимость, `renpy.show_screen` - это просто ссылка на `show_screen`):
`$ renpy.show_screen('my_screen')`

Передачу параметров можно сделать только в `python`-коде:
`$ show_screen('some_screen', 'some text', 0x008000)`
`$ show_screen('some_screen', 'some text', 0x0080FF, 20)`

Скрыть же скрин можно точно так же любым из 3-х этих способов, заменив при этом `show` на `hide` (передача каких-либо параметров при этом не имеет смысла и потому запрещена).

## Свойства

`clipping True` - обрезать вложенные объекты за границами текущего (`True`) или нет (`False`, по умолчанию).

`skip_mouse False` - "пропускать мышь через себя" (`True`, полезно при создании инвентаря) или нет (`False`, по умолчанию).

`alpha 0.75` - непрозрачность (`0` - полностью прозрачно, `1.0` - полностью видимо).

`rotate 45` - поворот по часовой стрелке, в градусах.

`xpos 10` - координата по оси X в пикселях (`0` - самая левая точка).
`ypos 15` - Y-координата (`0` сверху).
`pos (10, 15)` - более компактная форма записи 2-х предыдущих строк.

`xanchor 50` - X-координата в объекте, которая будет считаться началом, точкой отсчёта.
Например, если `xpos == 750`, а `xanchor == 650`, то фактически объект будет рисоваться, начиная с X-координаты `100`.
Аналогично для `yanchor`.
`anchor (50, 75)` - установка `xanchor` и `yanchor` одной строкой.

`xsize 100` - ширина объекта.
`ysize 50` - высота.
`size (100, 50)` - установка `xsize` и `ysize`.
Не стоит путать `size` с `text_size`, т. к. последнее отвечает за размер шрифта в текстовом поле.

`xzoom 0.5` - масштаб объекта (и всех вложенных в него) по оси X (по умолчанию `1.0`).
Аналогично для `yzoom`.
`zoom (2.0, 0.5)` - установка `xzoom` и `yzoom` сразу.

Если размер не задан явно, то он вычисляется.
Размер `vbox` и `hbox` вычисляется так (на примере `vbox`):

- Ширина - максимум среди ширины всех вложенных объектов,
- Высота - сумма высот всех объектов + учёт расстояния (`spacing`) между ними.

Аналогично, но наоборот, вычисляется размер `hbox`.
Размер текстового поля вычисляется похожим образом, но вместо вложенных объектов там строки.

`xalign 0.5` - размещение по центру оси X в родительском контейнере.
`yalign 1.0` - размещение снизу экрана.
`align (0.5, 1.0)` - сокращённая запись.

Как это работает:

    xalign  1.0
    # это сокращённая запись для:
    xpos    1.0 # помещаем в правый край экрана,
    xanchor 1.0 # ставим в качестве "нулевой точки" объекта его правый край
    # Т. е. мы ставим самый правый пиксель объекта в самый правый пиксель экрана

Если тип `xpos` - `float` (нецелое число), то это значение умножается на ширину родительского контейнера.
В случае `ypos` - на высоту.

Для `anchor` умножение идёт на размер текущего объекта.

А для `size` - на ширину и высоту окна.
Иначе такие штуки было бы непонятно как вычислять и их пришлось бы запретить:

    hbox:
        textbutton "Click Me!" xsize 0.9

Т. к. `0.9` было бы относительно ширины родительского контейнера, но она зависит от ширины этой кнопки.

Хотя можно было бы считать, что все эти размеры просто равны нулю, в этом не было бы абсолютно никакого смысла.
А если брать в расчёт не такие примитивные случаи, то мы бы получили ещё и кучу "магии", когда объект исчезает непонятно почему, непонятно куда и непонятно во что.

Свойства `alpha`, `rotate`, `pos` и `zoom` у объектов наследуются от "родителей", в которых они вложены.

    image parent_image:
        rotate 45
        alpha 0.5
        xpos 1000

        image child_image:
            rotate 135 # 135 + 45 = 180
            alpha  0.5 # 0.5 * 0.5 = 0.25
            xpos  -200 # так просто не вычислить, нужно учитывать поворот

Также компактная форма записи нескольких свойств (`pos`, `anchor`, `size`, `zoom` и `align`) может принимать 1 параметр вместо списка из 2, если `x` и `y` параметры должны быть одинаковыми, например:
`zoom 2 # вместо zoom (2, 2)`
`align 0.5 # вместо align (0.5, 0.5)`

С появлением масштабирования появляется и необходимость использовать нецелые числа в качестве абсолютных значений (а не как часть, например, ширины окна), для этого существует тип `absolute`:

    null:
        align 0.5
        size 0.1
        zoom 10

        image snow:
            align 0.5
            size absolute(0.5) # размер будет 0.5 пикселя, но масштабирование увеличит его до 5
            # size 0.5 - а это сделало бы размер снежинки в 0.5 экрана, после чего опять увеличило бы в 10 раз

В отличие от Ren'Py, где тип `absolute` просто наследуется от `float`, и любые операции с которым вернут обычный `float` (даже `absolute(1) + absolute(2))` вернёт `float(3.0)`), в Ren-Engine всё работает корректно (всегда возвращается `absolute`, если хотя бы 1 из операндов является `absolute`).

Ещё у объектов `image`, `imagemap`, `button` и `textbutton` есть свойство `crop` - список из 4-х значений, которые указывают, какую часть текстуры отрисовывать.
По умолчанию это `(0.0, 0.0, 1.0, 1.0)` - (x, y, ширина, высота).
Числа в этом списке домножаются на ширину (x и ширина) и высоту (y и высота) текстуры, если они являются `float`'ами. Надо заметить, что это свойство не влияет на размер отрисовываемого объекта, а лишь указывает, какую часть текстуры нужно отрисовывать на весь объект.

## Стили

Если какое-то свойство объекта не задано в явной форме, то оно берётся из его стиля.
Они хранятся в объекте `style` (см. файл `Ren-Engine/rpy/style.rpy`).

Компонент типа `text` по умолчанию имеет стиль `text`, `image` - стиль `image` и т. д.
Но можно создать свой стиль (как это сделать - см. в файле, что указан выше) и указать его, тогда недостающие свойства будут браться из него, а прежний стиль будет игнорироваться.

Указать стиль можно свойством `style`, например:

    image 'some_image.png':
        style 'my_super_style' # в кавычках, т. к. это указание строки
        anchor (0.5, 0.5)

(На самом деле переданная строка - назовём её `style_value` - заменяется на объект стиля `style[style_value]`, т. е. для случая выше это будет `style['my_super_style']` или `style.my_super_style`)

Но для этого стиль `my_super_style` должен быть создан как-то так через `python`:

    style.my_super_style = Style(style.image)
    style.my_super_style.pos = (10, 10)
    style.my_super_style.size = (50, 50)

Или так через команду `style` в `init`-блоке:

    style my_super_style is image:
        pos (10, 10)
        size (50, 50)

На самом деле этот код автоматически конвертируется в предыдущий код, практически дословно.

Кроме обычных свойств (`pos`, `size`...) стиль может содержать свойство `properties`, которое принимает в качестве значения словарь (`dict`):

    init:
        python:
            def my_func():
                return {
                    'pos': 10, # конечно, допустима и сокращённая запись, если x- и y- свойства одинаковы
                    'size': 50,
                }
        style my_super_style is image:
            anchor 0.5
            properties my_func() # допускается любой python-код, а не только указание заранее созданного словаря

Свойства стиля кэшируются при его первом использовании, дальнейшие изменения не будут учитываться. Раньше кэширование было в пределах отрисовываемого кадра, а не всего запущенного мода, но из-за этого производительность была ниже на 10%, поэтому от этого было решено отказаться.

Тем не менее, не смотря на неизменяемость конкретных стилей, ScreenLang-объекты могут менять один стиль на другой:

    init python:
        # стили вообще не обязаны быть в объекте <styles>

        my_style_1 = Style(style.textbutton)
        # тут настройка my_style_1

        my_style_2 = Style(style.textbutton)
        # тут настройка my_style_2

        my_current_style = my_style_1
        def change_my_style():
            global my_current_style
            if my_current_style is my_style_1:
                my_current_style = my_style_2
            else:
                my_current_style = my_style_1

    screen my_screen:
        key 'F7' action change_my_style

        textbutton 'Exit':
            style my_current_style
            action exit_from_game

## Действие (action)

Поддерживаются "действия" из renpy. Общая суть:
Создаётся вызываемый объект (`callable`), который запоминает переданные ему параметры:
`tmp = SetVariable('a', 123)`
А потом происходит вызов этого объекта:
`tmp()`

`Function` вызывает функцию (1-й параметр) с остальными параметрами:
`key "RETURN" action Function(my_func, 5)`

Также есть `SetVariable('var_name', value)`, `AddVariable('var_name', value)` и `ToggleVariable('var_name', true_value = True, false_value = False)`, которые устанавливают переменной `var_name` значение `value` (Add - добавляет, Toggle - меняет значение на противоположное).

Есть и аналогичные действия вроде `SetDict` и прочих (где Variable заменено на Dict, и дополнительным первым параметром идёт словарь), но они есть лишь для обратной совместимости, `SetVariable` и прочие тоже могут работать со словарями (и даже объектами):
`SetVariable('persistent.my_config.my_prop', 123)`
Тут стоит отметить, что точку между именами объектов и свойств следует использовать даже в том случае, если идёт работа со словарём (т. е. не допускается использование квадратных скобок).

Пример:

    $ my_var = 123
    key "SPACE" action AddVariable('my_var', 321)
    $ print my_var

`GetSetAttr('path.to.some.prop', value = _undefined, obj = None)` отвечает за установку и чтение свойств.
Запись - при указании параметра `value` (возврат `None`), иначе - чтение (возврат свойства).
`obj` - корневой объект, от которого ищется свойство (`None` - глобальное пространство имён).

Кстати, под капотом там используется функция `getset_attr` с такими же параметрами.
Возможно, она и сама по себе иногда будет кому-то полезна.

Действие `Jump('your_label')` (прыжок на метку сценария), реализованно следующим образом:

    def Jump(label):
        return Function(renpy.jump, label)

Подобным образом реализованы также:

- `Call('label_name')` - вызов метки сценария с последующим возвратом на прежнее место,
- `Play('file_name', 'channel')` и `Stop('channel')` - см. Музыка и звуки,
- `Notify('Your Message')` - вывод уведомления (`notification.out`),
- `Language('english')` - смена языка (`renpy.change_language`),
- `Show`, `ShowScreen` и `ShowMenu` - показать указанный скрин (`renpy.show_screen`),
- `Hide`, `HideScreen` и `HideMenu` - скрыть указанный скрин (`renpy.hide_screen`).

В статье [Сохранение и загрузка][./Сохранение-и-загрузка] (из категории "Конфигурирование игры") также описаны некоторые связанные с этим действия.

`If(cond, true, false)` возвращает аргумент `true`, если условие `cond` истинно, иначе возвращает `False` (это обычная функция, возвращающая результат немедленно, добавлена для совместимости с renpy).

`Eval(code, file_name = None, num_line = None)`
`Exec(code, file_name = None, num_line = None)`
Выполняют код `code`, при компиляции указывается место, которое будет выведено в случае вылета исключения (`None` - определить автоматически).
Разница между ними:

- `Eval` запускает код и возвращает результат (`Eval("2 + 3")()` -> `5`),
- `Exec` может делать присваивания, ничего не возвращает (`Exec("v = f(2, 3)")()` -> `None`).

`SetDictFuncRes(obj, 'var_name', func)` при вызове делает `obj[var_name] = func()`

`Return(value)` используется для возвращения значения из скрина.
Вызвать из сценария скрин, который может вернуть значение таким образом, можно так:
`$ renpy.call_screen('your_screen', 'your_variable')`
После этого сценарий будет ждать до тех пор, пока ваш скрин не вернёт результат с помощью `Return`.
Он будет записан в указанную переменную, которую сценарий сможет использовать.

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

- `key "F2" action SetVariable('my_prop', 2)` - вариант в стиле renpy,
- `key "F2" action 'my_prop = 2'` - вариант в стиле Ren-Engine, проще и понятнее.

Также следует сказать, что вместо 1 функции/строки в `action` можно передать список функций или строк.
И это ещё 1 причина использовать строки с кодом, а не функции. Ведь указанные выше "функции" в большинстве своём являются лишь обёртками, которые сначала запоминают все параметры, а уже потом вызываются.

В результате такой код:
`key "F2" action [SetVariable('my_prop', 23), Function(my_func, my_prop)]`
сначала запомнит старое значение `my_prop`, потом установит ему значение `23`, а затем вызовет `my_func` со старым значением `my_prop`, хотя на первый взгляд может показаться, что никакого запоминания старого значения здесь нет.

В то же время код
`key "F2" action ['my_prop = 23', 'my_func(my_prop)']`
будет работать ровно так, как и задумывалось.
Хотя тут следует обратить внимание на то, что в случае забывания кавычек вокруг вызова `my_func` всё тоже будет плохо (несмотря на отсутствие `Function` и, следовательно, запоминаний в ней, без указания кавычек код будет выполнен сразу во время составления списка, тогда как запись в переменную будет выполнена, очевидно, уже после этого составления, ведь нельзя же отправить в функцию выполнения недоделанный список).

Поэтому будьте предельно внимательными при составлении списка действий.

<- Назад