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-блоке:
```renpy
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` будет проигнорирован.

Список клавиш можно посмотреть [здесь](https://wiki.libsdl.org/SDL2/SDL_Keycode) (столбец "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` - зачёркивание.

***

## Кнопка

С текстом или обычная:
```renpy
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 условий внутри цикла с тысячей итераций, то лучше всё же этого не делать и попытаться это дело оптимизировать.
Допустим, у нас есть такой сильно упрощённый (и от этого даже несколько нелепый) пример:
```renpy
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 изображений, допустим, нам реально необходимы (например, у нас игра с кучей зомби, или мы хотим устроить красивый снегопад).
А вот избавиться от кучи условий вполне возможно (тем более, что их значительно больше, а потому в первую очередь избавляться нужно от них).

Конкретно в этом случае можно написать всё более кратко, но вряд ли часто будет нужно именно это, поэтому я опишу то, что будет работать для более общего случая:
```renpy
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-функциями](./Функции-для-изображений-(im-функции))).

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

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

***

## imagemap и hotspot

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

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

Элемент `hotspot` имеет свою область в `imagemap` (`x`, `y`, `width`, `height`) и свойство `action` (а так же остальные кнопочные свойства).  
Пример использования:
```renpy
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).
Пример:
```renpy
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`, об этом позже).
Имена этих свойств изменены для большей логичности и стандартизации.
Может использоваться и в качестве контейнера.  
Например, так можно сделать область в четверть экрана и расставить по её углам картинки:
```renpy
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-код в скринах выполняется в глобальном пространстве имён (т. е. так, как это и ожидается по умолчанию).  
С одной стороны, это вызывает некоторые неудобства, например, такой код может работать не так, как от него ожидается:
```renpy
label some_label:
	$ i = 100 + 23
	# В этом месте может начаться отрисовка скринов,
	# если какой-нибудь из них использует переменную i,
	# то её значение, разумеется, уже не будет равно 123.
	# Поэтому используйте более осмысленные имена переменных.
	np "100 + 23 = " + str(i)
```

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

Ну а раз скрины не имеют локальных пространств имён, то и приём параметров осуществляется не так, как в Ren'Py.
Например, если параметры передаются так: `use some_screen('text', color = 0xFFFFFF)`, то чтение может быть таким:
```renpy
# определение имён параметров (и их значений по умолчанию) как в 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] # вместо прямого указания строки можно использовать любую строковую переменную
```

Ну и простой пример использования скрина:
```renpy
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)` - сокращённая запись.

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

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

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

А для `size` - на ширину и высоту **окна**.  
Иначе такие штуки было бы непонятно как вычислять и их пришлось бы запретить:
```renpy
hbox:
	textbutton "Click Me!" xsize 0.9
```
Т. к. `0.9` было бы относительно ширины родительского контейнера, но она зависит от ширины этой кнопки.

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

Свойства `alpha`, `rotate`, `pos` и `zoom` у объектов наследуются от "родителей", в которых они вложены.
```renpy
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`:
```renpy
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`, например:
```renpy
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`:
```python
style.my_super_style = Style(style.image)
style.my_super_style.pos = (10, 10)
style.my_super_style.size = (50, 50)
```
Или так через команду `style` в `init`-блоке:
```renpy
style my_super_style is image:
	pos (10, 10)
	size (50, 50)
```
На самом деле этот код автоматически конвертируется в предыдущий код, практически дословно.

Кроме обычных свойств (`pos`, `size`...) стиль может содержать свойство `properties`,
которое принимает в качестве значения словарь (`dict`):
```renpy
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-объекты могут менять один стиль на другой:
```renpy
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)`  
Тут стоит отметить, что точку между именами объектов и свойств следует использовать даже в том случае,
если идёт работа со словарём (т. е. не допускается использование квадратных скобок).

Пример:
```renpy
$ 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')` (прыжок на метку сценария), реализованно следующим образом:
```python
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`.  
Он будет записан в указанную переменную, которую сценарий сможет использовать.


**Важно**: за исключением некоторых особых ситуаций
(см. [Сигналы и таймауты](./Сигналы-и-таймауты) и [Статьи из раздела конфигурирования игры](./Conf-Содержание)),
в скринах удобнее использовать не указанные ранее функции, а строки с кодом:  
* `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` и, следовательно, запоминаний в ней,
без указания кавычек код будет выполнен сразу во время составления списка,
тогда как запись в переменную будет выполнена, очевидно, уже после этого составления,
ведь нельзя же отправить в функцию выполнения недоделанный список).

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

***

[<- Назад](./Ввод-с-клавиатуры)
