From 2a671946c75f28c90ddaf1ec787b94dd7ef97312 Mon Sep 17 00:00:00 2001 From: Rob Durfee Date: Sat, 25 Sep 2021 00:39:31 -0400 Subject: [PATCH] Implement VSCode providers Implement VSCode language providers for Ren'Py --- .vscode/settings.json | 5 +- .vscodeignore | 5 +- CHANGELOG.md | 16 + examples/.vscode/settings.json | 5 + examples/game/gui.rpy | 475 +++++++++ examples/game/options.rpy | 209 ++++ examples/game/saves/navigation.json | 1 + examples/game/screens.rpy | 1512 +++++++++++++++++++++++++++ examples/game/script.rpy | 119 +++ package-lock.json | 8 +- package.json | 60 +- src/.vscode/settings.json | 5 + src/color.ts | 239 +++++ src/diagnostics.ts | 164 +++ src/extension.ts | 920 +++++++++++++++- src/navigation.ts | 351 +++++++ src/navigationdata.ts | 1344 ++++++++++++++++++++++++ src/renpy.json | 1 + src/renpyauto.json | 126 +++ src/semantics.ts | 224 ++++ src/workspace.ts | 122 +++ syntaxes/renpy.tmLanguage.json | 56 +- 22 files changed, 5952 insertions(+), 15 deletions(-) create mode 100644 examples/.vscode/settings.json create mode 100644 examples/game/gui.rpy create mode 100644 examples/game/options.rpy create mode 100644 examples/game/saves/navigation.json create mode 100644 examples/game/screens.rpy create mode 100644 examples/game/script.rpy create mode 100644 src/.vscode/settings.json create mode 100644 src/color.ts create mode 100644 src/diagnostics.ts create mode 100644 src/navigation.ts create mode 100644 src/navigationdata.ts create mode 100644 src/renpy.json create mode 100644 src/renpyauto.json create mode 100644 src/semantics.ts create mode 100644 src/workspace.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 22de090..dc7aa51 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,10 @@ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { - "out": false // set this to true to hide the "out" folder with the compiled JS files + "**/*.rpyc": true }, "search.exclude": { "out": true // set this to false to include "out" folder in search results - } + }, + "cmake.configureOnOpen": true } diff --git a/.vscodeignore b/.vscodeignore index 45c47b4..555cdec 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,9 +1,12 @@ .vscode/** .vscode-test/** +node_modules out/test/** out/**/*.map -src/** +**/*.ts .gitignore tsconfig.json vsc-extension-quickstart.md tslint.json +!renpy.json +!renpyauto.json \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf314b..30a9261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 2.0.0 (2021/09/01) + +### Features + +* Extension Settings - Allows you to enable/disable certain new functionality +* Hover - Hovering over a keyword will display the selected item's source file/location as well as documentation if available +* Definition - Adds support for right-click Go To Definition (F12), which will jump to the selected keyword's source +* Document Symbols - Document Symbols are displayed in the Outline window in the sidebar +* Signature Help - Shows the documentation pop-up as you enter a function's arguments +* Completion - Displays a pop-up auto-complete menu with appropriate choices as you type your script +* Document Color - Displays a color block next to detected colors in your script and allows you to pick new colors with a click +* Reference - Adds support for Go To Reference, which searches your documents for the selected keyword +* Folding - Adds folding support to collapse logical sections of your script in the editor +* Semantic Tokens - Detects parameters and variables in screens and functions and colorizes them correctly +* Diagnostics - Adds support for detection of issues with indentation or invalid filenames/variable names and marks them as errors or warnings in the editor + ## 1.1.0 (2021/08/13) ### Features diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json new file mode 100644 index 0000000..89519a2 --- /dev/null +++ b/examples/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.exclude": { + "**/*.rpyc": true + } +} \ No newline at end of file diff --git a/examples/game/gui.rpy b/examples/game/gui.rpy new file mode 100644 index 0000000..e39db89 --- /dev/null +++ b/examples/game/gui.rpy @@ -0,0 +1,475 @@ +################################################################################ +## Initialization +################################################################################ + +## The init offset statement causes the initialization statements in this file +## to run before init statements in any other file. +init offset = -2 + +## Calling gui.init resets the styles to sensible default values, and sets the +## width and height of the game. +init python: + gui.init(1280, 720) + + + +################################################################################ +## GUI Configuration Variables +################################################################################ + + +## Colors ###################################################################### +## +## The colors of text in the interface. + +## An accent color used throughout the interface to label and highlight text. +define gui.accent_color = u'#0099cc' + +## The color used for a text button when it is neither selected nor hovered. +define gui.idle_color = u'#888888' + +## The small color is used for small text, which needs to be brighter/darker to +## achieve the same effect. +define gui.idle_small_color = u'#aaaaaa' + +## The color that is used for buttons and bars that are hovered. +define gui.hover_color = u'#66c1e0' + +## The color used for a text button when it is selected but not focused. A +## button is selected if it is the current screen or preference value. +define gui.selected_color = u'#ffffff' + +## The color used for a text button when it cannot be selected. +define gui.insensitive_color = u'#8888887f' + +## Colors used for the portions of bars that are not filled in. These are not +## used directly, but are used when re-generating bar image files. +define gui.muted_color = u'#003d51' +define gui.hover_muted_color = u'#005b7a' + +## The colors used for dialogue and menu choice text. +define gui.text_color = u'#ffffff' +define gui.interface_text_color = u'#ffffff' + + +## Fonts and Font Sizes ######################################################## + +## The font used for in-game text. +define gui.text_font = "DejaVuSans.ttf" + +## The font used for character names. +define gui.name_text_font = "DejaVuSans.ttf" + +## The font used for out-of-game text. +define gui.interface_text_font = "DejaVuSans.ttf" + +## The size of normal dialogue text. +define gui.text_size = 22 + +## The size of character names. +define gui.name_text_size = 30 + +## The size of text in the game's user interface. +define gui.interface_text_size = 22 + +## The size of labels in the game's user interface. +define gui.label_text_size = 24 + +## The size of text on the notify screen. +define gui.notify_text_size = 16 + +## The size of the game's title. +define gui.title_text_size = 50 + + +## Main and Game Menus ######################################################### + +## The images used for the main and game menus. +define gui.main_menu_background = "gui/main_menu.png" +define gui.game_menu_background = "gui/game_menu.png" + + +## Dialogue #################################################################### +## +## These variables control how dialogue is displayed on the screen one line at a +## time. + +## The height of the textbox containing dialogue. +define gui.textbox_height = 185 + +## The placement of the textbox vertically on the screen. 0.0 is the top, 0.5 is +## center, and 1.0 is the bottom. +define gui.textbox_yalign = 1.0 + + +## The placement of the speaking character's name, relative to the textbox. +## These can be a whole number of pixels from the left or top, or 0.5 to center. +define gui.name_xpos = 240 +define gui.name_ypos = 0 + +## The horizontal alignment of the character's name. This can be 0.0 for left- +## aligned, 0.5 for centered, and 1.0 for right-aligned. +define gui.name_xalign = 0.0 + +## The width, height, and borders of the box containing the character's name, or +## None to automatically size it. +define gui.namebox_width = None +define gui.namebox_height = None + +## The borders of the box containing the character's name, in left, top, right, +## bottom order. +define gui.namebox_borders = Borders(5, 5, 5, 5) + +## If True, the background of the namebox will be tiled, if False, the +## background of the namebox will be scaled. +define gui.namebox_tile = False + + +## The placement of dialogue relative to the textbox. These can be a whole +## number of pixels relative to the left or top side of the textbox, or 0.5 to +## center. +define gui.dialogue_xpos = 268 +define gui.dialogue_ypos = 50 + +## The maximum width of dialogue text, in pixels. +define gui.dialogue_width = 744 + +## The horizontal alignment of the dialogue text. This can be 0.0 for left- +## aligned, 0.5 for centered, and 1.0 for right-aligned. +define gui.dialogue_text_xalign = 0.0 + + +## Buttons ##################################################################### +## +## These variables, along with the image files in gui/button, control aspects of +## how buttons are displayed. + +## The width and height of a button, in pixels. If None, Ren'Py computes a size. +define gui.button_width = None +define gui.button_height = None + +## The borders on each side of the button, in left, top, right, bottom order. +define gui.button_borders = Borders(4, 4, 4, 4) + +## If True, the background image will be tiled. If False, the background image +## will be linearly scaled. +define gui.button_tile = False + +## The font used by the button. +define gui.button_text_font = gui.interface_text_font + +## The size of the text used by the button. +define gui.button_text_size = gui.interface_text_size + +## The color of button text in various states. +define gui.button_text_idle_color = gui.idle_color +define gui.button_text_hover_color = gui.hover_color +define gui.button_text_selected_color = gui.selected_color +define gui.button_text_insensitive_color = gui.insensitive_color + +## The horizontal alignment of the button text. (0.0 is left, 0.5 is center, 1.0 +## is right). +define gui.button_text_xalign = 0.0 + + +## These variables override settings for different kinds of buttons. Please see +## the gui documentation for the kinds of buttons available, and what each is +## used for. +## +## These customizations are used by the default interface: + +define gui.radio_button_borders = Borders(18, 4, 4, 4) + +define gui.check_button_borders = Borders(18, 4, 4, 4) + +define gui.confirm_button_text_xalign = 0.5 + +define gui.page_button_borders = Borders(10, 4, 10, 4) + +define gui.quick_button_borders = Borders(10, 4, 10, 0) +define gui.quick_button_text_size = 14 +define gui.quick_button_text_idle_color = gui.idle_small_color +define gui.quick_button_text_selected_color = gui.accent_color + +## You can also add your own customizations, by adding properly-named variables. +## For example, you can uncomment the following line to set the width of a +## navigation button. + +# define gui.navigation_button_width = 250 + + +## Choice Buttons ############################################################## +## +## Choice buttons are used in the in-game menus. + +define gui.choice_button_width = 790 +define gui.choice_button_height = None +define gui.choice_button_tile = False +define gui.choice_button_borders = Borders(100, 5, 100, 5) +define gui.choice_button_text_font = gui.text_font +define gui.choice_button_text_size = gui.text_size +define gui.choice_button_text_xalign = 0.5 +define gui.choice_button_text_idle_color = "#cccccc" +define gui.choice_button_text_hover_color = "#ffffff" +define gui.choice_button_text_insensitive_color = "#444444" + + +## File Slot Buttons ########################################################### +## +## A file slot button is a special kind of button. It contains a thumbnail +## image, and text describing the contents of the save slot. A save slot uses +## image files in gui/button, like the other kinds of buttons. + +## The save slot button. +define gui.slot_button_width = 276 +define gui.slot_button_height = 206 +define gui.slot_button_borders = Borders(10, 10, 10, 10) +define gui.slot_button_text_size = 14 +define gui.slot_button_text_xalign = 0.5 +define gui.slot_button_text_idle_color = gui.idle_small_color +define gui.slot_button_text_selected_idle_color = gui.selected_color +define gui.slot_button_text_selected_hover_color = gui.hover_color + +## The width and height of thumbnails used by the save slots. +define config.thumbnail_width = 256 +define config.thumbnail_height = 144 + +## The number of columns and rows in the grid of save slots. +define gui.file_slot_cols = 3 +define gui.file_slot_rows = 2 + + +## Positioning and Spacing ##################################################### +## +## These variables control the positioning and spacing of various user interface +## elements. + +## The position of the left side of the navigation buttons, relative to the left +## side of the screen. +define gui.navigation_xpos = 40 + +## The vertical position of the skip indicator. +define gui.skip_ypos = 10 + +## The vertical position of the notify screen. +define gui.notify_ypos = 45 + +## The spacing between menu choices. +define gui.choice_spacing = 22 + +## Buttons in the navigation section of the main and game menus. +define gui.navigation_spacing = 4 + +## Controls the amount of spacing between preferences. +define gui.pref_spacing = 10 + +## Controls the amount of spacing between preference buttons. +define gui.pref_button_spacing = 0 + +## The spacing between file page buttons. +define gui.page_spacing = 0 + +## The spacing between file slots. +define gui.slot_spacing = 10 + +## The position of the main menu text. +define gui.main_menu_text_xalign = 1.0 + + +## Frames ###################################################################### +## +## These variables control the look of frames that can contain user interface +## components when an overlay or window is not present. + +## Generic frames. +define gui.frame_borders = Borders(4, 4, 4, 4) + +## The frame that is used as part of the confirm screen. +define gui.confirm_frame_borders = Borders(40, 40, 40, 40) + +## The frame that is used as part of the skip screen. +define gui.skip_frame_borders = Borders(16, 5, 50, 5) + +## The frame that is used as part of the notify screen. +define gui.notify_frame_borders = Borders(16, 5, 40, 5) + +## Should frame backgrounds be tiled? +define gui.frame_tile = False + + +## Bars, Scrollbars, and Sliders ############################################### +## +## These control the look and size of bars, scrollbars, and sliders. +## +## The default GUI only uses sliders and vertical scrollbars. All of the other +## bars are only used in creator-written screens. + +## The height of horizontal bars, scrollbars, and sliders. The width of vertical +## bars, scrollbars, and sliders. +define gui.bar_size = 25 +define gui.scrollbar_size = 12 +define gui.slider_size = 25 + +## True if bar images should be tiled. False if they should be linearly scaled. +define gui.bar_tile = False +define gui.scrollbar_tile = False +define gui.slider_tile = False + +## Horizontal borders. +define gui.bar_borders = Borders(4, 4, 4, 4) +define gui.scrollbar_borders = Borders(4, 4, 4, 4) +define gui.slider_borders = Borders(4, 4, 4, 4) + +## Vertical borders. +define gui.vbar_borders = Borders(4, 4, 4, 4) +define gui.vscrollbar_borders = Borders(4, 4, 4, 4) +define gui.vslider_borders = Borders(4, 4, 4, 4) + +## What to do with unscrollable scrollbars in the gui. "hide" hides them, while +## None shows them. +define gui.unscrollable = "hide" + + +## History ##################################################################### +## +## The history screen displays dialogue that the player has already dismissed. + +## The number of blocks of dialogue history Ren'Py will keep. +define config.history_length = 250 + +## The height of a history screen entry, or None to make the height variable at +## the cost of performance. +define gui.history_height = 140 + +## The position, width, and alignment of the label giving the name of the +## speaking character. +define gui.history_name_xpos = 155 +define gui.history_name_ypos = 0 +define gui.history_name_width = 155 +define gui.history_name_xalign = 1.0 + +## The position, width, and alignment of the dialogue text. +define gui.history_text_xpos = 170 +define gui.history_text_ypos = 2 +define gui.history_text_width = 740 +define gui.history_text_xalign = 0.0 + + +## NVL-Mode #################################################################### +## +## The NVL-mode screen displays the dialogue spoken by NVL-mode characters. + +## The borders of the background of the NVL-mode background window. +define gui.nvl_borders = Borders(0, 10, 0, 20) + +## The maximum number of NVL-mode entries Ren'Py will display. When more entries +## than this are to be show, the oldest entry will be removed. +define gui.nvl_list_length = 6 + +## The height of an NVL-mode entry. Set this to None to have the entries +## dynamically adjust height. +define gui.nvl_height = 115 + +## The spacing between NVL-mode entries when gui.nvl_height is None, and between +## NVL-mode entries and an NVL-mode menu. +define gui.nvl_spacing = 10 + +## The position, width, and alignment of the label giving the name of the +## speaking character. +define gui.nvl_name_xpos = 430 +define gui.nvl_name_ypos = 0 +define gui.nvl_name_width = 150 +define gui.nvl_name_xalign = 1.0 + +## The position, width, and alignment of the dialogue text. +define gui.nvl_text_xpos = 450 +define gui.nvl_text_ypos = 8 +define gui.nvl_text_width = 590 +define gui.nvl_text_xalign = 0.0 + +## The position, width, and alignment of nvl_thought text (the text said by the +## nvl_narrator character.) +define gui.nvl_thought_xpos = 240 +define gui.nvl_thought_ypos = 0 +define gui.nvl_thought_width = 780 +define gui.nvl_thought_xalign = 0.0 + +## The position of nvl menu_buttons. +define gui.nvl_button_xpos = 450 +define gui.nvl_button_xalign = 0.0 + +## Localization ################################################################ + +## This controls where a line break is permitted. The default is suitable +## for most languages. A list of available values can be found at https:// +## www.renpy.org/doc/html/style_properties.html#style-property-language + +define gui.language = "unicode" + + +################################################################################ +## Mobile devices +################################################################################ + +init python: + + ## This increases the size of the quick buttons to make them easier to touch + ## on tablets and phones. + if renpy.variant("touch"): + + gui.quick_button_borders = Borders(40, 14, 40, 0) + + ## This changes the size and spacing of various GUI elements to ensure they + ## are easily visible on phones. + if renpy.variant("small"): + + ## Font sizes. + gui.text_size = 30 + gui.name_text_size = 36 + gui.notify_text_size = 25 + gui.interface_text_size = 30 + gui.button_text_size = 30 + gui.label_text_size = 34 + + ## Adjust the location of the textbox. + gui.textbox_height = 240 + gui.name_xpos = 80 + gui.dialogue_xpos = 90 + gui.dialogue_width = 1100 + + ## Change the size and spacing of various things. + gui.slider_size = 36 + + gui.choice_button_width = 1240 + gui.choice_button_text_size = 30 + + gui.navigation_spacing = 20 + gui.pref_button_spacing = 10 + + gui.history_height = 190 + gui.history_text_width = 690 + + gui.quick_button_text_size = 20 + + ## File button layout. + gui.file_slot_cols = 2 + gui.file_slot_rows = 2 + + ## NVL-mode. + gui.nvl_height = 170 + + gui.nvl_name_width = 305 + gui.nvl_name_xpos = 325 + + gui.nvl_text_width = 915 + gui.nvl_text_xpos = 345 + gui.nvl_text_ypos = 5 + + gui.nvl_thought_width = 1240 + gui.nvl_thought_xpos = 20 + + gui.nvl_button_width = 1240 + gui.nvl_button_xpos = 20 + + + diff --git a/examples/game/options.rpy b/examples/game/options.rpy new file mode 100644 index 0000000..78662a1 --- /dev/null +++ b/examples/game/options.rpy @@ -0,0 +1,209 @@ +## This file contains options that can be changed to customize your game. +## +## Lines beginning with two '#' marks are comments, and you shouldn't uncomment +## them. Lines beginning with a single '#' mark are commented-out code, and you +## may want to uncomment them when appropriate. + + +## Basics ###################################################################### + +## A human-readable name of the game. This is used to set the default window +## title, and shows up in the interface and error reports. +## +## The _() surrounding the string marks it as eligible for translation. + +define config.name = _("Ren'Py Extension Sample for Visual Studio Code") + + +## Determines if the title given above is shown on the main menu screen. Set +## this to False to hide the title. + +define gui.show_name = True + + +## The version of the game. + +define config.version = "2.0" + + +## Text that is placed on the game's about screen. Place the text between the +## triple-quotes, and leave a blank line between paragraphs. + +define gui.about = _p(""" +Example project for the Ren'Py Extension for Visual Studio Code +""") + + +## A short name for the game used for executables and directories in the built +## distribution. This must be ASCII-only, and must not contain spaces, colons, +## or semicolons. + +define build.name = "Extension" + + +## Sounds and music ############################################################ + +## These three variables control which mixers are shown to the player by +## default. Setting one of these to False will hide the appropriate mixer. + +define config.has_sound = True +define config.has_music = True +define config.has_voice = True + + +## To allow the user to play a test sound on the sound or voice channel, +## uncomment a line below and use it to set a sample sound to play. + +# define config.sample_sound = "sample-sound.ogg" +# define config.sample_voice = "sample-voice.ogg" + + +## Uncomment the following line to set an audio file that will be played while +## the player is at the main menu. This file will continue playing into the +## game, until it is stopped or another file is played. + +# define config.main_menu_music = "main-menu-theme.ogg" + + +## Transitions ################################################################# +## +## These variables set transitions that are used when certain events occur. +## Each variable should be set to a transition, or None to indicate that no +## transition should be used. + +## Entering or exiting the game menu. + +define config.enter_transition = dissolve +define config.exit_transition = dissolve + + +## Between screens of the game menu. + +define config.intra_transition = dissolve + + +## A transition that is used after a game has been loaded. + +define config.after_load_transition = None + + +## Used when entering the main menu after the game has ended. + +define config.end_game_transition = None + + +## A variable to set the transition used when the game starts does not exist. +## Instead, use a with statement after showing the initial scene. + + +## Window management ########################################################### +## +## This controls when the dialogue window is displayed. If "show", it is always +## displayed. If "hide", it is only displayed when dialogue is present. If +## "auto", the window is hidden before scene statements and shown again once +## dialogue is displayed. +## +## After the game has started, this can be changed with the "window show", +## "window hide", and "window auto" statements. + +define config.window = "auto" + + +## Transitions used to show and hide the dialogue window + +define config.window_show_transition = Dissolve(.2) +define config.window_hide_transition = Dissolve(.2) + + +## Preference defaults ######################################################### + +## Controls the default text speed. The default, 0, is infinite, while any other +## number is the number of characters per second to type out. + +default preferences.text_cps = 0 + + +## The default auto-forward delay. Larger numbers lead to longer waits, with 0 +## to 30 being the valid range. + +default preferences.afm_time = 15 + + +## Save directory ############################################################## +## +## Controls the platform-specific place Ren'Py will place the save files for +## this game. The save files will be placed in: +## +## Windows: %APPDATA\RenPy\ +## +## Macintosh: $HOME/Library/RenPy/ +## +## Linux: $HOME/.renpy/ +## +## This generally should not be changed, and if it is, should always be a +## literal string, not an expression. + +define config.save_directory = "Extension-1630425934" + + +## Icon ######################################################################## +## +## The icon displayed on the taskbar or dock. + +define config.window_icon = "gui/window_icon.png" + + +## Build configuration ######################################################### +## +## This section controls how Ren'Py turns your project into distribution files. + +init python: + + ## The following functions take file patterns. File patterns are case- + ## insensitive, and matched against the path relative to the base directory, + ## with and without a leading /. If multiple patterns match, the first is + ## used. + ## + ## In a pattern: + ## + ## / is the directory separator. + ## + ## * matches all characters, except the directory separator. + ## + ## ** matches all characters, including the directory separator. + ## + ## For example, "*.txt" matches txt files in the base directory, "game/ + ## **.ogg" matches ogg files in the game directory or any of its + ## subdirectories, and "**.psd" matches psd files anywhere in the project. + + ## Classify files as None to exclude them from the built distributions. + + build.classify('**~', None) + build.classify('**.bak', None) + build.classify('**/.**', None) + build.classify('**/#**', None) + build.classify('**/thumbs.db', None) + + ## To archive files, classify them as 'archive'. + + # build.classify('game/**.png', 'archive') + # build.classify('game/**.jpg', 'archive') + + ## Files matching documentation patterns are duplicated in a mac app build, + ## so they appear in both the app and the zip file. + + build.documentation('*.html') + build.documentation('*.txt') + + +## A Google Play license key is required to download expansion files and perform +## in-app purchases. It can be found on the "Services & APIs" page of the Google +## Play developer console. + +# define build.google_play_key = "..." + + +## The username and project name associated with an itch.io project, separated +## by a slash. + +# define build.itch_project = "renpytom/test-project" diff --git a/examples/game/saves/navigation.json b/examples/game/saves/navigation.json new file mode 100644 index 0000000..0342659 --- /dev/null +++ b/examples/game/saves/navigation.json @@ -0,0 +1 @@ +{"name": "Ren'Py Extension Sample for Visual Studio Code", "version": "2.0", "location": {"callable": {"Inventory.add": ["game/script.rpy", 115], "sampleFunction": ["game/script.rpy", 100], "Inventory.__init__": ["game/script.rpy", 112]}, "screen": {"load": ["game/screens.rpy", 593], "help": ["game/screens.rpy", 972], "say": ["game/screens.rpy", 98], "notify": ["game/screens.rpy", 1256], "keyboard_help": ["game/screens.rpy", 1001], "gamepad_help": ["game/screens.rpy", 1071], "preferences": ["game/screens.rpy", 715], "confirm": ["game/screens.rpy", 1137], "hello_title": ["game/script.rpy", 89], "mouse_help": ["game/screens.rpy", 1048], "input": ["game/screens.rpy", 174], "hello_world": ["game/script.rpy", 78], "save": ["game/screens.rpy", 586], "skip_indicator": ["game/screens.rpy", 1200], "file_slots": ["game/screens.rpy", 600], "choice": ["game/screens.rpy", 207], "game_menu": ["game/screens.rpy", 420], "main_menu": ["game/screens.rpy", 355], "nvl": ["game/screens.rpy", 1294], "quick_menu": ["game/screens.rpy", 1416], "about": ["game/screens.rpy", 546], "nvl_dialogue": ["game/screens.rpy", 1326], "navigation": ["game/screens.rpy", 292], "history": ["game/screens.rpy", 882]}, "define": {"gui.accent_color": ["game/gui.rpy", 26], "gui.interface_text_size": ["game/gui.rpy", 73], "build.name": ["game/options.rpy", 41], "gui.bar_tile": ["game/gui.rpy", 314], "gui.nvl_thought_xpos": ["game/gui.rpy", 392], "gui.slot_button_text_idle_color": ["game/gui.rpy", 229], "config.history_length": ["game/gui.rpy", 338], "config.after_load_transition": ["game/options.rpy", 87], "gui.page_spacing": ["game/gui.rpy", 270], "gui.page_button_borders": ["game/gui.rpy", 187], "sidebar": ["game/script.rpy", 22], "gui.idle_color": ["game/gui.rpy", 29], "config.window": ["game/options.rpy", 109], "gui.slider_tile": ["game/gui.rpy", 316], "gui.main_menu_text_xalign": ["game/gui.rpy", 276], "gui.nvl_button_xalign": ["game/gui.rpy", 399], "gui.nvl_text_ypos": ["game/gui.rpy", 386], "gui.frame_borders": ["game/gui.rpy", 285], "gui.nvl_list_length": ["game/gui.rpy", 367], "gui.slot_button_borders": ["game/gui.rpy", 226], "gui.scrollbar_borders": ["game/gui.rpy", 320], "config.enter_transition": ["game/options.rpy", 76], "gui.pref_spacing": ["game/gui.rpy", 264], "gui.dialogue_text_xalign": ["game/gui.rpy", 139], "gui.nvl_text_xpos": ["game/gui.rpy", 385], "config.window_icon": ["game/options.rpy", 153], "gui.muted_color": ["game/gui.rpy", 47], "gui.dialogue_ypos": ["game/gui.rpy", 132], "config.has_sound": ["game/options.rpy", 49], "gui.button_text_font": ["game/gui.rpy", 159], "gui.nvl_name_ypos": ["game/gui.rpy", 380], "gui.game_menu_background": ["game/gui.rpy", 89], "gui.choice_button_text_hover_color": ["game/gui.rpy", 213], "gui.nvl_button_xpos": ["game/gui.rpy", 398], "gui.name_xalign": ["game/gui.rpy", 112], "gui.namebox_borders": ["game/gui.rpy", 121], "gui.frame_tile": ["game/gui.rpy", 297], "gui.nvl_text_xalign": ["game/gui.rpy", 388], "gui.vbar_borders": ["game/gui.rpy", 324], "config.end_game_transition": ["game/options.rpy", 92], "gui.slot_button_text_size": ["game/gui.rpy", 227], "gui.dialogue_xpos": ["game/gui.rpy", 131], "gui.namebox_height": ["game/gui.rpy", 117], "config.version": ["game/options.rpy", 26], "gui.history_text_width": ["game/gui.rpy", 354], "gui.radio_button_borders": ["game/gui.rpy", 181], "gui.button_text_size": ["game/gui.rpy", 162], "config.window_hide_transition": ["game/options.rpy", 115], "gui.about": ["game/options.rpy", 32], "gui.vslider_borders": ["game/gui.rpy", 326], "gui.navigation_spacing": ["game/gui.rpy", 261], "gui.choice_button_borders": ["game/gui.rpy", 208], "config.intra_transition": ["game/options.rpy", 82], "gui.slot_button_width": ["game/gui.rpy", 224], "gui.choice_button_text_xalign": ["game/gui.rpy", 211], "gui.show_name": ["game/options.rpy", 21], "gui.slot_button_text_selected_hover_color": ["game/gui.rpy", 231], "gui.title_text_size": ["game/gui.rpy", 82], "gui.slot_button_text_selected_idle_color": ["game/gui.rpy", 230], "gui.button_text_idle_color": ["game/gui.rpy", 165], "gui.notify_frame_borders": ["game/gui.rpy", 294], "gui.navigation_xpos": ["game/gui.rpy", 249], "gui.slot_button_height": ["game/gui.rpy", 225], "gui.history_text_ypos": ["game/gui.rpy", 353], "config.nvl_list_length": ["game/screens.rpy", 1347], "gui.notify_text_size": ["game/gui.rpy", 79], "config.has_voice": ["game/options.rpy", 51], "gui.selected_color": ["game/gui.rpy", 40], "gui.history_text_xpos": ["game/gui.rpy", 352], "gui.skip_frame_borders": ["game/gui.rpy", 291], "gui.confirm_button_text_xalign": ["game/gui.rpy", 185], "gui.text_font": ["game/gui.rpy", 58], "gui.history_name_xpos": ["game/gui.rpy", 346], "gui.main_menu_background": ["game/gui.rpy", 88], "gui.button_width": ["game/gui.rpy", 148], "gui.button_text_selected_color": ["game/gui.rpy", 167], "gui.nvl_thought_ypos": ["game/gui.rpy", 393], "config.save_directory": ["game/options.rpy", 146], "gui.skip_ypos": ["game/gui.rpy", 252], "gui.namebox_tile": ["game/gui.rpy", 125], "gui.nvl_text_width": ["game/gui.rpy", 387], "gui.hover_color": ["game/gui.rpy", 36], "gui.interface_text_color": ["game/gui.rpy", 52], "config.thumbnail_width": ["game/gui.rpy", 234], "gui.idle_small_color": ["game/gui.rpy", 33], "config.exit_transition": ["game/options.rpy", 77], "gui.button_text_xalign": ["game/gui.rpy", 172], "gui.name_xpos": ["game/gui.rpy", 107], "gui.name_text_size": ["game/gui.rpy", 70], "gui.history_text_xalign": ["game/gui.rpy", 355], "gui.file_slot_rows": ["game/gui.rpy", 239], "gui.button_text_insensitive_color": ["game/gui.rpy", 168], "gui.hover_muted_color": ["game/gui.rpy", 48], "gui.scrollbar_size": ["game/gui.rpy", 310], "gui.quick_button_text_selected_color": ["game/gui.rpy", 192], "gui.nvl_thought_width": ["game/gui.rpy", 394], "gui.history_height": ["game/gui.rpy", 342], "gui.text_size": ["game/gui.rpy", 67], "gui.unscrollable": ["game/gui.rpy", 330], "gui.choice_button_text_font": ["game/gui.rpy", 209], "gui.slot_spacing": ["game/gui.rpy", 273], "gui.quick_button_text_idle_color": ["game/gui.rpy", 191], "config.window_show_transition": ["game/options.rpy", 114], "config.narrator_menu": ["game/screens.rpy", 217], "gui.nvl_height": ["game/gui.rpy", 371], "gui.choice_button_height": ["game/gui.rpy", 206], "gui.nvl_spacing": ["game/gui.rpy", 375], "quick_menu": ["game/screens.rpy", 271], "gui.insensitive_color": ["game/gui.rpy", 43], "gui.bar_borders": ["game/gui.rpy", 319], "gui.dialogue_width": ["game/gui.rpy", 135], "gui.scrollbar_tile": ["game/gui.rpy", 315], "gui.quick_button_text_size": ["game/gui.rpy", 190], "config.has_music": ["game/options.rpy", 50], "gui.textbox_height": ["game/gui.rpy", 98], "gui.file_slot_cols": ["game/gui.rpy", 238], "gui.text_color": ["game/gui.rpy", 51], "gui.history_name_xalign": ["game/gui.rpy", 349], "config.name": ["game/options.rpy", 15], "gui.button_text_hover_color": ["game/gui.rpy", 166], "gui.textbox_yalign": ["game/gui.rpy", 102], "gui.nvl_name_width": ["game/gui.rpy", 381], "gui.choice_button_text_idle_color": ["game/gui.rpy", 212], "gui.button_borders": ["game/gui.rpy", 152], "gui.button_tile": ["game/gui.rpy", 156], "gui.choice_button_tile": ["game/gui.rpy", 207], "gui.history_name_width": ["game/gui.rpy", 348], "gui.language": ["game/gui.rpy", 407], "gui.notify_ypos": ["game/gui.rpy", 255], "inventory": ["game/script.rpy", 23], "gui.nvl_name_xalign": ["game/gui.rpy", 382], "gui.label_text_size": ["game/gui.rpy", 76], "gui.nvl_thought_xalign": ["game/gui.rpy", 395], "gui.namebox_width": ["game/gui.rpy", 116], "gui.choice_button_text_insensitive_color": ["game/gui.rpy", 214], "gui.slider_borders": ["game/gui.rpy", 321], "gui.history_allow_tags": ["game/screens.rpy", 922], "gui.name_text_font": ["game/gui.rpy", 61], "gui.name_ypos": ["game/gui.rpy", 108], "gui.nvl_borders": ["game/gui.rpy", 363], "variable": ["game/script.rpy", 26], "gui.pref_button_spacing": ["game/gui.rpy", 267], "config.thumbnail_height": ["game/gui.rpy", 235], "gui.bar_size": ["game/gui.rpy", 309], "gui.choice_button_width": ["game/gui.rpy", 205], "gui.interface_text_font": ["game/gui.rpy", 64], "e": ["game/script.rpy", 6], "gui.history_name_ypos": ["game/gui.rpy", 347], "gui.slot_button_text_xalign": ["game/gui.rpy", 228], "gui.nvl_name_xpos": ["game/gui.rpy", 379], "gui.confirm_frame_borders": ["game/gui.rpy", 288], "gui.slider_size": ["game/gui.rpy", 311], "gui.button_height": ["game/gui.rpy", 149], "gui.check_button_borders": ["game/gui.rpy", 183], "gui.choice_spacing": ["game/gui.rpy", 258], "gui.quick_button_borders": ["game/gui.rpy", 189], "gui.choice_button_text_size": ["game/gui.rpy", 210], "gui.vscrollbar_borders": ["game/gui.rpy", 325]}, "transform": {"delayed_blink": ["game/screens.rpy", 1218], "notify_appear": ["game/screens.rpy", 1267], "hello_t": ["game/script.rpy", 85]}, "label": {"start": ["game/script.rpy", 30], "sidebar_label": ["game/script.rpy", 67]}}, "error": false, "size": [1280, 720], "build": {"documentation_patterns": ["*.html", "*.txt"], "directory_name": "Extension-2.0", "display_name": "Ren'Py Extension Sample for Visual Studio Code", "mac_info_plist": {}, "script_version": true, "include_i686": true, "destination": "Extension-2.0-dists", "renpy": false, "allow_integrated_gpu": true, "include_update": false, "merge": [["linux_i686", "linux"], ["windows_i686", "windows"]], "base_patterns": [["*.py", null], ["*.sh", null], ["*.app/", null], ["*.dll", null], ["*.manifest", null], ["lib/", null], ["renpy/", null], ["update/", null], ["common/", null], ["update/", null], ["old-game/", null], ["icon.ico", null], ["icon.icns", null], ["project.json", null], ["log.txt", null], ["errors.txt", null], ["traceback.txt", null], ["image_cache.txt", null], ["text_overflow.txt", null], ["dialogue.txt", null], ["dialogue.tab", null], ["profile_screen.txt", null], ["files.txt", null], ["memory.txt", null], ["tmp/", null], ["game/saves/", null], ["game/bytecode.rpyb", null], ["archived/", null], ["launcherinfo.py", null], ["android.txt", null], ["game/presplash*.*", ["all"]], [".android.json", ["android"]], ["android-*.png", ["android"]], ["android-*.jpg", ["android"]], ["ouya_icon.png", null], ["ios-presplash.*", ["ios"]], ["ios-launchimage.png", null], ["ios-icon.png", null], ["web-presplash.png", ["web"]], ["web-presplash.jpg", ["web"]], ["web-presplash.webp", ["web"]], ["progressive_download.txt", ["web"]], ["steam_appid.txt", null], ["**~", null], ["**.bak", null], ["**/.**", null], ["**/#**", null], ["**/thumbs.db", null], [".*", null], ["**", ["all"]]], "version": "2.0", "change_icon_i686": true, "archives": [["archive", ["all"]]], "xbit_patterns": ["**.sh", "lib/linux-*/*", "lib/mac-*/*", "**.app/Contents/MacOS/*"], "exclude_empty_directories": true, "renpy_patterns": [["renpy/common/_compat/**", null], ["renpy/common/_roundrect/**", null], ["renpy/common/_outline/**", null], ["renpy/common/_theme**", null], ["**~", null], ["**/#*", null], ["**/.*", null], ["**.old", null], ["**.new", null], ["**.rpa", null], ["**/*.pyc", null], ["**/steam_appid.txt", null], ["renpy.py", ["all"]], ["renpy/", ["all"]], ["renpy/**.py", ["renpy"]], ["renpy/**.pyx", ["renpy"]], ["renpy/**.pyd", ["renpy"]], ["renpy/**.pxi", ["renpy"]], ["renpy/common/", ["all"]], ["renpy/common/_compat/**", ["renpy"]], ["renpy/common/**.rpy", ["renpy"]], ["renpy/common/**.rpym", ["renpy"]], ["renpy/common/_compat/**", ["renpy"]], ["renpy/common/**", ["all"]], ["renpy/**", ["all"]], ["lib/*/renpy", null], ["lib/*/renpy.exe", null], ["lib/*/pythonw.exe", null], ["lib/windows-i686/**", ["windows_i686"]], ["lib/windows-x86_64/**", ["windows"]], ["lib/linux-i686/**", ["linux_i686"]], ["lib/linux-*/**", ["linux"]], ["lib/mac-*/**", ["mac"]], ["lib/**", ["windows", "linux", "mac", "android", "ios"]], ["renpy.sh", ["linux", "mac"]]], "packages": [{"dlc": false, "hidden": false, "description": "PC: Windows and Linux", "formats": ["zip"], "file_lists": ["windows", "linux", "renpy", "all"], "update": true, "name": "pc"}, {"dlc": false, "hidden": false, "description": "Linux", "formats": ["tar.bz2"], "file_lists": ["linux", "renpy", "all"], "update": true, "name": "linux"}, {"dlc": false, "hidden": false, "description": "Macintosh", "formats": ["app-zip", "app-dmg"], "file_lists": ["mac", "renpy", "all"], "update": true, "name": "mac"}, {"dlc": false, "hidden": false, "description": "Windows", "formats": ["zip"], "file_lists": ["windows", "renpy", "all"], "update": true, "name": "win"}, {"dlc": false, "hidden": false, "description": "Windows, Mac, Linux for Markets", "formats": ["zip"], "file_lists": ["windows", "linux", "mac", "renpy", "all"], "update": true, "name": "market"}, {"dlc": false, "hidden": true, "description": "steam", "formats": ["zip"], "file_lists": ["windows", "linux", "mac", "renpy", "all"], "update": true, "name": "steam"}, {"dlc": true, "hidden": true, "description": "android", "formats": ["directory"], "file_lists": ["android", "all"], "update": false, "name": "android"}, {"dlc": true, "hidden": true, "description": "ios", "formats": ["directory"], "file_lists": ["ios", "all"], "update": false, "name": "ios"}, {"dlc": true, "hidden": true, "description": "web", "formats": ["zip"], "file_lists": ["web", "all"], "update": false, "name": "web"}], "executable_name": "Extension"}} \ No newline at end of file diff --git a/examples/game/screens.rpy b/examples/game/screens.rpy new file mode 100644 index 0000000..b629bd3 --- /dev/null +++ b/examples/game/screens.rpy @@ -0,0 +1,1512 @@ +################################################################################ +## Initialization +################################################################################ + +init offset = -1 + + +################################################################################ +## Styles +################################################################################ + +style default: + properties gui.text_properties() + language gui.language + +style input: + properties gui.text_properties("input", accent=True) + adjust_spacing False + +style hyperlink_text: + properties gui.text_properties("hyperlink", accent=True) + hover_underline True + +style gui_text: + properties gui.text_properties("interface") + + +style button: + properties gui.button_properties("button") + +style button_text is gui_text: + properties gui.text_properties("button") + yalign 0.5 + + +style label_text is gui_text: + properties gui.text_properties("label", accent=True) + +style prompt_text is gui_text: + properties gui.text_properties("prompt") + + +style bar: + ysize gui.bar_size + left_bar Frame("gui/bar/left.png", gui.bar_borders, tile=gui.bar_tile) + right_bar Frame("gui/bar/right.png", gui.bar_borders, tile=gui.bar_tile) + +style vbar: + xsize gui.bar_size + top_bar Frame("gui/bar/top.png", gui.vbar_borders, tile=gui.bar_tile) + bottom_bar Frame("gui/bar/bottom.png", gui.vbar_borders, tile=gui.bar_tile) + +style scrollbar: + ysize gui.scrollbar_size + base_bar Frame("gui/scrollbar/horizontal_[prefix_]bar.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) + thumb Frame("gui/scrollbar/horizontal_[prefix_]thumb.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) + +style vscrollbar: + xsize gui.scrollbar_size + base_bar Frame("gui/scrollbar/vertical_[prefix_]bar.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) + thumb Frame("gui/scrollbar/vertical_[prefix_]thumb.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) + +style slider: + ysize gui.slider_size + base_bar Frame("gui/slider/horizontal_[prefix_]bar.png", gui.slider_borders, tile=gui.slider_tile) + thumb "gui/slider/horizontal_[prefix_]thumb.png" + +style vslider: + xsize gui.slider_size + base_bar Frame("gui/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile) + thumb "gui/slider/vertical_[prefix_]thumb.png" + + +style frame: + padding gui.frame_borders.padding + background Frame("gui/frame.png", gui.frame_borders, tile=gui.frame_tile) + + + +################################################################################ +## In-game screens +################################################################################ + + +## Say screen ################################################################## +## +## The say screen is used to display dialogue to the player. It takes two +## parameters, who and what, which are the name of the speaking character and +## the text to be displayed, respectively. (The who parameter can be None if no +## name is given.) +## +## This screen must create a text displayable with id "what", as Ren'Py uses +## this to manage text display. It can also create displayables with id "who" +## and id "window" to apply style properties. +## +## https://www.renpy.org/doc/html/screen_special.html#say + +screen say(who, what): + style_prefix "say" + + window: + id "window" + + if who is not None: + + window: + id "namebox" + style "namebox" + text who id "who" + + text what id "what" + + + ## If there's a side image, display it above the text. Do not display on the + ## phone variant - there's no room. + if not renpy.variant("small"): + add SideImage() xalign 0.0 yalign 1.0 + + +## Make the namebox available for styling through the Character object. +init python: + config.character_id_prefixes.append('namebox') + +style window is default +style say_label is default +style say_dialogue is default +style say_thought is say_dialogue + +style namebox is default +style namebox_label is say_label + + +style window: + xalign 0.5 + xfill True + yalign gui.textbox_yalign + ysize gui.textbox_height + + background Image("gui/textbox.png", xalign=0.5, yalign=1.0) + +style namebox: + xpos gui.name_xpos + xanchor gui.name_xalign + xsize gui.namebox_width + ypos gui.name_ypos + ysize gui.namebox_height + + background Frame("gui/namebox.png", gui.namebox_borders, tile=gui.namebox_tile, xalign=gui.name_xalign) + padding gui.namebox_borders.padding + +style say_label: + properties gui.text_properties("name", accent=True) + xalign gui.name_xalign + yalign 0.5 + +style say_dialogue: + properties gui.text_properties("dialogue") + + xpos gui.dialogue_xpos + xsize gui.dialogue_width + ypos gui.dialogue_ypos + + +## Input screen ################################################################ +## +## This screen is used to display renpy.input. The prompt parameter is used to +## pass a text prompt in. +## +## This screen must create an input displayable with id "input" to accept the +## various input parameters. +## +## https://www.renpy.org/doc/html/screen_special.html#input + +screen input(prompt): + style_prefix "input" + + window: + + vbox: + xalign gui.dialogue_text_xalign + xpos gui.dialogue_xpos + xsize gui.dialogue_width + ypos gui.dialogue_ypos + + text prompt style "input_prompt" + input id "input" + +style input_prompt is default + +style input_prompt: + xalign gui.dialogue_text_xalign + properties gui.text_properties("input_prompt") + +style input: + xalign gui.dialogue_text_xalign + xmaximum gui.dialogue_width + + +## Choice screen ############################################################### +## +## This screen is used to display the in-game choices presented by the menu +## statement. The one parameter, items, is a list of objects, each with caption +## and action fields. +## +## https://www.renpy.org/doc/html/screen_special.html#choice + +screen choice(items): + style_prefix "choice" + + vbox: + for i in items: + textbutton i.caption action i.action + + +## When this is true, menu captions will be spoken by the narrator. When false, +## menu captions will be displayed as empty buttons. +define config.narrator_menu = True + + +style choice_vbox is vbox +style choice_button is button +style choice_button_text is button_text + +style choice_vbox: + xalign 0.5 + ypos 270 + yanchor 0.5 + + spacing gui.choice_spacing + +style choice_button is default: + properties gui.button_properties("choice_button") + +style choice_button_text is default: + properties gui.button_text_properties("choice_button") + + +## Quick Menu screen ########################################################### +## +## The quick menu is displayed in-game to provide easy access to the out-of-game +## menus. + +screen quick_menu(): + + ## Ensure this appears on top of other screens. + zorder 100 + + if quick_menu: + + hbox: + style_prefix "quick" + + xalign 0.5 + yalign 1.0 + + textbutton _("Back") action Rollback() + textbutton _("History") action ShowMenu('history') + textbutton _("Skip") action Skip() alternate Skip(fast=True, confirm=True) + textbutton _("Auto") action Preference("auto-forward", "toggle") + textbutton _("Save") action ShowMenu('save') + textbutton _("Q.Save") action QuickSave() + textbutton _("Q.Load") action QuickLoad() + textbutton _("Prefs") action ShowMenu('preferences') + + +## This code ensures that the quick_menu screen is displayed in-game, whenever +## the player has not explicitly hidden the interface. +init python: + config.overlay_screens.append("quick_menu") + +default quick_menu = True + +style quick_button is default +style quick_button_text is button_text + +style quick_button: + properties gui.button_properties("quick_button") + +style quick_button_text: + properties gui.button_text_properties("quick_button") + + +################################################################################ +## Main and Game Menu Screens +################################################################################ + +## Navigation screen ########################################################### +## +## This screen is included in the main and game menus, and provides navigation +## to other menus, and to start the game. + +screen navigation(): + + vbox: + style_prefix "navigation" + + xpos gui.navigation_xpos + yalign 0.5 + + spacing gui.navigation_spacing + + if main_menu: + + textbutton _("Start") action Start() + + else: + + textbutton _("History") action ShowMenu("history") + + textbutton _("Save") action ShowMenu("save") + + textbutton _("Load") action ShowMenu("load") + + textbutton _("Preferences") action ShowMenu("preferences") + + if _in_replay: + + textbutton _("End Replay") action EndReplay(confirm=True) + + elif not main_menu: + + textbutton _("Main Menu") action MainMenu() + + textbutton _("About") action ShowMenu("about") + + if renpy.variant("pc") or (renpy.variant("web") and not renpy.variant("mobile")): + + ## Help isn't necessary or relevant to mobile devices. + textbutton _("Help") action ShowMenu("help") + + if renpy.variant("pc"): + + ## The quit button is banned on iOS and unnecessary on Android and + ## Web. + textbutton _("Quit") action Quit(confirm=not main_menu) + + +style navigation_button is gui_button +style navigation_button_text is gui_button_text + +style navigation_button: + size_group "navigation" + properties gui.button_properties("navigation_button") + +style navigation_button_text: + properties gui.button_text_properties("navigation_button") + + +## Main Menu screen ############################################################ +## +## Used to display the main menu when Ren'Py starts. +## +## https://www.renpy.org/doc/html/screen_special.html#main-menu + +screen main_menu(): + + ## This ensures that any other menu screen is replaced. + tag menu + + add gui.main_menu_background + + ## This empty frame darkens the main menu. + frame: + style "main_menu_frame" + + ## The use statement includes another screen inside this one. The actual + ## contents of the main menu are in the navigation screen. + use navigation + + if gui.show_name: + + vbox: + style "main_menu_vbox" + + text "[config.name!t]": + style "main_menu_title" + + text "[config.version]": + style "main_menu_version" + + +style main_menu_frame is empty +style main_menu_vbox is vbox +style main_menu_text is gui_text +style main_menu_title is main_menu_text +style main_menu_version is main_menu_text + +style main_menu_frame: + xsize 280 + yfill True + + background "gui/overlay/main_menu.png" + +style main_menu_vbox: + xalign 1.0 + xoffset -20 + xmaximum 800 + yalign 1.0 + yoffset -20 + +style main_menu_text: + properties gui.text_properties("main_menu", accent=True) + +style main_menu_title: + properties gui.text_properties("title") + +style main_menu_version: + properties gui.text_properties("version") + + +## Game Menu screen ############################################################ +## +## This lays out the basic common structure of a game menu screen. It's called +## with the screen title, and displays the background, title, and navigation. +## +## The scroll parameter can be None, or one of "viewport" or "vpgrid". When +## this screen is intended to be used with one or more children, which are +## transcluded (placed) inside it. + +screen game_menu(title, scroll=None, yinitial=0.0): + + style_prefix "game_menu" + + if main_menu: + add gui.main_menu_background + else: + add gui.game_menu_background + + frame: + style "game_menu_outer_frame" + + hbox: + + ## Reserve space for the navigation section. + frame: + style "game_menu_navigation_frame" + + frame: + style "game_menu_content_frame" + + if scroll == "viewport": + + viewport: + yinitial yinitial + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + + side_yfill True + + vbox: + transclude + + elif scroll == "vpgrid": + + vpgrid: + cols 1 + yinitial yinitial + + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + + side_yfill True + + transclude + + else: + + transclude + + use navigation + + textbutton _("Return"): + style "return_button" + + action Return() + + label title + + if main_menu: + key "game_menu" action ShowMenu("main_menu") + + +style game_menu_outer_frame is empty +style game_menu_navigation_frame is empty +style game_menu_content_frame is empty +style game_menu_viewport is gui_viewport +style game_menu_side is gui_side +style game_menu_scrollbar is gui_vscrollbar + +style game_menu_label is gui_label +style game_menu_label_text is gui_label_text + +style return_button is navigation_button +style return_button_text is navigation_button_text + +style game_menu_outer_frame: + bottom_padding 30 + top_padding 120 + + background "gui/overlay/game_menu.png" + +style game_menu_navigation_frame: + xsize 280 + yfill True + +style game_menu_content_frame: + left_margin 40 + right_margin 20 + top_margin 10 + +style game_menu_viewport: + xsize 920 + +style game_menu_vscrollbar: + unscrollable gui.unscrollable + +style game_menu_side: + spacing 10 + +style game_menu_label: + xpos 50 + ysize 120 + +style game_menu_label_text: + size gui.title_text_size + color gui.accent_color + yalign 0.5 + +style return_button: + xpos gui.navigation_xpos + yalign 1.0 + yoffset -30 + + +## About screen ################################################################ +## +## This screen gives credit and copyright information about the game and Ren'Py. +## +## There's nothing special about this screen, and hence it also serves as an +## example of how to make a custom screen. + +screen about(): + + tag menu + + ## This use statement includes the game_menu screen inside this one. The + ## vbox child is then included inside the viewport inside the game_menu + ## screen. + use game_menu(_("About"), scroll="viewport"): + + style_prefix "about" + + vbox: + + label "[config.name!t]" + text _("Version [config.version!t]\n") + + ## gui.about is usually set in options.rpy. + if gui.about: + text "[gui.about!t]\n" + + text _("Made with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]") + + +style about_label is gui_label +style about_label_text is gui_label_text +style about_text is gui_text + +style about_label_text: + size gui.label_text_size + + +## Load and Save screens ####################################################### +## +## These screens are responsible for letting the player save the game and load +## it again. Since they share nearly everything in common, both are implemented +## in terms of a third screen, file_slots. +## +## https://www.renpy.org/doc/html/screen_special.html#save https:// +## www.renpy.org/doc/html/screen_special.html#load + +screen save(): + + tag menu + + use file_slots(_("Save")) + + +screen load(): + + tag menu + + use file_slots(_("Load")) + + +screen file_slots(title): + + default page_name_value = FilePageNameInputValue(pattern=_("Page {}"), auto=_("Automatic saves"), quick=_("Quick saves")) + + use game_menu(title): + + fixed: + + ## This ensures the input will get the enter event before any of the + ## buttons do. + order_reverse True + + ## The page name, which can be edited by clicking on a button. + button: + style "page_label" + + key_events True + xalign 0.5 + action page_name_value.Toggle() + + input: + style "page_label_text" + value page_name_value + + ## The grid of file slots. + grid gui.file_slot_cols gui.file_slot_rows: + style_prefix "slot" + + xalign 0.5 + yalign 0.5 + + spacing gui.slot_spacing + + for i in range(gui.file_slot_cols * gui.file_slot_rows): + + $ slot = i + 1 + + button: + action FileAction(slot) + + has vbox + + add FileScreenshot(slot) xalign 0.5 + + text FileTime(slot, format=_("{#file_time}%A, %B %d %Y, %H:%M"), empty=_("empty slot")): + style "slot_time_text" + + text FileSaveName(slot): + style "slot_name_text" + + key "save_delete" action FileDelete(slot) + + ## Buttons to access other pages. + hbox: + style_prefix "page" + + xalign 0.5 + yalign 1.0 + + spacing gui.page_spacing + + textbutton _("<") action FilePagePrevious() + + if config.has_autosave: + textbutton _("{#auto_page}A") action FilePage("auto") + + if config.has_quicksave: + textbutton _("{#quick_page}Q") action FilePage("quick") + + ## range(1, 10) gives the numbers from 1 to 9. + for page in range(1, 10): + textbutton "[page]" action FilePage(page) + + textbutton _(">") action FilePageNext() + + +style page_label is gui_label +style page_label_text is gui_label_text +style page_button is gui_button +style page_button_text is gui_button_text + +style slot_button is gui_button +style slot_button_text is gui_button_text +style slot_time_text is slot_button_text +style slot_name_text is slot_button_text + +style page_label: + xpadding 50 + ypadding 3 + +style page_label_text: + text_align 0.5 + layout "subtitle" + hover_color gui.hover_color + +style page_button: + properties gui.button_properties("page_button") + +style page_button_text: + properties gui.button_text_properties("page_button") + +style slot_button: + properties gui.button_properties("slot_button") + +style slot_button_text: + properties gui.button_text_properties("slot_button") + + +## Preferences screen ########################################################## +## +## The preferences screen allows the player to configure the game to better suit +## themselves. +## +## https://www.renpy.org/doc/html/screen_special.html#preferences + +screen preferences(): + + tag menu + + use game_menu(_("Preferences"), scroll="viewport"): + + vbox: + + hbox: + box_wrap True + + if renpy.variant("pc") or renpy.variant("web"): + + vbox: + style_prefix "radio" + label _("Display") + textbutton _("Window") action Preference("display", "window") + textbutton _("Fullscreen") action Preference("display", "fullscreen") + + vbox: + style_prefix "radio" + label _("Rollback Side") + textbutton _("Disable") action Preference("rollback side", "disable") + textbutton _("Left") action Preference("rollback side", "left") + textbutton _("Right") action Preference("rollback side", "right") + + vbox: + style_prefix "check" + label _("Skip") + textbutton _("Unseen Text") action Preference("skip", "toggle") + textbutton _("After Choices") action Preference("after choices", "toggle") + textbutton _("Transitions") action InvertSelected(Preference("transitions", "toggle")) + + ## Additional vboxes of type "radio_pref" or "check_pref" can be + ## added here, to add additional creator-defined preferences. + + null height (4 * gui.pref_spacing) + + hbox: + style_prefix "slider" + box_wrap True + + vbox: + + label _("Text Speed") + + bar value Preference("text speed") + + label _("Auto-Forward Time") + + bar value Preference("auto-forward time") + + vbox: + + if config.has_music: + label _("Music Volume") + + hbox: + bar value Preference("music volume") + + if config.has_sound: + + label _("Sound Volume") + + hbox: + bar value Preference("sound volume") + + if config.sample_sound: + textbutton _("Test") action Play("sound", config.sample_sound) + + + if config.has_voice: + label _("Voice Volume") + + hbox: + bar value Preference("voice volume") + + if config.sample_voice: + textbutton _("Test") action Play("voice", config.sample_voice) + + if config.has_music or config.has_sound or config.has_voice: + null height gui.pref_spacing + + textbutton _("Mute All"): + action Preference("all mute", "toggle") + style "mute_all_button" + + +style pref_label is gui_label +style pref_label_text is gui_label_text +style pref_vbox is vbox + +style radio_label is pref_label +style radio_label_text is pref_label_text +style radio_button is gui_button +style radio_button_text is gui_button_text +style radio_vbox is pref_vbox + +style check_label is pref_label +style check_label_text is pref_label_text +style check_button is gui_button +style check_button_text is gui_button_text +style check_vbox is pref_vbox + +style slider_label is pref_label +style slider_label_text is pref_label_text +style slider_slider is gui_slider +style slider_button is gui_button +style slider_button_text is gui_button_text +style slider_pref_vbox is pref_vbox + +style mute_all_button is check_button +style mute_all_button_text is check_button_text + +style pref_label: + top_margin gui.pref_spacing + bottom_margin 2 + +style pref_label_text: + yalign 1.0 + +style pref_vbox: + xsize 225 + +style radio_vbox: + spacing gui.pref_button_spacing + +style radio_button: + properties gui.button_properties("radio_button") + foreground "gui/button/radio_[prefix_]foreground.png" + +style radio_button_text: + properties gui.button_text_properties("radio_button") + +style check_vbox: + spacing gui.pref_button_spacing + +style check_button: + properties gui.button_properties("check_button") + foreground "gui/button/check_[prefix_]foreground.png" + +style check_button_text: + properties gui.button_text_properties("check_button") + +style slider_slider: + xsize 350 + +style slider_button: + properties gui.button_properties("slider_button") + yalign 0.5 + left_margin 10 + +style slider_button_text: + properties gui.button_text_properties("slider_button") + +style slider_vbox: + xsize 450 + + +## History screen ############################################################## +## +## This is a screen that displays the dialogue history to the player. While +## there isn't anything special about this screen, it does have to access the +## dialogue history stored in _history_list. +## +## https://www.renpy.org/doc/html/history.html + +screen history(): + + tag menu + + ## Avoid predicting this screen, as it can be very large. + predict False + + use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0): + + style_prefix "history" + + for h in _history_list: + + window: + + ## This lays things out properly if history_height is None. + has fixed: + yfit True + + if h.who: + + label h.who: + style "history_name" + substitute False + + ## Take the color of the who text from the Character, if + ## set. + if "color" in h.who_args: + text_color h.who_args["color"] + + $ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags) + text what: + substitute False + + if not _history_list: + label _("The dialogue history is empty.") + + +## This determines what tags are allowed to be displayed on the history screen. + +define gui.history_allow_tags = { "alt", "noalt" } + + +style history_window is empty + +style history_name is gui_label +style history_name_text is gui_label_text +style history_text is gui_text + +style history_text is gui_text + +style history_label is gui_label +style history_label_text is gui_label_text + +style history_window: + xfill True + ysize gui.history_height + +style history_name: + xpos gui.history_name_xpos + xanchor gui.history_name_xalign + ypos gui.history_name_ypos + xsize gui.history_name_width + +style history_name_text: + min_width gui.history_name_width + text_align gui.history_name_xalign + +style history_text: + xpos gui.history_text_xpos + ypos gui.history_text_ypos + xanchor gui.history_text_xalign + xsize gui.history_text_width + min_width gui.history_text_width + text_align gui.history_text_xalign + layout ("subtitle" if gui.history_text_xalign else "tex") + +style history_label: + xfill True + +style history_label_text: + xalign 0.5 + + +## Help screen ################################################################# +## +## A screen that gives information about key and mouse bindings. It uses other +## screens (keyboard_help, mouse_help, and gamepad_help) to display the actual +## help. + +screen help(): + + tag menu + + default device = "keyboard" + + use game_menu(_("Help"), scroll="viewport"): + + style_prefix "help" + + vbox: + spacing 15 + + hbox: + + textbutton _("Keyboard") action SetScreenVariable("device", "keyboard") + textbutton _("Mouse") action SetScreenVariable("device", "mouse") + + if GamepadExists(): + textbutton _("Gamepad") action SetScreenVariable("device", "gamepad") + + if device == "keyboard": + use keyboard_help + elif device == "mouse": + use mouse_help + elif device == "gamepad": + use gamepad_help + + +screen keyboard_help(): + + hbox: + label _("Enter") + text _("Advances dialogue and activates the interface.") + + hbox: + label _("Space") + text _("Advances dialogue without selecting choices.") + + hbox: + label _("Arrow Keys") + text _("Navigate the interface.") + + hbox: + label _("Escape") + text _("Accesses the game menu.") + + hbox: + label _("Ctrl") + text _("Skips dialogue while held down.") + + hbox: + label _("Tab") + text _("Toggles dialogue skipping.") + + hbox: + label _("Page Up") + text _("Rolls back to earlier dialogue.") + + hbox: + label _("Page Down") + text _("Rolls forward to later dialogue.") + + hbox: + label "H" + text _("Hides the user interface.") + + hbox: + label "S" + text _("Takes a screenshot.") + + hbox: + label "V" + text _("Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}.") + + +screen mouse_help(): + + hbox: + label _("Left Click") + text _("Advances dialogue and activates the interface.") + + hbox: + label _("Middle Click") + text _("Hides the user interface.") + + hbox: + label _("Right Click") + text _("Accesses the game menu.") + + hbox: + label _("Mouse Wheel Up\nClick Rollback Side") + text _("Rolls back to earlier dialogue.") + + hbox: + label _("Mouse Wheel Down") + text _("Rolls forward to later dialogue.") + + +screen gamepad_help(): + + hbox: + label _("Right Trigger\nA/Bottom Button") + text _("Advances dialogue and activates the interface.") + + hbox: + label _("Left Trigger\nLeft Shoulder") + text _("Rolls back to earlier dialogue.") + + hbox: + label _("Right Shoulder") + text _("Rolls forward to later dialogue.") + + + hbox: + label _("D-Pad, Sticks") + text _("Navigate the interface.") + + hbox: + label _("Start, Guide") + text _("Accesses the game menu.") + + hbox: + label _("Y/Top Button") + text _("Hides the user interface.") + + textbutton _("Calibrate") action GamepadCalibrate() + + +style help_button is gui_button +style help_button_text is gui_button_text +style help_label is gui_label +style help_label_text is gui_label_text +style help_text is gui_text + +style help_button: + properties gui.button_properties("help_button") + xmargin 8 + +style help_button_text: + properties gui.button_text_properties("help_button") + +style help_label: + xsize 250 + right_padding 20 + +style help_label_text: + size gui.text_size + xalign 1.0 + text_align 1.0 + + + +################################################################################ +## Additional screens +################################################################################ + + +## Confirm screen ############################################################## +## +## The confirm screen is called when Ren'Py wants to ask the player a yes or no +## question. +## +## https://www.renpy.org/doc/html/screen_special.html#confirm + +screen confirm(message, yes_action, no_action): + + ## Ensure other screens do not get input while this screen is displayed. + modal True + + zorder 200 + + style_prefix "confirm" + + add "gui/overlay/confirm.png" + + frame: + + vbox: + xalign .5 + yalign .5 + spacing 30 + + label _(message): + style "confirm_prompt" + xalign 0.5 + + hbox: + xalign 0.5 + spacing 100 + + textbutton _("Yes") action yes_action + textbutton _("No") action no_action + + ## Right-click and escape answer "no". + key "game_menu" action no_action + + +style confirm_frame is gui_frame +style confirm_prompt is gui_prompt +style confirm_prompt_text is gui_prompt_text +style confirm_button is gui_medium_button +style confirm_button_text is gui_medium_button_text + +style confirm_frame: + background Frame([ "gui/confirm_frame.png", "gui/frame.png"], gui.confirm_frame_borders, tile=gui.frame_tile) + padding gui.confirm_frame_borders.padding + xalign .5 + yalign .5 + +style confirm_prompt_text: + text_align 0.5 + layout "subtitle" + +style confirm_button: + properties gui.button_properties("confirm_button") + +style confirm_button_text: + properties gui.button_text_properties("confirm_button") + + +## Skip indicator screen ####################################################### +## +## The skip_indicator screen is displayed to indicate that skipping is in +## progress. +## +## https://www.renpy.org/doc/html/screen_special.html#skip-indicator + +screen skip_indicator(): + + zorder 100 + style_prefix "skip" + + frame: + + hbox: + spacing 6 + + text _("Skipping") + + text "▸" at delayed_blink(0.0, 1.0) style "skip_triangle" + text "▸" at delayed_blink(0.2, 1.0) style "skip_triangle" + text "▸" at delayed_blink(0.4, 1.0) style "skip_triangle" + + +## This transform is used to blink the arrows one after another. +transform delayed_blink(delay, cycle): + alpha .5 + + pause delay + + block: + linear .2 alpha 1.0 + pause .2 + linear .2 alpha 0.5 + pause (cycle - .4) + repeat + + +style skip_frame is empty +style skip_text is gui_text +style skip_triangle is skip_text + +style skip_frame: + ypos gui.skip_ypos + background Frame("gui/skip.png", gui.skip_frame_borders, tile=gui.frame_tile) + padding gui.skip_frame_borders.padding + +style skip_text: + size gui.notify_text_size + +style skip_triangle: + ## We have to use a font that has the BLACK RIGHT-POINTING SMALL TRIANGLE + ## glyph in it. + font "DejaVuSans.ttf" + + +## Notify screen ############################################################### +## +## The notify screen is used to show the player a message. (For example, when +## the game is quicksaved or a screenshot has been taken.) +## +## https://www.renpy.org/doc/html/screen_special.html#notify-screen + +screen notify(message): + + zorder 100 + style_prefix "notify" + + frame at notify_appear: + text "[message!tq]" + + timer 3.25 action Hide('notify') + + +transform notify_appear: + on show: + alpha 0 + linear .25 alpha 1.0 + on hide: + linear .5 alpha 0.0 + + +style notify_frame is empty +style notify_text is gui_text + +style notify_frame: + ypos gui.notify_ypos + + background Frame("gui/notify.png", gui.notify_frame_borders, tile=gui.frame_tile) + padding gui.notify_frame_borders.padding + +style notify_text: + properties gui.text_properties("notify") + + +## NVL screen ################################################################## +## +## This screen is used for NVL-mode dialogue and menus. +## +## https://www.renpy.org/doc/html/screen_special.html#nvl + +screen nvl(dialogue, items=None): + + window: + style "nvl_window" + + has vbox: + spacing gui.nvl_spacing + + ## Displays dialogue in either a vpgrid or the vbox. + if gui.nvl_height: + + vpgrid: + cols 1 + yinitial 1.0 + + use nvl_dialogue(dialogue) + + else: + + use nvl_dialogue(dialogue) + + ## Displays the menu, if given. The menu may be displayed incorrectly if + ## config.narrator_menu is set to True, as it is above. + for i in items: + + textbutton i.caption: + action i.action + style "nvl_button" + + add SideImage() xalign 0.0 yalign 1.0 + + +screen nvl_dialogue(dialogue): + + for d in dialogue: + + window: + id d.window_id + + fixed: + yfit gui.nvl_height is None + + if d.who is not None: + + text d.who: + id d.who_id + + text d.what: + id d.what_id + + +## This controls the maximum number of NVL-mode entries that can be displayed at +## once. +define config.nvl_list_length = gui.nvl_list_length + +style nvl_window is default +style nvl_entry is default + +style nvl_label is say_label +style nvl_dialogue is say_dialogue + +style nvl_button is button +style nvl_button_text is button_text + +style nvl_window: + xfill True + yfill True + + background "gui/nvl.png" + padding gui.nvl_borders.padding + +style nvl_entry: + xfill True + ysize gui.nvl_height + +style nvl_label: + xpos gui.nvl_name_xpos + xanchor gui.nvl_name_xalign + ypos gui.nvl_name_ypos + yanchor 0.0 + xsize gui.nvl_name_width + min_width gui.nvl_name_width + text_align gui.nvl_name_xalign + +style nvl_dialogue: + xpos gui.nvl_text_xpos + xanchor gui.nvl_text_xalign + ypos gui.nvl_text_ypos + xsize gui.nvl_text_width + min_width gui.nvl_text_width + text_align gui.nvl_text_xalign + layout ("subtitle" if gui.nvl_text_xalign else "tex") + +style nvl_thought: + xpos gui.nvl_thought_xpos + xanchor gui.nvl_thought_xalign + ypos gui.nvl_thought_ypos + xsize gui.nvl_thought_width + min_width gui.nvl_thought_width + text_align gui.nvl_thought_xalign + layout ("subtitle" if gui.nvl_text_xalign else "tex") + +style nvl_button: + properties gui.button_properties("nvl_button") + xpos gui.nvl_button_xpos + xanchor gui.nvl_button_xalign + +style nvl_button_text: + properties gui.button_text_properties("nvl_button") + + + +################################################################################ +## Mobile Variants +################################################################################ + +style pref_vbox: + variant "medium" + xsize 450 + +## Since a mouse may not be present, we replace the quick menu with a version +## that uses fewer and bigger buttons that are easier to touch. +screen quick_menu(): + variant "touch" + + zorder 100 + + if quick_menu: + + hbox: + style_prefix "quick" + + xalign 0.5 + yalign 1.0 + + textbutton _("Back") action Rollback() + textbutton _("Skip") action Skip() alternate Skip(fast=True, confirm=True) + textbutton _("Auto") action Preference("auto-forward", "toggle") + textbutton _("Menu") action ShowMenu() + +style window: + variant "small" + background "gui/phone/textbox.png" + +style radio_button: + variant "small" + foreground "gui/phone/button/radio_[prefix_]foreground.png" + +style check_button: + variant "small" + foreground "gui/phone/button/check_[prefix_]foreground.png" + +style nvl_window: + variant "small" + background "gui/phone/nvl.png" + +style main_menu_frame: + variant "small" + background "gui/phone/overlay/main_menu.png" + +style game_menu_outer_frame: + variant "small" + background "gui/phone/overlay/game_menu.png" + +style game_menu_navigation_frame: + variant "small" + xsize 340 + +style game_menu_content_frame: + variant "small" + top_margin 0 + +style pref_vbox: + variant "small" + xsize 400 + +style bar: + variant "small" + ysize gui.bar_size + left_bar Frame("gui/phone/bar/left.png", gui.bar_borders, tile=gui.bar_tile) + right_bar Frame("gui/phone/bar/right.png", gui.bar_borders, tile=gui.bar_tile) + +style vbar: + variant "small" + xsize gui.bar_size + top_bar Frame("gui/phone/bar/top.png", gui.vbar_borders, tile=gui.bar_tile) + bottom_bar Frame("gui/phone/bar/bottom.png", gui.vbar_borders, tile=gui.bar_tile) + +style scrollbar: + variant "small" + ysize gui.scrollbar_size + base_bar Frame("gui/phone/scrollbar/horizontal_[prefix_]bar.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) + thumb Frame("gui/phone/scrollbar/horizontal_[prefix_]thumb.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) + +style vscrollbar: + variant "small" + xsize gui.scrollbar_size + base_bar Frame("gui/phone/scrollbar/vertical_[prefix_]bar.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) + thumb Frame("gui/phone/scrollbar/vertical_[prefix_]thumb.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) + +style slider: + variant "small" + ysize gui.slider_size + base_bar Frame("gui/phone/slider/horizontal_[prefix_]bar.png", gui.slider_borders, tile=gui.slider_tile) + thumb "gui/phone/slider/horizontal_[prefix_]thumb.png" + +style vslider: + variant "small" + xsize gui.slider_size + base_bar Frame("gui/phone/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile) + thumb "gui/phone/slider/vertical_[prefix_]thumb.png" + +style slider_vbox: + variant "small" + xsize None + +style slider_slider: + variant "small" + xsize 600 diff --git a/examples/game/script.rpy b/examples/game/script.rpy new file mode 100644 index 0000000..752a8a2 --- /dev/null +++ b/examples/game/script.rpy @@ -0,0 +1,119 @@ +# The script of the game goes in this file. + +# Declare characters used by this game. The color argument colorizes the +# name of the character. + +define e = Character("Eileen", who_color="#c8ffc8") + +# sample colors +image red = Solid("#FF0000FF") +image green = Solid("#00FF00") +image white = Solid("#fff") +image translucent = Solid("#fff8") +image colorTest_solid = Solid(Color((255, 0, 0, 255))) +image colorTest_color = Color(color=(0, 255, 0, 255)) +image colorTest_rgb = Color(rgb=(0.0, 0.0, 1.0), alpha=1.0) + +# image definitions +image bg outside = "bg_outside.png" +image sprite = "sprite.png" + +# default variables +default sidebar = False +default inventory = Inventory() + +# obsolete method +default variable = im.Flip("test.png", horizontal=True) + +# The game starts here. + +label start: + + # Show a background. This uses a placeholder by default, but you can + # add a file (named either "bg room.png" or "bg room.jpg") to the + # images directory to show it. + + scene bg room + + # This shows a character sprite. A placeholder is used, but you can + # replace it by adding a file named "eileen happy.png" to the images + # directory. + + show eileen happy + with dissolve + + # These display lines of dialogue. + + e "You've created a new Ren'Py game." + + # call a label + + call sidebar_label + + if sidebar: + e "We had a sidebar conversation." + else: + e "Continuing on." + + e "Once you add a story, pictures, and music, you can release it to the world!" + + $ sampleFunction("Eileen", 1.0) + + # This ends the game. + + return + + +label sidebar_label: + $ save_name = "Sidebar" + $ sidebar = True + e "This is a sidebar conversation." + e "Take this business card." + $ inventory.add("business card") + + return + +# sample screen code + +screen hello_world(world): + tag example + zorder 1 + modal False + + text "Hello, {}.".format(world) + +transform hello_t: + align (0.7, 0.5) alpha 0.0 + linear 0.5 alpha 1.0 + +screen hello_title(): + text "Hello." at hello_t + text "Hello.": + at transform: + align (0.2, 0.5) alpha 0.0 + linear 0.5 alpha 1.0 + +# sample python code + +init python: + + def sampleFunction(name, delay, position=(0,0)): + """ + This is a sample function. + """ + renpy.pause(delay) + return name + + class Inventory: + """ + This is a fake inventory class. + """ + + def __init__(self): + self.items = [] + + def add(self, item): + """Add an item to the inventory.""" + self.items.append(item); + return + diff --git a/package-lock.json b/package-lock.json index 21df746..815124e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "languague-renpy", - "version": "1.0.7", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -49,9 +49,9 @@ "dev": true }, "@types/vscode": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.54.0.tgz", - "integrity": "sha512-sHHw9HG4bTrnKhLGgmEiOS88OLO/2RQytUN4COX9Djv81zc0FSZsSiYaVyjNidDzUSpXsySKBkZ31lk2/FbdCg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.59.0.tgz", + "integrity": "sha512-Zg38rusx2nU6gy6QdF7v4iqgxNfxzlBlDhrRCjOiPQp+sfaNrp3f9J6OHIhpGNN1oOAca4+9Hq0+8u3jwzPMlQ==", "dev": true }, "agent-base": { diff --git a/package.json b/package.json index 92c825b..2e0a7cd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "languague-renpy", "displayName": "Ren'Py Language", "description": "Adds syntax highlighting and snippets to Ren'Py files in Visual Studio Code", - "version": "1.1.0", + "version": "2.0.0", "publisher": "LuqueDaniel", "license": "MIT", "homepage": "https://github.com/LuqueDaniel/vscode-language-renpy", @@ -60,6 +60,64 @@ "language": "renpy", "path": "./snippets/snippets.json" } + ], + "commands": [ + { + "command": "renpy.refreshNavigationData", + "title": "Refresh Ren'Py Navigation Data" + }, + { + "command": "renpy.compileNavigationData", + "title": "Compile Ren'Py Navigation Data" + } + , + { + "command": "renpy.refreshDiagnostics", + "title": "Refresh Ren'Py diagnostics for the active editor window" + } + ], + "configuration": [ + { + "title": "Ren'Py", + "properties": { + "renpy.excludeRpycFilesFromWorkspace": { + "type": "boolean", + "default": true, + "description": "Exclude *.rpyc (compiled Ren'Py script) files from the workspace folder list. (This will add a .vscode settings file to your workspace.)" + }, + "renpy.watchFoldersForChanges": { + "type": "boolean", + "default": false, + "description": "Watch resource folders, such as images and audio, for file changes. (This may not be fully supported on all platforms.)" + }, + "renpy.showAutomaticImagesInCompletion": { + "type": "boolean", + "default": true, + "description": "Show Automatic Images in the displayable auto-completion list. If not checked (false), only images defined in the script will be shown. If checked (true), both script-defined images and images detected in the images folders will be shown." + }, + "renpy.warnOnObsoleteMethods": { + "type": "boolean", + "default": true, + "description": "Enable obsolete method warnings. If checked (true), obsolete methods (e.g., im.Crop) will be marked with a warning in the editor." + }, + "renpy.warnOnUndefinedPersistents": { + "type": "boolean", + "default": true, + "description": "Enable undefined persistent warnings. If checked (true), persistent variables will be marked with a warning in the editor if they haven't been defaulted/defined." + }, + "renpy.warnOnIndentationAndSpacingIssues": { + "type": "string", + "default": "Error", + "enum": ["Error", "Warning", "Disabled"], + "enumDescriptions": [ + "Display indentation issues as errors", + "Display indentation issues as warnings", + "Ignore indentation issues" + ], + "description": "Enable indentation and inconsistent spacing checks. If set to Error or Warning, tab characters and inconsistent indentation spacing will be marked in the editor. If set to Disabled, indentation issues will be ignored." + } + } + } ] }, "scripts": { diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json new file mode 100644 index 0000000..89519a2 --- /dev/null +++ b/src/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.exclude": { + "**/*.rpyc": true + } +} \ No newline at end of file diff --git a/src/color.ts b/src/color.ts new file mode 100644 index 0000000..334f27f --- /dev/null +++ b/src/color.ts @@ -0,0 +1,239 @@ +// Color conversion methods for Color provider +'use strict'; + +import { Color, ColorInformation, ColorPresentation, Range, TextDocument, TextEdit } from "vscode"; + +/** + * Finds all colors in the given document and returns their ranges and color + * @param document - the TextDocument to search + * @returns - ColorInformation[] - an array that provides a range and color for each match + */ +export function getColorInformation(document: TextDocument): ColorInformation[] { + // find all colors in the document + let colors: ColorInformation[] = []; + for (let i = 0; i < document.lineCount; ++i) { + const line = document.lineAt(i); + if (!line.isEmptyOrWhitespace) { + const text = line.text; + let matches = findColorMatches(text); + if (matches) { + let start = 0; + for (let idx in matches) { + const match = matches[idx]; + const range = new Range(line.lineNumber, text.indexOf(match, start), line.lineNumber, text.indexOf(match, start) + match.length); + if (match.startsWith('"#')) { + const color = convertHtmlToColor(match); + if (color) { + colors.push(new ColorInformation(range, color)); + } + } else if (match.startsWith('rgb')) { + const color = convertRgbColorToColor(match); + if (color) { + colors.push(new ColorInformation(range, color)); + } + } else if (match.startsWith('color')) { + const color = convertRenpyColorToColor(match); + if (color) { + colors.push(new ColorInformation(range, color)); + } + } else if (match.startsWith('Color(')) { + // match is Color((r, g, b[, a])) + const color = convertRenpyColorToColor(match); + if (color) { + // shift the range so the color block is inside the Color() declaration + const shifted = new Range(range.start.line, range.start.character + 6, range.end.line, range.end.character); + colors.push(new ColorInformation(shifted, color)); + } + } + start = text.indexOf(match, start) + 1; + } + } + } + } + return colors; +} + +/** + * Called when the user hovers or taps a color block, allowing the user to replace the original color match with a new color. + * @param color - The newly chosen Color from the color picker + * @param document - The TextDocument + * @param range - The Range of the color match + * @returns - ColorPresentation to replace the color in the document with the new chosen color + */ +export function getColorPresentations(color: Color, document: TextDocument, range: Range): ColorPresentation[] | undefined { + // user hovered/tapped the color block/return the color they picked + let colors: ColorPresentation[] = []; + const line = document.lineAt(range.start.line).text; + const text = line.substring(range.start.character, range.end.character); + const oldRange = new Range(range.start.line, range.start.character, range.start.line, range.start.character + text.length); + + const colR = Math.round(color.red * 255); + const colG = Math.round(color.green * 255); + const colB = Math.round(color.blue * 255); + const colA = Math.round(color.alpha * 255); + + if (text.startsWith('"#')) { + let hex: string = ""; + if (colA === 255 && (text.length === 6 || text.length === 9)) { + hex = convertRgbToHex(colR, colG, colB) || ""; + } else { + hex = convertRgbToHex(colR, colG, colB, colA) || ""; + } + + let hexColorPres = new ColorPresentation(`${hex}`); + hexColorPres.textEdit = new TextEdit(oldRange, `"${hex}"`); + colors.push(hexColorPres); + } else if (text.startsWith('rgb')) { + const rgbTuple = convertColorToRgbTuple(color); + let rgbColorPres = new ColorPresentation(rgbTuple); + rgbColorPres.textEdit = new TextEdit(oldRange, rgbTuple); + colors.push(rgbColorPres); + } else if (text.startsWith('color')) { + const rgbTuple = `color=(${colR}, ${colG}, ${colB}, ${colA})`; + let rgbColorPres = new ColorPresentation(rgbTuple); + rgbColorPres.textEdit = new TextEdit(oldRange, rgbTuple); + colors.push(rgbColorPres); + } else if (text.startsWith('(') && text.endsWith(')')) { + const rgbTuple = `(${colR}, ${colG}, ${colB}, ${colA})`; + let rgbColorPres = new ColorPresentation(rgbTuple); + rgbColorPres.textEdit = new TextEdit(oldRange, rgbTuple); + colors.push(rgbColorPres); + } + return colors; +} + + +/** + * Search the given text for any color references + * @remarks + * This method supports colors in the format `"#rrggbb[aa]"`, `"#rgb[a]"`, `Color((r, g, b[, a]))`, `rgb=(r, g, b)` + * + * @param text - The text to search + * @returns A `RegExpMatchArray` containing any color matches + */ +export function findColorMatches(text: string): RegExpMatchArray | null { + let rx = /("#)[0-9a-fA-F]{8}(")|("#)[0-9a-fA-F]{6}(")|("#)[0-9a-fA-F]{4}(")|("#)[0-9a-fA-F]{3}(")|Color\(\((\d+),\s*(\d+),\s*(\d+)?\)|Color\(\((\d+),\s*(\d+),\s*(\d+),\s*(\d+)?\)|rgb\s*=\s*\(([.\d]+),\s*([.\d]+),\s*([.\d]+)?\)|color\s*=\s*\((\d+),\s*(\d+),\s*(\d+)?\)|color\s*=\s*\((\d+),\s*(\d+),\s*(\d+),\s*(\d+)?\)/ig; + let matches = text.match(rx); + return matches; +} + +/** + * Converts r, g, b, a into a hex color string + * @param r - The red value (0-255) + * @param g - The green value (0-255) + * @param b - The green value (0-255) + * @param a - The alpha value (0-255) [optional] + * @returns The hex color representation (`"#rrggbbaa"`) of the given rgba values + */ +export function convertRgbToHex(r: number, g: number, b: number, a?: number): string | undefined { + if (r > 255 || g > 255 || b > 255) { + return; + } + + if (a === undefined) { + return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); + } else { + return "#" + (256 + r).toString(16).substr(1) + ((1 << 24) + (g << 16) + (b << 8) + a).toString(16).substr(1); + } +} + +/** + * Returns an rgb tuple representation of a color provider Color + * @param color - The color provider Color + * @returns The `rgb=(r, g, b)` tuple representation of the given Color + */ +export function convertColorToRgbTuple(color: Color): string { + const red = color.red.toFixed(4).toString().replace('.0000', '.0').replace('.000', '.0').replace('.00', '.0'); + const green = color.green.toFixed(4).toString().replace('.0000', '.0').replace('.000', '.0').replace('.00', '.0'); + const blue = color.blue.toFixed(4).toString().replace('.0000', '.0').replace('.000', '.0').replace('.00', '.0'); + + const tuple = `rgb=(${red}, ${green}, ${blue})`; + return tuple; +} + +/** + * Returns a Color provider object based on the given html hex color + * @param hex - The html hex representation + * @returns The `Color` provider object + */ +export function convertHtmlToColor(hex: string) : Color | null { + hex = hex.replace(/"/g, ''); + // Add alpha value if not supplied + if (hex.length === 4) { + hex = hex + 'f'; + } else if (hex.length === 7) { + hex = hex + 'ff'; + } + + // Expand shorthand form (e.g. "#03FF") to full form (e.g. "#0033FFFF") + const shorthandRegex = /^#?([A-Fa-f\d])([A-Fa-f\d])([A-Fa-f\d])([A-Fa-f\d])$/i; + hex = hex.replace(shorthandRegex, function(m, r, g, b, a) { + return r + r + g + g + b + b + a + a; + }); + + // Parse #rrggbbaa into Color object + const result = /^#?([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex); + return result ? new Color( + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + parseInt(result[4], 16) / 255 + ) : null; +} + +/** + * Returns a Color provider object based on the given Ren'Py Color tuple + * @remarks + * The Color tuple values should be numeric values between 0 and 255 (e.g., `Color((255, 0, 0, 255))`) + * @param renpy - Renpy `Color` tuple (e.g., `Color((r, g, b, a))`) + * @returns The `Color` provider object + */ +export function convertRenpyColorToColor(renpy: string): Color | null { + try { + const colorTuple = renpy.replace("Color(", "").replace("color", "").replace("=", "").replace(" ", "").replace('(','[').replace(')',']'); + const result = JSON.parse(colorTuple); + if (result.length === 3) { + return new Color( + parseInt(result[0], 16) / 255, + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + 1.0 + ); + } else if (result.length === 4) { + return new Color( + parseInt(result[0], 16) / 255, + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + ); + } + return null; + } catch (error) { + return null; + } +} + +/** + * Returns a Color provider object based on the given Ren'Py rgb tuple + * @remarks + * The rgb tuple values should be numeric values between 0.0 and 1.0 (e.g., `rgb=(1.0, 0.0, 0.0)`) + * @param renpy - Renpy `rgb` tuple (e.g., `rgb=(r, g, b)`) + * @returns The `Color` provider object + */ +export function convertRgbColorToColor(renpy: string): Color | null { + try { + const colorTuple = renpy.replace("rgb", "").replace("=", "").replace(" ", "").replace('(','[').replace(')',']'); + const result = JSON.parse(colorTuple); + if (result.length === 3) { + return new Color( + parseFloat(result[0]), + parseFloat(result[1]), + parseFloat(result[2]), + 1.0 + ); + } + return null; + } catch (error) { + return null; + } +} diff --git a/src/diagnostics.ts b/src/diagnostics.ts new file mode 100644 index 0000000..415e6ba --- /dev/null +++ b/src/diagnostics.ts @@ -0,0 +1,164 @@ +// Diagnostics (warnings and errors) +'use strict'; + +import { Diagnostic, DiagnosticCollection, DiagnosticSeverity, ExtensionContext, Range, TextDocument, window, workspace } from "vscode"; +import { NavigationData } from "./navigationdata"; +import { extractFilename } from "./workspace"; + +// Renpy Store Variables (https://www.renpy.org/doc/html/store_variables.html) +// These variables do not begin with '_' but should be ignored by store warnings because they are pre-defined by Ren'Py +const renpy_store = ['adv','default_mouse','main_menu','menu','mouse_visible','name_only','narrator','say','save_name']; + +/** + * Analyzes the text document for problems. + * @param doc text document to analyze + * @param diagnostics diagnostic collection + */ + export function refreshDiagnostics(doc: TextDocument, diagnosticCollection: DiagnosticCollection): void { + if (doc.languageId !== 'renpy') { + return; + } + + const diagnostics: Diagnostic[] = []; + const config = workspace.getConfiguration('renpy'); + + //Filenames must begin with a letter or number, + //and may not begin with "00", as Ren'Py uses such files for its own purposes. + const filename = extractFilename(doc.uri.path); + if (filename) { + if (!filename.match(/^[a-zA-Z0-9]/) || filename.startsWith('00')) { + let invalidRange = new Range(0, 0, doc.lineCount, 0); + let range = doc.validateRange(invalidRange); + const diagnostic = new Diagnostic(range, "Filenames must begin with a letter or number, but may not begin with '00' as Ren'Py uses such files for its own purposes.", DiagnosticSeverity.Error); + diagnostics.push(diagnostic); + } + } + + // check Document text for errors and warnings + const dataLoaded = NavigationData.data && NavigationData.data.location; + + // check for persistent variables that have not been defined/defaulted + let persistents = []; + if (dataLoaded) { + const gameObjects = NavigationData.data.location['persistent']; + for (let key in gameObjects) { + persistents.push(key); + } + } + + let firstIndentation = 0; + const rxVariableCheck = /^\s*(default|define)\s+([^a-zA-Z\s][a-zA-Z0-9_]*)\s+=/g; + const rxObsoleteCheck = /[\s\(=]+(LiveCrop|LiveComposite|Tooltip|im\.Rotozoom|im\.ImageBase|im\.ramp|im\.Map|im\.Flip|im\.math|im\.expands_bounds|im\.threading|im\.zipfile|im\.Recolor|im\.Color|im\.io|im\.Alpha|im\.Data|im\.Image|im\.Twocolor|im\.MatrixColor|im\.free_memory|im\.Tile|im\.FactorScale|im\.Sepia|im\.Crop|im\.AlphaMask|im\.Blur|im\.tobytes|im\.matrix|im\.Grayscale|ui\.add|ui\.bar|ui\.imagebutton|ui\.input|ui\.key|ui\.label|ui\.null|ui\.text|ui\.textbutton|ui\.timer|ui\.vbar|ui\.hotspot|ui\.hotbar|ui\.spritemanager|ui\.button|ui\.frame|ui\.transform|ui\.window|ui\.drag|ui\.fixed|ui\.grid|ui\.hbox|ui\.side|ui\.vbox|ui\.imagemap|ui\.draggroup)[^a-zA-Z]/g; + const rxPersistentDefines = /^\s*(default|define)\s*persistent.(\w*)\s.*=\s*(.*$)/g; + const rxPersistentCheck = /\s+persistent\.(\w+)[^a-zA-Z]/g; + const rxStoreCheck = /\s+store\.(\w+)[^a-zA-Z]/g; + const rxTabCheck = /^(\t+)/g; + + for (let lineIndex = 0; lineIndex < doc.lineCount; lineIndex++) { + const line = doc.lineAt(lineIndex).text; + //const line = NavigationData.filterStringLiterals(doc.lineAt(lineIndex).text); + + // check line for invalid define/default variable names + // Variables must begin with a letter or number, and may not begin with '_' + + let matches; + while ((matches = rxVariableCheck.exec(line)) !== null) { + const offset = matches.index + matches[0].indexOf(matches[2]); + const range = new Range(lineIndex, offset, lineIndex, offset + matches[2].length); + const diagnostic = new Diagnostic(range, `"${matches[2]}": Variables must begin with a letter (and may contain numbers, letters, or underscores). Variables may not begin with '_' as Ren'Py reserves such variables for its own purposes.`, DiagnosticSeverity.Error); + diagnostics.push(diagnostic); + } + + const checkSpacing: string = config.warnOnIndentationAndSpacingIssues; + if (checkSpacing.toLowerCase() !== "disabled") { + let severity = DiagnosticSeverity.Error; + if (checkSpacing.toLowerCase() === "warning") { + severity = DiagnosticSeverity.Warning; + } + matches = line.match(rxTabCheck); + if (matches) { + const offset = matches.indexOf(matches[0]); + const range = new Range(lineIndex, offset, lineIndex, offset + matches[0].length); + const diagnostic = new Diagnostic(range, `Tab characters are not allowed. Indentation must consist only of spaces in Ren'Py scripts. (4 spaces is strongly recommended.)`, severity); + diagnostics.push(diagnostic); + } else { + const indention = line.length - line.trimLeft().length; + if (indention > 0 && firstIndentation === 0) { + firstIndentation = indention; + } + + if (indention > 0 && indention % firstIndentation !== 0) { + const range = new Range(lineIndex, 0, lineIndex, indention); + const diagnostic = new Diagnostic(range, `Inconsistent spacing detected (${indention} given, expected a multiple of ${firstIndentation}). Indentation must consist only of spaces in Ren'Py scripts. Each indentation level must consist of the same number of spaces. (4 spaces is strongly recommended.)`, severity); + diagnostics.push(diagnostic); + } + } + } + + // check line for obsolete methods + if (config.warnOnObsoleteMethods) { + while ((matches = rxObsoleteCheck.exec(line)) !== null) { + const offset = matches.index + matches[0].indexOf(matches[1]); + const range = new Range(lineIndex, offset, lineIndex, offset + matches[1].length); + const diagnostic = new Diagnostic(range, `"${matches[1]}": This function is obsolete or outdated.`, DiagnosticSeverity.Warning); + diagnostics.push(diagnostic); + } + } + + // check store prefixed variables have been defaulted + const defaults = NavigationData.gameObjects['define_types']; + if (defaults) { + const filtered = Object.keys(defaults).filter(key => defaults[key].define === 'default'); + while ((matches = rxStoreCheck.exec(line)) !== null) { + if (!matches[1].startsWith('_') && !filtered.includes(matches[1]) && !renpy_store.includes(matches[1])) { + const offset = matches.index + matches[0].indexOf(matches[1]); + const range = new Range(lineIndex, offset, lineIndex, offset + matches[1].length); + const diagnostic = new Diagnostic(range, `"store.${matches[1]}": Use of a store variable that has not been defaulted.`, DiagnosticSeverity.Warning); + diagnostics.push(diagnostic); + } + } + } + + // check persistents + if (config.warnOnUndefinedPersistents) { + while ((matches = rxPersistentCheck.exec(line)) !== null) { + if (line.match(rxPersistentDefines)) { + if (!persistents.includes(matches[1])) { + persistents.push(matches[1]); + continue; + } + } + if (!matches[1].startsWith('_') && !persistents.includes(matches[1])) { + const offset = matches.index + matches[0].indexOf(matches[1]); + const range = new Range(lineIndex, offset, lineIndex, offset + matches[1].length); + const diagnostic = new Diagnostic(range, `"persistent.${matches[1]}": This persistent variable has not been defaulted or defined.`, DiagnosticSeverity.Warning); + diagnostics.push(diagnostic); + } + } + } + } + + diagnosticCollection.set(doc.uri, diagnostics); +} + +export function subscribeToDocumentChanges(context: ExtensionContext, diagnostics: DiagnosticCollection): void { + if (window.activeTextEditor) { + refreshDiagnostics(window.activeTextEditor.document, diagnostics); + } + + context.subscriptions.push( + window.onDidChangeActiveTextEditor(editor => { + if (editor) { + refreshDiagnostics(editor.document, diagnostics); + } + }) + ); + + context.subscriptions.push( + workspace.onDidChangeTextDocument(e => refreshDiagnostics(e.document, diagnostics)) + ); + + context.subscriptions.push( + workspace.onDidCloseTextDocument(doc => diagnostics.delete(doc.uri)) + ); +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 580baf8..3468ea9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,14 +2,922 @@ // // Licensed under MIT License. See LICENSE in the project root for license information. 'use strict'; -import { ExtensionContext, languages, IndentAction} from 'vscode'; -export function activate(context: ExtensionContext): any { +import { ExtensionContext, languages, commands, window, IndentAction, TextDocument, Position, CancellationToken, ProviderResult, HoverProvider, Hover, DefinitionProvider, Range, Location, MarkdownString, Uri, workspace, CompletionContext, CompletionItemProvider, CompletionItem, DocumentSymbol, DocumentSymbolProvider, SymbolKind, DocumentColorProvider, ColorInformation, ColorPresentation, Color, Definition, StatusBarItem, StatusBarAlignment, ConfigurationTarget, SignatureHelpProvider, SignatureHelp, SignatureHelpContext, CompletionTriggerKind, ReferenceContext, ReferenceProvider, DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensLegend, FoldingRangeProvider, FoldingContext, FoldingRange } from 'vscode'; +import { getColorInformation, getColorPresentations } from './color'; +import { formatDocumentationAsMarkdown, getArgumentParameterInfo, getCurrentContext, getPyDocsAtLine, Navigation, rangeAsString } from './navigation'; +import { NavigationData } from './navigationdata'; +import { cleanUpPath, extractFilename, findReferenceMatches, getFileWithPath, getWorkspaceFolder, stripWorkspaceFromFile } from './workspace'; +import { refreshDiagnostics, subscribeToDocumentChanges } from './diagnostics'; +import { getSemanticTokens } from './semantics'; +import * as fs from 'fs'; +import * as cp from 'child_process'; + +let myStatusBarItem: StatusBarItem; + +export async function activate(context: ExtensionContext): Promise { + console.log("Ren'Py extension activated"); + languages.setLanguageConfiguration('renpy', { onEnterRules: [{ - // indentation for Ren'Py and Python blocks - beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|label|menu|init|\":|\':|python|).*?:\s*$/, - action: { indentAction: IndentAction.Indent } - }] + // indentation for Ren'Py and Python blocks + beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|label|menu|init|\":|\':|python|).*?:\s*$/, + action: { indentAction: IndentAction.Indent } + }] + }); + + const filepath = getNavigationJsonFilepath(); + if (!fs.existsSync(filepath)) { + return; + } + + // hide rpyc files if the setting is enabled + const config = workspace.getConfiguration('renpy'); + if (config && config.excludeRpycFilesFromWorkspace) { + hideRpycFilesFromWorkspace(); + } + + // hover provider for code tooltip + let hoverProvider = languages.registerHoverProvider('renpy', + new (class implements HoverProvider { + async provideHover( + document: TextDocument, position: Position, token: CancellationToken + ): Promise { + let range = document.getWordRangeAtPosition(position); + if (!range) { + return undefined; + } + + const line = document.lineAt(position).text; + if (!NavigationData.positionIsCleanForCompletion(line, new Position(position.line, range.start.character))) { + return undefined; + } + + let word = document.getText(range); + if (word === 'kwargs' && range.start.character > 2) { + const newRange = new Range(range.start.line, range.start.character - 2, range.end.line, range.end.character); + if (document.getText(newRange) === '**kwargs') { + range = newRange; + word = document.getText(range); + } + } + + // check if the hover is a Semantic Token + const filename = stripWorkspaceFromFile(document.uri.path); + const range_key = rangeAsString(filename, range); + const navigation = NavigationData.gameObjects['semantic'][range_key]; + if (navigation) { + let contents = new MarkdownString(); + if (navigation && navigation instanceof Navigation) { + const args = [{ uri: document.uri, range: navigation.toRange() }]; + const commandUri = Uri.parse(`command:renpy.jumpToFileLocation?${encodeURIComponent(JSON.stringify(args))}`); + contents.appendMarkdown(`(${navigation.source}) **${document.getText(range)}** [${extractFilename(filename)}:${navigation.location}](${commandUri})`); + if (navigation.documentation.length > 0) { + contents.appendMarkdown('\n\n---\n\n'); + contents.appendCodeblock(navigation.documentation); + } + contents.isTrusted = true; + } else { + contents.appendMarkdown(`(${navigation.source}) **${document.getText(range)}**`); + } + return new Hover(contents); + } + + // search the Navigation dump entries + if (range && position.character > 2) { + const prefix = getKeywordPrefix(document, position, range); + if (prefix && prefix !== 'store') { + word = `${prefix}.${word}`; + } + } + + const locations = getNavigationDumpEntries(word); + if (locations) { + let contents = getHoverMarkdownString(locations); + return new Hover(contents); + } + } + })() + ); + context.subscriptions.push(hoverProvider); + + // provider for Go To Definition + let definitionProvider = languages.registerDefinitionProvider('renpy', + new (class implements DefinitionProvider { + provideDefinition( + document: TextDocument, position: Position, token: CancellationToken + ): ProviderResult { + const range = document.getWordRangeAtPosition(position); + if (!range) { + return; + } + + // check if this range is a semantic token + const filename = stripWorkspaceFromFile(document.uri.path); + const range_key = rangeAsString(filename, range); + const navigation = NavigationData.gameObjects['semantic'][range_key]; + if (navigation) { + const uri = Uri.file(getFileWithPath(navigation.filename)); + return new Location(uri, navigation.toRange()); + } + + const line = document.lineAt(position).text; + if (!NavigationData.positionIsCleanForCompletion(line, new Position(position.line, range.start.character))) { + return undefined; + } + + let word = document.getText(range); + if (range && position.character > 2) { + const prefix = getKeywordPrefix(document, position, range); + if (prefix) { + word = `${prefix}.${word}`; + } + } + + let definitions: Definition = []; + const locations = getNavigationDumpEntries(word); + if (locations) { + for (let location of locations) { + if (location.filename !== "") { + const uri = Uri.file(getFileWithPath(location.filename)); + definitions.push(new Location(uri, location.toRange())); + } + } + } + return definitions; + } + })() + ); + context.subscriptions.push(definitionProvider); + + // provider for Outline view + let symbolProvider = languages.registerDocumentSymbolProvider('renpy', + new (class implements DocumentSymbolProvider { + provideDocumentSymbols(document: TextDocument, token: CancellationToken) : ProviderResult { + if (!document) { + return; + } + const uri = Uri.file(document.fileName); + const documentFilename = stripWorkspaceFromFile(uri.path); + let results: DocumentSymbol[] = []; + const range = new Range(0, 0, 0, 0); + for (let type in NavigationData.data.location) { + const category = NavigationData.data.location[type]; + let parentSymbol = new DocumentSymbol(type, "", getDocumentSymbolKind(type, false), range, range); + for (let key in category) { + if (category[key][0] === documentFilename) { + const childRange = new Range(category[key][1] - 1, 0, category[key][1] - 1, 0); + parentSymbol.children.push( + new DocumentSymbol(key, `:${category[key][1]}`, getDocumentSymbolKind(type, true), childRange, childRange) + ); + } + } + if (parentSymbol.children.length > 0) { + if (type === 'class') { + // put class at the top (before callable) + results.unshift(parentSymbol); + } else { + results.push(parentSymbol); + } + } + } + return results; + } + })() + ); + context.subscriptions.push(symbolProvider); + + // provider for Method Signature Help + let signatureProvider = languages.registerSignatureHelpProvider('renpy', + new (class implements SignatureHelpProvider { + provideSignatureHelp( + document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext + ): ProviderResult { + let triggerWord = ""; + + //find the keyword before the last '(' character before the current position + const currentLine = document.lineAt(position.line).text; + const currentLinePrefix = currentLine.substring(0, position.character); + const openParenthesis = currentLinePrefix.lastIndexOf('('); + if (openParenthesis) { + const prevPosition = new Position(position.line, openParenthesis - 1); + const prevRange = document.getWordRangeAtPosition(prevPosition); + if (!prevRange) { + return; + } + triggerWord = document.getText(prevRange); + const prefix = getKeywordPrefix(document, position, prevRange); + if (prefix) { + triggerWord = `${prefix}.${triggerWord}`; + } + } + + // show the documentation for the keyword that triggered this signature + let signatureHelp: SignatureHelp = new SignatureHelp(); + const locations = getNavigationDumpEntries(triggerWord); + if (locations) { + for (let location of locations) { + if (!location.args || location.args.length === 0) { + location = NavigationData.getClassData(location); + } + if (location.args && location.args.length > 0) { + let signature = getArgumentParameterInfo(location, currentLine, position.character); + signatureHelp.activeParameter = 0; + signatureHelp.activeSignature = 0; + signatureHelp.signatures.push(signature); + } + } + } + return signatureHelp; + } + })(), + '(', ',', '=' + ); + context.subscriptions.push(signatureProvider); + + // Completion provider + let completionProvider = languages.registerCompletionItemProvider('renpy', + new (class implements CompletionItemProvider { + provideCompletionItems( + document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext + ): ProviderResult { + if (context.triggerKind === CompletionTriggerKind.TriggerCharacter) { + const line = document.lineAt(position).text; + const linePrefix = line.substr(0, position.character); + if (!NavigationData.positionIsCleanForCompletion(line, position)) { + return; + } + + if (linePrefix.endsWith('renpy.')) { + return NavigationData.renpyAutoComplete; + } else if (linePrefix.endsWith('config.')) { + return NavigationData.configAutoComplete; + } else if (linePrefix.endsWith('gui.')) { + return NavigationData.guiAutoComplete; + } else if (linePrefix.endsWith('renpy.music.')) { + return NavigationData.getAutoCompleteList('renpy.music.'); + } else if (linePrefix.endsWith('renpy.audio.')) { + return NavigationData.getAutoCompleteList('renpy.audio.'); + } else if (linePrefix.endsWith('persistent.')) { + return NavigationData.getAutoCompleteList('persistent.'); + } else if (linePrefix.endsWith('store.')) { + return NavigationData.getAutoCompleteList('store.'); + } else { + const prefixPosition = new Position(position.line, position.character - 1); + const range = document.getWordRangeAtPosition(prefixPosition); + if (range) { + const parentPosition = new Position(position.line, line.length - line.trimLeft().length); + let parent = document.getText(document.getWordRangeAtPosition(parentPosition)); + const kwPrefix = document.getText(range); + const parent_context = getCurrentContext(document, position); + return NavigationData.getAutoCompleteList(kwPrefix, parent, parent_context); + } else if (context.triggerCharacter === '-' || context.triggerCharacter === '@' || context.triggerCharacter === '=') { + const parentPosition = new Position(position.line, line.length - line.trimLeft().length); + let parent = document.getText(document.getWordRangeAtPosition(parentPosition)); + if (parent) { + if (context.triggerCharacter === '=') { + return NavigationData.getAutoCompleteList(parent); + } else { + return NavigationData.getAutoCompleteList(context.triggerCharacter, parent); + } + } + } + } + } + return undefined; + } + })(), + '.', ' ', '@', '-' + ); + context.subscriptions.push(completionProvider); + + // Color Provider + let colorProvider = languages.registerColorProvider('renpy', + new (class implements DocumentColorProvider { + provideDocumentColors( + document: TextDocument, token: CancellationToken + ): ProviderResult { + + return getColorInformation(document); + + } + provideColorPresentations( + color: Color, context: { document: TextDocument, range: Range }, token: CancellationToken + ): ProviderResult { + + return getColorPresentations(color, context.document, context.range); + + } + })() + ); + context.subscriptions.push(colorProvider); + + // Find All References provider + let references = languages.registerReferenceProvider('renpy', + new (class implements ReferenceProvider { + async provideReferences( + document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken + ): Promise { + const range = document.getWordRangeAtPosition(position); + let keyword = document.getText(range); + if (!keyword) { + return; + } + + if (range) { + const prefix = getKeywordPrefix(document, position, range); + if (prefix && prefix !== 'store') { + keyword = `${prefix}.${keyword}`; + } + } + + let references: Location[] = []; + const files = await workspace.findFiles('**/*.rpy'); + if (files && files.length > 0) { + for (let file of files) { + document = await workspace.openTextDocument(file); + const locations = findReferenceMatches(keyword, document); + if (locations) { + for (let l of locations) { + references.push(l); + } + } + } + } + + return references; + } + })() + ); + context.subscriptions.push(references); + + const tokenTypes = ['class', 'parameter', 'variable']; + const tokenModifiers = ['declaration']; + const legend = new SemanticTokensLegend(tokenTypes, tokenModifiers); + + // Semantic Token Provider + let semanticTokens = languages.registerDocumentSemanticTokensProvider('renpy', + new (class implements DocumentSemanticTokensProvider { + //onDidChangeSemanticTokens?: Event | undefined; + provideDocumentSemanticTokens( + document: TextDocument, token: CancellationToken + ): ProviderResult { + if (document.languageId !== 'renpy') { + return; + } else { + return getSemanticTokens(document, legend); + } + } + })(), + legend + ); + context.subscriptions.push(semanticTokens); + + let foldingRange = languages.registerFoldingRangeProvider('renpy', + new (class implements FoldingRangeProvider { + provideFoldingRanges( + document: TextDocument, context: FoldingContext, token: CancellationToken + ): ProviderResult { + let ranges: FoldingRange[] = []; + const rxFolding = /\s*(screen|label|class|layeredimage|def)\s+([a-zA-Z_]+)\((.*)\)\s*:|\s*(screen|label|class|layeredimage|def)\s+([a-zA-Z_]+)\s*:/; + let parent = ''; + let parent_line = 0; + let indent_level = 0; + + for (let i = 0; i < document.lineCount; ++i) { + try { + const line = document.lineAt(i).text; + let end_line = i - 1; + if (parent.length > 0 && line.length > 0 && line.length - line.trimLeft().length <= indent_level && end_line > parent_line) { + while (end_line > 1 && document.lineAt(end_line).text.length === 0) { + end_line--; + } + + if (end_line > parent_line) { + ranges.push(new FoldingRange(parent_line, end_line)); + } + parent = ''; + parent_line = 0; + } + const matches = line.match(rxFolding); + if (matches) { + if (indent_level > 0 && line.length - line.trimLeft().length > indent_level) { + continue; + } + + if (matches[2]) { + parent = matches[2]; + } else { + parent = matches[4]; + } + parent_line = i; + indent_level = line.length - line.trimLeft().length; + } + } catch (error) { + console.log(`foldingProvider error: ${error}`); + } + } + + if (parent.length > 0) { + let end_line = document.lineCount - 1; + if (parent.length > 0 && end_line > parent_line) { + while (end_line > 1 && document.lineAt(end_line).text.length === 0) { + end_line--; + } + + if (end_line > parent_line) { + ranges.push(new FoldingRange(parent_line, end_line)); + } + } + } + + return ranges; + } + })() + ); + context.subscriptions.push(foldingRange); + + // A TextDocument was changed + context.subscriptions.push(workspace.onDidSaveTextDocument( + document => { + if (document.languageId !== 'renpy') { + return; + } + + const filesConfig = workspace.getConfiguration('files'); + if (filesConfig.get('autoSave') === undefined || filesConfig.get('autoSave') !== "off") { + // only trigger document refreshes if file autoSave is off + return; + } + + const config = workspace.getConfiguration('renpy'); + if (config && config.compileOnDocumentSave) { + if (!NavigationData.isCompiling) { + ExecuteRenpyCompile(); + } + } + + if (!NavigationData.isImporting) { + updateStatusBar("$(sync~spin) Initializing Ren'Py static data..."); + const uri = Uri.file(document.fileName); + const filename = stripWorkspaceFromFile(uri.path); + NavigationData.clearScannedDataForFile(filename); + NavigationData.scanDocumentForClasses(filename, document); + updateStatusBar(""); + } + }) + ); + + // diagnostics (errors and warnings) + const diagnostics = languages.createDiagnosticCollection("renpy"); + context.subscriptions.push(diagnostics); + subscribeToDocumentChanges(context, diagnostics); + + // custom command - refresh data + let refreshCommand = commands.registerCommand('renpy.refreshNavigationData', async () => { + updateStatusBar("$(sync~spin) Refreshing Ren'Py navigation data..."); + try { + await NavigationData.refresh(true); + } catch (error) { + console.log(error); + } finally { + updateStatusBar(""); + } + }); + context.subscriptions.push(refreshCommand); + + // custom command - jump to location + let gotoFileLocationCommand = commands.registerCommand('renpy.jumpToFileLocation', (args) => { + const uri = Uri.file(cleanUpPath(args.uri.path)); + const range = new Range(args.range[0].line, args.range[0].character, args.range[0].line, args.range[0].character); + try { + window.showTextDocument(uri, { selection: range }); + } catch (error) { + window.showWarningMessage(`Could not jump to the location (error: ${error})`); + } + }); + context.subscriptions.push(gotoFileLocationCommand); + + // custom command - refresh diagnositcs + let refreshDiagnosticsCommand = commands.registerCommand('renpy.refreshDiagnostics', () => { + if (window.activeTextEditor) { + refreshDiagnostics(window.activeTextEditor.document, diagnostics); + } + }); + context.subscriptions.push(refreshDiagnosticsCommand); + + // custom command - call renpy to compile + let compileCommand = commands.registerCommand('renpy.compileNavigationData', () => { + // check Settings has the path to Ren'Py executable + // Call Ren'Py with the workspace folder and the json-dump argument + const config = workspace.getConfiguration('renpy'); + if (!config) { + window.showErrorMessage("Ren'Py executable location not configured or is invalid."); + } else { + if (isValidExecutable(config.renpyExecutableLocation)) { + // call renpy + const result = ExecuteRenpyCompile(); + if (result) { + window.showInformationMessage("Ren'Py compilation has completed."); + } + } else { + window.showErrorMessage("Ren'Py executable location not configured or is invalid."); + } + } }); + context.subscriptions.push(compileCommand); + + // Custom status bar + myStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 100); + context.subscriptions.push(myStatusBarItem); + myStatusBarItem.text = "$(sync~spin) Initializing Ren'Py static data..."; + myStatusBarItem.show(); + + // Detect file system change to the navigation.json file and trigger a refresh + updateStatusBar("$(sync~spin) Initializing Ren'Py static data..."); + await NavigationData.init(context.extensionPath); + updateStatusBar(""); + + try { + fs.watch(getNavigationJsonFilepath(), async (event, filename) => { + if (filename) { + console.log(`${filename} changed`); + updateStatusBar("$(sync~spin) Refreshing Ren'Py navigation data..."); + try { + await NavigationData.refresh(); + } catch (error) { + console.log(`${Date()}: error refreshing NavigationData: ${error}`); + } finally { + updateStatusBar(""); + } + } + }); + } catch (error) { + console.log(`Watch navigation.json file error: ${error}`); + } + + /* + try { + fs.watchFile(getNavigationJsonFilepath(), async (curr, prev) => { + if (curr.mtime !== prev.mtime) { + console.log(`Navigation.json changed`); + updateStatusBar("$(sync~spin) Refreshing Ren'Py navigation data..."); + try { + await NavigationData.refresh(); + } catch (error) { + console.log(`${Date()}: error refreshing NavigationData: ${error}`); + } finally { + updateStatusBar(""); + } + } + }); + } catch (error) { + console.log(`Watch navigation.json file error: ${error}`); + } + */ + + if (config && config.watchFoldersForChanges) { + const workspaceFolder = getWorkspaceFolder(); + + console.log("Starting Watcher for images folder."); + try { + fs.watch(workspaceFolder + '/game/images', {recursive: true}, async (event, filename) => { + if (filename && event === 'rename') { + console.log(`${filename} created/deleted`); + await NavigationData.scanForImages(); + } + }); + } catch (error) { + console.log(`Watch image folder error: ${error}`); + } + + console.log("Starting Watcher for audio folder."); + try { + fs.watch(workspaceFolder + '/game/audio', {recursive: true}, async (event, filename) => { + if (filename && event === 'rename') { + console.log(`${filename} created/deleted`); + updateStatusBar("$(sync~spin) Refreshing Ren'Py navigation data..."); + await NavigationData.scanForAudio(); + } + }); + } catch (error) { + console.log(`Watch audio folder error: ${error}`); + } + } +} + +export function deactivate() { + console.log("Ren'Py extension deactivating"); + fs.unwatchFile(getNavigationJsonFilepath()); +} + +export function getKeywordPrefix(document: TextDocument, position: Position, range: Range): string | undefined { + if (range.start.character <= 0) { + return; + } + const rangeBefore = new Range(new Position(range.start.line, range.start.character - 1), new Position(range.end.line, range.start.character)); + const spaceBefore = document.getText(rangeBefore); + if (spaceBefore === '.') { + const prevPosition = new Position(position.line, range.start.character - 1); + const prevRange = document.getWordRangeAtPosition(prevPosition); + if (prevRange) { + const prevWord = document.getText(prevRange); + if (prevWord === 'music' || prevWord === 'sound') { + // check for renpy.music.* or renpy.sound.* + const newPrefix = getKeywordPrefix(document, prevPosition, prevRange); + if (newPrefix === 'renpy') { + return `${newPrefix}.${prevWord}`; + } + } + if (prevWord !== 'store') { + return prevWord; + } + } + } + return; +} + +export function getNavigationDumpEntry(keyword: string): Navigation | undefined { + const data = getNavigationDumpEntries(keyword); + if (data) { + return data[0]; + } +} + +export function getNavigationDumpEntries(keyword: string): Navigation[] | undefined { + let entries = NavigationData.find(keyword); + if (!entries || entries.length === 0 && keyword.indexOf('.') > 0) { + entries = NavigationData.resolve(keyword); + } + return entries; +} + +export function getNavigationJsonFilepath() { + const filename = "saves/navigation.json"; + const filepath = getFileWithPath(filename); + return filepath; +} + +export function readNavigationJson() { + try { + const filepath = getNavigationJsonFilepath(); + console.log(`readNavigationJson: ${filepath}`); + let flatData; + try { + flatData = fs.readFileSync(filepath, 'utf-8'); + } catch (error) { + flatData = '{}'; + } + const json = JSON.parse(flatData); + return json; + } catch (error) { + window.showErrorMessage(`readNavigationJson error: ${error}`); + } +} + +export function getDefinitionFromFile(filename: string, line: number): Navigation | undefined { + const filepath = getFileWithPath(filename); + try { + let data = fs.readFileSync(filepath, 'utf-8'); + const lines = data.split('\n'); + if (line <= lines.length) { + let text = lines[line - 1].trim(); + if (text.endsWith(':')) { + text = text.slice(0, -1); + } else if (text.endsWith('(')) { + text = text + ')'; + } else if (text.endsWith('[')) { + text = text + ']'; + } else if (text.endsWith('{')) { + text = text + '}'; + } + + let docs = ""; + if (lines[line].indexOf('"""') >= 0) { + docs = getPyDocsAtLine(lines, line); + } + + let args = ""; + if (text.indexOf('(') > 0) { + args = text.substr(text.indexOf('(')); + args = args.replace('(self, ', '('); + args = args.replace('(self)', '()'); + } + + return new Navigation( + "workspace", + text, + filename, + line, + docs, + args, + "", + 0 + ); + } + } catch (error) { + return undefined; + } +} + +export function getDocumentSymbolKind(category: string, child: boolean) : SymbolKind { + switch (category) { + case "callable": + return child ? SymbolKind.Method : SymbolKind.Module; + case "screen": + return child ? SymbolKind.Struct : SymbolKind.Module; + case "define": + return child ? SymbolKind.Variable : SymbolKind.Module; + case "transform": + return child ? SymbolKind.Variable : SymbolKind.Module; + case "label": + return child ? SymbolKind.String : SymbolKind.Module; + case "class": + return child ? SymbolKind.Class : SymbolKind.Module; + case "displayable": + return child ? SymbolKind.File : SymbolKind.Module; + case "persistent": + return child ? SymbolKind.Constant : SymbolKind.Module; + default: + return SymbolKind.Variable; + } } + +export function getHoverMarkdownString(locations: Navigation[]) : MarkdownString { + let contents = new MarkdownString(); + let index = 0; + + for (let location of locations) + { + index++; + if (index > 1) { + contents.appendMarkdown('\n\n---\n\n'); + if (location.keyword.startsWith('gui.') || location.keyword.startsWith('config.')) { + if (location.documentation && location.documentation.length > 0) { + contents.appendMarkdown(formatDocumentationAsMarkdown(location.documentation)); + continue; + } + } + } + + let source = ""; + if (location.filename && location.filename.length > 0 && location.location >= 0) { + source = `: ${extractFilename(location.filename)}:${location.location}`; + } + + let documentation = location.documentation; + let fileContents = ""; + if (documentation === "" && location.filename !== "" && location.location >= 0) { + const fileData = getDefinitionFromFile(location.filename, location.location); + if (fileData) { + fileContents = fileData?.keyword; + documentation = fileData?.documentation; + } + } + if (location.source === 'class') { + const classData = NavigationData.getClassData(location); + if (classData) { + //fileContents = `${classData.source} ${classData.keyword}${classData.args}`; + documentation = classData.documentation; + } + } + + let type = location.source; + let character = NavigationData.gameObjects['characters'][location.keyword]; + if (character) { + type = 'character'; + } + + if (location.filename && location.filename.length > 0 && location.location >= 0) { + const uri = Uri.file(getFileWithPath(location.filename)); + const args = [{ uri: uri, range: location.toRange() }]; + const commandUri = Uri.parse(`command:renpy.jumpToFileLocation?${encodeURIComponent(JSON.stringify(args))}`); + contents.appendMarkdown(`(${type}) **${location.keyword}** [${source}](${commandUri})`); + } else { + contents.appendMarkdown(`(${type}) **${location.keyword}** ${source}`); + } + contents.isTrusted = true; + + if (character && documentation.length === 0) { + contents.appendMarkdown('\n\n---\n\n'); + contents.appendText(`Character definition for ${character.resolved_name}.`); + contents.appendMarkdown('\n\n---\n\n'); + } + + if (location.args && location.args.length > 0) { + contents.appendMarkdown('\n\n---\n\n'); + const pytype = getPyType(location); + contents.appendCodeblock(`${pytype}${location.keyword}${location.args}`, 'renpy'); + } + + if (fileContents && fileContents.length > 0) { + if (!location.args || location.args.length === 0) { + contents.appendMarkdown('\n\n---\n\n'); + } + contents.appendCodeblock(fileContents, 'renpy'); + } + + if (documentation && documentation.length > 0) { + if (!location.args || location.args.length === 0) { + contents.appendMarkdown('\n\n---\n\n'); + } + documentation = formatDocumentationAsMarkdown(documentation); + const split = documentation.split('::'); + if (split.length > 1) { + contents.appendCodeblock(split[1]); + contents.appendMarkdown('\n\n---\n\n'); + contents.appendMarkdown(split[0]); + } else { + contents.appendMarkdown(split[0]); + } + } + } + + return contents; +} + +function getPyType(location: Navigation) { + let pytype = location.type || ""; + + if (pytype === "var" && (location.keyword.startsWith("gui.") || location.keyword.startsWith("config."))) { + pytype = "define"; + } + else if (pytype === "var" || pytype === "function") { + pytype = "def"; + } + + if (pytype !== "") { + pytype = pytype + " "; + } + return pytype; +} + +function updateStatusBar(text:string) { + if (text === "") { + myStatusBarItem.hide(); + } else { + myStatusBarItem.text = text; + myStatusBarItem.show(); + } +} + +function hideRpycFilesFromWorkspace() { + var jsonDumpFile = getNavigationJsonFilepath(); + if (fs.existsSync(jsonDumpFile)) { + const config = workspace.getConfiguration("files"); + if (config["exclude"]["**/*.rpc"] === undefined) { + config.update("exclude", { "**/*.rpyc": true }, ConfigurationTarget.Workspace); + } + } +} + +function isValidExecutable(renpyExecutableLocation: string): boolean { + if (!renpyExecutableLocation || renpyExecutableLocation === "") { + return false; + } + return fs.existsSync(renpyExecutableLocation); +} + +function ExecuteRenpyCompile(): boolean { + const config = workspace.getConfiguration('renpy'); + const renpy = config.renpyExecutableLocation; + if (isValidExecutable(renpy)) { + let renpyPath = cleanUpPath(Uri.file(renpy).path); + let cwd = renpyPath.substring(0, renpyPath.lastIndexOf("/")); + + let wf = getWorkspaceFolder(); + if (wf.endsWith('/game')) { + wf = wf.substr(0, wf.length - 5); + } + const navData = getNavigationJsonFilepath(); + //const args = `${wf} compile --json-dump ${navData}`; + const args: string[] = [ + `${wf}`, + "compile", + "--json-dump", + `${navData}` + ]; + try { + NavigationData.isCompiling = true; + updateStatusBar("$(sync~spin) Compiling Ren'Py navigation data..."); + let result = cp.spawnSync(renpy, args, { cwd:`${cwd}`, env: { PATH: process.env.PATH }, encoding:"utf-8", windowsHide: true}); + if (result.error) { + console.log(`renpy spawn error: ${result.error}`); + return false; + } + if (result.stderr && result.stderr.length > 0) { + console.log(`renpy spawn stderr: ${result.stderr}`); + return false; + } + } catch (error) { + console.log(`renpy spawn error: ${error}`); + return false; + } finally { + NavigationData.isCompiling = false; + updateStatusBar(""); + } + return true; + } + return false; +} + diff --git a/src/navigation.ts b/src/navigation.ts new file mode 100644 index 0000000..b45d515 --- /dev/null +++ b/src/navigation.ts @@ -0,0 +1,351 @@ +// Navigation classes +'use strict'; + +import { MarkdownString, ParameterInformation, Position, Range, SignatureInformation, TextDocument } from "vscode"; +import { NavigationData } from "./navigationdata"; + +export class Navigation { + source: string; + keyword: string; + filename: string; + location: number; + character: number; + args: string; + type: string; + documentation: string; + + constructor(source: string, keyword: string, filename: string, location: number, documentation: string = "", args: string = "", type: string = "", character: number = 0) { + this.source = source; + this.keyword = keyword; + this.filename = filename; + this.location = location; + this.character = character; + this.documentation = documentation; + this.args = args; + this.type = type; + if (this.documentation) { + this.documentation = this.documentation.replace(/\\\\/g, '\"'); + } + } + + toRange() : Range { + return new Range(this.location - 1, this.character, this.location - 1, this.character + this.keyword.length); + } +} + +export class DataType { + variable: string; + define: string; + baseclass: string; + type: string; + + constructor(variable:string, define:string, baseclass:string) { + this.variable = variable; + this.define = define; + this.baseclass = baseclass; + this.type = ""; + if (baseclass === 'True' || baseclass === 'False') { + this.type = 'boolean'; + } else if (!isNaN(+this.baseclass)) { + this.type = 'number'; + } else if (baseclass === '_' || baseclass.startsWith('"') || baseclass.startsWith('`') || baseclass.startsWith("'")) { + this.type = 'string'; + } else if (baseclass === '[') { + this.type = 'set'; + } else if (baseclass === '{') { + this.type = 'dictionary'; + } + } + + checkTypeArray(type: string, typeArray: string[]) { + if (typeArray.includes(this.baseclass)) { + this.type = type; + } + } +} + +export function getPyDocsAtLine(lines: string[], line: number): string { + let lb: string[] = []; + let index: number = line; + + const margin = lines[index].length - lines[index].trimLeft().length; + let text = lines[index].replace('"""', '').trim(); + if (text.indexOf('"""')) { + text = text.replace('"""', '').trim(); + if (text.length > 0) { + return text; + } + } + + index++; + while (lines[index].indexOf('"""') < 0 && index < lines.length) { + let line = lines[index].trim(); + if (line.length === 0 || lines[index].length - lines[index].trimLeft().length >= margin + 3) { + line = '\n\n' + line; + } + + lb.push(line); + index++; + } + + return lb.join(" ").trim(); +} + +export function getPyDocsFromTextDocumentAtLine(document: TextDocument, line: number): string { + let lb: string[] = []; + let index: number = line; + + let text = document.lineAt(index).text; + if (text.indexOf('"""') < 0) { + return ''; + } + + text = text.replace('"""', '').trim(); + if (text.indexOf('"""') > 0) { + // this is a single line comment + text = text.replace('"""', '').trim(); + if (text.length > 0) { + return text; + } + } + + index++; + while (document.lineAt(index).text.indexOf('"""') < 0 && index < document.lineCount - 1) { + lb.push(document.lineAt(index).text.trim()); + index++; + } + + return lb.join(" ").trim(); +} + +export function getBaseTypeFromDefine(keyword: string, line: string): string | undefined { + const rx = /^(default|define)\s+(\w*)\s*=\s*(\w*)\(/; + line = line.trim(); + const matches = line.match(rx); + if (matches && matches.length >= 4) { + const cls = matches[3]; + return cls; + } + return; +} + +export function getArgumentParameterInfo(location: Navigation, line: string, position: number) : SignatureInformation { + let documentation = new MarkdownString(); + documentation.appendMarkdown(formatDocumentationAsMarkdown(location.documentation)); + let signature = new SignatureInformation(`${location.keyword}${location.args}`, documentation); + + let parsed=''; + let insideQuote = false; + let insideParens = false; + let insideBrackets = false; + let insideBraces = false; + let isFirstParen = true; + + // preprocess fragment + for (let c of line) { + if (c === '"') { + c = "'"; + if (!insideQuote) { + insideQuote = true; + } else { + insideQuote = false; + } + } else if (c === ' ') { + c = "_"; + } else if (c === '(') { + if (!isFirstParen) { + insideParens = true; + } + isFirstParen = false; + } else if (c === '[') { + insideBrackets = true; + } else if (c === '{') { + insideBraces = true; + } else if (c === ')') { + insideParens = false; + } else if (c === ']') { + insideBrackets = false; + } else if (c === '}') { + insideBraces = false; + } else if (c === ',' && (insideQuote || insideParens || insideBrackets || insideBraces)) { + c = ';'; + } + parsed += c; + } + + // split the user's args + const firstParenIndex = parsed.indexOf('('); + let parameterStart = firstParenIndex + 1; + const parsedIndex = parsed.substr(parameterStart); + const split = parsedIndex.split(','); + + const fragment = parsed.substring(0, position); + const fragmentSplit = parsed.substr(fragment.indexOf('(') + 1).split(','); + + // calculate the current parameter + let currentArgument: number = fragmentSplit.length - 1; + let kwarg = ""; + if (split[currentArgument].indexOf('=') > 0) { + const kwargSplit = split[currentArgument].split('='); + kwarg = kwargSplit[0].trim().replace('_',''); + } + + // process the method's args + let parameters: ParameterInformation[] = []; + let args = location.args; + if (args) { + if (args.startsWith('(')) { + args = args.substr(1); + } + if (args.endsWith(')')) { + args = args.substr(0, args.length - 1); + } + + const argsList = args.split(','); + if (argsList) { + let index = 0; + + if (kwarg && kwarg.length > 0) { + if (argsList[argsList.length - 1].trim() === "**kwargs") { + currentArgument = argsList.length - 1; + } + } + + for (let arg of argsList) { + const split = arg.trim().split('='); + let argDocs = "`" + split[0].trim() + "` parameter"; + if (split.length > 1) { + argDocs = argDocs + ' (optional). Default is `' + split[1].trim() + "`."; + } else { + argDocs = argDocs + '.'; + } + + const prm = new ParameterInformation(arg.trim(), new MarkdownString(argDocs)); + parameters.push(prm); + + if (arg.trim().indexOf('=') > 0) { + const kwargSplit = arg.trim().split('='); + if (kwargSplit[0] === kwarg) { + currentArgument = index; + } + } else if (arg.trim() === kwarg) { + currentArgument = index; + } + + index++; + } + } + } + + signature.activeParameter = currentArgument; + signature.parameters = parameters; + + return signature; +} + +export function formatDocumentationAsMarkdown(documentation: string): string { + documentation = documentation.replace(/\\/g, '"'); + documentation = documentation.replace("```", '\n\n```'); + documentation = documentation.replace(/:other:/g, '').replace(/:func:/g, '').replace(/:var:/g, '').replace(/:ref:/g, '').replace(/:class:/g,'').replace(/:tpref:/g,'').replace(/:propref:/g,''); + return documentation.trim(); +} + +export function splitParameters(line: string, trim=false): string[] { + let args: string[] = []; + + let parsed=''; + let insideQuote = false; + let insideParens = false; + let insideBrackets = false; + let insideBraces = false; + + for (let c of line) { + if (c === '"') { + if (!insideQuote) { + insideQuote = true; + } else { + insideQuote = false; + } + } else if (c === '(') { + insideParens = true; + } else if (c === '[') { + insideBrackets = true; + } else if (c === '{') { + insideBraces = true; + } else if (c === ')') { + insideParens = false; + } else if (c === ']') { + insideBrackets = false; + } else if (c === '}') { + insideBraces = false; + } else if (c === ',' && (insideQuote || insideParens || insideBrackets || insideBraces)) { + c = '\uFE50'; + } + parsed += c; + } + + const split = parsed.split(','); + for (let s of split) { + if (trim) { + s = s.trim(); + } + s = s.replace('\uFE50', ','); + if (trim) { + while (s.indexOf(' =') > 0) { + s = s.replace(' =', '='); + } + while (s.indexOf('= ') > 0) { + s = s.replace('= ', '='); + } + } + args.push(s); + } + + return args; +} + +export function getNamedParameter(strings: string[], named: string): string { + const search = `${named}=`; + let value = ''; + const filtered = strings.filter(function (str) { return str.indexOf(search) === 0; }); + if (filtered && filtered.length > 0) { + var split = filtered[0].split('='); + value = stripQuotes(split[1]); + } + return value; +} + +export function stripQuotes(value: string): string { + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substr(1); + value = value.substr(0, value.length - 1); + } else if (value.startsWith("'") && value.endsWith("'")) { + value = value.substr(1); + value = value.substr(0, value.length - 1); + } else if (value.startsWith("`") && value.endsWith("`")) { + value = value.substr(1); + value = value.substr(0, value.length - 1); + } + return value; +} + +export function rangeAsString(filename: string, range: Range): string { + return `${filename}:${range.start.line};${range.start.character}-${range.end.character}`; +} + +export function getCurrentContext(document: TextDocument, position: Position): string | undefined { + const rxParentTypes = /\s*(screen|label|transform|def|class|style)\s+([a-zA-Z0-9_]+)\s*(\((.*)\):|:)/; + + let i = position.line; + while (i >= 0) { + let line = NavigationData.filterStringLiterals(document.lineAt(i).text); + //let indent_level = line.length - line.trimLeft().length; + let match = line.match(rxParentTypes); + if (match) { + return match[1]; + } + i--; + } + + return; +} \ No newline at end of file diff --git a/src/navigationdata.ts b/src/navigationdata.ts new file mode 100644 index 0000000..b3aa50b --- /dev/null +++ b/src/navigationdata.ts @@ -0,0 +1,1344 @@ +'use strict'; + +import { commands, CompletionItem, CompletionItemKind, Position, TextDocument, window, workspace } from "vscode"; +import { getDefinitionFromFile, readNavigationJson } from "./extension"; +import { DataType, getBaseTypeFromDefine, getNamedParameter, getPyDocsFromTextDocumentAtLine, Navigation, splitParameters, stripQuotes } from "./navigation"; +import { cleanUpPath, extractFilenameWithoutExtension, getFileWithPath, stripWorkspaceFromFile } from "./workspace"; + +const filterCharacter = '\u2588'; + +export class NavigationData { + static data: any = {}; + static renpyFunctions: any; + static autoCompleteKeywords: any; + static renpyAutoComplete: CompletionItem[]; + static configAutoComplete: CompletionItem[]; + static guiAutoComplete: CompletionItem[]; + static internalAutoComplete: CompletionItem[]; + static displayableAutoComplete: CompletionItem[]; + static displayableQuotedAutoComplete: CompletionItem[]; + static gameObjects: any = {}; + static isImporting: boolean = false; + static isCompiling: boolean = false; + + static async init(extensionPath: string) { + console.log(`NavigationData init`); + + const data = require(`${extensionPath}/src/renpy.json`); + NavigationData.renpyFunctions = data; + + const kwData = require(`${extensionPath}/src/renpyauto.json`); + NavigationData.autoCompleteKeywords = kwData; + + NavigationData.renpyAutoComplete = []; + for (let key in NavigationData.renpyFunctions.renpy) { + if (key.charAt(0) === key.charAt(0).toUpperCase()) { + NavigationData.renpyAutoComplete.push(new CompletionItem(key.substr(6), CompletionItemKind.Class)); + } else { + NavigationData.renpyAutoComplete.push(new CompletionItem(key.substr(6), CompletionItemKind.Method)); + } + } + + NavigationData.configAutoComplete = []; + for (let key in NavigationData.renpyFunctions.config) { + NavigationData.configAutoComplete.push(new CompletionItem(key.substring(7), CompletionItemKind.Property)); + } + + NavigationData.guiAutoComplete = []; + NavigationData.internalAutoComplete = []; + for (let key in NavigationData.renpyFunctions.internal) { + NavigationData.internalAutoComplete.push(new CompletionItem(key, CompletionItemKind.Class)); + if (key.startsWith('gui.')) { + NavigationData.guiAutoComplete.push(new CompletionItem(key.substr(4), CompletionItemKind.Variable)); + } + } + + await NavigationData.refresh(); + } + + static getDisplayableAutoComplete(quoted: boolean = false): CompletionItem[] { + if (NavigationData.displayableAutoComplete === undefined || NavigationData.displayableAutoComplete.length === 0 + || NavigationData.displayableQuotedAutoComplete === undefined || NavigationData.displayableQuotedAutoComplete.length === 0) { + NavigationData.displayableAutoComplete = []; + NavigationData.displayableQuotedAutoComplete = []; + + const config = workspace.getConfiguration('renpy'); + let showAutoImages = true; + if (config && !config.showAutomaticImagesInCompletion) { + showAutoImages = false; + } + const category = NavigationData.data.location['displayable']; + for (let key in category) { + const display: Displayable = category[key]; + if (display.location < 0 && showAutoImages) { + let ci = new CompletionItem(key, CompletionItemKind.Folder); + ci.sortText = '1' + key; + NavigationData.displayableAutoComplete.push(ci); + + ci = new CompletionItem('"' + key + '"', CompletionItemKind.Folder); + ci.sortText = '1' + key; + NavigationData.displayableQuotedAutoComplete.push(ci); + } else if (display.location >= 0) { + let ci = new CompletionItem(key, CompletionItemKind.Value); + ci.sortText = '0' + key; + NavigationData.displayableAutoComplete.push(ci); + + ci = new CompletionItem('"' + key + '"', CompletionItemKind.Value); + ci.sortText = '0' + key; + NavigationData.displayableQuotedAutoComplete.push(ci); + } + } + + if (!NavigationData.displayableAutoComplete.some(e => e.label === 'black')) { + let black = new CompletionItem("black", CompletionItemKind.Value); + black.sortText = '0black'; + NavigationData.displayableAutoComplete.push(black); + } + if (!NavigationData.displayableQuotedAutoComplete.some(e => e.label === 'black')) { + let black = new CompletionItem('"black"', CompletionItemKind.Value); + black.sortText = '0black'; + NavigationData.displayableQuotedAutoComplete.push(black); + } + } + + if (quoted) { + return NavigationData.displayableQuotedAutoComplete; + } else { + return NavigationData.displayableAutoComplete; + } + } + + static async refresh(interactive:boolean = false): Promise { + console.log(`${Date()}: NavigationData refresh`); + NavigationData.isImporting = true; + try { + NavigationData.data = readNavigationJson(); + if (NavigationData.data.error) { + window.showWarningMessage("Navigation data is empty. Ren'Py could not compile your project. Please check your project can start successfully."); + } + if (NavigationData.data.location === undefined) { + NavigationData.data.location = {}; + } + NavigationData.data.location['class'] = {}; + NavigationData.data.location['displayable'] = {}; + NavigationData.data.location['persistent'] = {}; + NavigationData.data.location['statement'] = {}; + NavigationData.gameObjects['characters'] = {}; + NavigationData.gameObjects['attributes'] = {}; + NavigationData.gameObjects['channels'] = {}; + NavigationData.gameObjects['properties'] = {}; + NavigationData.gameObjects['fields'] = {}; + NavigationData.gameObjects['define_types'] = {}; + NavigationData.gameObjects['semantic'] = {}; + NavigationData.gameObjects['audio'] = {}; + NavigationData.gameObjects['fonts'] = {}; + NavigationData.gameObjects['outlines'] = {}; + NavigationData.displayableAutoComplete = []; + NavigationData.displayableQuotedAutoComplete = []; + var scriptResult = await NavigationData.scanForScriptFiles(); + var imageResult = await NavigationData.scanForImages(); + if (scriptResult && imageResult) { + await NavigationData.getCharacterImageAttributes(); + } + await NavigationData.scanForFonts(); + await NavigationData.scanForAudio(); + commands.executeCommand('renpy.refreshDiagnostics'); + console.log(`NavigationData for ${NavigationData.data.name} v${NavigationData.data.version} loaded`); + if (interactive) { + window.showInformationMessage(`NavigationData for ${NavigationData.data.name} v${NavigationData.data.version} loaded`); + } + } catch (error) { + console.log(`Error loading NavigationData.json: ${error}`); + } finally { + NavigationData.isImporting = false; + } + return true; + } + + static find(keyword: string): Navigation[] | undefined { + let locations: Navigation[] = []; + let split = keyword.split('.'); + + if (split.length === 2 && split[0] === 'persistent') { + const type = NavigationData.data.location[split[0]]; + if (type) { + const location = type[split[1]]; + if (location) { + locations.push(new Navigation( + split[0], + split[1], + location[0], + location[1] + )); + return locations; + } + } + } + + for (let key in NavigationData.data.location) { + let type = NavigationData.data.location[key]; + if (type[keyword]) { + const data = type[keyword]; + if (data instanceof Navigation) { //!data.length + locations.push(data); + } else { + locations.push(new Navigation( + key, + keyword, + data[0], + data[1] + )); + } + } + } + + if (NavigationData.renpyFunctions.internal[keyword]) { + let type = NavigationData.renpyFunctions.internal[keyword]; + locations.push(NavigationData.getNavigationObject("internal", keyword, type)); + } + + if (NavigationData.renpyFunctions.config[keyword]) { + let type = NavigationData.renpyFunctions.config[keyword]; + locations.push(NavigationData.getNavigationObject("config", keyword, type)); + } + + if (NavigationData.renpyFunctions.renpy[keyword]) { + let type = NavigationData.renpyFunctions.renpy[keyword]; + locations.push(NavigationData.getNavigationObject("equivalent", keyword, type)); + } + + if (split.length === 2 && NavigationData.gameObjects['properties'][split[0]]) { + if (NavigationData.gameObjects['properties'][split[0]]) { + const properties = NavigationData.gameObjects['properties'][split[0]]; + const filtered = Object.keys(properties).filter(key => properties[key].keyword === split[1]); + if (filtered && filtered.length > 0) { + let nav: Navigation = Object.create(properties[filtered[0]]); + nav.keyword = `${nav.type}.${nav.keyword}`; + locations.push(nav); + } + } + if (NavigationData.gameObjects['fields'][split[0]]) { + const fields = NavigationData.gameObjects['fields'][split[0]]; + const filtered = Object.keys(fields).filter(key => fields[key].keyword === split[1]); + if (filtered && filtered.length > 0) { + let nav: Navigation = Object.create(fields[filtered[0]]); + nav.keyword = `${nav.type}.${nav.keyword}`; + locations.push(nav); + } + } + } + + if (locations && locations.length > 0) { + return locations; + } + } + + /** + * Returns a list of CompletionItem objects for the previous keyword/statement + * @param prefix - The previous keyword/statement + * @param parent - The parent statement + * @param context - The context of this keyword + * @returns A list of CompletionItem objects + */ + static getAutoCompleteList(prefix: string, parent: string = "", context: string = ""): CompletionItem[] | undefined { + let newlist: CompletionItem[] = []; + const channels = NavigationData.getAudioChannels(); + const characters = Object.keys(NavigationData.gameObjects['characters']); + + if (prefix === 'renpy.music.' || prefix === 'renpy.audio.') { + prefix = prefix.replace('renpy.', '').trim(); + const list = NavigationData.renpyAutoComplete.filter(item => {if (typeof item.label === 'string') {item.label.startsWith(prefix);} }); + for (let item of list) { + if (typeof item.label === 'string') { + newlist.push(new CompletionItem(item.label.replace(prefix, ''), item.kind)); + } + } + return newlist; + } else if (prefix === 'persistent.') { + // get list of persistent definitions + const gameObjects = NavigationData.data.location['persistent']; + for (let key in gameObjects) { + newlist.push(new CompletionItem(key, CompletionItemKind.Value)); + } + return newlist; + } else if (prefix === 'store.') { + // get list of default variables + const defaults = NavigationData.gameObjects['define_types']; + const filtered = Object.keys(defaults).filter(key => defaults[key].define === 'default'); + for (let key of filtered) { + newlist.push(new CompletionItem(key, CompletionItemKind.Variable)); + } + return newlist; + } else if (prefix === 'show') { + // a displayable (plus expression/screen/text) + newlist = NavigationData.getDisplayableAutoComplete(false); + if (!newlist.some(e => e.label === 'expression')) { + let ci = new CompletionItem("expression", CompletionItemKind.Reference); + newlist.push(ci); + } + if (!newlist.some(e => e.label === 'screen')) { + let ci = new CompletionItem("screen", CompletionItemKind.Reference); + newlist.push(ci); + } + if (!newlist.some(e => e.label === 'text')) { + let ci = new CompletionItem("text", CompletionItemKind.Reference); + newlist.push(ci); + } + return newlist; + } else if (prefix === 'hide' || prefix === 'scene') { + // a displayable + return NavigationData.getDisplayableAutoComplete(false); + } else if (prefix === 'jump' || prefix === 'call') { + // get list of labels + newlist.push(new CompletionItem("expression", CompletionItemKind.Keyword)); + const category = NavigationData.data.location['label']; + for (let key in category) { + newlist.push(new CompletionItem(key, CompletionItemKind.Value)); + } + return newlist; + } else if (channels.includes(prefix)) { + // get list of audio definitions + if (parent && parent === 'stop') { + newlist.push(new CompletionItem("fadeout", CompletionItemKind.Keyword)); + } else { + const category = NavigationData.data.location['define']; + let audio = Object.keys(category).filter(key => key.startsWith('audio.')); + for (let key of audio) { + newlist.push(new CompletionItem(key.substr(6), CompletionItemKind.Variable)); + } + } + return newlist; + } else if (prefix === 'with') { + // get list of Transitions + const category = NavigationData.renpyFunctions.internal; + newlist.push(new CompletionItem("None", CompletionItemKind.Value)); + // get the Renpy default transitions and Transition classes + let transitions = Object.keys(category).filter(key => category[key][0] === 'transitions'); + for (let key of transitions) { + const detail = category[key][2]; + newlist.push(new CompletionItem({label:key, detail:detail}, CompletionItemKind.Value)); + } + // get the user define transitions + const defines = NavigationData.gameObjects['define_types']; + let deftransitions = Object.keys(defines).filter(key => defines[key].type === 'transitions'); + for (let key of deftransitions) { + newlist.push(new CompletionItem(key, CompletionItemKind.Value)); + } + + } else if (prefix === 'at') { + // get list of Transforms + const category = NavigationData.data.location['transform']; + for (let key in category) { + newlist.push(new CompletionItem(key, CompletionItemKind.Value)); + } + } else if (prefix === 'action' || prefix === 'alternate') { + // get list of screen Actions + const category = NavigationData.renpyFunctions.internal; + let transitions = Object.keys(category).filter(key => category[key][4] === 'Action'); + for (let key of transitions) { + const detail = category[key][2]; + newlist.push(new CompletionItem({label:key, detail:detail}, CompletionItemKind.Value)); + } + } else if (NavigationData.isClass(prefix)) { + const className = NavigationData.isClass(prefix); + if (className) { + return NavigationData.getClassAutoComplete(className); + } + } else if (context === 'label' && characters.includes(parent)) { + // get attributes for character if we're in the context of a label + const category = NavigationData.gameObjects['attributes'][parent]; + if (category) { + for (let key of category) { + newlist.push(new CompletionItem(key, CompletionItemKind.Value)); + } + } + } else { + return NavigationData.getAutoCompleteKeywords(prefix, parent, context); + } + + return newlist; + } + + /** + * Returns a list of CompletionItem objects for the given keyword + * @param keyword - The keyword to search + * @param parent - The keyword's parent keyword + * @param context - The context of the keyword + * @returns A list of CompletionItem objects + */ + static getAutoCompleteKeywords(keyword: string, parent: string, context: string): CompletionItem[] { + let newlist: CompletionItem[] = []; + let enumerations; + if (context) { + enumerations = NavigationData.autoCompleteKeywords[`${context}.${keyword}`]; + } + if (!enumerations) { + enumerations = NavigationData.autoCompleteKeywords[keyword]; + } + + if (enumerations) { + const split = enumerations.split('|'); + for (let index in split) { + if (split[index].startsWith('{')) { + let gameDataKey = split[index].replace('{','').replace('}',''); + let quoted = false; + let args = 0; + if (gameDataKey.indexOf('!') > 0) { + const split = gameDataKey.split('!'); + gameDataKey = split[0]; + quoted = split[1] === 'q'; + if (isNormalInteger(split[1])) { + args = Math.floor(Number(split[1])); + } + } + + if (gameDataKey === 'action') { + // get list of screen Actions + const category = NavigationData.renpyFunctions.internal; + let transitions = Object.keys(category).filter(key => category[key][4] === 'Action'); + if (transitions) { + for (let key of transitions) { + const detail = category[key][2]; + newlist.push(new CompletionItem({label:key, detail:detail}, CompletionItemKind.Value)); + } + } + continue; + } else if (gameDataKey === 'function') { + // get list of callable functions + const callables = NavigationData.data.location['callable']; + if (callables) { + const filtered = Object.keys(callables).filter(key => key.indexOf('.') === -1); + for (let key of filtered) { + const callable = callables[key]; + const navigation = getDefinitionFromFile(callable[0], callable[1]); + let detail = ''; + if (navigation) { + detail = navigation.args; + if (args > 0) { + if (navigation.args.split(',').length !== args) { + continue; + } + } + } + newlist.push(new CompletionItem({label:key, detail:detail}, CompletionItemKind.Function)); + } + } + } else if (gameDataKey === 'layer') { + const layers = NavigationData.getLayerConfiguration(quoted); + if (layers) { + for (let key of layers) { + newlist.push(key); + } + } + continue; + } else if (gameDataKey === 'screens') { + // get list of screens + const category = NavigationData.data.location['screen']; + for (let key in category) { + newlist.push(new CompletionItem(key, CompletionItemKind.Variable)); + } + return newlist; + } else if (gameDataKey === 'outlines') { + let gameObjects = []; + if (NavigationData.data.location[gameDataKey]) { + gameObjects = NavigationData.data.location[gameDataKey]['array'] || []; + if (gameObjects) { + for (let key of gameObjects) { + let ci = new CompletionItem(key, CompletionItemKind.Value); + ci.sortText = '1' + key; + newlist.push(ci); + } + } else { + gameObjects = []; + } + } + + if (!gameObjects.includes('[(1, "#000000", 0, 0)]')) { + newlist.push(new CompletionItem('[(1, "#000000", 0, 0)]', CompletionItemKind.Value)); + } + if (!gameObjects.includes('[(1, "#000000", 1, 1)]')) { + newlist.push(new CompletionItem('[(1, "#000000", 1, 1)]', CompletionItemKind.Value)); + } + newlist.push(new CompletionItem('[(absolute(1), "#000000", absolute(1), absolute(1))]', CompletionItemKind.Value)); + newlist.push(new CompletionItem('[(size, color, xoffset, yoffset)]', CompletionItemKind.Value)); + continue; + } else if (gameDataKey === 'displayable') { + const display = NavigationData.getDisplayableAutoComplete(quoted); + if (display) { + for (let ci of display) { + newlist.push(ci); + } + } + continue; + } else if (gameDataKey === 'audio') { + // get defined audio variables + const category = NavigationData.data.location['define']; + let audio = Object.keys(category).filter(key => key.startsWith('audio.')); + for (let key of audio) { + key = key; + let ci = new CompletionItem(key, CompletionItemKind.Variable); + ci.sortText = "0" + key; + newlist.push(ci); + } + // get auto detected audio variables + const gameObjects = NavigationData.gameObjects['audio']; + if (gameObjects) { + for (let key in gameObjects) { + if (!newlist.some(e => e.label === key)) { + var obj = gameObjects[key]; + let ci: CompletionItem; + if (obj.startsWith('"')) { + ci = new CompletionItem(gameObjects[key], CompletionItemKind.Folder); + ci.sortText = "2" + key; + } else { + ci = new CompletionItem(gameObjects[key], CompletionItemKind.Value); + ci.sortText = "1" + key; + } + newlist.push(ci); + } + } + } + continue; + } + + const gameObjects = NavigationData.gameObjects[gameDataKey]; + if (gameObjects) { + for (let key in gameObjects) { + if (quoted) { + key = '"' + key + '"'; + } + let ci = new CompletionItem(key, CompletionItemKind.Value); + ci.sortText = quoted ? '2' + key : '1' + key; + newlist.push(ci); + } + } else { + const navObjects = NavigationData.data.location[gameDataKey]; + if (navObjects) { + for (let key in navObjects) { + if (quoted) { + key = '"' + key + '"'; + } + let ci = new CompletionItem(key, CompletionItemKind.Value); + ci.sortText = quoted ? '2' + key : '1' + key; + newlist.push(ci); + } + } + } + } else { + let ci = new CompletionItem(split[index], CompletionItemKind.Constant); + ci.sortText = '0' + split[index]; + newlist.push(ci); + } + } + } + + if (newlist.length === 0 && parent.length > 0) { + return NavigationData.getAutoCompleteKeywords(`parent.${parent}`, '', ''); + } + + return newlist; + } + + static getAudioChannels(): string[] { + let newlist: string[] = []; + const enumerations = NavigationData.autoCompleteKeywords['play']; + if (enumerations) { + const split = enumerations.split('|'); + for (let index in split) { + if (split[index].startsWith('{')) { + const gameDataKey = split[index].replace('{','').replace('}',''); + const gameObjects = NavigationData.gameObjects[gameDataKey]; + for (let key in gameObjects) { + newlist.push(key); + } + } else { + newlist.push(split[index]); + } + } + } + return newlist; + } + + /** + * Determines if the line position is valid for completion or hover events + * @remarks + * This prevents hover/completion from triggering over keywords inside a comment or inside a string literal + * + * @param line The current line in the TextDocument + * @param position The current position in the TextDocument + * @returns `True` if the position is valid for completion or hover events + */ + static positionIsCleanForCompletion(line: string, position: Position): boolean { + const prefix = line.substr(0, position.character); + // ignore hover/completion if we're in a comment + if (prefix.indexOf('#') >= 0) { + return false; + } + + // ignore completion if we're inside a quoted strings + const filtered = NavigationData.filterStringLiterals(line); + if (filtered.substr(position.character, 1) === filterCharacter) { + return false; + } + + return true; + } + + static filterStringLiterals(line: string): string { + let parsed=''; + let insideSingleQuote = false; + let insideDoubleQuote = false; + let insideBracket = false; + let escaped = false; + + for (let c of line) { + if (c === '\\') { + if (!insideBracket && (insideSingleQuote || insideDoubleQuote)) { + escaped = true; + parsed += filterCharacter; + } else { + parsed += c; + } + continue; + } else if (!escaped && c === '"') { + if (!insideSingleQuote) { + insideDoubleQuote = !insideDoubleQuote; + if (!insideDoubleQuote) { + parsed += filterCharacter; + continue; + } + } + } else if (!escaped && c === "'") { + if (!insideDoubleQuote) { + insideSingleQuote = !insideSingleQuote; + if (!insideSingleQuote) { + parsed += filterCharacter; + continue; + } + } + } else if (c === "[") { + if (insideBracket) { + // a second bracket + insideBracket = false; + } else { + insideBracket = true; + } + } else if (c === "]") { + insideBracket = false; + parsed += c; + continue; + } else if (c === '#') { + if (!insideSingleQuote && !insideDoubleQuote) { + parsed += c; + parsed += filterCharacter.repeat(line.length - parsed.length); + return parsed; + } + } + + if (!insideBracket && (insideSingleQuote || insideDoubleQuote)) { + parsed += filterCharacter; + } else { + parsed += c; + } + + escaped = false; + } + + return parsed; + } + + /** + * Returns whether or not the given keyword is the name of a Class + * @param keyword - The keyword to validate + * @returns `True` if the keyword is a Class definition + */ + static isClass(keyword: string): string | undefined { + const classes = NavigationData.data.location['class']; + if (keyword in classes) { + return keyword; + } else { + const init = keyword + ".__init__"; + const base = NavigationData.resolve(init); + if (base && base[0].keyword.indexOf('.') > 0) { + return base[0].keyword.split('.')[0]; + } + } + + return; + } + + static getClassAutoComplete(keyword: string): CompletionItem[] | undefined { + let newlist: CompletionItem[] = []; + const prefix = keyword + '.'; + + // get the callables + const callables = NavigationData.data.location['callable']; + if (callables) { + const filtered = Object.keys(callables).filter(key => key.indexOf(prefix) === 0); + if (filtered) { + for (let key in filtered) { + const label = filtered[key].substr(prefix.length); + newlist.push(new CompletionItem(label, CompletionItemKind.Method)); + } + } + } + + // get any properties + const properties = NavigationData.gameObjects['properties'][keyword]; + if (properties) { + for (let p of properties) { + newlist.push(new CompletionItem(p.keyword, CompletionItemKind.Property)); + } + } + + // get any fields + const fields = NavigationData.gameObjects['fields'][keyword]; + if (fields) { + for (let p of fields) { + newlist.push(new CompletionItem(p.keyword, CompletionItemKind.Field)); + } + } + + return newlist; + } + + static resolve(keyword: string): Navigation[] | undefined { + const split = keyword.split('.'); + if (split.length < 2) { + return; + } + + const entries = NavigationData.find(split[0]); + if (entries && entries.length === 1) { + const entry = entries[0]; + if (entry && entry.filename && entry.filename !== "") { + const line = getDefinitionFromFile(entry.filename, entry.location); + if (line) { + const base = getBaseTypeFromDefine(keyword, line.keyword); + if (base) { + return NavigationData.find(`${base}.${split[1]}`); + } + } + } + } + } + + static getClassData(location: Navigation): Navigation { + const def = getDefinitionFromFile(location.filename, location.location); + if (def) { + if (!location.documentation || location.documentation.length === 0) { + location.documentation = def.documentation; + } + + if (!location.args || location.args.length === 0) { + if (location.source === 'class' && def.args.length === 0) { + const init = NavigationData.data.location['callable'][`${location.keyword}.__init__`]; + if (init) { + const initData = getDefinitionFromFile(init[0], init[1]); + if (initData) { + let args = initData.args.replace('(self, ', '('); + args = args.replace('(self)', '()'); + location.args = args; + } + } + } else { + location.args = def.args; + } + } + } + return location; + } + + static getClassProperties(keyword: string, document: TextDocument, location: number): Navigation[] { + // find @property attributes in the class definition + let props: Navigation[] = []; + const rxDef = /^def\s+(\w*).*:/; + const filename = stripWorkspaceFromFile(document.uri.path); + try { + let index = location; + let line = document.lineAt(index - 1).text; + const spacing = line.indexOf('class ' + keyword); + let finished = false; + while (!finished && index < document.lineCount) { + let line = document.lineAt(index).text.replace('\r',''); + if (line.length > 0 && (line.length - line.trimLeft().length <= spacing)) { + finished = true; + break; + } + line = line.trim(); + + const matches = line.match(rxDef); + if (matches) { + // if document edits moved the callables, then update the location + let define_keyword = `${keyword}.${matches[1]}`; + let current_def = NavigationData.data.location['callable'][define_keyword]; + if (current_def && current_def[0] === filename) { + if (current_def[1] !== index + 1) { + current_def[1] = index + 1; + } + } + } + + if (line.startsWith('@property')) { + const matches = document.lineAt(index + 1).text.trim().match(rxDef); + if (matches) { + const pyDocs = getPyDocsFromTextDocumentAtLine(document, index + 2); + const documentation = `${pyDocs}::class ${keyword}:\n @property\n def ${matches[1]}(self):`; + const nav = new Navigation('property', matches[1], filename, index + 2, documentation, "", keyword, matches.index); + props.push(nav); + } + index++; + } + index++; + } + return props; + } catch (error) { + return props; + } + } + + static getClassFields(keyword: string, document: TextDocument, location: number): Navigation[] { + // find fields in the __init__ method of the class definition + let props: Navigation[] = []; + if (!document || !document.lineCount) { + return props; + } + + const filename = stripWorkspaceFromFile(document.uri.path); + const rxDef = /^\s*(self)\.(\w*)\s*=\s*(.*$)/; + try { + let index = location; + let line = document.lineAt(index - 1).text; + if (!line) { + return props; + } + + const spacing = line.indexOf('def '); + if (spacing < 0) { + return props; + } + let finished = false; + while (!finished && index < document.lineCount) { + let line = document.lineAt(index).text.replace('\r',''); + if (line.length > 0 && (line.length - line.trimLeft().length <= spacing)) { + finished = true; + break; + } + line = line.trim(); + const matches = line.match(rxDef); + if (matches) { + const pyDocs = getPyDocsFromTextDocumentAtLine(document, index); + const documentation = `${pyDocs}::class ${keyword}:\n def __init__(self):\n self.${matches[2]} = ...`; + const nav = new Navigation('field', matches[2], filename, index + 1, documentation, "", keyword, matches.index); + if (!props.includes(nav)) { + props.push(nav); + } + } + index++; + } + return props; + } catch (error) { + return props; + } + } + + static getNavigationObject(source: string, keyword: string, array: any) : Navigation { + /* + 0 basefile, + 1 kind, + 2 args, + 3 class, + 4 type, + 5 docs + */ + + if (typeof array[0] === 'string' && (array[0] === 'obsolete' || array[0] === 'transitions')) { + source = array[0]; + } else if (typeof array[4] === 'string' && (array[4] === 'Action')) { + source = array[4].toLowerCase(); + } else if (source === 'equivalent') { + source = array[0]; + } + + return new Navigation( + source, + keyword, + "", //filename + 0, //location + array[5], //documentation + array[2], //args + array[4] //type + ); + } + + /** + * Scans the workspace for any supported script files and searches them for class definitions + */ + static async scanForScriptFiles(): Promise { + const files = await workspace.findFiles('**/*.rpy'); + if (files && files.length > 0) { + for (let file of files) { + let document = await workspace.openTextDocument(file); + const filename = stripWorkspaceFromFile(cleanUpPath(file.path)); + NavigationData.scanDocumentForClasses(filename, document); + } + } + return true; + } + + /** + * Scans the workspace for any supported images + */ + static async scanForImages(): Promise { + const files = await workspace.findFiles('**/*.{png,jpg,jpeg,webp}'); + if (files && files.length > 0) { + for (let file of files) { + const filename = stripWorkspaceFromFile(file.path); + const displayable = NavigationData.getPythonName(filename); + if (displayable) { + const key = extractFilenameWithoutExtension(filename); + if (key) { + const path = stripWorkspaceFromFile(cleanUpPath(file.path)); + const displayable = new Displayable(key, 'image', '', path, -1); + NavigationData.data.location['displayable'][key] = displayable; + } + } + } + } + return true; + } + + /** + * Scans the workspace for any supported fonts + */ + static async scanForFonts(): Promise { + const files = await workspace.findFiles('**/*.{ttf,otf,ttc}'); + if (files && files.length > 0) { + for (let file of files) { + const filename = stripWorkspaceFromFile(file.path); + let key = filename.replace(/^(game\/)/, ''); + if (key.endsWith('.ttc')) { + key = "0@" + key; + } + NavigationData.gameObjects['fonts'][key] = key; + } + } + return true; + } + + /** + * Scans the workspace for any supported audio files + */ + static async scanForAudio(): Promise { + const files = await workspace.findFiles('**/*.{opus,ogg,mp3,wav}'); + if (files && files.length > 0) { + for (let file of files) { + const filename = stripWorkspaceFromFile(file.path); + + // add the path relative to the workspace/game folder + const key = filename.replace(/^(game\/)/, ''); + NavigationData.gameObjects['audio'][key] = '"' + key + '"'; + + // if this file is in the game/audio folder and can be represented + // as a python variable, add it to the audio namespace + if (filename.startsWith('game/audio/')) { + const pythonName = NavigationData.getPythonName(filename); + if (pythonName) { + const key = "audio." + extractFilenameWithoutExtension(filename); + if (key && !NavigationData.gameObjects['audio'][key]) { + NavigationData.gameObjects['audio'][key] = key; + } + } + } + } + } + return true; + } + + /** + * Deletes any scanned data related to the given filename + * @param filename - The filename for the script file + */ + static clearScannedDataForFile(filename: string) { + if (NavigationData.data.location === undefined) { + NavigationData.data.location = {}; + } + const categories = ['class','displayable','persistent','properties','fields']; + for (let cat in categories) { + let category = NavigationData.data.location[cat]; + for (let key in category) { + if (category[key][0] === filename) { + delete category[key]; + } + } + } + } + + /** + * Scans the given TextDocument looking for Ren'Py commands and definitions + * @param filename - The filename for the script file + * @param document - The TextDocument for the script file + */ + static scanDocumentForClasses(filename: string, document: TextDocument) { + const rxClass = /^\s*class\s+(\w*).*:/; + const rxDisplayable = /^\s*(image)\s+([^=.]*)\s*[=]\s*(.+)|(layeredimage)\s+(.+):/; + const rxChannels = /.*renpy.(audio|music).register_channel\s*\("(\w+)"(.*)\)/; + const rxPersistent = /^\s*(default|define)\s*persistent.(\w*)\s.*=\s*(.*$)/; + const rxDefaultDefine = /^(default|define)\s+(\w*)\s*=\s*([\w'"`\[{]*)/; + const rxCharacters = /^\s*(define)\s*(\w*)\s*=\s*(Character|DynamicCharacter)\s*\((.*)\)/; + const rxStatements = /.*renpy.register_statement\s*\("(\w+)"(.*)\)/; + const rxOutlines = /[\s_]*outlines\s+(\[.*\])/; + + const internal = NavigationData.renpyFunctions.internal; + let transitions = Object.keys(internal).filter(key => internal[key][0] === 'transitions'); + + for (let i = 0; i < document.lineCount; ++i) { + const line = document.lineAt(i).text; + // match class definitions + const matches = line.match(rxClass); + if (matches) { + const match = matches[1]; + NavigationData.data.location['class'][match] = [filename, i + 1]; + const properties = NavigationData.getClassProperties(match, document, i+1); + if (properties) { + NavigationData.gameObjects['properties'][match] = properties; + } + const initData = NavigationData.data.location['callable'][`${match}.__init__`]; + if (initData && initData[0] === stripWorkspaceFromFile(document.uri.path)) { + const fields = NavigationData.getClassFields(match, document, initData[1]); + if (fields) { + NavigationData.gameObjects['fields'][match] = fields; + } + } + continue; + } + // match displayable definitions + const displayables = line.match(rxDisplayable); + if (displayables) { + let match = ""; + let image_type = "image"; + if (displayables[2]) { + // image match + match = displayables[2].trim(); + } else if (displayables[5]) { + // layered image match + match = displayables[5].trim(); + image_type = displayables[4].trim(); + } + if (match.length > 0) { + const displayable = new Displayable(match, image_type, displayables[3], filename, i + 1); + NavigationData.data.location['displayable'][match] = displayable; + } + continue; + } + // match audio channels created with register_channel + const channels = line.match(rxChannels); + if (channels) { + let match = channels[2]; + NavigationData.gameObjects['channels'][match] = [filename, i + 1]; + continue; + } + // match persistent definitions (define persistent) + const persistents = line.match(rxPersistent); + if (persistents) { + let match = persistents[2]; + NavigationData.data.location['persistent'][match] = [filename, i + 1]; + continue; + } + // match outlines + const outlines = line.match(rxOutlines); + if (outlines) { + let match = outlines[1]; + if (!NavigationData.data.location['outlines']) { + NavigationData.data.location['outlines'] = {}; + NavigationData.data.location['outlines']['array'] = []; + } + let current_outlines = NavigationData.data.location['outlines']['array']; + if (current_outlines === undefined) { + current_outlines = []; + } + if (!current_outlines.includes(match)) { + current_outlines.push(match); + } + NavigationData.data.location['outlines']['array'] = current_outlines; + continue; + } + // match default/defines + const defaults = line.match(rxDefaultDefine); + if (defaults) { + const datatype = new DataType(defaults[2], defaults[1], defaults[3]); + datatype.checkTypeArray('transitions', transitions); + NavigationData.gameObjects['define_types'][defaults[2]] = datatype; + } + // match characters + const characters = line.match(rxCharacters); + if (characters) { + let char_name = ''; + let char_image = ''; + let dynamic = ''; + let split = splitParameters(characters[4], true); + if (split) { + char_name = stripQuotes(split[0]); + char_image = getNamedParameter(split, 'image'); + dynamic = getNamedParameter(split, 'dynamic'); + } + if (characters[3] === 'DynamicCharacter') { + dynamic = 'True'; + } + else if (!dynamic || dynamic.length === 0) { + dynamic = 'False'; + } + + var chr_object = new Character(char_name, char_image, dynamic, split, filename, i); + NavigationData.gameObjects['characters'][characters[2]] = chr_object; + continue; + } + + // creator defined statements + const statements = line.match(rxStatements); + if (statements) { + const statement_name = statements[1]; + const statement_args = splitParameters(statements[1] + statements[2], true); + let statement_docs = ''; + const execute = getNamedParameter(statement_args, 'execute'); + if (execute && execute.length > 0) { + let entries = NavigationData.find(execute); + if (entries) { + const def = getDefinitionFromFile(entries[0].filename, entries[0].location); + if (def) { + statement_docs = def.documentation; + } + } + } + + const statement = new Navigation('statement', statement_name, filename, i + 1, statement_docs, "", "cds", statements.index); + NavigationData.data.location['statement'][statement_name] = statement; + continue; + } + } + } + + static async getCharacterImageAttributes() { + //console.log("getCharacterImageAttributes"); + const characters = NavigationData.gameObjects['characters']; + const displayables = NavigationData.data.location['displayable']; + for (let key in characters) { + const char = characters[key]; + let attributes: string[] = []; + if (char && char.image && char.image.length > 0) { + const displayable = displayables[char.image]; + if (displayable) { + // find layered image attributes + if (displayable.image_type === 'layeredimage') { + const li_attributes = await NavigationData.getLayeredImageAttributes(displayable.name, displayable.filename, displayable.location); + if (li_attributes) { + for (let attr of li_attributes) { + if (!attributes.includes(attr)) { + attributes.push(attr); + } + } + } + } + // find defined image attributes + const filtered = Object.keys(displayables).filter(key => displayables[key].tag === char.image); + if (filtered) { + for (let key of filtered) { + if (key !== char.image) { + const attr = key.substr(char.image.length + 1); + if (!attributes.includes(attr)) { + attributes.push(attr); + } + } + } + } + } + NavigationData.gameObjects['attributes'][key] = attributes; + } + } + } + + static async getLayeredImageAttributes(keyword: string, filename: string, location: number): Promise { + // find attributes in the layered image definition + const displayables = NavigationData.data.location['displayable']; + const path = getFileWithPath(filename); + let document = await workspace.openTextDocument(path); + let attributes: string[] = []; + const rxAttr = /^\s*attribute\s+(\w*)/; + try { + let index = location; + let line = document.lineAt(index - 1).text; + const spacing = line.indexOf('layeredimage ' + keyword); + let finished = false; + while (!finished && index < document.lineCount) { + let line = document.lineAt(index).text.replace('\r',''); + if (line.length > 0 && (line.length - line.trimLeft().length <= spacing)) { + finished = true; + break; + } + line = line.trim(); + if (line.startsWith('attribute')) { + // specific attribute + const match = line.match(rxAttr); + if (match) { + if (!attributes.includes(match[1])) { + attributes.push(match[1]); + } + } + } else if (line.startsWith('group') && line.indexOf('auto') > 0) { + // auto attributes + const match = line.match(/^\s*group\s+(\w*)/); + if (match) { + const image_key = `${keyword}_${match[1]}_`; + const filtered = Object.keys(displayables).filter(key => key.startsWith(image_key)); + if (filtered) { + for (let d of filtered) { + if (d !== image_key) { + const attr = d.substr(image_key.length); + if (attr.indexOf('_') > 0) { + const split = attr.split('_'); + if (!attributes.includes(split[0])) { + attributes.push(split[0]); + } + } else { + if (!attributes.includes(attr)) { + attributes.push(attr); + } + } + } + } + } + + } + } + index++; + } + return attributes; + } catch (error) { + return attributes; + } + } + + /** + * Returns whether or not the given filename can be represented as a Python variable + * @param filename - The filename + * @returns True if the filename can be represented as a Python variable + */ + static getPythonName(filename: string): boolean | undefined { + const rx = /^[a-zA-Z_][a-zA-Z0-9_]*$/; + let fn_only = extractFilenameWithoutExtension(filename) || ''; + const split = fn_only.split(' '); + for (let i=0; i 0) { + this.tag = name.split(' ')[0]; + } else { + this.tag = name; + } + } +} + +function isNormalInteger(str: string) { + var n = Math.floor(Number(str)); + return n !== Infinity && String(n) === str && n >= 0; +} \ No newline at end of file diff --git a/src/renpy.json b/src/renpy.json new file mode 100644 index 0000000..d0cc21e --- /dev/null +++ b/src/renpy.json @@ -0,0 +1 @@ +{"renpy": {"renpy.count_seen_dialogue_blocks": ["internal", "function", "()", "", "", "Returns the number of dialogue blocks the user has seen in any play-through of the current game."], "renpy.timeout": ["internal", "function", "(seconds)", "", "", "Causes an event to be generated before `seconds` seconds have elapsed. This ensures that the event method of a user-defined displayable will be called."], "renpy.context": ["internal", "function", "()", "", "", "Returns an object that is unique to the current context. The object is copied when entering a new context, but changes to the copy do not change the original.\n\nThe object is saved and participates in rollback."], "renpy.sound.get_pos": ["internal", "function", "(channel=\"sound\")", "", "", "Returns the current position of the audio or video file on `channel`, in seconds. Returns None if no audio is playing on `channel`.\n\nAs this may return None before a channel starts playing, or if the audio channel involved has been muted, callers of this function should always handle a None value."], "renpy.clear_keymap_cache": ["internal", "function", "()", "", "", "Clears the keymap cache. This allows changes to :var:`config.keymap` to take effect without restarting Ren'Py."], "renpy.error": ["internal", "function", "(msg)", "", "", "Reports `msg`, a string, as as error for the user. This is logged as a parse or lint error when approprate, and otherwise it is raised as an exception."], "renpy.fnmatch": ["internal", "function", "()", "", "", "Filename matching with shell patterns.\n\nfnmatch(FILENAME, PATTERN) matches according to the local convention. fnmatchcase(FILENAME, PATTERN) always takes case in account.\n\nThe functions operate by translating the pattern into a regular expression. They cache the compiled regular expressions for speed.\n\nThe function translate(PATTERN) returns a regular expression corresponding to PATTERN. (It does not compile it.)"], "renpy.get_physical_size": ["internal", "function", "()", "", "", "Returns the size of the physical window."], "renpy.ios": ["other", "renpy.ios", "", "", "var", "Has a true value when running on iOS."], "renpy.language_tailor": ["other", "renpy.language_tailor", "(chars, cls)", "", "function", "This can be used to override the line breaking class of a character. For example, the linebreaking class of a character can be set to ID to treat it as an ideograph, which allows breaks before and after that character.\n\n`chars` A string containing each of the characters to tailor.\n\n`cls` A string giving a character class. This should be one of the classes defined in Table 1 of `UAX #14: Unicode Line Breaking Algorithm `_."], "renpy.music.queue": ["internal", "function", "(filenames, channel=\"music\", loop=None, clear_queue=True, fadein=0, tight=None, relative_volume=1.0)", "", "", "This queues the given filenames on the specified channel.\n\n`filenames` This may be a single file, or a list of files to be played.\n\n`channel` The channel to play the sound on.\n\n`loop` If this is True, the tracks will loop while they are the last thing in the queue.\n\n`clear_queue` If True, then the queue is cleared, making these files the files that are played when the currently playing file finishes. If it is False, then these files are placed at the back of the queue. In either case, if no music is playing these files begin playing immediately.\n\n`fadein` This is the number of seconds to fade the music in for, on the first loop only.\n\n`tight` If this is True, then fadeouts will span into the next-queued sound. If None, this is true when loop is True, and false otherwise.\n\n`relative_volume` This is the volume relative to the current channel volume. The specified file will be played at that relative volume. If not specified, it will always default to 1.0, which plays the file at the original volume as determined by the mixer, channel and secondary volume.\n\nThis clears the pause flag for `channel`."], "renpy.shown_window": ["internal", "function", "()", "", "", "Call this to indicate that the window has been shown. This interacts with the \\window show\\ statement, which shows an empty window whenever this functions has not been called during an interaction."], "renpy.with_statement": ["internal", "function", "(trans, always=False)", "", "", "Causes a transition to occur. This is the Python equivalent of the with statement.\n\n`trans` The transition.\n\n`always` If True, the transition will always occur, even if the user has disabled transitions.\n\nThis function returns true if the user chose to interrupt the transition, and false otherwise."], "renpy.get_placement": ["internal", "function", "(d)", "", "", "This gets the placement of displayable d. There's very little warranty on this information, as it might change when the displayable is rendered, and might not exist until the displayable is first rendered.\n\nThis returns an object with the following fields, each corresponding to a style property:\n\n* pos * xpos * ypos * anchor * xanchor * yanchor * offset * xoffset * yoffset * subpixel"], "renpy.try_compile": ["internal", "function", "(where, expr, additional=None)", "", "", "Tries to compile an expression, and writes an error to lint.txt if it fails.\n\n`where` A string giving the location the expression is found. Used to generate an error message of the form \\Could not evaluate `expr` in `where`.\\\n\n`expr` The expression to try compiling.\n\n`additional` If given, an additional line of information that is addded to the error message."], "renpy.Render.add_uniform": ["cdd", "add_uniform", "(name, value)", "renpy.Render", "method", "Causes the uniform `name` to have `value` when this Render or its children are drawn."], "renpy.show_screen": ["internal", "function", "(_screen_name, *_args, **kwargs)", "", "", "The programmatic equivalent of the show screen statement.\n\nShows the named screen. This takes the following keyword arguments:\n\n`_screen_name` The name of the screen to show. `_layer` The layer to show the screen on. `_zorder` The zorder to show the screen on. If not specified, defaults to the zorder associated with the screen. It that's not specified, it is 0 by default. `_tag` The tag to show the screen with. If not specified, defaults to the tag associated with the screen. It that's not specified, defaults to the name of the screen. `_widget_properties` A map from the id of a widget to a property name -> property value map. When a widget with that id is shown by the screen, the specified properties are added to it. `_transient` If true, the screen will be automatically hidden at the end of the current interaction.\n\nNon-keyword arguments, and keyword arguments that do not begin with an underscore, are passed to the screen."], "renpy.threading": ["internal", "function", "()", "", "", "Thread module emulating a subset of Java's threading model."], "renpy.get_autoreload": ["internal", "function", "()", "", "", "Gets the autoreload flag."], "renpy.movie_cutscene": ["internal", "function", "(filename, delay=None, loops=0, stop_music=True)", "", "", "This displays a movie cutscene for the specified number of seconds. The user can click to interrupt the cutscene. Overlays and Underlays are disabled for the duration of the cutscene.\n\n`filename` The name of a file containing any movie playable by Ren'Py.\n\n`delay` The number of seconds to wait before ending the cutscene. Normally the length of the movie, in seconds. If None, then the delay is computed from the number of loops (that is, loops + 1) * the length of the movie. If -1, we wait until the user clicks.\n\n`loops` The number of extra loops to show, -1 to loop forever.\n\nReturns True if the movie was terminated by the user, or False if the given delay elapsed uninterrupted."], "renpy.force_autosave": ["internal", "function", "(take_screenshot=False, block=False)", "", "", "Forces a background autosave to occur.\n\n`take_screenshot` If True, a new screenshot will be taken. If False, the existing screenshot will be used.\n\n`block` If True, blocks until the autosave completes."], "renpy.register_statement": ["internal", "function", "(name, parse=None, lint=None, execute=None, predict=None, next=None, scry=None, block=False, init=False, translatable=False, execute_init=None, init_priority=0, label=None, warp=None, translation_strings=None, force_begin_rollback=False, post_execute=None, post_label=None, predict_all=True, predict_next=None)", "", "", "This registers a user-defined statement.\n\n`name` This is either a space-separated list of names that begin the statement, or the empty string to define a new default statement (the default statement will replace the say statement).\n\n`block` When this is False, the statement does not expect a block. When True, it expects a block, but leaves it up to the lexer to parse that block. If the string \\script\\, the block is interpreted as containing one or more Ren'Py script language statements. If the string \\possible\\, the block expect condition is determined by the parse function.\n\n`parse` This is a function that takes a Lexer object. This function should parse the statement, and return an object. This object is passed as an argument to all the other functions.\n\n`lint` This is called to check the statement. It is passed a single argument, the object returned from parse. It should call renpy.error to report errors.\n\n`execute` This is a function that is called when the statement executes. It is passed a single argument, the object returned from parse.\n\n`execute_init` This is a function that is called at init time, at priority 0.\n\n`predict` This is a function that is called to predict the images used by the statement. It is passed a single argument, the object returned from parse. It should return a list of displayables used by the statement.\n\n`next` This is a function that is called to determine the next statement.\n\nIf `block` is not \\script\\, this is passed a single argument, the object returned from the parse function. If `block` is \\script\\, an additional argument is passed, an object that names the first statement in the block.\n\nThe function should return either a string giving a label to jump to, the second argument to transfer control into the block, or None to continue to the statement after this one.\n\n`label` This is a function that is called to determine the label of this statement. If it returns a string, that string is used as the statement label, which can be called and jumped to like any other label.\n\n`warp` This is a function that is called to determine if this statement should execute during warping. If the function exists and returns true, it's run during warp, otherwise the statement is not run during warp.\n\n`scry` Used internally by Ren'Py.\n\n`init` True if this statement should be run at init-time. (If the statement is not already inside an init block, it's automatically placed inside an init block.) This calls the execute function, in addition to the execute_init function.\n\n`init_priority` An integer that determines the priority of initialization of the init block.\n\n`translation_strings` A function that is called with the parsed block. It's expected to return a list of strings, which are then reported as being available to be translated.\n\n`force_begin_rollback` This should be set to true on statements that are likely to cause the end of a fast skip, similar to ``menu`` or ``call screen``.\n\n`post_execute` A function that is executed as part the next statement after this one. (Adding a post_execute function changes the contents of the RPYC file, meaning a Force Compile is necessary.)\n\n`post_label` This is a function that is called to determine the label of this the post execute statement. If it returns a string, that string is used as the statement label, which can be called and jumped to like any other label. This can be used to create a unique return point.\n\n`predict_all` If True, then this predicts all sub-parses of this statement and the statement after this statement.\n\n`predict_next` This is called with a single argument, the label of the statement that would run after this statement.\n\nThis should be called to predict the statements that can run after this one. It's expected to return a list of of labels or SubParse objects. This is not called if `predict_all` is true."], "renpy.sound.register_channel": ["audio", "renpy.music.register_channel", "(name, mixer=None, loop=None, stop_on_mute=True, tight=False, file_prefix=\"\", file_suffix=\"\", buffer_queue=True, movie=False, framedrop=True)", "", "", "This registers a new audio channel named `name`. Audio can then be played on the channel by supplying the channel name to the play or queue statements.\n\n`mixer` The name of the mixer the channel uses. By default, Ren'Py knows about the \\music\\, \\sfx\\, and \\voice\\ mixers. Using other names is possible, but may require changing the preferences screens.\n\n`loop` If true, sounds on this channel loop by default.\n\n`stop_on_mute` If true, music on the channel is stopped when the channel is muted.\n\n`tight` If true, sounds will loop even when fadeout is occurring. This should be set to True for a sound effects or seamless music channel, and False if the music fades out on its own.\n\n`file_prefix` A prefix that is prepended to the filenames of the sound files being played on this channel.\n\n`file_suffix` A suffix that is appended to the filenames of the sound files being played on this channel.\n\n`buffer_queue` Should we buffer the first second or so of a queued file? This should be True for audio, and False for movie playback.\n\n`movie` If true, this channel will be set up to play back videos.\n\n`framedrop` This controls what a video does when lagging. If true, frames will be dropped to keep up with realtime and the soundtrack. If false, Ren'Py will display frames late rather than dropping them."], "renpy.save_persistent": ["internal", "function", "()", "", "", "Saves the persistent data to disk."], "renpy.list_saved_games": ["internal", "function", "(regexp=r'.', fast=False)", "", "", "Lists the save games. For each save game, returns a tuple containing:\n\n* The filename of the save. * The extra_info that was passed in. * A displayable that, when displayed, shows the screenshot that was used when saving the game. * The time the game was stayed at, in seconds since the UNIX epoch.\n\n`regexp` A regular expression that is matched against the start of the filename to filter the list.\n\n`fast` If fast is true, the filename is returned instead of the tuple."], "renpy.mark_label_unseen": ["internal", "function", "(label)", "", "", "Marks the named label as if it has not been executed on the current user's system yet."], "renpy.transition": ["internal", "function", "(trans, layer=None, always=False)", "", "", "Sets the transition that will be used during the next interaction.\n\n`layer` The layer the transition applies to. If None, the transition applies to the entire scene.\n\n`always` If false, this respects the transition preference. If true, the transition is always run."], "renpy.context_dynamic": ["internal", "function", "(*vars)", "", "", "This can be given one or more variable names as arguments. This makes the variables dynamically scoped to the current context. The variables will be reset to their original value when the call returns.\n\nAn example call is\n```\n$ renpy.context_dynamic(\\x\\, \\y\\, \\z\\)\n```"], "renpy.reset_physical_size": ["internal", "function", "()", "", "", "Attempts to set the size of the physical window to the specified values in renpy.config. (That is, screen_width and screen_height.) This has the side effect of taking the screen out of fullscreen mode."], "renpy.can_show": ["internal", "function", "(name, layer=None, tag=None)", "", "", "Determines if `name` can be used to show an image. This interprets `name` as a tag and attributes. This is combined with the attributes of the currently-showing image with `tag` on `layer` to try to determine a unique image to show. If a unique image can be show, returns the name of that image as a tuple. Otherwise, returns None.\n\n`tag` The image tag to get attributes from. If not given, defaults to the first component of `name`.\n\n`layer` The layer to check. If None, uses the default layer for `tag`."], "renpy.has_image": ["internal", "function", "(name, exact=False)", "", "", "Return true if an image with `name` exists, and false if no such image exists.\n\n`name` Either a string giving an image name, or a tuple of strings giving the name components.\n\n`exact` Returns true if and only if an image with the exact name exists - parameterized matches are not included."], "renpy.get_mode": ["internal", "function", "()", "", "", "Returns the current mode, or None if it is not defined."], "renpy.image_size": ["internal", "function", "(im)", "", "", "Given an image manipulator, loads it and returns a (``width``, ``height``) tuple giving its size.\n\nThis reads the image in from disk and decompresses it, without using the image cache. This can be slow."], "renpy.sound.get_duration": ["internal", "function", "(channel=\"sound\")", "", "", "Returns the duration of the audio or video file on `channel`. Returns 0.0 if no file is playing on `channel`."], "renpy.force_full_redraw": ["internal", "function", "()", "", "", "Forces the screen to be redrawn in full. Call this after using pygame to redraw the screen directly."], "renpy.try_eval": ["internal", "function", "(where, expr, additional=None)", "", "", "Tries to evaluate an expression, and writes an error to lint.txt if it fails.\n\n`where` A string giving the location the expression is found. Used to generate an error message of the form \\Could not evaluate `expr` in `where`.\\\n\n`expr` The expression to try evaluating.\n\n`additional` If given, an additional line of information that is addded to the error message."], "renpy.version_string": ["other", "renpy.version_string", "", "", "var", "The version number of Ren'Py, as a string of the form \\Ren'Py 1.2.3.456\\."], "renpy.music.set_pan": ["internal", "function", "(pan, delay, channel=\"music\")", "", "", "Sets the pan of this channel.\n\n`pan` A number between -1 and 1 that control the placement of the audio. If this is -1, then all audio is sent to the left channel. If it's 0, then the two channels are equally balanced. If it's 1, then all audio is sent to the right ear.\n\n`delay` The amount of time it takes for the panning to occur.\n\n`channel` The channel the panning takes place on. This can be a sound or a music channel. Often, this is channel 7, the default music channel."], "renpy.load": ["internal", "function", "(filename)", "", "", "Loads the game state from the save slot `filename`. If the file is loaded successfully, this function never returns."], "renpy.predict_show_display_say": ["internal", "function", "(who, what, who_args, what_args, window_args, image=False, two_window=False, side_image=None, screen=None, properties=None, **kwargs)", "", "", "This is the default function used by Character to predict images that will be used by show_display_say. It's called with more-or-less the same parameters as show_display_say, and it's expected to return a list of images used by show_display_say."], "renpy.MenuEntry": ["internal", "class", "()", "", "", "The object passed into the choice screen."], "renpy.get_skipping": ["internal", "function", "()", "", "", "Returns \\slow\\ if the Ren'Py is skipping, \\fast\\ if Ren'Py is fast skipping, and None if it is not skipping."], "renpy.seen_audio": ["internal", "function", "(filename)", "", "", "Returns True if the given filename has been played at least once on the current user's system."], "renpy.focus_coordinates": ["internal", "function", "()", "", "", "This attempts to find the coordinates of the currently-focused displayable. If it can, it will return them as a (x, y, w, h) tuple. If not, it will return a (None, None, None, None) tuple."], "renpy.list_images": ["internal", "function", "()", "", "", "Returns a list of images that have been added to Ren'Py, as a list of strings with spaces between the name components."], "renpy.Render.canvas": ["cdd", "canvas", "()", "renpy.Render", "method", "Returns a canvas object. A canvas object has methods corresponding to the `pygame.draw `_ functions, with the first parameter (the surface) omitted.\n\nCanvas objects also have a get_surface() method that returns the pygame Surface underlying the canvas."], "renpy.rename_save": ["internal", "function", "(old, new)", "", "", "Renames a save from `old` to `new`. (Does nothing if `old` does not exist.)"], "renpy.get_side_image": ["internal", "function", "(prefix_tag, image_tag=None, not_showing=None, layer=None)", "", "", "This attempts to find an image to show as the side image.\n\nIt begins by determining a set of image attributes. If `image_tag` is given, it gets the image attributes from the tag. Otherwise, it gets them from the currently showing character.\n\nIt then looks up an image with the tag `prefix_tag` and those attributes, and returns it if it exists.\n\nIf not_showing is True, this only returns a side image if the image the attributes are taken from is not on the screen. If Nome, the value is taken from :var:`config.side_image_only_not_showing`.\n\nIf `layer` is None, uses the default layer for the currently showing tag."], "renpy.predicting": ["internal", "function", "()", "", "", "Returns true if Ren'Py is currently predicting the screen."], "renpy.call": ["internal", "function", "(label, *args, **kwargs)", "", "", "Causes the current Ren'Py statement to terminate, and a jump to a `label` to occur. When the jump returns, control will be passed to the statement following the current statement.\n\n`from_current` If true, control will return to the current statement, rather than the statement following the current statement. (This will lead to the current statement being run twice. This must be passed as a keyword argument.)"], "renpy.get_registered_image": ["internal", "function", "(name)", "", "", "If an image with the same name has been registered with renpy.register_image, returns it. Otherwise, returns None."], "renpy.in_rollback": ["internal", "function", "()", "", "", "Returns true if the game has been rolled back."], "renpy.get_identifier_checkpoints": ["internal", "function", "(identifier)", "", "", "Given a rollback_identifier from a HistoryEntry object, returns the number of checkpoints that need to be passed to :func:`renpy.rollback` to reach that identifier. Returns None of the identifier is not in the rollback history."], "renpy.mark_image_unseen": ["internal", "function", "(name)", "", "", "Marks the named image as if it has not been displayed on the current user's system yet."], "renpy.has_label": ["internal", "function", "(name)", "", "", "Returns true if `name` is a valid label the program, or false otherwise.\n\n`name` Should be a string to check for the existence of a label. It can also be an opaque tuple giving the name of a non-label statement."], "renpy.slot_screenshot": ["internal", "function", "(slotname)", "", "", "Returns a display that can be used as the screenshot for `slotname`, or None if the slot is empty."], "renpy.get_filename_line": ["internal", "function", "()", "", "", "Returns a pair giving the filename and line number of the current statement."], "renpy.get_attributes": ["internal", "function", "(layer, tag, default=())", "", "", "Get the attributes associated the image with tag on the given layer."], "renpy.get_mouse_name": ["internal", "function", "(interaction=False)", "", "", "Returns the name of the mouse that should be shown.\n\n `interaction` If true, get a mouse name that is based on the type of interaction occuring. (This is rarely useful.)"], "renpy.mark_audio_seen": ["internal", "function", "(filename)", "", "", "Marks the given filename as if it has been already played on the current user's system."], "renpy.license": ["other", "renpy.license", "", "", "var", "A string giving license text that should be included in a game's about screen."], "renpy.is_selected": ["internal", "function", "(action)", "", "", "Returns true if `action` indicates it is selected, or false otherwise."], "renpy.list_slots": ["internal", "function", "(regexp=None)", "", "", "Returns a list of non-empty save slots. If `regexp` exists, only slots that begin with `regexp` are returned. The slots are sorted in string-order."], "renpy.munge": ["internal", "function", "(name, filename=None)", "", "", "Munges `name`, which must begin with __.\n\n`filename` The filename the name is munged into. If None, the name is munged into the filename containing the call to this function."], "renpy.render": ["udd_utility", "renpy.render", "(d, width, height, st, at)", "", "function", "Causes a displayable to be rendered, and a renpy.Render object to be returned.\n\n`d` The displayable to render.\n\n`width`, `height` The width and height available for the displayable to render into.\n\n`st`, `at` The shown and animation timebases.\n\nRenders returned by this object may be cached, and should not be modified once they have been retrieved."], "renpy.map_event": ["internal", "function", "(ev, keysym)", "", "", "Returns true if the pygame event `ev` matches `keysym`\n\n`keysym` One of:\n\n* The name of a keybinding in :var:`config.keymap`. * A keysym, as documented in the :ref:`keymap` section. * A list containing one or more keysyms."], "renpy.start_predict_screen": ["internal", "function", "(_screen_name, *args, **kwargs)", "", "", "Causes Ren'Py to start predicting the screen named `_screen_name` with the given arguments. This replaces any previous prediction of `_screen_name`. To stop predicting a screen, call :func:`renpy.stop_predict_screen`."], "renpy.call_screen": ["internal", "function", "(_screen_name, *args, **kwargs)", "", "", "The programmatic equivalent of the call screen statement.\n\nThis shows `_screen_name` as a screen, then causes an interaction to occur. The screen is hidden at the end of the interaction, and the result of the interaction is returned.\n\nPositional arguments, and keyword arguments that do not begin with _ are passed to the screen.\n\nIf the keyword argument `_with_none` is false, \\with None\\ is not run at the end of end of the interaction.\n\nIf the keyword argument `_mode` in kwargs, it will be mode of this interaction, otherwise it will be \\screen\\ mode."], "renpy.has_screen": ["internal", "function", "(name)", "", "", "Returns true if a screen with the given name exists."], "renpy.Render.add_property": ["cdd", "add_property", "(name, value)", "renpy.Render", "method", "Causes the GL property `name` to have `value` when this Render or one of its children are drawn."], "renpy.eval_who": ["internal", "function", "(who, fast=None)", "", "", "Evaluates the `who` parameter to a say statement."], "renpy.set_tag_attributes": ["internal", "function", "(name, layer=None)", "", "", "This sets the attributes associated with an image tag when that image tag is not showing. The main use of this would be to directly set the attributes used by a side image.\n\nFor example::\n\n$ renpy.set_tag_attributes(\\lucy mad\\) $ renpy.say(l, \\I'm rather cross.\\)\n\nand::\n\nl mad \\I'm rather cross.\\\n\nare equivalent."], "renpy.mobile": ["other", "renpy.mobile", "", "", "var", "Has a true value when running on Android or iOS or in the browser."], "renpy.get_refresh_rate": ["internal", "function", "(precision=5)", "", "", "Returns the refresh rate of the current screen, as a floating-point number of frames per second.\n\n`precision` The raw data Ren'Py gets is number of frames per second, rounded down. This means that a monitor that runs at 59.95 frames per second will be reported at 59 fps. The precision argument reduces the precision of this reading, such that the only valid readings are multiples of the precision.\n\nSince all monitor framerates tend to be multiples of 5 (25, 30, 60, 75, and 120), this likely will improve accuracy. Setting precision to 1 disables this."], "renpy.flush_cache_file": ["internal", "function", "(fn)", "", "", "This flushes all image cache entries that refer to the file `fn`. This may be called when an image file changes on disk to force Ren'Py to use the new version."], "renpy.get_ordered_image_attributes": ["internal", "function", "(tag, attributes=(), sort=None)", "", "", "Returns a list of image tags, ordered in a way that makes sense to present to the user.\n\n`attributes` If present, only attributes that are compatible with the given attributes are considered. (Compatible means that the attributes can be in a single image at the same time.)\n\n`sort` If not None, the returned list of attributes is sorted. This is a function that should be used as a tiebreaker."], "renpy.Render.get_size": ["cdd", "get_size", "()", "renpy.Render", "method", "Returns a (width, height) tuple giving the size of this render."], "renpy.stop_predict": ["internal", "function", "(*args)", "", "", "This function takes one or more displayables as arguments. It causes Ren'Py to stop predicting those displayables during every interaction.\n\nWildcard patterns can be used as described in :func:`renpy.start_predict`."], "renpy.free_memory": ["internal", "function", "()", "", "", "Clears the font cache."], "renpy.restart_interaction": ["internal", "function", "()", "", "", "Restarts the current interaction. Among other things, this displays images added to the scene, re-evaluates screens, and starts any queued transitions.\n\nThis only does anything when called from within an interaction (for example, from an action). Outside an interaction, this function has no effect."], "renpy.log": ["internal", "function", "(msg)", "", "", "If :var:`config.log` is not set, this does nothing. Otherwise, it opens the logfile (if not already open), formats the message to :var:`config.log_width` columns, and prints it to the logfile."], "renpy.music.is_playing": ["internal", "function", "(channel=\"music\")", "", "", "Returns True if the channel is currently playing a sound, False if it is not, or if the sound system isn't working."], "renpy.get_return_stack": ["internal", "function", "()", "", "", "Returns a list giving the current return stack. The return stack is a list of statement names.\n\nThe statement names will be strings (for labels), or opaque tuples (for non-label statements)."], "renpy.set_autoreload": ["internal", "function", "(autoreload)", "", "", "Sets the autoreload flag, which determines if the game will be automatically reloaded after file changes. Autoreload will not be fully enabled until the game is reloaded with :func:`renpy.utter_restart`."], "renpy.context_nesting_level": ["internal", "function", "()", "", "", "Returns the nesting level of the current context. This is 0 for the outermost context (the context that is saved, loaded, and rolled-back), and is non-zero in other contexts, such as menu and replay contexts."], "renpy.quit": ["internal", "function", "(relaunch=False, status=0, save=False)", "", "", "This causes Ren'Py to exit entirely.\n\n`relaunch` If true, Ren'Py will run a second copy of itself before quitting.\n\n`status` The status code Ren'Py will return to the operating system. Generally, 0 is success, and positive integers are failure.\n\n`save` If true, the game is saved in :var:`_quit_slot` before Ren'Py terminates."], "renpy.register_style_preference": ["internal", "function", "(preference, alternative, style, property, value)", "", "", "Registers information about an alternative for a style preference.\n\n`preference` A string, the name of the style preference.\n\n`alternative` A string, the name of the alternative.\n\n`style` The style that will be updated. This may be a style object or a string giving the style name.\n\n`property` A string giving the name of the style property that will be update.\n\n`value` The value that will be assigned to the style property."], "renpy.unlink_save": ["internal", "function", "(filename)", "", "", "Deletes the save slot with the given name."], "renpy.movie_stop": ["internal", "function", "(clear=True, only_fullscreen=False)", "", "", "Stops the currently playing movie."], "renpy.set_mouse_pos": ["internal", "function", "(x, y, duration=0)", "", "", "Jump the mouse pointer to the location given by arguments x and y. If the device does not have a mouse pointer, this does nothing.\n\n`duration` The time it will take to perform the move, in seconds. During this time, the mouse may be unresponsive."], "renpy.image_exists": ["internal", "function", "(name, expression, tag, precise=True)", "", "", "Checks a scene or show statement for image existence."], "renpy.loadable": ["internal", "function", "(filename)", "", "", "Returns True if the given filename is loadable, meaning that it can be loaded from the disk or from inside an archive. Returns False if this is not the case."], "renpy.block_rollback": ["internal", "function", "()", "", "", "Prevents the game from rolling back to before the current statement."], "renpy.version_tuple": ["other", "renpy.version_tuple", "", "", "var", "The version number of Ren'Py, as a tuple of the form (1, 2, 3, 456)."], "renpy.scene": ["internal", "function", "(layer='master')", "", "", "Removes all displayables from `layer`. This is equivalent to the scene statement, when the scene statement is not given an image to show.\n\nA full scene statement is equivalent to a call to renpy.scene followed by a call to :func:`renpy.show`. For example::\n\nscene bg beach\n\nis equivalent to::\n\n$ renpy.scene() $ renpy.show(\\bg beach\\)"], "renpy.Displayable.visit": ["cdd", "visit", "(self)", "renpy.Displayable", "method", "If the displayable has child displayables, this method should be overridden to return a list of those displayables. This ensures that the per_interact methods of those displayables are called, and also allows images used by those displayables to be predicted."], "renpy.sound.play": ["internal", "function", "(filenames, channel=\"sound\", loop=None, fadeout=None, synchro_start=False, fadein=0, tight=None, if_changed=False, relative_volume=1.0)", "", "", "This stops the music currently playing on the numbered channel, dequeues any queued music, and begins playing the specified file or files.\n\n`filenames` This may be a single file, or a list of files to be played.\n\n`channel` The channel to play the sound on.\n\n`loop` If this is True, the tracks will loop while they are the last thing in the queue.\n\n`fadeout` If not None, this is a time in seconds to fade for. Otherwise the fadeout time is taken from config.fade_music.\n\n`synchro_start` Ren'Py will ensure that all channels of with synchro_start set to true will start playing at exactly the same time. Synchro_start should be true when playing two audio files that are meant to be synchronized with each other.\n\n`fadein` This is the number of seconds to fade the music in for, on the first loop only.\n\n`tight` If this is True, then fadeouts will span into the next-queued sound. If None, this is true when loop is True, and false otherwise.\n\n`if_changed` If this is True, and the music file is currently playing, then it will not be stopped/faded out and faded back in again, but instead will be kept playing. (This will always queue up an additional loop of the music.)\n\n`relative_volume` This is the volume relative to the current channel volume. The specified file will be played at that relative volume. If not specified, it will always default to 1.0, which plays the file at the original volume as determined by the mixer, channel and secondary volume.\n\nThis clears the pause flag for `channel`."], "renpy.get_available_image_tags": ["internal", "function", "()", "", "", "Returns a list of image tags that have been defined."], "renpy.get_displayable_properties": ["internal", "function", "(id, screen=None, layer=None)", "", "", "Returns the properties for the displayable with `id` in the `screen` on `layer`. If `screen` is None, returns the properties for the current screen. This can be used from Python or property code inside a screen.\n\nNote that this returns a dictionary containing the widget properties, and so to get an individual property, the dictionary must be accessed."], "renpy.scry": ["internal", "function", "(self)", "", "", "Called to return an object with some general, user-definable information about the future."], "renpy.music.get_pos": ["internal", "function", "(channel=\"music\")", "", "", "Returns the current position of the audio or video file on `channel`, in seconds. Returns None if no audio is playing on `channel`.\n\nAs this may return None before a channel starts playing, or if the audio channel involved has been muted, callers of this function should always handle a None value."], "renpy.mark_image_seen": ["internal", "function", "(name)", "", "", "Marks the named image as if it has been already displayed on the current user's system."], "renpy.return_statement": ["internal", "function", "(value=None)", "", "", "Causes Ren'Py to return from the current Ren'Py-level call."], "renpy.android": ["other", "renpy.android", "", "", "var", "Has a true value when running on Android."], "renpy.call_stack_depth": ["internal", "function", "()", "", "", "Returns the depth of the call stack of the current context - the number of calls that have run without being returned from or popped from the call stack."], "renpy.maximum_framerate": ["internal", "function", "(t)", "", "", "Forces Ren'Py to draw the screen at the maximum framerate for `t` seconds. If `t` is None, cancels the maximum framerate request."], "renpy.version_only": ["other", "renpy.version_only", "", "", "var", "The version number of Ren'Py, without the Ren'Py prefix. A string of the form \\1.2.3.456\\."], "renpy.get_image_bounds": ["internal", "function", "(tag, width=None, height=None, layer=None)", "", "", "If an image with `tag` exists on `layer`, returns the bounding box of that image. Returns None if the image is not found.\n\nThe bounding box is an (x, y, width, height) tuple. The components of the tuples are expressed in pixels, and may be floating point numbers.\n\n`width`, `height` The width and height of the area that contains the image. If None, defaults the width and height of the screen, respectively.\n\n`layer` If None, uses the default layer for `tag`."], "renpy.sound": ["basefile", "", "", "", "", "Most renpy.sound functions have aliases in renpy.music. These functions are similar, except they default to the music channel rather than the sound channel, and default to looping."], "renpy.quit_event": ["internal", "function", "()", "", "", "Triggers a quit event, as if the player clicked the quit button in the window chrome."], "renpy.rollback": ["internal", "function", "(force=False, checkpoints=1, defer=False, greedy=True, label=None, abnormal=True)", "", "", "Rolls the state of the game back to the last checkpoint.\n\n`force` If true, the rollback will occur in all circumstances. Otherwise, the rollback will only occur if rollback is enabled in the store, context, and config.\n\n`checkpoints` Ren'Py will roll back through this many calls to renpy.checkpoint. It will roll back as far as it can, subject to this condition.\n\n`defer` If true, the call will be deferred until control returns to the main context.\n\n`greedy` If true, rollback will finish just after the previous checkpoint. If false, rollback finish just before the current checkpoint.\n\n`label` If not None, a label that is called when rollback completes.\n\n`abnormal` If true, the default, script executed after the transition is run in an abnormal mode that skips transitions that would have otherwise occured. Abnormal mode ends when an interaction begins."], "renpy.screenshot_to_bytes": ["internal", "function", "(size)", "", "", "Returns a screenshot as a bytes object, that can be passed to im.Data(). The bytes will be a png-format image, such that\n```\n$ data = renpy.screenshot_to_bytes((640, 360))\nshow expression im.Data(data, \\screenshot.png\\):\nalign (0, 0)\n\nWill show the image. The bytes objects returned can be stored in save\nfiles and persistent data. However, these may be large, and care should\nbe taken to not include too many.\n\n`size`\nThe size the screenshot will be resized to. If None, the screenshot\nwill be resized, and hence will be the size of the player's window,\nwithout any letterbars.\n\nThis function may be slow, and so it's intended for save-like screenshots,\nand not realtime effects.\n```"], "renpy.Render.place": ["cdd", "place", "(d, x=0, y=0, width=None, height=None, st=None, at=None, render=None, main=True)", "renpy.Render", "method", "Renders `d` and places it into the rectangle defined by the `x`, `y`, `width`, and `height`, using Ren'Py's standard placement algorithm.\n\n`x`, `y`, `width`, `height` The rectangle to place in. If `width` or `height`, when None, are the width and height of this render, respectively.\n\n`st`, `at` The times passed to Render. If None, defaults to the times passed to the render method calling this method.\n\n`render` If not None, this is used instead of rendering `d`.\n\n`main` As for .blit()."], "renpy.showing": ["internal", "function", "(layer, name, exact=False)", "", "", "Returns true if name is the prefix of an image that is showing on layer, or false otherwise."], "renpy.sound.set_pause": ["internal", "function", "(value, channel=\"sound\")", "", "", "Sets the pause flag for `channel` to `value`. If True, the channel will pause, otherwise it will play normally."], "renpy.macintosh": ["other", "renpy.macintosh", "", "", "var", "Has a true value when running on macOS."], "renpy.show_layer_at": ["internal", "function", "(at_list, layer='master', reset=True, camera=False)", "", "", "The Python equivalent of the ``show layer`` `layer` ``at`` `at_list` statement. If `camera` is True, the equivalent of the ``camera`` statement.\n\n`reset` If true, the transform state is reset to the start when it is shown. If false, the transform state is persisted, allowing the new transform to update that state."], "renpy.copy_images": ["internal", "function", "(old, new)", "", "", "Copies images beginning with one prefix to images beginning with another. For example\n```\nrenpy.copy_images(\\eileen\\, \\eileen2\\)\n\nwill create an image beginning with \\eileen2\\ for every image beginning\nwith \\eileen\\. If \\eileen happy\\ exists, \\eileen2 happy\\ will be\ncreated.\n\n`old`\nA space-separated string giving the components of the old image\nname.\n\n`new`\nA space-separated string giving the components of the new image\nname.\n\n```"], "renpy.dynamic": ["internal", "function", "(*vars, **kwargs)", "", "", "This can be given one or more variable names as arguments. This makes the variables dynamically scoped to the current call. The variables will be reset to their original value when the call returns.\n\nIf the variables are given as keyword arguments, the value of the argument is assigned to the variable name.\n\nExample calls are\n```\n$ renpy.dynamic(\\x\\, \\y\\, \\z\\)\n$ renpy.dynamic(players=2, score=0)\n```"], "renpy.get_adjustment": ["internal", "function", "(bar_value)", "", "", "Given `bar_value`, a :class:`BarValue`, returns the :func:`ui.adjustment` if uses. The adjustment has the following to attributes defined:\n\n.. attribute:: value\n\nThe current value of the bar.\n\n.. attribute:: range\n\nThe current range of the bar."], "renpy.set_return_stack": ["internal", "function", "(stack)", "", "", "Sets the current return stack. The return stack is a list of statement names.\n\nStatement names may be strings (for labels) or opaque tuples (for non-label statements)."], "renpy.set_physical_size": ["internal", "function", "(size)", "", "", "Attempts to set the size of the physical window to `size`. This has the side effect of taking the screen out of fullscreen mode."], "renpy.music.set_volume": ["internal", "function", "(volume, delay=0, channel=\"music\")", "", "", "Sets the volume of this channel, as a fraction of the volume of the mixer controlling the channel.\n\n`volume` This is a number between 0.0 and 1.0, and is interpreted as a fraction of the mixer volume for the channel.\n\n`delay` It takes delay seconds to change/fade the volume from the old to the new value. This value is persisted into saves, and participates in rollback.\n\n`channel` The channel to be set"], "renpy.queue_event": ["internal", "function", "(name, up=False, **kwargs)", "", "", "Queues an event with the given name. `Name` should be one of the event names in :var:`config.keymap`, or a list of such names.\n\n`up` This should be false when the event begins (for example, when a keyboard button is pressed.) It should be true when the event ends (when the button is released.)\n\nThe event is queued at the time this function is called. This function will not work to replace an event with another - doing so will change event order. (Use :var:`config.keymap` instead.)\n\nThis method is threadsafe."], "renpy.load_module": ["internal", "function", "(name, **kwargs)", "", "", "This loads the Ren'Py module named name. A Ren'Py module consists of Ren'Py script that is loaded into the usual (store) namespace, contained in a file named name.rpym or name.rpymc. If a .rpym file exists, and is newer than the corresponding .rpymc file, it is loaded and a new .rpymc file is created.\n\nAll of the init blocks (and other init-phase code) in the module are run before this function returns. An error is raised if the module name cannot be found, or is ambiguous.\n\nModule loading may only occur from inside an init block."], "renpy.mode": ["internal", "function", "(mode)", "", "", "Causes Ren'Py to enter the named mode, or stay in that mode if it's already in it."], "renpy.suspend_rollback": ["internal", "function", "(flag)", "", "", "Rollback will skip sections of the game where rollback has been suspended.\n\n`flag`: When `flag` is true, rollback is suspended. When false, rollback is resumed."], "renpy.get_mouse_pos": ["internal", "function", "()", "", "", "Returns an (x, y) tuple giving the location of the mouse pointer or the current touch location. If the device does not support a mouse and is not currently being touched, x and y are numbers, but not meaningful."], "renpy.run": ["internal", "function", "(action)", "", "", "Run an action or list of actions. A single action is called with no arguments, a list of actions is run in order using this function, and None is ignored.\n\nReturns the result of the last action to return a value."], "renpy.version_name": ["other", "renpy.version_name", "", "", "var", "A human readable version name, of the form \\Example Version.\\"], "renpy.pause": ["internal", "function", "(delay=None, hard=False)", "", "", "Causes Ren'Py to pause. Returns true if the user clicked to end the pause, or false if the pause timed out or was skipped.\n\n`delay` If given, the number of seconds Ren'Py should pause for.\n\n`hard` This must be given as a keyword argument. When True, Ren'Py may prevent the user from clicking to interrupt the pause. If the player enables skipping, the hard pause will be skipped. There may be other circumstances where the hard pause ends early or prevents Ren'Py from operating properly, these will not be treated as bugs.\n\nIn general, using hard pauses is rude. When the user clicks to advance the game, it's an explicit request - the user wishes the game to advance. To override that request is to assume you understand what the player wants more than the player does.\n\nCalling renpy.pause guarantees that whatever is on the screen will be displayed for at least one frame, and hence has been shown to the player.\n\ntl;dr - Don't use renpy.pause with hard=True."], "renpy.add_layer": ["internal", "function", "(layer, above=None, below=None, menu_clear=True)", "", "", "Adds a new layer to the screen. If the layer already exists, this function does nothing.\n\nOne of `behind` or `above` must be given.\n\n`layer` A string giving the name of the new layer to add.\n\n`above` If not None, a string giving the name of a layer the new layer will be placed above.\n\n`below` If not None, a string giving the name of a layer the new layer will be placed below.\n\n`menu_clear` If true, this layer will be cleared when entering the game menu context, and restored when leaving the"], "renpy.variant": ["internal", "function", "(name)", "", "", "Returns true if a `name` is a screen variant that can be chosen by Ren'Py. See :ref:`screen-variants` for more details. This function can be used as the condition in a Python if statement to set up the appropriate styles for the selected screen variant.\n\n`name` can also be a list of variants, in which case this function returns True if any of the variants is selected."], "renpy.invoke_in_thread": ["internal", "function", "(fn, *args, **kwargs)", "", "", "Invokes the function `fn` in a background thread, passing it the provided arguments and keyword arguments. Restarts the interaction once the thread returns.\n\nThis function creates a daemon thread, which will be automatically stopped when Ren'Py is shutting down.\n\nThis thread is very limited in what it can do with the Ren'Py API. Changing store variables is allowed, as is calling the :func:`renpy.queue_event` function. Most other portions of the Ren'Py API are expected to be called from the main thread.\n\nThe primary use of this function is to place accesss to a web API in a second thread, and then update variables with the results of that call, by storing the result in variables and then relying on the interaction restart to cause screens to display those variables."], "renpy.predict_screen": ["internal", "function", "(_screen_name, *_args, **kwargs)", "", "", "Predicts the displayables that make up the given screen.\n\n`_screen_name` The name of the screen to show. `_widget_properties` A map from the id of a widget to a property name -> property value map. When a widget with that id is shown by the screen, the specified properties are added to it.\n\nKeyword arguments not beginning with underscore (_) are used to initialize the screen's scope."], "renpy.sound.stop": ["internal", "function", "(channel=\"sound\", fadeout=None)", "", "", "This stops the music that is currently playing, and dequeues all queued music. If fadeout is None, the music is faded out for the time given in config.fade_music, otherwise it is faded for fadeout seconds.\n\nThis sets the last queued file to None.\n\n`channel` The channel to stop the sound on.\n\n`fadeout` If not None, this is a time in seconds to fade for. Otherwise the fadeout time is taken from config.fade_music."], "renpy.choice_for_skipping": ["internal", "function", "()", "", "", "Tells Ren'Py that a choice is coming up soon. This currently has two effects:\n\n* If Ren'Py is skipping, and the Skip After Choices preferences is set to stop skipping, skipping is terminated.\n\n* An auto-save is triggered."], "renpy.in_fixed_rollback": ["internal", "function", "()", "", "", "Returns true if rollback is currently occurring and the current context is before an executed renpy.fix_rollback() statement."], "renpy.sound.queue": ["internal", "function", "(filenames, channel=\"sound\", loop=None, clear_queue=True, fadein=0, tight=None, relative_volume=1.0)", "", "", "This queues the given filenames on the specified channel.\n\n`filenames` This may be a single file, or a list of files to be played.\n\n`channel` The channel to play the sound on.\n\n`loop` If this is True, the tracks will loop while they are the last thing in the queue.\n\n`clear_queue` If True, then the queue is cleared, making these files the files that are played when the currently playing file finishes. If it is False, then these files are placed at the back of the queue. In either case, if no music is playing these files begin playing immediately.\n\n`fadein` This is the number of seconds to fade the music in for, on the first loop only.\n\n`tight` If this is True, then fadeouts will span into the next-queued sound. If None, this is true when loop is True, and false otherwise.\n\n`relative_volume` This is the volume relative to the current channel volume. The specified file will be played at that relative volume. If not specified, it will always default to 1.0, which plays the file at the original volume as determined by the mixer, channel and secondary volume.\n\nThis clears the pause flag for `channel`."], "renpy.hide_screen": ["internal", "function", "(tag, layer=None)", "", "", "The programmatic equivalent of the hide screen statement.\n\nHides the screen with `tag` on `layer`."], "renpy.fsencode": ["internal", "function", "(s)", "", "", "Converts s from unicode to the filesystem encoding."], "renpy.music.stop": ["internal", "function", "(channel=\"music\", fadeout=None)", "", "", "This stops the music that is currently playing, and dequeues all queued music. If fadeout is None, the music is faded out for the time given in config.fade_music, otherwise it is faded for fadeout seconds.\n\nThis sets the last queued file to None.\n\n`channel` The channel to stop the sound on.\n\n`fadeout` If not None, this is a time in seconds to fade for. Otherwise the fadeout time is taken from config.fade_music."], "renpy.copy_save": ["internal", "function", "(old, new)", "", "", "Copies the save at `old` to `new`. (Does nothing if `old` does not exist.)"], "renpy.get_renderer_info": ["internal", "function", "()", "", "", "Returns a dictionary, giving information about the renderer Ren'Py is currently using. Defined keys are:\n\n``\\renderer\\`` A string giving the name of the renderer that is in use.\n\n``\\resizable\\`` True if and only if the window is resizable.\n\n``\\additive\\`` True if and only if the renderer supports additive blending.\n\n``\\model\\`` Present and true if model-based rendering is supported.\n\nOther, renderer-specific, keys may also exist. The dictionary should be treated as immutable. This should only be called once the display has been started (that is, after the init phase has finished)."], "renpy.sound.set_pan": ["internal", "function", "(pan, delay, channel=\"sound\")", "", "", "Sets the pan of this channel.\n\n`pan` A number between -1 and 1 that control the placement of the audio. If this is -1, then all audio is sent to the left channel. If it's 0, then the two channels are equally balanced. If it's 1, then all audio is sent to the right ear.\n\n`delay` The amount of time it takes for the panning to occur.\n\n`channel` The channel the panning takes place on. This can be a sound or a music channel. Often, this is channel 7, the default music channel."], "renpy.end_replay": ["internal", "function", "()", "", "", "If we're in a replay, ends the replay immediately. Otherwise, does nothing."], "renpy.exists": ["internal", "function", "(filename)", "", "", "Returns true if the given filename can be found in the searchpath. This only works if a physical file exists on disk. It won't find the file if it's inside of an archive.\n\nYou almost certainly want to use :func:`renpy.loadable` in preference to this function."], "renpy.play": ["internal", "function", "(filename, channel=None, **kwargs)", "", "", "Plays a sound effect. If `channel` is None, it defaults to :var:`config.play_channel`. This is used to play sounds defined in styles, :propref:`hover_sound` and :propref:`activate_sound`."], "renpy.end_interaction": ["internal", "function", "(value)", "", "", "If `value` is not None, immediately ends the current interaction, causing the interaction to return `value`. If `value` is None, does nothing.\n\nThis can be called from inside the render and event methods of a creator-defined displayable."], "renpy.get_hidden_tags": ["internal", "function", "(layer)", "", "", "Returns the set of tags on layer that have attributes, but aren't being shown."], "renpy.sound.set_volume": ["internal", "function", "(volume, delay=0, channel=\"sound\")", "", "", "Sets the volume of this channel, as a fraction of the volume of the mixer controlling the channel.\n\n`volume` This is a number between 0.0 and 1.0, and is interpreted as a fraction of the mixer volume for the channel.\n\n`delay` It takes delay seconds to change/fade the volume from the old to the new value. This value is persisted into saves, and participates in rollback.\n\n`channel` The channel to be set"], "renpy.mark_audio_unseen": ["internal", "function", "(filename)", "", "", "Marks the given filename as if it has not been played on the current user's system yet."], "renpy.cancel_gesture": ["internal", "function", "()", "", "", "Cancels the current gesture, preventing the gesture from being recognized. This should be called by displayables that have gesture-like behavior."], "renpy.has_live2d": ["internal", "function", "()", "", "", "Returns True if Live2d is supported on the current platform, and False otherwise."], "renpy.stop_predict_screen": ["internal", "function", "(name)", "", "", "Causes Ren'Py to stop predicting the screen named `name`."], "renpy.music.get_loop": ["internal", "function", "(channel=\"music\")", "", "", "Return a list of filenames that are being looped on `channel`, or None if no files are being looped. In the case where a loop is queued, but is not yet playing, the loop is returned, not the currently playing music."], "renpy.get_showing_tags": ["internal", "function", "(layer)", "", "", "Returns the set of tags being shown on `layer`."], "renpy.newest_slot": ["internal", "function", "(regexp=None)", "", "", "Returns the name of the newest save slot (the save slot with the most recent modification time), or None if there are no (matching) saves.\n\nIf `regexp` exists, only slots that begin with `regexp` are returned."], "renpy.get_sdl_window_pointer": ["internal", "function", "()", "", "", "Returns a pointer (of type ctypes.c_void_p) to the main window, or None if the main window is not displayed, or some other problem occurs."], "renpy.music": ["basefile", "", "", "", "", "Most renpy.music functions have aliases in renpy.sound. These functions are similar, except they default to the sound channel rather than the music channel, and default to not looping."], "renpy.is_seen": ["internal", "function", "(ever=True)", "", "", "Returns true if the current line has been seen by the player.\n\nIf `ever` is true, we check to see if the line has ever been seen by the player. If false, we check if the line has been seen in the current play-through."], "renpy.get_say_image_tag": ["internal", "function", "()", "", "", "Returns the tag corresponding to the currently speaking character (the `image` argument given to that character). Returns None if no character is speaking or the current speaking character does not have a corresponding image tag."], "renpy.substitute": ["internal", "function", "(s, scope=None, translate=True)", "", "", "Applies translation and new-style formatting to the string `s`.\n\n`scope` If not None, a scope which is used in formatting, in addition to the default store.\n\n`translate` Determines if translation occurs.\n\nReturns the translated and formatted string."], "renpy.get_screen": ["internal", "function", "(name, layer=None)", "", "", "Returns the ScreenDisplayable with the given `name` on layer. `name` is first interpreted as a tag name, and then a screen name. If the screen is not showing, returns None.\n\nThis can also take a list of names, in which case the first screen that is showing is returned.\n\nThis function can be used to check if a screen is showing\n```\nif renpy.get_screen(\\say\\):\ntext \\The say screen is showing.\\\nelse:\ntext \\The say screen is hidden.\\\n\n```"], "renpy.is_skipping": ["internal", "function", "()", "", "", "Returns True if Ren'Py is currently skipping (in fast or slow skip mode), or False otherwise."], "renpy.check_image_attributes": ["internal", "function", "(tag, attributes)", "", "", "Checks to see if there is a unique image with the given tag and attributes. If there is, returns the attributes in order. Otherwise, returns None."], "renpy.fsdecode": ["internal", "function", "(s)", "", "", "Converts s from filesystem encoding to unicode."], "renpy.load_image": ["internal", "function", "(im)", "", "", "Loads the image manipulator `im` using the image cache, and returns a render."], "renpy.take_screenshot": ["internal", "function", "(scale=None, background=False)", "", "", "Causes a screenshot to be taken. This screenshot will be saved as part of a save game."], "renpy.register_sl_statement": ["internal", "class", "(name, children=u'many', screen=None)", "", "", "Registers a custom screen language statement with Ren'Py.\n\n`name` This must be a word. It's the name of the custom screen language statement.\n\n`children` The number of children this custom statement takes. This should be 0, 1, or \\many\\, which means zero or more.\n\n`screen` The screen to use. If not given, defaults to `name`.\n\nReturns an object that can have positional arguments and properties added to it. This object has the same .add_ methods as the objects returned by :class:`renpy.register_sl_displayable`."], "renpy.input": ["internal", "function", "(prompt, default='', allow=None, exclude='{}', length=None, with_none=None, pixel_width=None, screen=\"input\", mask=None, **kwargs)", "", "", "Calling this function pops up a window asking the player to enter some text. It returns the entered text.\n\n`prompt` A string giving a prompt to display to the player.\n\n`default` A string giving the initial text that will be edited by the player.\n\n`allow` If not None, a string giving a list of characters that will be allowed in the text.\n\n`exclude` If not None, if a character is present in this string, it is not allowed in the text.\n\n`length` If not None, this must be an integer giving the maximum length of the input string.\n\n`pixel_width` If not None, the input is limited to being this many pixels wide, in the font used by the input to display text.\n\n`screen` The name of the screen that takes input. If not given, the ``input`` screen is used.\n\n`mask` If not None, a single-character string that replaces the input text that is shown to the player, such as to conceal a password.\n\nIf :var:`config.disable_input` is True, this function only returns `default`.\n\nKeywords prefixed with ``show_`` have the prefix stripped and are passed to the screen."], "renpy.get_say_attributes": ["internal", "function", "()", "", "", "Gets the attributes associated with the current say statement, or None if no attributes are associated with this statement.\n\nThis is only valid when executing or predicting a say statement."], "renpy.define_screen": ["internal", "function", "(name, function, modal=\"False\", zorder=\"0\", tag=None, variant=None)", "", "", "Defines a screen with `name`, which should be a string.\n\n`function` The function that is called to display the screen. The function is called with the screen scope as keyword arguments. It should ignore additional keyword arguments.\n\nThe function should call the ui functions to add things to the screen.\n\n`modal` A string that, when evaluated, determines of the created screen should be modal. A modal screen prevents screens underneath it from receiving input events.\n\n`zorder` A string that, when evaluated, should be an integer. The integer controls the order in which screens are displayed. A screen with a greater zorder number is displayed above screens with a lesser zorder number.\n\n`tag` The tag associated with this screen. When the screen is shown, it replaces any other screen with the same tag. The tag defaults to the name of the screen.\n\n`predict` If true, this screen can be loaded for image prediction. If false, it can't. Defaults to true.\n\n`variant` String. Gives the variant of the screen to use."], "renpy.music_stop": ["internal", "function", "(fadeout=None)", "", "", "Deprecated music start function, retained for compatibility. Use renpy.music.play() or .queue() instead."], "renpy.clear_game_runtime": ["internal", "function", "()", "", "", "Resets the game runtime counter."], "renpy.jump": ["internal", "function", "(label)", "", "", "Causes the current statement to end, and control to jump to the given label."], "renpy.Displayable.event": ["cdd", "event", "(ev, x, y, st)", "renpy.Displayable", "method", "The event method is called to pass a pygame event to the creator-defined displayable. If the event method returns a value other than None, that value is returned as the result of the interaction. If the event method returns None, the event is passed on to other displayables.\n\nTo ignore the event without returning None, raise :class:`renpy.IgnoreEvent`.\n\nThe event method exists on other displayables, allowing the creator-defined displayable to pass on the event.\n\n`ev` An `event object `_\n\n`x`, `y` The x and y coordinates of the event, relative to the upper-left corner of the displayable. These should be used in preference to position information found in the pygame event objects.\n\n`st` A float, the shown timebase, in seconds.\n\nAn event is generated at the start of each interaction, and :func:`renpy.timeout` can be used to cause another event to occur."], "renpy.start_predict": ["internal", "function", "(*args)", "", "", "This function takes one or more displayables as arguments. It causes Ren'Py to predict those displayables during every interaction until the displayables are removed by :func:`renpy.stop_predict`.\n\nIf a displayable name is a string containing one or more \\\\* characters, the asterisks are used as a wildcard pattern. If there is at least one . in the string, the pattern is matched against filenames, otherwise it is matched against image names.\n\nFor example::\n\n$ renpy.start_predict(\\eileen *\\)\n\nstarts predicting all images with the name eileen, while::\n\n$ renpy.start_predict(\\images/concert*.*\\)\n\nmatches all files starting with concert in the images directory."], "renpy.reload_script": ["internal", "function", "()", "", "", "Causes Ren'Py to save the game, reload the script, and then load the save."], "renpy.types": ["internal", "function", "()", "", "", "Define names for all type symbols known in the standard interpreter.\n\nTypes that are part of optional modules (e.g. array) are not listed."], "renpy.Displayable.per_interact": ["cdd", "per_interact", "(self)", "renpy.Displayable", "method", "This method is called at the start of each interaction. It can be used to trigger a redraw, and probably should be used to trigger a redraw if the object participates in rollback."], "renpy.load_surface": ["internal", "function", "(im)", "", "", "Loads the image manipulator `im` using the image cache, and returns a pygame Surface."], "renpy.music.get_duration": ["internal", "function", "(channel=\"music\")", "", "", "Returns the duration of the audio or video file on `channel`. Returns 0.0 if no file is playing on `channel`."], "renpy.can_load": ["internal", "function", "(filename, test=False)", "", "", "Returns true if `filename` exists as a save slot, and False otherwise."], "renpy.pop_call": ["internal", "function", "()", "", "", "Pops the current call from the call stack, without returning to the location.\n\nThis can be used if a label that is called decides not to return to its caller."], "renpy.run_unhovered": ["internal", "function", "(var)", "", "", "Calls the unhovered method on the variable, if it exists."], "renpy.redraw": ["cdd", "renpy.redraw", "(d, when)", "", "function", "Causes the displayable `d` to be redrawn after `when` seconds have elapsed."], "renpy.Render.zoom": ["cdd", "zoom", "(xzoom, yzoom)", "renpy.Render", "method", "Sets the zoom level of the children of this displayable in the horitzontal and vertical axes. Only the children of the displayable are zoomed \u2013 the width, height, and blit coordinates are not zoomed.\n\nThe following attributes and methods are only used when model-based rendering is enabled:"], "renpy.count_newly_seen_dialogue_blocks": ["internal", "function", "()", "", "", "Returns the number of dialogue blocks the user has seen for the first time during this session."], "renpy.register_shader": ["internal", "function", "(name, **kwargs)", "", "", "This registers a shader part. This takes `name`, and then keyword arguments.\n\n`name` A string giving the name of the shader part. Names starting with an underscore or \\renpy.\\ are reserved for Ren'Py.\n\n`variables` The variables used by the shader part. These should be listed one per line, a storage (uniform, attribute, or varying) followed by a type, name, and semicolon. For example\n```\nvariables='''\nuniform sampler2D tex0;\nattribute vec2 a_tex_coord;\nvarying vec2 v_tex_coord;\n'''\n\n`vertex_functions`\nIf given, a string containing functions that will be included in the\nvertex shader.\n\n`fragment_functions`\nIf given, a string containing functions that will be included in the\nfragment shader.\n\nOther keyword arguments should start with ``vertex_`` or ``fragment_``,\nand end with an integer priority. So \\fragment_200\\ or \\vertex_300\\. These\ngive text that's placed in the appropriate shader at the given priority,\nwith lower priority numbers inserted before higher priority numbers.\n```"], "renpy.iconify": ["internal", "function", "()", "", "", "Iconifies the game."], "renpy.is_sensitive": ["internal", "function", "(action)", "", "", "Returns true if `action` indicates it is sensitive, or False otherwise."], "renpy.is_mouse_visible": ["internal", "function", "()", "", "", "Returns True if the mouse cursor is visible, False otherwise."], "renpy.sound.get_pause": ["internal", "function", "(channel=\"sound\")", "", "", "Returns the pause flag for `channel`."], "renpy.get_displayable": ["internal", "function", "(screen, id, layer=None)", "", "", "From the `screen` on `layer`, returns the displayable with `id`. Returns None if the screen doesn't exist, or there is no widget with that id on the screen."], "renpy.file": ["internal", "function", "(fn)", "", "", "Returns a read-only file-like object that accesses the file named `fn`. The file is accessed using Ren'Py's standard search method, and may reside in an RPA archive. or as an Android asset.\n\nThe object supports a wide subset of the fields and methods found on Python's standard file object, opened in binary mode. (Basically, all of the methods that are sensible for a read-only file.)"], "renpy.slot_json": ["internal", "function", "(slotname)", "", "", "Returns the json information for `slotname`, or None if the slot is empty."], "renpy.sound.set_queue_empty_callback": ["internal", "function", "(callback, channel=\"sound\")", "", "", "This sets a callback that is called when the queue is empty. This callback is called when the queue first becomes empty, and at least once per interaction while the queue is empty.\n\nThe callback is called with no parameters. It can queue sounds by calling renpy.music.queue with the appropriate arguments. Please note that the callback may be called while a sound is playing, as long as a queue slot is empty."], "renpy.sound.get_playing": ["internal", "function", "(channel=\"sound\")", "", "", "If the given channel is playing, returns the playing file name. Otherwise, returns None."], "renpy.predict": ["internal", "function", "(self)", "", "", "This is called to predictively load images from this node. It should cause renpy.display.predict.displayable and renpy.display.predict.screen to be called as necessary."], "renpy.checkpoint": ["internal", "function", "(data=None)", "", "", "Makes the current statement a checkpoint that the user can rollback to. Once this function has been called, there should be no more interaction with the user in the current statement.\n\nThis will also clear the current screenshot used by saved games.\n\n`data` This data is returned by :func:`renpy.roll_forward_info` when the game is being rolled back.\n\n`hard` If true, this is a hard checkpoint that rollback will stop at. If false, this is a soft checkpoint that will not stop rollback."], "renpy.music.set_pause": ["internal", "function", "(value, channel=\"music\")", "", "", "Sets the pause flag for `channel` to `value`. If True, the channel will pause, otherwise it will play normally."], "renpy.vibrate": ["internal", "function", "(duration)", "", "", "Causes the device to vibrate for `duration` seconds. Currently, this is only supported on Android."], "renpy.change_zorder": ["internal", "function", "(layer, tag, zorder)", "", "", "Changes the zorder of `tag` on `layer` to `zorder`."], "renpy.can_rollback": ["internal", "function", "()", "", "", "Returns true if we can rollback."], "renpy.Displayable.__init__": ["cdd", "__init__", "(**properties)", "renpy.Displayable", "method", "A subclass may override the constructor, perhaps adding new parameters. If it does, it should pass all unknown keyword arguments to the renpy.Displayable constructor, with the call\n```\nsuper(MyDisplayable, self).__init__(**properties)\n\n```"], "renpy.Render.add_shader": ["cdd", "add_shader", "(shader)", "renpy.Render", "method", "This causes the shader part `shader` to be used when this Render or its children are drawn. The part should be a string, or can be a string beginning with \\-\\ to prevent a shader from being drawn."], "renpy.fix_rollback": ["internal", "function", "()", "", "", "Prevents the user from changing decisions made before the current statement."], "renpy.Keymap": ["internal", "class", "(replaces=None, activate_sound=None, **keymap)", "", "", "This is a behavior that maps keys to actions that are called when the key is pressed. The keys are specified by giving the appropriate k_constant from pygame.constants, or the unicode for the key."], "renpy.save": ["internal", "function", "(filename, extra_info='')", "", "", "Saves the game state to a save slot.\n\n`filename` A string giving the name of a save slot. Despite the variable name, this corresponds only loosely to filenames.\n\n`extra_info` An additional string that should be saved to the save file. Usually, this is the value of :var:`save_name`.\n\n:func:`renpy.take_screenshot` should be called before this function."], "renpy.Render.blit": ["cdd", "blit", "(source, pos, main=True)", "renpy.Render", "method", "Draws another render object into this render object.\n\n`source` The render object to draw.\n\n`pos` The location to draw into. This is an (x, y) tuple with the coordinates being pixels relative to the upper-left corner of the target render.\n\n`main` A keyword-only parameter. If true, `source` will be displayed in the style inspector."], "renpy.notify": ["internal", "function", "(message)", "", "", "Causes Ren'Py to display the `message` using the notify screen. By default, this will cause the message to be dissolved in, displayed for two seconds, and dissolved out again.\n\nThis is useful for actions that otherwise wouldn't produce feedback, like screenshots or quicksaves.\n\nOnly one notification is displayed at a time. If a second notification is displayed, the first notification is replaced.\n\nThis function just calls :var:`config.notify`, allowing its implementation to be replaced by assigning a new function to that variable."], "renpy.get_zorder_list": ["internal", "function", "(layer)", "", "", "Returns a list of (tag, zorder) pairs for `layer`."], "renpy.windows": ["other", "renpy.windows", "", "", "var", "Has a true value when running on Windows."], "renpy.music.set_queue_empty_callback": ["internal", "function", "(callback, channel=\"music\")", "", "", "This sets a callback that is called when the queue is empty. This callback is called when the queue first becomes empty, and at least once per interaction while the queue is empty.\n\nThe callback is called with no parameters. It can queue sounds by calling renpy.music.queue with the appropriate arguments. Please note that the callback may be called while a sound is playing, as long as a queue slot is empty."], "renpy.Displayable.render": ["cdd", "render", "(width, height, st, at)", "renpy.Displayable", "method", "Subclasses must override this, to return a :class:`renpy.Render` object. The render object determines what, if anything, is shown on the screen.\n\n`width`, `height` The amount of space available to this displayable, in pixels.\n\n`st` A float, the shown timebase, in seconds. The shown timebase begins when this displayable is first shown on the screen.\n\n`at` A float, the animation timebase, in seconds. The animation timebase begins when an image with the same tag was shown, without being hidden. (When the displayable is shown without a tag, this is the same as the shown timebase.)\n\nThe render method is called when the displayable is first shown. It can be called again if :func:`renpy.redraw` is called on this object."], "renpy.lint": ["internal", "function", "()", "", "", "The master lint function, that's responsible for staging all of the other checks."], "renpy.get_game_runtime": ["internal", "function", "()", "", "", "Returns the game runtime counter.\n\nThe game runtime counter counts the number of seconds that have elapsed while waiting for user input in the top-level context. (It does not count time spent in the main or game menus.)"], "renpy.get_on_battery": ["internal", "function", "()", "", "", ":other:\n\nReturns True if Ren'Py is running on a device that is powered by an internal battery, or False if the device is being charged by some external source."], "renpy.linux": ["other", "renpy.linux", "", "", "var", "Has a true value when running on Linux or other POSIX-like operating systems."], "renpy.emscripten": ["other", "renpy.emscripten", "", "", "var", "Has a true value when running in the browser."], "renpy.get_at_list": ["internal", "function", "(name, layer=None, camera=False)", "", "", "Returns the list of transforms being applied to the image with tag `name` on `layer`. Returns an empty list if no transforms are being applied, or None if the image is not shown.\n\nIf `layer` is None, uses the default layer for the given tag."], "renpy.music.get_pause": ["internal", "function", "(channel=\"music\")", "", "", "Returns the pause flag for `channel`."], "renpy.is_init_phase": ["internal", "function", "()", "", "", "Returns True if Ren'Py is currently executing init code, or False otherwise."], "renpy.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "renpy.display_notify": ["internal", "function", "(message)", "", "", "The default implementation of :func:`renpy.notify`."], "renpy.Displayable": ["cdd", "renpy.Displayable", "", "", "class", "Base class for creator-defined displayables."], "renpy.set_style_preference": ["internal", "function", "(preference, alternative)", "", "", "Sets the selected alternative for the style preference.\n\n`preference` A string giving the name of the style preference.\n\n`alternative` A string giving the name of the alternative."], "renpy.roll_forward_info": ["internal", "function", "()", "", "", "When in rollback, returns the data that was supplied to :func:`renpy.checkpoint` the last time this statement executed. Outside of rollback, returns None."], "renpy.music_start": ["internal", "function", "(filename, loops=True, fadeout=None, fadein=0)", "", "", "Deprecated music start function, retained for compatibility. Use renpy.music.play() or .queue() instead."], "renpy.retain_after_load": ["internal", "function", "()", "", "", "Causes data modified between the current statement and the statement containing the next checkpoint to be retained when a load occurs."], "renpy.get_menu_args": ["internal", "function", "()", "", "", ":other:\n\nReturns a tuple giving the arguments (as a tuple) and the keyword arguments (as a dict) passed to the current menu statement."], "renpy.IgnoreEvent": ["cdd", "renpy.IgnoreEvent", "", "", "class", "This is an exception that, if raised, causes Ren'Py to ignore the event. To raise this inside the event method, write\n```\nraise renpy.IgnoreEvent()\n\n```"], "renpy.load_string": ["internal", "function", "(s, filename=\"\")", "", "", "Loads `s` as Ren'Py script that can be called.\n\nReturns the name of the first statement in s.\n\n`filename` is the name of the filename that statements in the string will appear to be from."], "renpy.get_transition": ["internal", "function", "(layer=None)", "", "", "Gets the transition for `layer`, or the entire scene if `layer` is None. This returns the transition that is queued up to run during the next interaction, or None if no such transition exists."], "renpy.seen_image": ["internal", "function", "(name)", "", "", "Returns True if the named image has been seen at least once on the user's system. An image has been seen if it's been displayed using the show statement, scene statement, or :func:`renpy.show` function. (Note that there are cases where the user won't actually see the image, like a show immediately followed by a hide.)"], "renpy.register_sl_displayable": ["internal", "function", "(name, displayable, style, nchildren=0, scope=False, replaces=False, default_keywords={}, default_properties=True)", "", "", "Registers a screen language statement that creates a displayable.\n\n`name` The name of the screen language statement, a string containing a Ren'Py keyword. This keyword is used to introduce the new statement.\n\n`displayable` This is a function that, when called, returns a displayable object. All position arguments, properties, and style properties are passed as arguments to this function. Other keyword arguments are also given to this function, a described below.\n\nThis must return a Displayable. If it returns multiple displayables, the _main attribute of the outermost displayable should be set to the \\main\\ displayable - the one that children should be added to.\n\n`style` The base name of the style of this displayable. If the style property is not given, this will have the style prefix added to it. The computed style is passed to the `displayable` function as the ``style`` keyword argument.\n\n`nchildren` The number of children of this displayable. One of:\n\n0 The displayable takes no children. 1 The displayable takes 1 child. If more than one child is given, the children are placed in a Fixed. \\many\\ The displayable takes more than one child.\n\n The following arguments should be passed in using keyword arguments:\n\n`replaces` If true, and the displayable replaces a prior displayable, that displayable is passed as a parameter to the new displayable.\n\n`default_keywords` The default set of keyword arguments to supply to the displayable.\n\n`default_properties` If true, the ui and position properties are added by default.\n\nReturns an object that can have positional arguments and properties added to it by calling the following methods. Each of these methods returns the object it is called on, allowing methods to be chained together.\n\n.. method:: add_positional(name)\n\nAdds a positional argument with `name`\n\n.. method:: add_property(name)\n\nAdds a property with `name`. Properties are passed as keyword arguments.\n\n.. method:: add_style_property(name)\n\nAdds a family of properties, ending with `name` and prefixed with the various style property prefixes. For example, if called with (\\size\\), this will define size, idle_size, hover_size, etc.\n\n.. method:: add_prefix_style_property(prefix, name)\n\nAdds a family of properties with names consisting of `prefix`, a style property prefix, and `name`. For example, if called with a prefix of `text_` and a name of `size`, this will create text_size, text_idle_size, text_hover_size, etc.\n\n.. method:: add_property_group(group, prefix='')\n\nAdds a group of properties, prefixed with `prefix`. `Group` may be one of the strings:\n\n* \\bar\\ * \\box\\ * \\button\\ * \\position\\ * \\text\\ * \\window\\\n\nThese correspond to groups of :ref:`style-properties`. Group can also be \\ui\\, in which case it adds the :ref:`common ui properties `."], "renpy.sound.is_playing": ["internal", "function", "(channel=\"sound\")", "", "", "Returns True if the channel is currently playing a sound, False if it is not, or if the sound system isn't working."], "renpy.is_start_interact": ["internal", "function", "()", "", "", "Returns true if restart_interaction has not been called during the current interaction. This can be used to determine if the interaction is just being started, or has been restarted."], "renpy.add_to_all_stores": ["internal", "function", "(name, value)", "", "", "Adds the `value` by the `name` to all creator defined namespaces. If the name already exist in that namespace - do nothing for it.\n\nThis function may only be run from inside an init block. It is an error to run this function once the game has started."], "renpy.full_restart": ["internal", "function", "(transition=False, label=\"_invoke_main_menu\", target=\"_main_menu\", save=False)", "", "", "Causes Ren'Py to restart, returning the user to the main menu.\n\n`transition` If given, the transition to run, or None to not run a transition. False uses :var:`config.end_game_transition`.\n\n`save` If true, the game is saved in :var:`_quit_slot` before Ren'Py restarts and returns the user to the main menu."], "renpy.count_displayables_in_layer": ["internal", "function", "(layer)", "", "", "Returns how many displayables are in the supplied layer."], "renpy.get_style_preference": ["internal", "function", "(preference)", "", "", "Returns a string giving the name of the selected alternative for the named style preference.\n\n`preference` A string giving the name of the style preference."], "renpy.slot_mtime": ["internal", "function", "(slotname)", "", "", "Returns the modification time for `slot`, or None if the slot is empty."], "renpy.seen_label": ["internal", "function", "(label)", "", "", "Returns true if the named label has executed at least once on the current user's system, and false otherwise. This can be used to unlock scene galleries, for example."], "renpy.set_focus": ["internal", "function", "(screen, id, layer=\"screens\")", "", "", "This attempts to focus the displayable with `id` in the screen `screen`. Focusing will fail if the displayable isn't found, the window isn't focused, or something else is grabbing focus.\n\nThe focus may change if the mouse moves, even slightly, after this call is processed."], "renpy.get_all_labels": ["internal", "function", "()", "", "", "Returns the set of all labels defined in the program, including labels defined for internal use in the libraries."], "renpy.image": ["internal", "function", "(name, d)", "", "", "Defines an image. This function is the Python equivalent of the image statement.\n\n`name` The name of the image to display, a string.\n\n`d` The displayable to associate with that image name.\n\nThis function may only be run from inside an init block. It is an error to run this function once the game has started."], "renpy.get_sdl_dll": ["internal", "function", "()", "", "", "This returns a ctypes.cdll object that refers to the library that contains the instance of SDL2 that Ren'Py is using.\n\nIf this can not be done, None is returned."], "renpy.hide": ["internal", "function", "(name, layer=None)", "", "", "Hides an image from a layer. The Python equivalent of the hide statement.\n\n`name` The name of the image to hide. Only the image tag is used, and any image with the tag is hidden (the precise name does not matter).\n\n`layer` The layer on which this function operates. If None, uses the default layer associated with the tag."], "renpy.music.get_playing": ["internal", "function", "(channel=\"music\")", "", "", "If the given channel is playing, returns the playing file name. Otherwise, returns None."], "renpy.sound.get_loop": ["internal", "function", "(channel=\"sound\")", "", "", "Return a list of filenames that are being looped on `channel`, or None if no files are being looped. In the case where a loop is queued, but is not yet playing, the loop is returned, not the currently playing music."], "renpy.get_image_load_log": ["internal", "function", "(age=None)", "", "", "A generator that yields a log of image loading activity. For the last 100 image loads, this returns:\n\n* The time the image was loaded (in seconds since the epoch). * The filename of the image that was loaded. * A boolean that is true if the image was preloaded, and false if the game stalled to load it.\n\nThe entries are ordered from newest to oldest.\n\n`age` If not None, only images that have been loaded in the past `age` seconds are included.\n\nThe image load log is only kept if config.developer = True."], "renpy.Render": ["cdd", "renpy.Render", "(width, height)", "", "class", "Creates a new Render object.\n\n`width`, `height` The width and height of the render object, in pixels."], "renpy.music.play": ["internal", "function", "(filenames, channel=\"music\", loop=None, fadeout=None, synchro_start=False, fadein=0, tight=None, if_changed=False, relative_volume=1.0)", "", "", "This stops the music currently playing on the numbered channel, dequeues any queued music, and begins playing the specified file or files.\n\n`filenames` This may be a single file, or a list of files to be played.\n\n`channel` The channel to play the sound on.\n\n`loop` If this is True, the tracks will loop while they are the last thing in the queue.\n\n`fadeout` If not None, this is a time in seconds to fade for. Otherwise the fadeout time is taken from config.fade_music.\n\n`synchro_start` Ren'Py will ensure that all channels of with synchro_start set to true will start playing at exactly the same time. Synchro_start should be true when playing two audio files that are meant to be synchronized with each other.\n\n`fadein` This is the number of seconds to fade the music in for, on the first loop only.\n\n`tight` If this is True, then fadeouts will span into the next-queued sound. If None, this is true when loop is True, and false otherwise.\n\n`if_changed` If this is True, and the music file is currently playing, then it will not be stopped/faded out and faded back in again, but instead will be kept playing. (This will always queue up an additional loop of the music.)\n\n`relative_volume` This is the volume relative to the current channel volume. The specified file will be played at that relative volume. If not specified, it will always default to 1.0, which plays the file at the original volume as determined by the mixer, channel and secondary volume.\n\nThis clears the pause flag for `channel`."], "renpy.version": ["internal", "function", "(tuple=False)", "", "", "If `tuple` is false, returns a string containing \\Ren'Py \\, followed by the current version of Ren'Py.\n\nIf `tuple` is true, returns a tuple giving each component of the version as an integer."], "renpy.jump_out_of_context": ["internal", "function", "(label)", "", "", "Causes control to leave the current context, and then to be transferred in the parent context to the given label."], "renpy.Render.subsurface": ["cdd", "subsurface", "(rect)", "renpy.Render", "method", "Returns a render consisting of a rectangle cut out of this render.\n\n`rect` A (x, y, width, height) tuple."], "renpy.Render.mesh": ["cdd", "mesh", "", "renpy.Render", "attribute", "This field enables model-based rendering for this Render. If true:\n\nIf set to True:\n\n* All of the children of this displayable are rendered to textures. * A mesh the size of the first child is assocated with this displayable. * A model is created with the mesh, shaders, uniforms, and properties associated with this Render.\n\nThe model will then be drawn in a single operation."], "renpy.display_menu": ["internal", "function", "(items, interact=True, screen=\"choice\")", "", "", "This displays a menu to the user. `items` should be a list of 2-item tuples. In each tuple, the first item is a textual label, and the second item is the value to be returned if that item is selected. If the value is None, the first item is used as a menu caption.\n\nThis function takes many arguments, of which only a few are documented. Except for `items`, all arguments should be given as keyword arguments.\n\n`interact` If false, the menu is displayed, but no interaction is performed.\n\n`screen` The name of the screen used to display the menu.\n\nNote that most Ren'Py games do not use menu captions, but use narration instead. To display a menu using narration, write\n```\n$ narrator(\\Which direction would you like to go?\\, interact=False)\n$ result = renpy.display_menu([ (\\East\\, \\east\\), (\\West\\, \\west\\) ])\n\n```"], "renpy.screenshot": ["internal", "function", "(filename)", "", "", "Saves a screenshot in `filename`.\n\nReturns True if the screenshot was saved successfully, False if saving failed for some reason.\n\nThe :var:`config.screenshot_pattern` and :var:`_screenshot_pattern` variables control the file the screenshot is saved in."], "renpy.show": ["internal", "function", "(name, at_list=[], layer='master', what=None, zorder=0, tag=None, behind=[])", "", "", "Shows an image on a layer. This is the programmatic equivalent of the show statement.\n\n`name` The name of the image to show, a string.\n\n`at_list` A list of transforms that are applied to the image. The equivalent of the ``at`` property.\n\n`layer` A string, giving the name of the layer on which the image will be shown. The equivalent of the ``onlayer`` property. If None, uses the default layer associated with the tag.\n\n`what` If not None, this is a displayable that will be shown in lieu of looking on the image. (This is the equivalent of the show expression statement.) When a `what` parameter is given, `name` can be used to associate a tag with the image.\n\n`zorder` An integer, the equivalent of the ``zorder`` property. If None, the zorder is preserved if it exists, and is otherwise set to 0.\n\n`tag` A string, used to specify the image tag of the shown image. The equivalent of the ``as`` property.\n\n`behind` A list of strings, giving image tags that this image is shown behind. The equivalent of the ``behind`` property."], "renpy.mark_label_seen": ["internal", "function", "(label)", "", "", "Marks the named label as if it has been already executed on the current user's system."], "renpy.count_dialogue_blocks": ["internal", "function", "()", "", "", "Returns the number of dialogue blocks in the game's original language."], "renpy.music.register_channel": ["audio", "renpy.music.register_channel", "(name, mixer=None, loop=None, stop_on_mute=True, tight=False, file_prefix=\"\", file_suffix=\"\", buffer_queue=True, movie=False, framedrop=True)", "", "", "This registers a new audio channel named `name`. Audio can then be played on the channel by supplying the channel name to the play or queue statements.\n\n`mixer` The name of the mixer the channel uses. By default, Ren'Py knows about the \\music\\, \\sfx\\, and \\voice\\ mixers. Using other names is possible, but may require changing the preferences screens.\n\n`loop` If true, sounds on this channel loop by default.\n\n`stop_on_mute` If true, music on the channel is stopped when the channel is muted.\n\n`tight` If true, sounds will loop even when fadeout is occurring. This should be set to True for a sound effects or seamless music channel, and False if the music fades out on its own.\n\n`file_prefix` A prefix that is prepended to the filenames of the sound files being played on this channel.\n\n`file_suffix` A suffix that is appended to the filenames of the sound files being played on this channel.\n\n`buffer_queue` Should we buffer the first second or so of a queued file? This should be True for audio, and False for movie playback.\n\n`movie` If true, this channel will be set up to play back videos.\n\n`framedrop` This controls what a video does when lagging. If true, frames will be dropped to keep up with realtime and the soundtrack. If false, Ren'Py will display frames late rather than dropping them."], "renpy.list_files": ["internal", "function", "(common=False)", "", "", "Lists the files in the game directory and archive files. Returns a list of files, with / as the directory separator.\n\n`common` If true, files in the common directory are included in the listing."]}, "config": {"config.menu_arguments_callback": ["config", "config.menu_arguments_callback", " = None", "", "var", "If not None, this should be a function that takes positional and/or keyword arguments. It's called whenever a menu statement runs, with the arguments to that menu statement.\n\nThis should return a pair, containing a tuple of positional arguments (almost always empty), and a dictionary of keyword arguments."], "config.new_substitutions": ["config", "config.new_substitutions", " = True", "", "var", "If True, Ren'Py will apply new-style (square-bracket) substitutions to all text displayed."], "config.tag_zorder": ["config", "config.tag_zorder", " = { }", "", "var", "A dictionary mapping image tag strings to zorders. When an image is newly-shown without a zorder clause, the image's tag is looked up in this dictionary to find a zorder to use. If no zorder is found, 0 is used."], "config.show": ["config", "config.show", " = renpy.show", "", "var", "A function that is used in place of renpy.show by the :ref:`show ` and :ref:`scene ` statements. This should have the same signature as renpy.show."], "config.enter_transition": ["config", "config.enter_transition", " = None", "", "var", "If not None, this variable should give a transition that will be used when entering the game menu."], "config.script_version": ["config", "config.script_version", " = None", "", "var", "If not None, this is interpreted as a script version. The library will use this script version to enable some compatibility features, if necessary. If None, we assume this is a latest-version script.\n\nThis is normally set in a file added by the Ren'Py launcher when distributions are built."], "config.exception_handler": ["config", "config.exception_handler", " = None", "", "var", "If not None, this should be a function that takes three arguments:\n\n* A string giving the text of a traceback, abbreviated so that it only includes creator-written files. * The full text of the traceback, including both creator-written and Ren'Py files. * The path to a file containing a traceback method.\n\nThis function can present the error to a user in any way fit. If it returns True, the exception is ignored and control is transferred to the next statement. If it returns False, the built-in exception handler is use. This function may also call :func:`renpy.jump` to transfer control to some other label."], "config.displayable_prefix": ["config", "config.displayable_prefix", " = { }", "", "var", "See :ref:`Displayable prefixes `."], "config.history_length": ["gui", "config.history_length", " = 250", "", "var", "The number of blocks of dialogue Ren'Py will keep at history."], "config.fade_music": ["config", "config.fade_music", " = 0.0", "", "var", "This is the amount of time in seconds to spend fading the old track out before a new music track starts. This should probably be fairly short, so the wrong music doesn't play for too long."], "config.voice_filename_format": ["config", "config.voice_filename_format", " = \"{filename}\"", "", "var", "A string that is formatted with the string argument to the voice statement to produce the filename that is played to the user. For example, if this is \\{filename}.ogg\\, the ``voice \\test\\`` statement will play test.ogg."], "config.after_load_transition": ["config", "config.after_load_transition", " = None", "", "var", "A transition that is used after loading, when entering the loaded game."], "config.mipmap_text": ["config", "config.mipmap_text", " = False", "", "var", "The default value of the mipmap argument to :func:`Text`, including text used in screen statements."], "config.scene": ["config", "config.scene", " = renpy.scene", "", "var", "A function that's used in place of renpy.scene by the :ref:`scene statement `. Note that this is used to clear the screen, and config.show is used to show a new image. This should have the same signature as renpy.scene."], "config.gl_resize": ["config", "config.gl_resize", " = True", "", "var", "Determines if the user is allowed to resize an OpenGL-drawn window."], "config.missing_background": ["config", "config.missing_background", " = \"black\"", "", "var", "This is the background that is used when :var:`config.developer` is True and an undefined image is used in a :ref:`scene statement `. This should be an image name (a string), not a displayable."], "config.log_gl_shaders": ["model", "config.log_gl_shaders", " = False", "", "var", "If true, source code for the GLSL shader programs will be written to log.txt on start."], "config.end_splash_transition": ["config", "config.end_splash_transition", " = None", "", "var", "The transition that is used to display the main menu after the end of the splashscreen."], "config.with_callback": ["config", "config.with_callback", " = None", "", "var", "If not None, this should be a function that is called when a :ref:`with statement ` occurs. This function can be responsible for putting up transient things on the screen during the transition. The function is called with a single argument, which is the transition that is occurring. It is expected to return a transition, which may or may not be the transition supplied as its argument."], "config.context_clear_layers": ["config", "config.context_clear_layers", " = [ 'screens' ]", "", "var", "A list of layers that are cleared when entering a new context."], "config.nvl_adv_transition": ["config", "config.nvl_adv_transition", " = None", "", "var", "A transition that is used when showing ADV-mode text directly after NVL-mode text."], "config.clear_layers": ["config", "config.clear_layers", " = []", "", "var", "A list of names of layers to clear when entering the main and game menus."], "config.enter_replay_transition": ["config", "config.enter_replay_transition", " = None", "", "var", "If not None, a transition that is used when entering a replay."], "config.history_callbacks": ["config", "config.history_callbacks", " = [ ... ]", "", "var", "This contains a list of callbacks that are called before Ren'Py adds a new object to _history_list. The callbacks are called with the new HistoryEntry object as the first argument, and can add new fields to that object.\n\nRen'Py uses history callbacks internally, so creators should append their own callbacks to this list, rather than replacing it entirely."], "config.enable_language_autodetect": ["config", "config.enable_language_autodetect", " = False", "", "var", "If true, Ren'Py will attempt to determine the name of the language to use based on the locale of the player's system. If successful, this language will be used as the default language."], "config.say_menu_text_filter": ["config", "config.say_menu_text_filter", " = None", "", "var", "If not None, then this is a function that is given the text found in strings in the :ref:`say ` and :ref:`menu ` statements. It is expected to return new (or the same) strings to replace them."], "config.archives": ["config", "config.archives", " = [ ]", "", "var", "A list of archive files that will be searched for images and other data. The entries in this should consist of strings giving the base names of archive files, without the .rpa extension.\n\nThe archives are searched in the order they are found in this list. A file is taken from the first archive it is found in.\n\nAt startup, Ren'Py will automatically populate this variable with the names of all archives found in the game directory, sorted in reverse ascii order. For example, if Ren'Py finds the files data.rpa, patch01.rpa, and patch02.rpa, this variable will be populated with ``['patch02', 'patch01', 'data']``."], "config.imagemap_auto_function": ["config", "config.imagemap_auto_function", " = ...", "", "var", "A function that expands the `auto` property of a screen language :ref:`imagebutton ` or :ref:`imagemap ` statement into a displayable. It takes the value of the auto property, and the desired image, one of: \\insensitive\\, \\idle\\, \\hover\\, \\selected_idle\\, \\selected_hover\\, or \\ground\\. It should return a displayable or None.\n\nThe default implementation formats the `auto` property with the desired image, and then checks if the computed filename exists."], "config.layer_clipping": ["config", "config.layer_clipping", " = { }", "", "var", "Controls layer clipping. This is a map from layer names to (x, y, height, width) tuples, where x and y are the coordinates of the upper-left corner of the layer, with height and width giving the layer size.\n\nIf a layer is not mentioned in config.layer_clipping, then it is assumed to take up the full screen."], "config.skip_sounds": ["config", "config.skip_sounds", " = False", "", "var", "If False, non-looping audio will not be played when Ren'Py is skipping."], "config.version": ["config", "config.version", " = \"\"", "", "var", "This should be a string giving the version of the game. This is included as part of tracebacks and other log files, helping to identify the version of the game being used."], "config.nearest_neighbor": ["config", "config.nearest_neighbor", " = False", "", "var", "Uses nearest-neighbor filtering by default, to support pixel art or melting players' eyes."], "config.idle_gc_count": ["config", "config.idle_gc_count", " = 2500", "", "var", "The net number of objects that triggers a collection when Ren'Py has reached a steady state. (The fourth frame or later after the screen has been updated.)"], "config.allow_screensaver": ["config", "config.allow_screensaver", " = True", "", "var", "If True, the screensaver may activite while the game is running. If False, the screensaver is disabled."], "config.gc_print_unreachable": ["config", "config.gc_print_unreachable", " = False", "", "var", "If True, Ren'Py will print to its console and logs information about the objects that are triggering collections."], "config.longpress_vibrate": ["config", "config.longpress_vibrate", " = .1", "", "var", "The amount of time the device will vibrate for after a longpress."], "config.say_arguments_callback": ["config", "config.say_arguments_callback", " = None", "", "var", "If not None, this should be a function that takes the speaking character, followed by positional and keyword arguments. It's called whenever a say statement occurs with the arguments to that say statement. This always includes an interact argument, and can include others provided in the say statement.\n\nThis should return a pair, containing a tuple of positional arguments (almost always empty), and a dictionary of keyword arguments (almost always with at least interact in it).\n\nFor example\n```\ndef say_arguments_callback(who, interact=True, color=\\#fff\\):\nreturn (), { \\interact\\ : interact, \\what_color\\ : color }\n\nconfig.say_arguments_callback = say_arguments_callback\n\n```"], "config.adjust_view_size": ["config", "config.adjust_view_size", " = None", "", "var", "If not None, this should be a function taking two arguments, the width and height of the physical window. It is expected to return a tuple giving the width and height of the OpenGL viewport, the portion of the screen that Ren'Py will draw pictures to.\n\nThis can be used to configure Ren'Py to only allow certain sizes of screen. For example, the following allows only integer multiples of the original screen size\n```\ninit python:\n\ndef force_integer_multiplier(width, height):\nmultiplier = min(width / config.screen_width, height / config.screen_height)\nmultiplier = max(int(multiplier), 1)\nreturn (multiplier * config.screen_width, multiplier * config.screen_height)\n\nconfig.adjust_view_size = force_integer_multiplier\n\n```"], "config.language": ["config", "config.language", " = None", "", "var", "If not None, this should be a string giving the default language that the game is translated into by the translation framework."], "config.profile": ["config", "config.profile", " = False", "", "var", "If set to True, some profiling information will be output to stdout."], "config.game_menu_music": ["config", "config.game_menu_music", " = None", "", "var", "If not None, a music file to play when at the game menu."], "config.enter_sound": ["config", "config.enter_sound", " = None", "", "var", "If not None, this is a sound file that is played when entering the game menu."], "config.side_image_change_transform": ["side_image", "config.side_image_change_transform", " = None", "", "var", "If not None, a transform that is used when the new side image does not share the name image tag (or one of the new or old side images does not exist)."], "config.window_icon": ["config", "config.window_icon", " = None", "", "var", "If not None, this is expected to be the filename of an image giving an icon that is used for the game's main window. This does not set the icon used by windows executables and mac apps, as those are controlled by :ref:`special-files`."], "config.side_image_prefix_tag": ["side_image", "config.side_image_prefix_tag", " = 'side'", "", "var", "The prefix that is used when searching for a side image."], "config.menu_window_subtitle": ["config", "config.menu_window_subtitle", " = \"\"", "", "var", "The :var:`_window_subtitle` variable is set to this value when entering the main or game menus."], "config.loadable_callback": ["config", "config.loadable_callback", " = None", "", "var", "When not None, a function that's called with a filename. It should return True if the file is loadable, and False if not. This can be used with :var:`config.file_open_callback` or :var:`config.missing_image_callback`."], "config.mouse_hide_time": ["config", "config.mouse_hide_time", " = 30", "", "var", "The mouse is hidden after this number of seconds has elapsed without any mouse input. This should be set to longer than the expected time it will take to read a single screen, so mouse users will not experience the mouse appearing then disappearing between clicks.\n\nIf None, the mouse will never be hidden."], "config.gestures": ["gesture", "config.gestures", " = { \"n_s_w_e_w_e\" : \"progress_screen\" }", "", "var", "A map from gesture to the event activated by the gesture."], "config.replace_text": ["config", "config.replace_text", " = None", "", "var", "If not None, a function that is called with a single argument, a text to be displayed to the user. The function can return the same text it was passed, or a replacement text that will be displayed instead.\n\nThe function is called after substitutions have been performed and after the text has been split on tags, so its argument contains nothing but actual text. All displayed text passes through the function: not only dialogue text, but also user interface text.\n\nThis can be used to replace specific ASCII sequences with corresponding Unicode characters, as demonstrated by the following\n```\ndef replace_text(s):\ns = s.replace(\\'\\, u'\\u2019') # apostrophe\ns = s.replace('--', u'\\u2014') # em dash\ns = s.replace('...', u'\\u2026') # ellipsis\nreturn s\nconfig.replace_text = replace_text\n\n```"], "config.screen_width": ["config", "config.screen_width", " = 800", "", "var", "The width of the screen. Usually set by :func:`gui.init`."], "config.cache_surfaces": ["config", "config.cache_surfaces", " = False", "", "var", "If True, the underlying data of an image is stored in RAM, allowing image manipulators to be applied to that image without reloading it from disk. If False, the data is dropped from the cache, but kept as a texture in video memory, reducing RAM usage."], "config.custom_text_tags": ["custom_text_tags", "config.custom_text_tags", "", "", "var", "Maps text tag names to text tag functions, when the text tag can wrap other text."], "config.predict_statements": ["config", "config.predict_statements", " = 32", "", "var", "This is the number of statements, including the current one, to consider when doing predictive image loading. A breadth-first search from the current statement is performed until this number of statements is considered, and any image referenced in those statements is potentially predictively loaded. Setting this to 0 will disable predictive loading of images."], "config.play_channel": ["config", "config.play_channel", " = \"audio\"", "", "var", "The name of the audio channel used by :func:`renpy.play`, :propref:`hover_sound`, and :propref:`activate_sound`."], "config.transform_uses_child_position": ["config", "config.transform_uses_child_position", " = True", "", "var", "If True, transforms will inherit :ref:`position properties ` from their child. If not, they won't."], "config.after_load_callbacks": ["config", "config.after_load_callbacks", " = [ ... ]", "", "var", "A list of functions that are called (with no arguments) when a load occurs."], "config.save_physical_size": ["config", "config.save_physical_size", " = True", "", "var", "If True, the physical size of the window will be saved in the preferences, and restored when the game resumes."], "config.say_attribute_transition": ["config", "config.say_attribute_transition", " = None", "", "var", "If not None, a transition to use when the image is changed by a say statement with image attributes."], "config.lint_hooks": ["config", "config.lint_hooks", " = ...", "", "var", "This is a list of functions that are called, with no arguments, when lint is run. The functions are expected to check the script data for errors, and print any they find to standard output (using the Python ``print`` statement is fine in this case)."], "config.side_image_same_transform": ["side_image", "config.side_image_same_transform", " = None", "", "var", "If not None, a transform that is used when the new side image shares the same image tag as the previous side image."], "config.context_copy_remove_screens": ["config", "config.context_copy_remove_screens", " = [ 'notify' ]", "", "var", "Contains a list of screens that are removed when a context is copied for rollback or saving."], "config.sound": ["config", "config.sound", " = True", "", "var", "If True, sound works. If False, the sound/mixer subsystem is completely disabled."], "config.searchpath": ["config", "config.searchpath", " = [ 'common', 'game' ]", "", "var", "A list of directories that are searched for images, music, archives, and other media, but not scripts. This is initialized to a list containing \\common\\ and the name of the game directory."], "config.window": ["config", "config.window", " = None", "", "var", "This controls the default method of dialogue window management. If not None, this should be one of \\show\\, \\hide\\, or \\auto\\.\n\nWhen set to \\show\\, the dialogue window is shown at all times. When set to \\hide\\, the dialogue window is hidden when not in a say statement or other statement that displays dialogue. When set to \\auto\\, the dialogue window is hidden before scene statements, and shown again when dialogue is shown.\n\nThis sets the default. Once set, the default can be changed using the ``window show``, ``window hide`` and ``window auto`` statements. See :ref:`dialogue-window-management` for more information."], "config.window_auto_hide": ["config", "config.window_auto_hide", " = [ 'scene', 'call screen', 'menu', \"say-centered\" ]", "", "var", "A list of statements that cause ``window auto`` to hide the empty dialogue window."], "config.say_layer": ["config", "config.say_layer", " = \"screens\"", "", "var", "The layer the say screen is shown on."], "config.exit_replay_transition": ["config", "config.exit_replay_transition", " = None", "", "var", "If not None, a transition that is used when exiting a replay."], "config.autosave_on_input": ["config", "config.autosave_on_input", " = True", "", "var", "If True, Ren'Py will autosave when the user inputs text. (When :func:`renpy.input` is called.)"], "config.default_sfx_volume": ["preferences", "config.default_sfx_volume", " = 1.0", "", "var", "The default volume of the sfx mixer, which is used for the sound audio channel. This should be a number between 0.0 and 1.0, with 1.0 being full volume."], "config.developer": ["config", "config.developer", " = \"auto\"", "", "var", "If set to True, developer mode is enabled. Developer mode gives access to the shift+D developer menu, shift+R reloading, and various other features that are not intended for end users.\n\nThis can be True, False, or \\auto\\. If \\auto\\, Ren'Py will detect if the game has been packaged into a distribution, and set config.developer as appropriate."], "config.intra_transition": ["config", "config.intra_transition", " = None", "", "var", "The transition that is used between screens of the game menu."], "config.end_game_transition": ["config", "config.end_game_transition", " = None", "", "var", "The transition that is used to display the main menu after the game ends normally, either by invoking return with no place to return to, or by calling :func:`renpy.full_restart`."], "config.narrator_menu": ["config", "config.narrator_menu", " = False", "", "var", "(This is set to True by the default screens.rpy file.) If true, then narration inside a menu is displayed using the narrator character. Otherwise, narration is displayed as captions within the menu itself."], "config.nvl_paged_rollback": ["nvl_mode", "config.nvl_paged_rollback", " = False", "", "var", "If true, NVL-mode rollback will occur a full page at a time."], "config.after_replay_callback": ["config", "config.after_replay_callback", " = None", "", "var", "If not None, a function that is called with no arguments after a replay completes."], "config.movie_mixer": ["config", "config.movie_mixer", " = \"music\"", "", "var", "The mixer that is used when a :func:`Movie` automatically defines a channel for video playback."], "config.keep_running_transform": ["config", "config.keep_running_transform", " = True", "", "var", "If True, showing an image without supplying a transform or ATL block will cause the image to continue the previous transform an image with that tag was using, if any. If False, the transform is stopped."], "config.mouse": ["config", "config.mouse", " = None", "", "var", "This variable controls the use of user-defined mouse cursors. If None, the system mouse is used, which is usually a black-and-white mouse cursor.\n\nOtherwise, this should be a dictionary giving the mouse animations for various mouse types. Keys used by the default library include \\default\\, \\say\\, \\with\\, \\menu\\, \\prompt\\, \\imagemap\\, \\pause\\, \\mainmenu\\, and \\gamemenu\\. The \\default\\ key should always be present, as it is used when a more specific key is absent.\n\nEach value in the dictionary should be a list of (`image`, `xoffset`, `yoffset`) tuples, representing frames.\n\n`image` The mouse cursor image. The maximum size for this image varies based on the player's hardware. 32x32 is guaranteed to work everywhere, while 64x64 works on most hardware. Larger images may not work.\n\n`xoffset` The offset of the hotspot pixel from the left side of the cursor.\n\n`yoffset` The offset of the hotspot pixel from the top of the cursor.\n\nThe frames are played back at 20Hz, and the animation loops after all frames have been shown."], "config.game_main_transition": ["config", "config.game_main_transition", " = None", "", "var", "The transition that is used to display the main menu after leaving the game menu. This is used when the load and preferences screens are invoked from the main menu, and it's also used when the user picks \\Main Menu\\ from the game menu."], "config.file_open_callback": ["config", "config.file_open_callback", " = None", "", "var", "If not None, this is a function that is called with the file name when a file needs to be opened. It should return a file-like object, or None to load the file using the usual Ren'Py mechanisms. Your file-like object must implement at least the read, seek, tell, and close methods.\n\nOne may want to also define a :var:`config.loadable_callback` that matches this."], "config.per_frame_screens": ["config", "config.per_frame_screens", " = [ ... ]", "", "var", "This is a list of strings giving the name of screens that are updated once per frame, rather than once per interaction. Ren'Py uses this internally, so if you add a screen, append the name rather than replacing the list in its entirety."], "config.mouse_displayable": ["config", "config.mouse_displayable", " = None", "", "var", "If not None, this should either be a displayable, or a callable that returns a displayable. The callable may return None, in which case Ren'Py proceeds if the displayable is None.\n\nIf a displayable is given, the mouse cursor is hidden, and the displayable is shown above anything else. This displayable is responsible for positioning and drawing a sythetic mouse cursor, and so should probably be a :func:`MouseDisplayable` or something very similar."], "config.skip_delay": ["config", "config.skip_delay", " = 75", "", "var", "The amount of time that dialogue will be shown for, when skipping statements using ctrl, in milliseconds. (Although it's nowhere near that precise in practice.)"], "config.nvl_page_ctc": ["nvl_mode", "config.nvl_page_ctc", " = None", "", "var", "If not None, this is the click-to-continue indicator that is used for NVL mode characters that are at the end of a page. (That is, immediately followed by an nvl clear statement.) This replaces the ctc parameter of :func:`Character`."], "config.screenshot_pattern": ["config", "config.screenshot_pattern", " = \"screenshot%04d.png\"", "", "var", "The pattern used to create screenshot files. This pattern is applied (using Python's %-formatting rules) to the natural numbers to generate a sequence of filenames. The filenames may be absolute, or relative to config.renpy_base. The first filename that does not exist is used as the name of the screenshot.\n\nDirectories are created if they do not exist.\n\nSee also :var:`_screenshot_pattern`, which is used in preference to this variable if not None."], "config.controller_blocklist": ["config", "config.controller_blocklist", " = [ ... ]", "", "var", "A list of strings, where each string is matched against the GUID of a game controller. These strings are mached as a prefix to the controller GUID (which cand be found in log.txt), and if matched, prevent the controller from being initialized."], "config.locale_to_language_function": ["config", "config.locale_to_language_function", " = ...", "", "var", "A function that determines the language the game should use, based on the the user's locale. It takes 2 string arguments that give the ISO code of the locale and the ISO code of the region.\n\nIt should return a string giving the name of a translation to use, or None to use the default translation."], "config.overlay_screens": ["config", "config.overlay_screens", " = [ ... ]", "", "var", "A list of screens that are displayed when the overlay is enabled, and hidden when the overlay is suppressed. (The screens are shown on the screens layer, not the overlay layer.)"], "config.afm_characters": ["config", "config.afm_characters", " = 250", "", "var", "The number of characters in a string it takes to cause the amount of time specified in the auto forward mode preference to be delayed before auto-forward mode takes effect."], "config.adv_nvl_transition": ["config", "config.adv_nvl_transition", " = None", "", "var", "A transition that is used when showing NVL-mode text directly after ADV-mode text."], "config.window_hide_transition": ["config", "config.window_hide_transition", " = None", "", "var", "The transition used by the window hide statement when no transition has been explicitly specified."], "config.keep_side_render_order": ["config", "config.keep_side_render_order", " = True", "", "var", "If True, the order of substrings in the Side positions will be determine the order of children render."], "config.gl2": ["model", "config.gl2", " = False", "", "var", "If true, Ren'Py will default to using a model-based renderer."], "config.imagemap_cache": ["config", "config.imagemap_cache", " = True", "", "var", "If True, imagemap hotspots will be cached to PNG files, reducing time and memory usage, but increasing the size of the game on disk. Set this to False to disable this behavior."], "config.longpress_duration": ["config", "config.longpress_duration", " = 0.5", "", "var", "The amount of time the player must press the screen for a longpress to be recognized on a touch device."], "config.rollback_enabled": ["config", "config.rollback_enabled", " = True", "", "var", "Should the user be allowed to rollback the game? If set to False, the user cannot interactively rollback."], "config.longpress_radius": ["config", "config.longpress_radius", " = 15", "", "var", "The number of pixels the touch must remain within for a press to be recognized as a longpress."], "config.defer_styles": ["config", "config.defer_styles", " = False", "", "var", "When true, the execution of style statements is deferred until after all ``translate python`` blocks have executed. This lets a ``translate python`` block update variables that are then used in style (not translate style) statements.\n\nWhile this defaults to False, it's set to True when :func:`gui.init` is called."], "config.descriptive_text_character": ["self_voicing", "config.descriptive_text_character", " = None", "", "var", "If not None, this should be a character object that is used to display the descriptive text."], "config.side_image_only_not_showing": ["side_image", "config.side_image_only_not_showing", " = False", "", "var", "When set to true, the side image will only show if an image with that tag is not already being shown on the screen."], "config.say_allow_dismiss": ["config", "config.say_allow_dismiss", " = None", "", "var", "If not None, this should be a function. The function is called with no arguments when the user attempts to dismiss a :ref:`say statement `. If this function returns True, the dismissal is allowed, otherwise it is ignored."], "config.missing_label_callback": ["config", "config.missing_label_callback", " = None", "", "var", "If not None, this function is called when Ren'Py attempts to access a label that does not exist in the game. The callback should take a single parameter, the name of the missing label. It should return the name of a label to use as a replacement for the missing label, or None to cause Ren'Py to raise an exception."], "config.game_menu": ["config", "config.game_menu", " = [ ... ]", "", "var", "This is used to customize the choices on the game menu. Please read Main and Game Menus for more details on the contents of this variable.\n\nThis is not used when the game menu is defined using screens."], "config.all_character_callbacks": ["config", "config.all_character_callbacks", " = [ ]", "", "var", "A list of callbacks that are called by all characters. This list is prepended to the list of character-specific callbacks."], "config.autosave_frequency": ["config", "config.autosave_frequency", " = 200", "", "var", "Roughly, the number of interactions that will occur before an autosave occurs. To disable autosaving, set :var:`config.has_autosave` to False, don't change this variable."], "config.nvl_page_ctc_position": ["nvl_mode", "config.nvl_page_ctc_position", " = \"nestled\"", "", "var", "If not None, this is the click-to-continue indicator position that is used for NVL mode characters that are at the end of a page. (That is, immediately followed by an nvl clear statement.) This replaces the ctc_position parameter of :func:`Character`."], "config.default_music_volume": ["preferences", "config.default_music_volume", " = 1.0", "", "var", "The default volume of the music mixer, which is used for the music and movie audio channels. This should be a number between 0.0 and 1.0, with 1.0 being full volume."], "config.minimum_presplash_time": ["config", "config.minimum_presplash_time", " = 0.0", "", "var", "The minimum amount of time, in seconds, a presplash, Android presplash, or iOS LaunchImage is displayed for. If Ren'Py initializes before this amount of time has been reached, it will sleep to ensure the image is shown for at least this amount of time. The image may be shown longer if Ren'Py takes longer to start up."], "config.overlay_functions": ["config", "config.overlay_functions", " = [ ]", "", "var", "A list of functions. When called, each function is expected to use ui functions to add displayables to the overlay layer."], "config.reload_modules": ["config", "config.reload_modules", " = [ ]", "", "var", "A list of strings giving the names of python modules that should be reloaded along with the game. Any submodules of these modules will also be reloaded."], "config.quit_callbacks": ["config", "config.quit_callbacks", " = ...", "", "var", "A list of functions that are called (without any arguments) when Ren'Py terminates. This is intended to free resources, such as opened files or started threads."], "config.context_callback": ["config", "config.context_callback", " = None", "", "var", "This is a callback that is called when Ren'Py enters a new context, such as a menu context."], "config.pause_after_rollback": ["config", "config.pause_after_rollback", " = False", "", "var", "If False, the default, rolling back will skip any pauses (timed or not) and stop only at other interactions such as dialogues, menus... If True, renpy will include timeless pauses to the valid places a rollback can take the user."], "config.image_cache_size_mb": ["config", "config.image_cache_size_mb", " = 300", "", "var", "This is used to set the size of the :ref:`image cache `, in megabytes. If :var:`config.cache_surfaces` is False, an image takes 4 bytes per pixel, otherwise it takes 8 bytes per pixel.\n\nIf set too large, this can waste memory. If set too small, images can be repeatedly loaded, hurting performance. If not none, :var:`config.image_cache_size` is used instead of this variable."], "config.name": ["config", "config.name", " = \"\"", "", "var", "This should be a string giving the name of the game. This is included as part of tracebacks and other log files, helping to identify the version of the game being used."], "config.nvl_list_length": ["nvl_mode", "config.nvl_list_length", " = None", "", "var", "If not None, the maximum length of the the list of NVL dialogue. This can be set (often in conjuction with forcing the dialogue to have a fixed height) in order to emulate an infinite scrolling NVL window."], "config.emphasize_audio_volume": ["config", "config.emphasize_audio_volume", " = 0.5", "", "var", "See above."], "config.mipmap_dissolves": ["config", "config.mipmap_dissolves", " = False", "", "var", "The default value of the mipmap argument to :func:`Dissolve`, :func:`ImageDissolve`, :func:`AlphaDissolve`, and :func:`AlphaMask`."], "config.missing_image_callback": ["config", "config.missing_image_callback", " = None", "", "var", "If not None, this function is called when an attempt to load an image fails. It may return None, or it may return an image manipulator. If an image manipulator is returned, that image manipulator is loaded in the place of the missing image.\n\nOne may want to also define a :var:`config.loadable_callback`, especially if this is used with a :func:`DynamicImage`."], "config.implicit_with_none": ["config", "config.implicit_with_none", " = True", "", "var", "If True, then by default the equivalent of a :ref:`with None ` statement will be performed after interactions caused by dialogue, menus input, and imagemaps. This ensures that old screens will not show up in transitions."], "config.disable_input": ["config", "config.disable_input", " = False", "", "var", "When true, :func:`renpy.input` terminates immediately and returns its `default` argument."], "config.afm_voice_delay": ["config", "config.afm_voice_delay", " = .5", "", "var", "The number of seconds after a voice file finishes playing before AFM can advance text."], "config.keymap": ["config", "config.keymap", " = dict(...)", "", "var", "This variable contains a keymap giving the keys and mouse buttons assigned to each possible operation. Please see the section on Keymaps for more information."], "config.auto_choice_delay": ["config", "config.auto_choice_delay", " = None", "", "var", "If not None, this variable gives a number of seconds that Ren'Py will pause at an in-game menu before picking a random choice from that menu. We'd expect this variable to always be set to None in released games, but setting it to a number will allow for automated demonstrations of games without much human interaction."], "config.profile_init": ["config", "config.profile_init", " = 0.25", "", "var", "``init`` and ``init python`` blocks taking longer than this amount of time to run are reported to log file."], "config.load_before_transition": ["config", "config.load_before_transition", " = True", "", "var", "If True, the start of an interaction will be delayed until all images used by that interaction have loaded. (Yeah, it's a lousy name.)"], "config.save_on_mobile_background": ["config", "config.save_on_mobile_background", " = True", "", "var", "If True, the mobile app will save its state when it loses focus. The state is saved in a way that allows it to be automatically loaded (and the game to resume its place) when the app starts again."], "config.hide": ["config", "config.hide", " = renpy.hide", "", "var", "A function that is called when the :ref:`hide statement ` is executed. This should take the same arguments as renpy.hide."], "config.auto_channels": ["config", "config.auto_channels", " = { \"audio\" : ( \"sfx\", \"\", \"\" ) }", "", "var", "This is used to define automatic audio channels. It's a map the channel name to a tuple containing 3 components:\n\n* The mixer the channel uses. * A prefix that is given to files played on the channel. * A suffix that is given to files played on the channel."], "config.exit_yesno_transition": ["config", "config.exit_yesno_transition", " = None", "", "var", "If not None, a transition that is used when exiting the yes/no prompt screen."], "config.audio_filename_callback": ["config", "config.audio_filename_callback", " = None", "", "var", "If not None, this is a function that is called with an audio filename, and is expected to return a second audio filename, the latter of which will be played.\n\nThis is intended for use when an a games has audio file formats changed, but it's not destired to update the game script."], "config.conditionswitch_predict_all": ["config", "config.conditionswitch_predict_all", " = False", "", "var", "The default value of the predict_all argument for :func:`ConditionSwitch` and :func:`ShowingSwitch`, which determines if all possible displayables are shown."], "config.tag_layer": ["config", "config.tag_layer", " = { }", "", "var", "A dictionary mapping image tag strings to layer name strings. When an image is shown without a specific layer name, the image's tag is looked up in this dictionary to get the layer to show it on. If the tag is not found here, :var:`config.default_tag_name` is used."], "config.save_directory": ["config", "config.save_directory", " = \"...\"", "", "var", "This is used to generate the directory in which games and persistent information are saved. The name generated depends on the platform:\n\nWindows %APPDATA%/RenPy/`save_directory`\n\nMac OS X ~/Library/RenPy/`save_directory`\n\nLinux/Other ~/.renpy/`save_directory`\n\nSetting this to None creates a \\saves\\ directory underneath the game directory. This is not recommended, as it prevents the game from being shared between multiple users on a system. It can also lead to problems when a game is installed as Administrator, but run as a user.\n\nThis must be set with either the define statement, or in a ``python early`` block. In either case, this will be run before any other statement, and so it should be set to a string, not an expression.\n\nTo locate the save directory, read :var:`config.savedir` instead of this variable."], "config.label_callback": ["config", "config.label_callback", " = None", "", "var", "If not None, this is a function that is called whenever a label is reached. It is called with two parameters. The first is the name of the label. The second is True if the label was reached through jumping, calling, or creating a new context, and False otherwise."], "config.allow_underfull_grids": ["config", "config.allow_underfull_grids", " = False", "", "var", "If True, Ren'Py will not require grids to be full in order to display."], "config.thumbnail_width": ["config", "config.thumbnail_width", " = 100", "", "var", "The width of the thumbnails that are taken when the game is saved. These thumbnails are shown when the game is loaded. Please note that the thumbnail is shown at the size it was taken at, rather than the value of this setting when the thumbnail is shown to the user.\n\nThis is changed by the default GUI."], "config.steam_appid": ["achievement", "config.steam_appid", " = None", "", "var", "If not None, this should be the Steam appid. Ren'Py will automatically set this appid when it starts. This needs to be set using the define statement\n\ndefine config.steam_appid = 12345"], "config.input_caret_blink": ["config", "config.input_caret_blink", " = 1.0", "", "var", "If not False, sets the blinking period of the default caret, in seconds."], "config.main_menu_music_fadein": ["config", "config.main_menu_music_fadein", " = 0.0", "", "var", "The number of seconds to take to fade in :var:`config.main_menu_music`."], "config.exit_sound": ["config", "config.exit_sound", " = None", "", "var", "If not None, this is a sound file that is played when exiting the game menu."], "config.overlay_layers": ["config", "config.overlay_layers", " = [ 'overlay' ]", "", "var", "This is a list of all of the overlay layers. Overlay layers are cleared before the overlay functions are called. \\overlay\\ should always be in this list."], "config.character_callback": ["config", "config.character_callback", " = None", "", "var", "The default value of the callback parameter of Character."], "config.has_autosave": ["config", "config.has_autosave", " = True", "", "var", "If true, the game will autosave. If false, no autosaving will occur."], "config.context_fadein_music": ["config", "config.context_fadein_music", " = 0", "", "var", "The amount of time in seconds Ren'Py spends fading in music when the music is played due to a context change. (Usually, when the game is loaded.)"], "config.python_callbacks": ["config", "config.python_callbacks", " = [ ]", "", "var", "A list of functions. The functions in this list are called, without any arguments, whenever a Python block is run outside of the init phase.\n\nOne possible use of this would be to have a function limit a variable to within a range each time it is adjusted.\n\nThe functions may be called while Ren'Py is starting up, before the start of the game proper, and potentially before the variables the function depends on are initialized. The functions are required to deal with this, perhaps by using ``hasattr(store, 'varname')`` to check if a variable is defined."], "config.skip_indicator": ["config", "config.skip_indicator", " = True", "", "var", "If True, the library will display a skip indicator when skipping through the script."], "config.side_image_null": ["side_image", "config.side_image_null", " = Null()", "", "var", "The Null displayable to use when not displaying a side image. This be changed, but only to other Null objects. One reason for doing so would be to set the side of the Null (eg. ``Null(width=200, height=150)``) to prevent dissolves from being cut off."], "config.exit_transition": ["config", "config.exit_transition", " = None", "", "var", "If not None, this variable should give a transition that will be performed when exiting the game menu."], "config.adjust_attributes": ["config", "config.adjust_attributes", " = { }", "", "var", "If not None, this is a dictionary. When a statement or function that contains image attributes executes or is predicted, the tag is looked up in this dictionary. If it is not found, the None key is looked up in this dictionary.\n\nIf either is found, they're expected to be a function. The function is given an image name, a tuple consisting of the tag and any attributes. It should return an adjusted tuple, which contains and a potential new set of attributes.\n\nAs this function may be called during prediction, it should not rely on the image's state."], "config.label_overrides": ["config", "config.label_overrides", " = { }", "", "var", "This variable gives a way of causing jumps and calls of labels in Ren'Py script to be redirected to other labels. For example, if you add a mapping from \\start\\ to \\mystart\\, all jumps and calls to \\start\\ will go to \\mystart\\ instead."], "config.variants": ["config", "config.variants", " = [ ... ]", "", "var", "A list of screen variants that are searched when choosing a screen to display to the user. This should always end with None, to ensure that the default screens are chosen. See :ref:`screen-variants`."], "config.mipmap_movies": ["config", "config.mipmap_movies", " = False", "", "var", "The default value of the mipmap argument to :func:`Movie`."], "config.log_width": ["config", "config.log_width", " = 78", "", "var", "The width of lines logged when :var:`config.log` is used."], "config.window_overlay_functions": ["config", "config.window_overlay_functions", " = []", "", "var", "A list of overlay functions that are only called when the window is shown."], "config.rollback_length": ["config", "config.rollback_length", " = 128", "", "var", "When there are more than this many statements in the rollback log, Ren'Py will consider trimming the log. This also covers how many steps Ren'Py will rollback when trying to load a save when the script has changed.\n\nDecreasing this below the default value may cause Ren'Py to become unstable."], "config.sound_sample_rate": ["config", "config.sound_sample_rate", " = 48000", "", "var", "The sample rate that the sound card will be run at. If all of your wav files are of a lower rate, changing this to that rate may make things more efficient."], "config.return_not_found_label": ["config", "config.return_not_found_label", " = None", "", "var", "If not None, a label that is jumped to when a return site is not found. The call stack is cleared before this jump occurs."], "config.debug_image_cache": ["config", "config.debug_image_cache", " = False", "", "var", "If True, Ren'Py will write information about the :ref:`image cache ` to image_cache.txt."], "config.search_prefixes": ["config", "config.search_prefixes", " = [ \"\", \"images/\" ]", "", "var", "A list of prefixes that are prepended to filenames that are searched for."], "config.default_voice_volume": ["preferences", "config.default_voice_volume", " = 1.0", "", "var", "The default volume of the voice mixer, which is used for the voice audio channel (And hence the voice statement, auto-voice, etc.). This should be a number between 0.0 and 1.0, with 1.0 being full volume."], "config.choice_layer": ["config", "config.choice_layer", " = \"screens\"", "", "var", "The layer the choice screen (used by the menu statement) is shown on."], "config.default_transform": ["config", "config.default_transform", " = ...", "", "var", "When a displayable is shown using the show or scene statements, the transform properties are taken from this transform and used to initialize the values of the displayable's transform.\n\nThe default transform is :var:`center`."], "config.tts_voice": ["config", "config.tts_voice", " = None", "", "var", "If not None, a string giving a non-default voice that is used to play back text-to-speech for self voicing. The possible choices are platform specific, and so this should be set in a platform-specific manner. (It may make sense to change this in translations, as well.)"], "config.say_sustain_callbacks": ["config", "config.say_sustain_callbacks", " = ...", "", "var", "A list of functions that are called, without arguments, before the second and later interactions caused by a line of dialogue with pauses in it. Used to sustain voice through pauses."], "config.say_attribute_transition_layer": ["config", "config.say_attribute_transition_layer", " = None", "", "var", "If not None, this must be a string giving the name of a layer. (Almost always \\master\\.) The say attribute is applied to the named layer, and Ren'Py will not pause to wait for the transition to occur. This will have the effect of transitioning in the attribute as dialogue is shown."], "config.character_id_prefixes": ["config", "config.character_id_prefixes", " = [ ]", "", "var", "This specifies a list of style property prefixes that can be given to a :func:`Character`. When a style prefixed with one of the given prefix is given, it is applied to the displayable with that prefix as its ID.\n\nFor example, the default GUI adds \\namebox\\ to this. When a Character is given the `namebox_background` property, it sets :propref:`background` on the displayable in the say screen with the id \\namebox\\."], "config.interact_callbacks": ["config", "config.interact_callbacks", " = ...", "", "var", "A list of functions that are called (without any arguments) when an interaction is started or restarted."], "config.replay_scope": ["config", "config.replay_scope", " = { \"_game_menu_screen\" : \"preferences\" }", "", "var", "A dictionary mapping variables in the default store to the values the variables will be given when entering a replay."], "config.window_show_transition": ["config", "config.window_show_transition", " = None", "", "var", "The transition used by the window show statement when no transition has been explicitly specified."], "config.dispatch_gesture": ["gesture", "config.dispatch_gesture", " = None", "", "var", "The function that is used to dispatch gestures. This function is passed the raw gesture string. If it returns non-None, the interaction ends. If this variable is None, a default dispatch function is used."], "config.load_failed_label": ["config", "config.load_failed_label", " = None", "", "var", "If a string, this is a label that is jumped to when a load fails because the script has changed so much that Ren'Py can't recover. Before performing the load, Ren'Py will revert to the start of the last statement, then it will clear the call stack.\n\nThis may also be a function. If it is, the function is called with no arguments, and is expected to return a string giving the label."], "config.savedir": ["config", "config.savedir", " = ...", "", "var", "The complete path to the directory in which the game is saved. This should only be set in a ``python early`` block. See also config.save_directory, which generates the default value for this if it is not set during a ``python early`` block."], "config.side_image_tag": ["side_image", "config.side_image_tag", " = None", "", "var", "If this is given, then the side image will track the given image tag, rather than the image associated with currently speaking character. For example,\n```\ndefine e = Character(\\Eileen\\, image=\\eileen\\)\n\ninit python:\n config.side_image_tag = \\eileen\\\n\nWill make the side image track the \\eileen\\ image tag, which is associated\nwith the ``e`` character.\n\n```"], "config.autosave_slots": ["config", "config.autosave_slots", " = 10", "", "var", "The number of slots used by autosaves."], "config.manage_gc": ["config", "config.manage_gc", " = True", "", "var", "If True, Ren'Py will manage the GC itself. This means that it will apply the settings below."], "config.hyperlink_protocol": ["config", "config.hyperlink_protocol", " = \"call_in_new_context\"", "", "var", "The protocol that is used for hyperlinks that do not have a protocol assigned to them. See :ref:`the a text tag ` for a description as to what the possible protocols mean."], "config.hyperlink_handlers": ["config", "config.hyperlink_handlers", " = { ... }", "", "var", "A dictionary mapping a hyperlink protocol to the handler for that protocol. A handler is a function that takes the value (everything after the :) and performs some action. If a value is returned, the interaction ends. Otherwise, the click is ignored and the interaction continues."], "config.focus_crossrange_penalty": ["config", "config.focus_crossrange_penalty", " = 1024", "", "var", "This is the amount of penalty to apply to moves perpendicular to the selected direction of motion, when moving focus with the keyboard."], "config.overlay_during_with": ["config", "config.overlay_during_with", " = True", "", "var", "True if we want overlays to be shown during :ref:`with statements `, or False if we'd prefer that they be hidden during the with statements."], "config.tag_transform": ["config", "config.tag_transform", " = { }", "", "var", "A dictionary mapping image tag strings to transforms or lists of transforms. When an image is newly-shown without an at clause, the image's tag is looked up in this dictionary to find a transform or list of transforms to use."], "config.save_dump": ["config", "config.save_dump", " = False", "", "var", "If set to True, Ren'Py will create the file save_dump.txt whenever it saves a game. This file contains information about the objects contained in the save file. Each line consists of a relative size estimate, the path to the object, information about if the object is an alias, and a representation of the object."], "config.gl_test_image": ["config", "config.gl_test_image", " = \"black\"", "", "var", "The name of the image that is used when running the OpenGL performance test. This image will be shown for 5 frames or .25 seconds, on startup. It will then be automatically hidden."], "config.periodic_callback": ["config", "config.periodic_callback", " = None", "", "var", "If not None, this should be a function. The function is called, with no arguments, at around 20Hz."], "config.quicksave_slots": ["config", "config.quicksave_slots", " = 10", "", "var", "The number of slots used by quicksaves."], "config.empty_window": ["config", "config.empty_window", " = ...", "", "var", "This is called when _window is True, and no window has been shown on the screen. (That is, no call to :func:`renpy.shown_window` has occurred.) It's expected to show an empty window on the screen, and return without causing an interaction.\n\nThe default implementation of this uses the narrator character to display a blank line without interacting."], "config.fast_skipping": ["config", "config.fast_skipping", " = False", "", "var", "Set this to True to allow fast skipping outside of developer mode."], "config.old_substitutions": ["config", "config.old_substitutions", " = False", "", "var", "If True, Ren'Py will apply old-style (percent) substitutions to text displayed by the :ref:`say ` and :ref:`menu ` statements."], "config.auto_voice": ["config", "config.auto_voice", " = None", "", "var", "This may be a string, a function, or None. If None, auto-voice is disabled.\n\nIf a string, this is formatted with the ``id`` variable bound to the identifier of the current line of dialogue. If this gives an existing file, that file is played as voice audio.\n\nIf a function, the function is called with a single argument, the identifier of the current line of dialogue. The function is expected to return a string. If this gives an existing file, that file is played as voice audio.\n\nSee :ref:`Automatic Voice ` for more details."], "config.auto_movie_channel": ["config", "config.auto_movie_channel", " = True", "", "var", "If True, and the `play` argument is give to :func:`Movie`, an audio channel name is automatically generated for each movie.\n\n:var:`config.single_movie_channel` takes precendece over this variable."], "config.gl_clear_color": ["config", "config.gl_clear_color", " = \"#000\"", "", "var", "The color that the window is cleared to before images are drawn. This is mainly seen as the color of the letterbox or pillarbox edges drawn when aspect ratio of the window or monitor in fullscreen mode) does not match the aspect ratio of the game."], "config.quit_on_mobile_background": ["config", "config.quit_on_mobile_background", " = False", "", "var", "If True, the mobile app will quit when it loses focus."], "config.pause_with_transition": ["config", "config.pause_with_transition", " = False", "", "var", "If false, :func:`renpy.pause` is always, used by the ``pause`` statement. If true, when given a delay, ``pause`` is equivalent to ``with Pause(...)``."], "config.menu_include_disabled": ["config", "config.menu_include_disabled", " = False", "", "var", "When this variable is set, choices disables with the if statement are included as disabled buttons."], "config.autosave_on_choice": ["config", "config.autosave_on_choice", " = True", "", "var", "If True, Ren'Py will autosave upon encountering an in-game choice. (When :func:`renpy.choice_for_skipping` is called.)"], "config.window_title": ["config", "config.window_title", " = None", "", "var", "The static portion of the title of the window containing the Ren'Py game. :var:`_window_subtitle` is appended to this to get the full title of the window.\n\nIf None, the default, this defaults to the value of :var:`config.name`."], "config.hard_rollback_limit": ["config", "config.hard_rollback_limit", " = 100", "", "var", "This is the number of steps that Ren'Py will let the user interactively rollback. Set this to 0 to disable rollback entirely, although we don't recommend that, as rollback is useful to let the user see text he skipped by mistake."], "config.screenshot_callback": ["config", "config.screenshot_callback", " = ...", "", "var", "A function that is called when a screenshot is taken. The function is called with a single parameter, the full filename the screenshot was saved as."], "config.help": ["config", "config.help", " = None", "", "var", "This controls the functionality of the help system invoked by the help button on the main and game menus, or by pressing F1 or Command-?.\n\nIf None, the help system is disabled and does not show up on menus. If a string corresponding to a label found in the script, that label is invoked in a new context. This allows you to define an in-game help-screen. Otherwise, this is interpreted as a filename relative to the base directory, that is opened in a web browser. If the file is not exist, the action is ignored."], "config.start_interact_callbacks": ["config", "config.start_interact_callbacks", " = ...", "", "var", "A list of functions that are called (without any arguments) when an interaction is started. These callbacks are not called when an interaction is restarted."], "config.new_translate_order": ["config", "config.new_translate_order", " = True", "", "var", "Enables the new order of style and translate statements introduced in :ref:`Ren'Py 6.99.11 `."], "config.automatic_images_strip": ["config", "config.automatic_images_strip", " = [ ]", "", "var", "A list of strings giving prefixes that are stripped out when defining automatic images. This can be used to remove directory names, when directories contain images."], "config.main_game_transition": ["config", "config.main_game_transition", " = None", "", "var", "The transition used when entering the game menu from the main menu, as is done when clicking \\Load Game\\ or \\Preferences\\."], "config.enter_yesno_transition": ["config", "config.enter_yesno_transition", " = None", "", "var", "If not None, a transition that is used when entering the yes/no prompt screen."], "config.font_replacement_map": ["config", "config.font_replacement_map", " = { }", "", "var", "This is a map from (font, bold, italics) to (font, bold, italics), used to replace a font with one that's specialized as having bold and/or italics. For example, if you wanted to have everything using an italic version of \\Vera.ttf\\ use \\VeraIt.ttf\\ instead, you could write\n```\ninit python:\nconfig.font_replacement_map[\\Vera.ttf\\, False, True] = (\\VeraIt.ttf\\, False, False)\n\nPlease note that these mappings only apply to specific variants of\na font. In this case, requests for a bold italic version of vera\nwill get a bold italic version of vera, rather than a bold version\nof the italic vera.\n\n```"], "config.image_cache_size": ["config", "config.image_cache_size", " = None", "", "var", "If not None, this is used to set the size of the :ref:`image cache `, as a multiple of the screen size. This number is multiplied by the size of the screen, in pixels, to get the size of the image cache in pixels.\n\nIf set too large, this can waste memory. If set too small, images can be repeatedly loaded, hurting performance."], "config.debug_text_overflow": ["config", "config.debug_text_overflow", " = False", "", "var", "When true, Ren'Py will log text overflows to text_overflow.txt. A text overflow occurs when a :class:`Text` displayable renders to a size larger than that allocated to it. By setting this to True and setting the :propref:`xmaximum` and :propref:`ymaximum` style properties of the dialogue window to the window size, this can be used to report cases where the dialogue is too large for its window."], "config.autoreload": ["config", "config.autoreload", " = True", "", "var", "If True, Shift+R will toggle automatic reloading. When automatic reloading is enabled, Ren'Py will reload the game whenever a used file is modified.\n\nIf False, Ren'Py will reload the game once per press of Shift+R."], "config.notify": ["config", "config.notify", " = ...", "", "var", "This is called by :func:`renpy.notify` or :func:`Notify` with a single `message` argument, to display the notification. The default implementation is :func:`renpy.display_notify`. This is intended to allow creators to intercept notifications."], "config.afm_callback": ["config", "config.afm_callback", " = None", "", "var", "If not None, a Python function that is called to determine if it is safe to auto-forward. The intent is that this can be used by a voice system to disable auto-forwarding when a voice is playing."], "config.optimize_texture_bounds": ["config", "config.optimize_texture_bounds", " = False", "", "var", "When True, Ren'Py will scan images to find the bounding box of the non-transparent pixels, and only load those pixels into a texture."], "config.nvl_layer": ["nvl_mode", "config.nvl_layer", " = \"screens\"", "", "var", "The layer the nvl screens are shown on."], "config.say_attribute_transition_callback": ["config", "config.say_attribute_transition_callback", " = ...", "", "var", "This is a function that return a transition to apply and a layer to apply it on\n\nThis should be a function that takes four arguments, the image tag being shown, a `mode` parameter, a `set` containing pre-transition tags and a `set` containing post-transition tags. Where the value of the `mode` parameter is one of:\n\n* \\permanent\\, for permanent attribute change (one that lasts longer than the current say statement). * \\temporary\\, for a temporary attribute change (one that is restored at the end of the current say statement). * \\both\\, for a simultaneous permanent and temporary attribute change (one that in part lasts longer than the current say statement, and in part is restored at the end of the current say statement). * \\restore\\, for when a temporary (or both) change is being restored.\n\nThis should return a 2-component tuple, consisting of:\n\n* The transition to use, or None if no transition should occur. * The layer the transition should be on, either a string or None. This is almost always None.\n\nThe default implementation of this returns (config.say_attribute_transition, config.say_attribute_transition_layer)."], "config.emphasize_audio_time": ["config", "config.emphasize_audio_time", " = 0.5", "", "var", "See above."], "config.gl_blend_func": ["model", "config.gl_blend_func", " = { ... }", "", "var", "A dictionaryt used to map a blend mode name to a blend function. The blend modes are suppled to the blend func property, given below."], "config.debug": ["config", "config.debug", " = False", "", "var", "Enables debugging functionality (mostly by turning some missing files into errors.) This should always be turned off in a release."], "config.hw_video": ["config", "config.hw_video", " = False", "", "var", "If true, hardware video playback will be used on mobile platforms. This may be faster, but only some formats are supported and only fullscreen video is available. If false, software playback will be used."], "config.gl_lod_bias": ["config", "config.gl_lod_bias", " = -0.5", "", "var", "The default value of the :ref:`u_lod_bias ` uniform, which controls the mipmap level Ren'Py uses."], "config.layers": ["config", "config.layers", " = [ 'master', 'transient', 'screens', 'overlay' ]", "", "var", "This variable gives a list of all of the layers that Ren'Py knows about, in the order that they will be displayed to the screen. (The lowest layer is the first entry in the list.) Ren'Py uses the layers \\master\\, \\transient\\, \\screens\\, and \\overlay\\ internally, so they should always be in this list."], "config.log": ["config", "config.log", " = None", "", "var", "If not None, this is expected to be a filename. Much of the text shown to the user by :ref:`say ` or :ref:`menu ` statements will be logged to this file."], "config.rollback_side_size": ["config", "config.rollback_side_size", " = .2", "", "var", "If the rollback side is enabled, the fraction of the screen on the rollback side that, when clicked or touched, causes a rollback to occur."], "config.window_auto_show": ["config", "config.window_auto_show", " = [ 'say', 'menu-with-caption' ]", "", "var", "A list of statements that cause ``window auto`` to show the empty dialogue window."], "config.start_callbacks": ["config", "config.start_callbacks", " = [ ... ]", "", "var", "A list of callbacks functions that are called with no arguments after the init phase, but before the game (including the splashscreen) starts. This is intended to be used by frameworks to initialize variables that will be saved.\n\nThe default value of this variable includes callbacks that Ren'Py uses internally to implement features such as nvl-mode. New callbacks can be appended to this list, but the existing callbacks should not be removed."], "config.transition_screens": ["config", "config.transition_screens", " = True", "", "var", "If True, screens will participate in transitions, dissolving from the old state of the screen to the new state of the screen. If False, only the latest state of the screen will be shown."], "config.main_menu": ["config", "config.main_menu", " = [ ... ]", "", "var", "The default main menu, when not using screens. For more details, see Main and Game Menus."], "config.emphasize_audio_channels": ["config", "config.emphasize_audio_channels", " = [ 'voice' ]", "", "var", "A list of strings giving audio channel names.\n\nIf the \\emphasize audio\\ preference is enabled, when one of the audio channels listed starts playing a sound, all channels that are not listed in this variable have their secondary audio volume reduced to :var:`config.emphasize_audio_volume` over :var:`config.emphasize_audio_time` seconds.\n\nWhen no channels listed in this variable are playing audio, all channels that are not listed have their secondary audio volume raised to 1.0 over :var:`config.emphasize_audio_time` seconds.\n\nFor example, setting this to ``[ 'voice' ]]`` will lower the volume of all non-voice channels when a voice is played."], "config.speaking_attribute": ["config", "config.speaking_attribute", " = None", "", "var", "If not None, this should be a string giving the name of an image attribute. The image attribute is added to the image when the character's image tag when the character is speaking, and removed when the character stops.\n\nThis is applied to the image on the default layer for the tag, which can be set using :var:`config.tag_layer`."], "config.translate_clean_stores": ["config", "config.translate_clean_stores", " = [ \"gui\" ]", "", "var", "A list of named stores that are cleaned to their state at the end of the init phase when the translation language changes."], "config.automatic_images": ["config", "config.automatic_images", " = None", "", "var", "If not None, this causes Ren'Py to automatically define images.\n\nWhen not set to None, this should be set to a list of separators. (For example, ``[ ' ', '_', '/' ]``.)\n\nRen'Py will scan through the list of files on disk and in archives. When it finds a file ending with .png or .jpg, it will strip the extension, then break the name at separators, to create an image name. If the name consists of at least two components, and no image with that name already is defined, Ren'Py will define that image to refer to a filename.\n\nWith the example list of separators, if your game directory contains:\n\n* eileen_happy.png, Ren'Py will define the image \\eileen happy\\. * lucy/mad.png, Ren'Py will define the image \\lucy mad\\. * mary.png, Ren'Py will do nothing. (As the image does not have two components.)"], "config.afm_bonus": ["config", "config.afm_bonus", " = 25", "", "var", "The number of bonus characters added to every string when auto-forward mode is in effect."], "config.mode_callbacks": ["config", "config.mode_callbacks", " = [ ... ]", "", "var", "A list of callbacks called when entering a mode. For more documentation, see the section on :ref:`Modes`.\n\nThe default value includes a callback that implements :var:`config.adv_nvl_transition` and :var:`config.nvl_adv_transition`."], "config.transient_layers": ["config", "config.transient_layers", " = [ 'transient' ]", "", "var", "This variable gives a list of all of the transient layers. Transient layers are layers that are cleared after each interaction. \\transient\\ should always be in this list."], "config.gc_thresholds": ["config", "config.gc_thresholds", " = (25000, 10, 10)", "", "var", "The GC thresholds that Ren'Py uses when not idle. These are set to try to ensure that garbage collection doesn't happen. The three numbers are:\n\n* The net number of objects that need to be allocated before a level-0 collection. * The number of level-0 collections that trigger a level-1 collection. * The number of level-1 collections that trigger a level-2 collection.\n\n(Level-0 collections should be fast enough to not cause a frame drop, level-1 collections might, level-2 will.)"], "config.fix_rollback_without_choice": ["config", "config.fix_rollback_without_choice", " = False", "", "var", "This option determines how the built in menus or imagemaps behave during fixed rollback. The default value is False, which means that menu only the previously selected option remains clickable. If set to True, the selected option is marked but no options are clickable. The user can progress forward through the rollback buffer by clicking."], "config.screenshot_crop": ["config", "config.screenshot_crop", " = None", "", "var", "If not None, this should be a (`x`, `y`, `height`, `width`) tuple. Screenshots are cropped to this rectangle before being saved."], "config.quit_action": ["config", "config.quit_action", " = ...", "", "var", "The action that is called when the user clicks the quit button on a window. The default action prompts the user to see if he wants to quit the game."], "config.thumbnail_height": ["gui", "config.thumbnail_height", " = 216", "", "var", "The width and height of the save thumbnails. Note that these live in the config namespace, not the gui namespace. These do not take effect until the file is saved and loaded."], "config.context_fadeout_music": ["config", "config.context_fadeout_music", " = 0", "", "var", "The amount of time in seconds Ren'Py spends fading out music when the music is played due to a context change. (Usually, when the game is loaded.)"], "config.top_layers": ["config", "config.top_layers", " = [ ]", "", "var", "This is a list of names of layers that are displayed above all other layers, and do not participate in a transition that is applied to all layers. If a layer name is listed here, it should not be listed in config.layers."], "config.default_tag_layer": ["config", "config.default_tag_layer", " = \"master\"", "", "var", "The layer an image is shown on if its tag is not found in config.tag_layer."], "config.screen_height": ["config", "config.screen_height", " = 600", "", "var", "The height of the screen. Usually set by :func:`gui.init`."], "config.preload_fonts": ["config", "config.preload_fonts", " = [ ]", "", "var", "A list of the names of TrueType and OpenType fonts that Ren'Py should load when starting up. Including the name of a font here can prevent Ren'Py from pausing when introducing a new typeface."], "config.allow_skipping": ["config", "config.allow_skipping", " = True", "", "var", "If set to False, the user is not able to skip over the text of the game. See :var:`_skipping`."], "config.save_json_callbacks": ["config", "config.save_json_callbacks", " = [ ]", "", "var", "A list of callback functions that are used to create the json object that is stored with each save and marked accessible through :func:`FileJson` and :func:`renpy.slot_json`.\n\nEach callback is called with a Python dictionary that will eventually be saved. Callbacks should modify that dictionary by adding JSON-compatible Python types, such as numbers, strings, lists, and dicts. The dictionary at the end of the last callback is then saved as part of the save slot.\n\nThe dictionary passed to the callbacks may have already have keys beginning with an underscore ``_``. These keys are used by Ren'Py, and should not be changed."], "config.auto_load": ["config", "config.auto_load", " = None", "", "var", "If not None, the name of a save file to automatically load when Ren'Py starts up. This is intended for developer use, rather than for end users. Setting this to \\1\\ will automatically load the game in save slot 1."], "config.main_menu_music": ["config", "config.main_menu_music", " = None", "", "var", "If not None, a music file to play when at the main menu."], "config.debug_sound": ["config", "config.debug_sound", " = False", "", "var", "Enables debugging of sound functionality. This disables the suppression of errors when generating sound. However, if a sound card is missing or flawed, then such errors are normal, and enabling this may prevent Ren'Py from functioning normally. This should always be False in a released game."], "config.menu_clear_layers": ["config", "config.menu_clear_layers", " = []", "", "var", "A list of layer names (as strings) that are cleared when entering the game menu."], "config.single_movie_channel": ["config", "config.single_movie_channel", " = None", "", "var", "If not None, and the `play` argument is give to :func:`Movie`, this is the name used for the channel the movie is played on. This should not be \\movie\\, as that name is reserved for Ren'Py's internal use."], "config.autosave_on_quit": ["config", "config.autosave_on_quit", " = True", "", "var", "If True, Ren'Py will attempt to autosave when the user attempts to quit, return to the main menu, or load a game over the existing game. (To save time, the autosave occurs while the user is being prompted to confirm his or her decision.)"], "config.self_closing_custom_text_tags": ["custom_text_tags", "config.self_closing_custom_text_tags", "", "", "var", "Maps text tag names to a self-closing text tag functions, when the text tag does not wrap other text."]}, "internal": {"InputValue.enter": ["screen_python", "enter", "(self)", "InputValue", "method", "Called when the user presses enter. If this returns a non-None value, that value is returned from the interacton. This may also raise renpy.IgnoreEvent() to ignore the press. Otherwise, the enter-press is propagated to other displayables.\n\nThe following actions are available as methods on InputValue:"], "director.director_spacing": ["director", "director.director_spacing", " = 0", "", "var", "The spacing between two consecutive director lines."], "gui.TogglePreference": ["internal", "class", "(name, a, b, rebuild=True)", "", "Action", "This Action toggles the gui preference with `name` between value `a` and value `b`. It is selected if the value is equal to `a`.\n\n`rebuild` If true, the default, :func:`gui.rebuild` is called to make the changes take effect. This should generally be true, except in the case of multiple gui.SetPreference actions, in which case it should be False in all but the last one.\n\nThis is a very slow action, and probably not suitable for use when a button is hovered."], "gui.slider_tile": ["gui", "gui.slider_tile", " = True", "", "var", "If true, the frame containing the bar of a slider is tiled. If False, if it scaled."], "ui.Action": ["obsolete", "class", "()", "", "", "This can be passed to the clicked method of a button or hotspot. It is called when the action is selected. The other methods determine if the action should be displayed insensitive or disabled."], "AnimatedValue": ["internal", "class", "(value=0.0, range=1.0, delay=1.0, old_value=None)", "", "Action", "This animates a value, taking `delay` seconds to vary the value from `old_value` to `value`.\n\n `value` The value itself, a number.\n\n `range` The range of the value, a number.\n\n `delay` The time it takes to animate the value, in seconds. Defaults to 1.0.\n\n `old_value` The old value. If this is None, then the value is taken from the AnimatedValue we replaced, if any. Otherwise, it is initialized to `value`."], "Live2D": ["internal", "class", "(filename, zoom=None, top=0.0, base=1.0, height=1.0, loop=False, aliases={}, fade=None, motions=None, expression=None, nonexclusive=None, used_nonexclusive=None, seamless=None, sustain=False, attribute_function=None, attribute_filter=None, update_function=None, default_fade=1.0, **properties)", "", "", "This displayable displays a Live2D animation.\n\nOnly filename should be given positionally, and all other arguments should be given as keyword arguments."], "Matrix.identity": ["matrix", "Matrix.identity", "()", "", "function", "Returns an identity matrix."], "moveouttop": ["transitions", "moveouttop", "", "", "var", "Also: **moveoutleft, moveoutright, moveoutbottom**\n\nThese move leaving images off the screen via the appropriate side, taking 0.5 seconds to do so."], "alt": ["internal", "function", "(what, interact=True)", "", "", ""], "FactorZoom": ["internal", "function", "()", "", "", ""], "preferences.set_volume": ["preferences", "preferences.set_volume", "(mixer, volume)", "", "function", "Sets `mixer` to `volume`.\n\n`mixer` A string giving the name of the mixer. By default, the mixers are \\music\\, \\sfx\\, and \\voice\\.\n\n`volume` A number between 0.0 and 1.0."], "RoundRect": ["internal", "function", "(color, small=False)", "", "", ""], "iap.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "im.Rotozoom": ["obsolete", "class", "(im, angle, zoom, **properties)", "", "", "This is an image manipulator that is a smooth rotation and zoom of another image manipulator."], "nvl_show": ["internal", "function", "(with_)", "", "", "The Python equivalent of the ``nvl show`` statement.\n\n`with_` The transition to use to show the NVL-mode window."], "easeinbottom": ["transitions", "easeinbottom", "", "", "var", "Also: **easeinright, easeinleft, easeintop, ease, easeoutright, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "build.classify_renpy": ["internal", "function", "(pattern, groups)", "", "", "Classifies files in the Ren'Py base directory according to pattern."], "BarValue.get_tooltip": ["screen_python", "get_tooltip", "(self)", "BarValue", "method", "This gets a default tooltip for this button, if a specific tooltip is not assigned. It should return the tooltip value, or None if a tooltip is not known.\n\nThis defaults to returning None."], "SetDict": ["internal", "class", "(dict, key, value)", "", "Action", "Causes the value of `key` in `dict` to be set to `value`."], "gui.slot_button_borders": ["gui", "gui.slot_button_borders", " = Borders(15, 15, 15, 15)", "", "var", "The borders applied to each save slot."], "DisableAllInputValues": ["internal", "class", "()", "", "Action", "Disables all active InputValue. This will re-focus the default InputValue, if there is one. Otherwise, no InputValue will be focused."], "division": ["internal", "function", "()", "", "", ""], "pushright": ["transitions", "pushright", "", "", "var", "Also: **pushleft, pushup, pushdown**\n\nThese use the new scene to slide the old scene out the named side. Instances of the :func:`PushMove` transition class."], "ui.child_or_fixed": ["obsolete", "function", "()", "", "", "Causes the current widget to be given child-fixed semantics. This means that we will queue up children added to it. If there is one child, that child will be added to the widget directly. Otherwise, a fixed will be created, and the children will be added to that."], "SpriteManager": ["internal", "class", "(update=None, event=None, predict=None, ignore_time=False, **properties)", "", "", "This displayable manages a collection of sprites, and displays them at the fastest speed possible."], "im.ImageBase": ["obsolete", "class", "(*args, **properties)", "", "", "This is the base class for all of the various kinds of images that we can possibly have."], "gui.dialogue_text_xalign": ["gui", "gui.dialogue_text_xalign", " = 0.0", "", "var", "The horizontal alignment of dialogue text. 0.0 is left aligned, 0.5 is centered, and 1.0 is right-aligned."], "LiveCrop": ["obsolete", "function", "(rect, child, **properties)", "", "", "LiveCrop is now :func:`Crop`."], "preferences.voice_sustain": ["preferences", "preferences.voice_sustain", " = False", "", "var", "If True, voice keeps playing until finished, or another voice line replaces it. If False, the voice line ends when the line of dialogue advances. The equivalent of the \\voice sustain\\ preference."], "SetMixer": ["internal", "class", "(mixer, volume)", "", "Action", "Sets the volume of `mixer` to `value`.\n\n`mixer` The mixer to set the volume of. A string, usually one of \\music\\, \\sfx\\, or \\voice\\. `value` The value to set the volume to. A number between 0.0 and 1.0, inclusive."], "updater": ["internal", "function", "()", "", "", ""], "Fixed": ["internal", "function", "(*args, **properties)", "", "", "A box that fills the screen. Its members are laid out from back to front, with their position properties controlling their position."], "FileNewest": ["internal", "function", "(name, page=None, slot=False)", "", "Action", "Returns True if this is the newest file slot, or False otherwise."], "truecenter": ["internal", "function", "()", "", "", ""], "AlphaBlend": ["internal", "function", "(control, old, new, alpha=False)", "", "", "This transition uses a `control` displayable (almost always some sort of animated transform) to transition from one displayable to another. The transform is evaluated. The `new` displayable is used where the transform is opaque, and the `old` displayable is used when it is transparent.\n\n`alpha` If true, the image is composited with what's behind it. If false, the default, the image is opaque and overwrites what's behind it."], "gui": ["internal", "function", "()", "", "", ""], "list": ["internal", "class", "(*args)", "", "", ""], "TintMatrix": ["internal", "class", "(color)", "", "", "A ColorMatrix can be used with :tpref:`matrixcolor` to tint an image, while leaving the alpha channel alone.\n\n`color` The color that the matrix will tint things to. This is passed to :func:`Color`, and so may be anything that Color supports as its first argument."], "Matrix.is_unit_aligned": ["internal", "function", "()", "", "", "Returns true if exactly one of abs(xdx) or abs(xdy) is 1.0, and the same for xdy and ydy. This is intended to report if a matrix is aligned to the axes."], "build.documentation": ["internal", "function", "(pattern)", "", "", "Declares a pattern that matches documentation. In a mac app build, files matching the documentation pattern are stored twice - once inside the app package, and again outside of it."], "Lexer.match": ["cds", "match", "(re)", "Lexer", "method", "Matches an arbitrary regexp string.\n\nAll of the statements in the lexer that match things are implemented in terms of this function. They first skip whitespace, then attempt to match against the line. If the match succeeds, the matched text is returned. Otherwise, None is returned, and the state of the lexer is unchanged."], "VariableInputValue": ["internal", "class", "(variable, default=True, returnable=False)", "", "Action", "An input value that updates `variable`.\n\n`variable` A string giving the name of the variable to update.\n\n`default` If true, this input can be editable by default.\n\n`returnable` If true, the value of this input will be returned when the user presses enter."], "ScreenVariableInputValue": ["internal", "class", "(variable, default=True, returnable=False)", "", "Action", "An input value that updates variable.\n\n`variable` A string giving the name of the variable to update.\n\n`default` If true, this input can be editable by default.\n\n`returnable` If true, the value of this input will be returned when the user presses enter."], "MusicRoom": ["internal", "class", "(channel=u'music', fadeout=0.0, fadein=0.0, loop=True, single_track=False, shuffle=False, stop_action=None)", "", "", "This action toggles the value of the shuffle property."], "build.allow_integrated_gpu": ["build", "build.allow_integrated_gpu", " = True", "", "var", "Allows Ren'Py to run on the integrated GPU on platforms that have both integrated and discrete GPUs. Right now, this is only supported on Mac OS X."], "ComposeTransition": ["transitions", "function", "(trans, before, after)", "", "", "Returns a transition that composes up to three transitions. If not None, the `before` and `after` transitions are applied to the old and new scenes, respectively. These updated old and new scenes are then supplied to the `trans` transition.\n```\n# Move the images in and out while dissolving. (This is a fairly expensive transition.)\ndefine moveinoutdissolve = ComposeTransition(dissolve, before=moveoutleft, after=moveinright)\n```"], "build.classify": ["internal", "function", "(pattern, file_list)", "", "", "Classifies files that match `pattern` into `file_list`."], "director.voice_channel": ["director", "director.voice_channel", " = \"voice\"", "", "var", "The name of the audio channel used by voice."], "SnowBlossom": ["internal", "function", "(d, count=10, border=50, xspeed=(20, 50), yspeed=(100, 200), start=0, fast=False, horizontal=False)", "", "", ""], "wipedown": ["transitions", "wipedown", "", "", "var", "Also: **wiperight, wipeup, wipeleft**\n\nWipes the scene in the given direction. Instances of the :func:`CropMove` transition class."], "iap.with_background": ["internal", "function", "(f, *args, **kwargs)", "", "", "Displays the background, then invokes `f`."], "wiperight": ["transitions", "wiperight", "", "", "var", "Also: **wipeleft, wipeup, wipedown**\n\nWipes the scene in the given direction. Instances of the :func:`CropMove` transition class."], "director.transforms": ["director", "director.transforms", " = [ \"left\", \"center\", \"right\" ]", "", "var", "A list of transforms that will be presented as part of the editor. In addition to these, any transform defined using the transform statement outside of Ren'Py itself will be added to the list of transforms, which is then sorted."], "FileTakeScreenshot": ["internal", "class", "(*args, **kwargs)", "", "Action", "Take a screenshot to be used when the game is saved. This can be used to ensure that the screenshot is accurate, by taking a picture of the screen before a file save screen is shown."], "ui.frame": ["obsolete", "ui.frame", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "preferences": ["internal", "function", "()", "", "", ""], "Lexer.catch_error": ["cds", "catch_error", "()", "Lexer", "method", "This is a context decorator, used in conjunction with the with statement, that catches and reports lexer errors inside its context block, then continues after the block.\n\nHere's an example of how it can be used to report multiple errors in a single subblock.\n```\ndef mystatement_parse(l):\n\nl.require(':')\nl.expect_eol()\nl.expect_block(\\mystatement\\)\n\nstrings = [ ]\nll = l.subblock_lexer()\n\nwhile ll.advance():\nwith ll.catch_errors():\nstrings.append(ll.require(ll.string))\nll.expect_noblock(\\string inside mystatement\\)\nll.expect_eol()\n\nreturn { \\strings\\ : strings }\n\n\n\n\n\n\n```"], "build.archive": ["internal", "function", "(name, file_list=\"all\")", "", "", "Declares the existence of an archive. If one or more files are classified with `name`, `name`.rpa is build as an archive. The archive is included in the named file lists."], "default_mouse": ["store_variables", "default_mouse", "", "store", "var", "This is undefined by default. If defined, and if :var:`config.mouse` is set at game startup, this is a key that is used to look up a mouse cursor in config.mouse when the current cursor does not exist, or is the default."], "Grid": ["internal", "function", "(cols, rows, *args, **properties)", "", "", "Lays out displayables in a grid. The first two positional arguments are the number of columns and rows in the grid. This must be followed by `columns * rows` positional arguments giving the displayables that fill the grid."], "preferences.show_empty_window": ["preferences", "preferences.show_empty_window", " = True", "", "var", "If True, the window show and window auto statements will function. If False, those statements are disabled. The equivalent of the \\show empty window\\ preference."], "BarValue": ["internal", "class", "()", "", "", "This can be passed to the value method of bar and hotbar."], "Lexer.arguments": ["cds", "arguments", "()", "Lexer", "method", "This must be called before the parentheses with the arguments list, if they are not specified returns None, otherwise returns an object representing the arguments to a function call. This object has an ``evaluate`` method on it that takes an optional `scope` dictionary, and returns a tuple in which the first component is a tuple of positional arguments, and the second component is a dictionary of keyword arguments."], "SetField": ["internal", "class", "(object, field, value)", "", "Action", "Causes the a field on an object to be set to a given value. `object` is the object, `field` is a string giving the name of the field to set, and `value` is the value to set it to."], "Viewport": ["internal", "class", "(child=None, child_size=(None, None), offsets=(None, None), xadjustment=None, yadjustment=None, set_adjustments=True, mousewheel=False, draggable=False, edgescroll=None, style=u'viewport', xinitial=None, yinitial=None, replaces=None, arrowkeys=False, pagekeys=False, **properties)", "", "", ""], "layeredimage.parse_property": ["internal", "function", "(l, o, names)", "", "", "Parses a property, returns True if one is found."], "Zoom": ["internal", "function", "()", "", "", ""], "layeredimage.python_object": ["internal", "class", "()", "", "", "The most base type"], "gui.skip_frame_borders": ["gui", "gui.skip_frame_borders", " = Borders(24, 8, 75, 8)", "", "var", "The borders of the frame that is used by the skip screen."], "nvl_window": ["internal", "function", "()", "", "", ""], "gui.title_text_size": ["gui", "gui.title_text_size", " = 75", "", "var", "The size of the game's title."], "layout": ["internal", "function", "()", "", "", ""], "director.viewport_height": ["director", "director.viewport_height", " = 280", "", "var", "The maximum height of scrolling viewports used by the director."], "im.ramp": ["obsolete", "function", "(start, end)", "", "", "Returns a 256 character linear ramp, where the first character has the value start and the last character has the value end. Such a ramp can be used as a map argument of im.Map."], "pushleft": ["transitions", "pushleft", "", "", "var", "Also: **pushright, pushup, pushdown**\n\nThese use the new scene to slide the old scene out the named side. Instances of the :func:`PushMove` transition class."], "layeredimage.ConditionGroup": ["internal", "class", "(conditions)", "", "", "Combines a list of conditions into a single ConditionSwitch."], "voice": ["internal", "function", "(filename, tag=None)", "", "", "Plays `filename` on the voice channel. The equivalent of the voice statement.\n\n`filename` The filename to play. This is used with :var:`config.voice_filename_format` to produce the filename that will be played.\n\n`tag` If this is not None, it should be a string giving a voice tag to be played. If None, this takes its default value from the voice_tag of the Character that causes the next interaction.\n\nThe voice tag is used to specify which character is speaking, to allow a user to mute or unmute the voices of particular characters."], "hpunch": ["transitions", "hpunch", "", "", "var", "When invoked, this transition shakes the screen horizontally for a quarter second."], "ui.key": ["obsolete", "ui.key", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "preferences.get_mute": ["preferences", "preferences.get_mute", "(mute):", "", "function", "Gets the mute setting for `mixer`."], "updater.subprocess": ["internal", "function", "()", "", "", "Subprocesses with accessible I/O streams\n\nThis module allows you to spawn processes, connect to their input/output/error pipes, and obtain their return codes.\n\nFor a complete description of this module see the Python documentation.\n\nMain API ======== call(...): Runs a command, waits for it to complete, then returns the return code. check_call(...): Same as call() but raises CalledProcessError() if return code is not 0 check_output(...): Same as check_call() but returns the contents of stdout instead of a return code Popen(...): A class for flexibly executing a command in a new process\n\nConstants --------- PIPE: Special value that indicates a pipe should be created STDOUT: Special value that indicates that stderr should go to stdout"], "ToggleScreenVariable": ["internal", "class", "(name, true_value=None, false_value=None)", "", "Action", "Toggles the value of the variable `name` in the current screen.\n\n `true_value` If not None, then this is the true value used. `false_value` If not None, then this is the false value used."], "FilePageNameInputValue": ["internal", "class", "(pattern=u'Page {}', auto=u'Automatic saves', quick=u'Quick saves', page=None, default=False)", "", "Action", "An input value that updates the name of a file page.\n\n`pattern` This is used for the default name of a page. Python-style substition is performed, such that {} is replaced with the number of the page.\n\n`auto` The name of the autosave page.\n\n`quick` The name of the quicksave page.\n\n`page` If given, the number of the page to display. This should usually be left as None, to give the current page.\n\n`default` If true, this input can be editable by default."], "FilePagePrevious": ["internal", "class", "(max=None, wrap=False, auto=True, quick=True)", "", "Action", "Goes to the previous file page, if possible.\n\n`max` If set, this should be an integer that gives the number of the maximum file page we can go to. This is required to enable wrap.\n\n`wrap` If true, we can go to the last page when on the first file page if max is set.\n\n`auto` If true, this can bring the player to the page of automatic saves.\n\n`quick` If true, this can bring the player to the page of automatic saves."], "director.audio_patterns": ["director", "director.audio_patterns", " = [ \"*.opus\", \"*.ogg\", \"*.mp3\" ]", "", "var", "The default list of audio patterns that are used to match the files available in an audio channel."], "gui.selected_color": ["gui", "gui.selected_color", " = '#555555'", "", "var", "The color used by the text of selected buttons. (This takes priority over the hover and idle colors.)"], "Matrix.texture_projection": ["internal", "function", "()", "", "", "This generates a matrix that project the Ren'Py space, where (0, 0) is the top left and (`w`, `h`) is the bottom right, into the OpenGL render-to-texture space, where (-1.0, -1.0) is the top left and (1.0, 1.0) is the bottom.\n\nGenerates the matrix that projects the Ren'Py screen to the OpenGL screen."], "ShowTransient": ["internal", "function", "(screen, transition=None, *args, **kwargs)", "", "Action", "Shows a transient screen. A transient screen will be hidden when the current interaction completes. The arguments are passed to the screen being shown.\n\n If not None, `transition` is use to show the new screen."], "Null": ["internal", "class", "(width=0, height=0, **properties)", "", "", "A displayable that creates an empty box on the screen. The size of the box is controlled by `width` and `height`. This can be used when a displayable requires a child, but no child is suitable, or as a spacer inside a box.\n```\nimage logo spaced = HBox(\\logo.png\\, Null(width=100), \\logo.png\\)\n\n```"], "define.move_transitions": ["internal", "function", "(prefix, delay, time_warp=None, in_time_warp=None, out_time_warp=None, old=False, layers=[ 'master' ], **kwargs)", "", "", "This defines a family of move transitions, similar to the move and ease transitions. For a given `prefix`, this defines the transitions:\n\n* *prefix*- A transition that takes `delay` seconds to move images that changed positions to their new locations.\n\n* *prefix*\\ inleft, *prefix*\\ inright, *prefix*\\ intop, *prefix*\\ inbottom - Transitions that take `delay` seconds to move images that changed positions to their new locations, with newly shown images coming in from the appropriate side.\n\n* *prefix*\\ outleft, *prefix*\\ outright, *prefix*\\ outtop, *prefix*\\ outbottom - Transitions that take `delay` seconds to move images that changed positions to their new locations, with newly hidden images leaving via the appropriate side.\n\n`time_warp`, `in_time_warp`, `out_time_warp` Time warp functions that are given a time from 0.0 to 1.0 representing the fraction of the move that is complete, and return a value in the same range giving the fraction of a linear move that is complete.\n\nThis can be used to define functions that ease the images around, rather than moving them at a constant speed.\n\nThe three arguments are used for images remaining on the screen, newly shown images, and newly hidden images, respectively.\n\n`old` If true, the transitions to move the old displayables, rather than the new ones.\n\n`layers` The layers the transition will apply to.\n```\n# This defines all of the pre-defined transitions beginning\n# with \\move\\.\ninit python:\ndefine.move_transitions(\\move\\, 0.5)\n\n```"], "achievement.progress": ["internal", "function", "(name, complete)", "", "", "Reports progress towards the achievement with `name`, if that achievement has not been granted. The achievement must be defined with a completion amount.\n\n`name` The name of the achievement. This should be the name of the achievement, and not the stat.\n\n`complete` An integer giving the number of units completed towards the achievement."], "Style": ["internal", "class", "()", "", "", "`parent` The parent of this style. One of:\n\n * A Style object. * A string giving the name of a style. * A tuple giving the name of an indexed style. * None, to indicate there is no parent.\n\n`properties` A map from style property to its value.\n\n`name` If given, a tuple that will be the name of this style.\n\n`help` Help information for this style.\n\n`heavy` Ignored, but retained for compatibility."], "Matrix": ["internal", "class", "(l)", "", "", "This represents a 4x4 matrix, that is used in various places in Ren'Py.\n\nWhen used to transform coordinates, the 16 elements of this matrix are::\n\n xdx, xdy, xdz, xdw, ydx, ydy, ydz, ydw, zdx, zdy, zdz, zdw, wdx, wdy, wdz, wdw\n\nwhere x' = xdx * x + xdy * y + xdz * z + xdw * w, where x is the original value of x and x' is the transformed value, and similarly for x, y, z, and w. This is usually applied to a position where w is 1, allowing any combination of translation, rotation, and scaling to be expressed in a single matrix.\n\nWhen used to transform colors, the 16 elements of this matrix are::\n\n rdr, rdg, rdb, rda, gdr, gdg, gdg, gda, bdr, bdg, bdb, bda, adr, adg, adb, ada,\n\nFor the red, green, blue, and alpha channels.\n\nMatrix objects can be multiplied using the Python multiplication operator, to generate a matrix that performs both operations. The order in which the matrixes appear can matter. Assuming `v` is a position or color being transformed::\n\n (step2 * step1) * v\n\nis equivalent to::\n\n step2 * (step1 * v)\n\n`l` This can be a list of 4, 9, or 16 numbers that is used to introduce this matrix. If not the full 16, the top-left corner of the matrix is initialized, with zdz and wdw set to 1.0 if not given. For example::\n\n Matrix([ 1, 2, 3, 4 ])\n\n would result in the Matrix::\n\n 1.0, 2.0, 0.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,"], "style.rebuild": ["style", "style.rebuild", "()", "", "function", "This causes named styles to be rebuilt, allowing styles to be changed after the init phase has finished.\n\n.. warning\n```\nNamed styles are not saved as part of the per-game data. This\n means that changes to them will not be persisted through a save\n and load cycle.\n```"], "gui.skip_ypos": ["gui", "gui.skip_ypos", " = 15", "", "var", "The vertical position of the skip indicator, in pixels from the top of the window."], "ColorizeMatrix": ["internal", "class", "(black_color, white_color)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to colorize black and white displayables. It uses the color of each pixel in the black and white to interpolate between the black color and the white color.\n\nThe alpha channel is not touched.\n\nThis is inteded for use with a black and white image (or one that has been desaturated with :func:`SaturationMatrix`), and will yield strange results when used with images that are not black and white.\n\n`black_color`, `white_color` The colors used in the interpolation."], "MainMenu": ["internal", "class", "(confirm=True, save=True)", "", "Action", "Causes Ren'Py to return to the main menu.\n\n `confirm` If true, causes Ren'Py to ask the user if he wishes to return to the main menu, rather than returning directly.\n\n`save` If true, the game is saved in :var:`_quit_slot` before Ren'Py restarts and returns the user to the main menu. The game is not saved if :var:`_quit_slot` is None."], "director.scene_tags": ["director", "director.scene_tags", " = { \"bg\" }", "", "var", "The set of tags that will be presented for the scene statement, and hidden from the show statement."], "OffsetMatrix": ["internal", "class", "(x, y, z)", "", "", "A TransformMatrix that returns a matrix that offsets the vertex by a fixed amount."], "offscreenleft": ["internal", "function", "()", "", "", ""], "FileJson": ["internal", "function", "(name, key=None, empty=None, missing=None, page=None, slot=False)", "", "Action", "Accesses the Json information associated with `name`.\n\nIf `key` is None, returns the entire Json other object, or `empty` if the slot is empty.\n\nOtherwise, this returns json[key] if `key` is defined on the json object of the save, `missing` if there is a save with the given name, but it does not contain `key`, or `empty` if the save slot is empty.\n\nJson is added to a save slot by callbacks registered using :var:`config.save_json_callbacks`."], "ui.label": ["obsolete", "ui.label", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "Transform.hide_response": ["trans_trans_python", "hide_response", "", "Transform", "attribute", "If hide request is true, this can be set to false to prevent the transform from being hidden."], "SetVoiceMute": ["internal", "class", "(voice_tag, mute)", "", "Action", "If `mute` is true, mutes voices that are played with the given `voice_tag`. If `mute` is false, unmutes voices that are played with `voice_tag`."], "LiveTile": ["internal", "class", "(child, style=u'tile', **properties)", "", "", ""], "slideup": ["transitions", "slideup", "", "", "var", "Also: **slideright, slideleft, slidedown**\n\nSlides the new scene in the given direction. Instances of the :func:`CropMove` transition class."], "VBox": ["internal", "function", "(*args, **properties)", "", "", "A layout that lays out its members from top to bottom."], "updater.traceback": ["internal", "function", "()", "", "", "Extract, format and print information about Python stack traces."], "ui.combine_style": ["obsolete", "function", "(style_prefix, style_suffix)", "", "", "Combines a style prefix and style suffix to create a style name, then returns the style object corresoinding to that name."], "Drag": ["internal", "class", "(d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, mouse_drop=False, **properties)", "", "", "A displayable that represents an object that can be dragged around its enclosing area. A Drag can also represent an area that other Drags can be dropped on.\n\nA Drag can be moved around inside is parent. Generally, its parent should be either a :func:`Fixed` or :class:`DragGroup`.\n\nA Drag has one child. The child's state reflects the status of the drag and drop operation:\n\n* ``selected_hover`` - when it is being dragged. * ``selected_idle`` - when it can be dropped on. * ``hover`` - when the draggable will be dragged when the mouse is clicked. * ``idle`` - otherwise.\n\nThe drag handle is a rectangle inside the child. The mouse must be over a non-transparent pixel inside the drag handle for dragging or clicking to occur.\n\nA newly-created draggable is added to the default DragGroup. A draggable can only be in a single DragGroup - if it's added to a second group, it's removed from the first.\n\nWhen a Drag is first rendered, if it's position cannot be determined from the DragGroup it is in, the position of its upper-left corner is computed using the standard layout algorithm. Once that position has been computed, the layout properties are ignored in favor of the position stored inside the Drag.\n\n`d` If present, the child of this Drag. Drags use the child style in preference to this, if it's not None.\n\n`drag_name` If not None, the name of this draggable. This is available as the `name` property of draggable objects. If a Drag with the same name is or was in the DragGroup, the starting position of this Drag is taken from that Draggable.\n\n`draggable` If true, the Drag can be dragged around the screen with the mouse.\n\n`droppable` If true, other Drags can be dropped on this Drag.\n\n`drag_raise` If true, this Drag is raised to the top when it is dragged. If it is joined to other Drags, all joined drags are raised.\n\n`activated` A callback (or list of callbacks) that is called when the mouse is pressed down on the drag. It is called with one argument, a a list of Drags that are being dragged. The return value of this callback is ignored.\n\n`dragged` A callback (or list of callbacks) that is called when the Drag has been dragged. It is called with two arguments. The first is a list of Drags that are being dragged. The second is either a Drag that is being dropped onto, or None of a drop did not occur. If the callback returns a value other than None, that value is returned as the result of the interaction.\n\n`dropped` A callback (or list of callbacks) that is called when this Drag is dropped onto. It is called with two arguments. The first is the Drag being dropped onto. The second is a list of Drags that are being dragged. If the callback returns a value other than None, that value is returned as the result of the interaction.\n\nWhen a dragged and dropped callback are triggered for the same event, the dropped callback is only called if dragged returns None.\n\n`clicked` A callback this is called, with no arguments, when the Drag is clicked without being moved. A droppable can also be focused and clicked. If the callback returns a value other than None, that value is returned as the result of the interaction.\n\n`alternate` An action that is run when the Drag is right-clicked (on the desktop) or long-pressed without moving (on mobile). It may be necessary to increase :var:`config.longpress_duration` if this triggers to early on mobile platforms.\n\n`drag_handle` A (x, y, width, height) tuple, giving the position of the drag handle within the child. In this tuple, integers are considered to be a literal number of pixels, while floats are relative to the size of the child.\n\n`drag_joined` This is called with the current Drag as an argument. It's expected to return a list of [ (drag, x, y) ] tuples, giving the draggables to drag as a unit. `x` and `y` are the offsets of the drags relative to each other, they are not relative to the corner of this drag.\n\n`drag_offscreen` If true, this draggable can be moved offscreen. This can be dangerous to use with drag_joined or drags that can change size, as the drags can leave the screen entirely, with no way to get them back on the screen.\n\n`mouse_drop` If true, the drag is dropped on the first droppable under the cursor. If false, the default, the drag is dropped onto the droppable with the largest degree of overlap.\n\n`drop_allowable` A callback that is called to determine whether this drop allow the current drags dropped onto. It is called with two arguments. The first is the Drag which determines its sensitivity. The second is a list of Drags that are being dragged.\n\nExcept for `d`, all of the parameters are available as fields (with the same name) on the Drag object. In addition, after the drag has been rendered, the following fields become available:\n\n`x`, `y` The position of the Drag relative to its parent, in pixels.\n\n`w`, `h` The width and height of the Drag's child, in pixels."], "nvl_list": ["internal", "function", "()", "", "", ""], "gui.file_slot_rows": ["gui", "gui.file_slot_rows", " = 2", "", "var", "The number of columns and rows in the grid of save slots."], "dict": ["internal", "class", "(*args, **kwargs)", "", "", ""], "gui.button_text_insensitive_color": ["gui", "gui.button_text_insensitive_color", " = gui.insensitive_color", "", "var", "The color of the button text in various states."], "ui.at": ["obsolete", "function", "(transform)", "", "", "Specifies a transform that is applied to the next displayable to be created. This is largely obsolete, as all UI functions now take an `at` argument."], "gui.scrollbar_size": ["gui", "gui.scrollbar_size", " = 24", "", "var", "The height of horizontal scrollbars, and width of vertical scrollbars."], "preferences.gl_framerate": ["preferences", "preferences.gl_framerate", " = None", "", "var", "This is either an integer, or None. If not None, it's a target framerate that Ren'Py will attempt to achieve. If this is set low (for example, to 30), on a monitor with a high framerate (say, 60 frames per second), Ren'Py will only draw on every other frame.\n\nIf None, Ren'Py will attempt to draw at the monitor's full framerate."], "Lexer.has_block": ["cds", "has_block", "()", "Lexer", "method", "True if the current line has a non-empty block."], "gui.bar_tile": ["gui", "gui.bar_tile", " = False", "", "var", "If true, the bar images are tiled. If false, the images are linearly scaled."], "im.Map": ["obsolete", "class", "(im, rmap='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&\\'()*+,-./0123456789:;NotSet?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff', gmap='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&\\'()*+,-./0123456789:;NotSet?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff', bmap='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&\\'()*+,-./0123456789:;NotSet?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff', amap='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&\\'()*+,-./0123456789:;NotSet?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff', force_alpha=False, **properties)", "", "", "This adjusts the colors of the image that is its child. It takes as arguments 4 256 character strings. If a pixel channel has a value of 192, then the value of the 192nd character in the string is used for the mapped pixel component."], "ui.imagebutton": ["obsolete", "ui.imagebutton", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "nvl_narrator": ["internal", "function", "()", "", "", ""], "FileUsedSlots": ["internal", "function", "(page=None, highest_first=True)", "", "Action", "Returns a list of used numeric file slots on the page.\n\n`page` The name of the page that will be scanned. If None, the current page is used.\n\n`highest_first` If true, the highest-numbered file slot is listed first. Otherwise, the lowest-numbered slot is listed first."], "nvl_show_core": ["internal", "function", "(who=None, what=None, multiple=None)", "", "", ""], "Matrix.offset": ["matrix", "Matrix.offset", "(x, y, z)", "", "function", "Returns a matrix that offsets the vertex by a fixed amount."], "ui.ChildOrFixed": ["obsolete", "class", "(style_prefix)", "", "", "If one widget is added, then it is added directly to our parent. Otherwise, a fixed is added to our parent, and all the widgets are added to that."], "Text": ["internal", "class", "(text, slow=None, scope=None, substitute=None, slow_done=None, mipmap=None, **properties)", "", "", "A displayable that displays text on the screen.\n\n`text` The text to display on the screen. This may be a string, or a list of strings and displayables.\n\n`slow` Determines if the text is displayed slowly, being typed out one character at the time. If None, slow text mode is determined by the :propref:`slow_cps` style property. Otherwise, the truth value of this parameter determines if slow text mode is used.\n\n`scope` If not None, this should be a dictionary that provides an additional scope for text interpolation to occur in.\n\n`substitute` If true, text interpolation occurs. If false, it will not occur. If None, they are controlled by :var:`config.new_substitutions`."], "Frame": ["internal", "class", "(image, left=0, top=0, right=None, bottom=None, tile=False, **properties)", "", "", "A displayable that resizes an image to fill the available area, while preserving the width and height of its borders. It is often used as the background of a window or button.\n\n Using a frame to resize an image to double its size.\n\n`image` An image manipulator that will be resized by this frame.\n\n`left` The size of the border on the left side. This can also be a :func:`Borders` object, in which case that object is used in place of the other parameters.\n\n`top` The size of the border on the top.\n\n`right` The size of the border on the right side. If None, defaults to `left`.\n\n`bottom` The side of the border on the bottom. If None, defaults to `top`.\n\n`tile` If set to True, tiling is used to resize sections of the image, rather than scaling. If set to the string \\integer\\, the nearest integer number of tiles will be used in each direction. That set of full tiles will then be scaled up or down to fit the required area.\n```\n# Resize the background of the text window if it's too small.\n init python:\n style.window.background = Frame(\\frame.png\\, 10, 10)\n```"], "VoiceReplay": ["internal", "class", "(*args, **kwargs)", "", "Action", "Replays the most recently played voice."], "FieldEquality": ["internal", "class", "(*args, **kwargs)", "", "", "Declares two objects equal if their types are the same, and the listed fields are equal."], "updater.update": ["internal", "function", "(url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, confirm=True)", "", "", "Updates this Ren'Py game to the latest version.\n\n`url` The URL to the updates.json file.\n\n`base` The base directory that will be updated. Defaults to the base of the current game. (This can usually be ignored.)\n\n`force` Force the update to occur even if the version numbers are the same. (Used for testing.)\n\n`public_key` The path to a PEM file containing a public key that the update signature is checked against. (This can usually be ignored.)\n\n`simulate` This is used to test update guis without actually performing an update. This can be:\n\n* None to perform an update. * \\available\\ to test the case where an update is available. * \\not_available\\ to test the case where no update is available. * \\error\\ to test an update error.\n\n`add` A list of packages to add during this update. This is only necessary for dlc.\n\n`restart` Restart the game after the update.\n\n`confirm` Should Ren'Py prompt the user to confirm the update? If False, the update will proceed without confirmation."], "ui.Imagemap": ["obsolete", "class", "(insensitive, idle, selected_idle, hover, selected_hover, selected_insensitive, alpha, cache)", "", "", "Stores information about the images used by an imagemap."], "Flatten": ["internal", "class", "(child, **properties)", "", "", "This flattens `child`, which may be made up of multiple textures, into a single texture.\n\nCertain operations, like the alpha transform property, apply to every texture making up a displayable, which can yield incorrect results when the textures overlap on screen. Flatten creates a single texture from multiple textures, which can prevent this problem.\n\nFlatten is a relatively expensive operation, and so should only be used when absolutely required."], "dissolve": ["transitions", "dissolve", "", "", "var", "Takes 0.5 seconds to dissolve from the old to the new screen. An instance of the :func:`Dissolve` transition class."], "Lexer.float": ["cds", "float", "()", "Lexer", "method", "Matches a floating point number, returns a string containing the floating point number."], "renpy": ["internal", "function", "()", "", "", "To allow Ren'Py to be scripted in Python, each Ren'Py statement has a Python equivalent. This usually consists of a Python function, but may also consist of a pattern of Python calls that perform an action equivalent to the statement."], "SetMute": ["internal", "class", "(mixer, mute)", "", "Action", "Sets the mute status of one or more mixers. When a mixer is muted, audio channels associated with that mixer will stop playing audio.\n\n`mixer` Either a single string giving a mixer name, or a list of strings giving a list of mixers. The strings should be mixer names, usually \\music\\, \\sfx\\, or \\voice\\.\n\n`mute` True to mute the mixer, False to ummute it."], "Lexer.word": ["cds", "word", "()", "Lexer", "method", "Matches any word, including keywords. Returns the text of the matched word."], "Tooltip": ["internal", "class", "(default)", "", "Action", "A tooltip object can be used to define a portion of a screen that is updated when the mouse hovers an area.\n\nA tooltip object has a ``value`` field, which is set to the `default` value passed to the constructor when the tooltip is created. When a button using an action created by the tooltip is hovered, the value field changes to the value associated with the action."], "Lexer.integer": ["cds", "integer", "()", "Lexer", "method", "Matches an integer, returns a string containing the integer."], "layeredimage.format_function": ["internal", "function", "(what, name, group, variant, attribute, image, image_format, **kwargs)", "", "", "This is called to format the information about an attribute or condition into a displayable. This can be replaced by a creator, but the new function should ignore unknown kwargs.\n\n`what` A string giving a description of the thing being formatted, which is used to create better error messages.\n\n`name` The name of the layeredimage.\n\n`group` The group of an attribute, None if not supplied or if it's part of a condition.\n\n`variant` The variant argument to the group, or None if it is not supplied.\n\n`attribute` The attribute itself.\n\n`image` Either a displayable or string.\n\n`image_format` The image_format argument of the LayeredImage.\n\nIf `image` is None, then `name`, `group` (if not None), `variant` (if not None), and `attribute` are combined with underscores to create `image`, which will then be a string.\n\nIf `images` is a string, and `image_format` is not None, `image` is formatted into the string to get the final displayable.\n\nSo if `name` is \\eileen\\, `group` is \\expression\\, and `attribute` is \\happy\\, `image` would be set to \\eileen_expression_happy\\. If `image_format` is \\images/{image}.png\\, the final image Ren'Py finds is \\images/eileen_expression_happy.png\\. But note that it would have found the same image without the format argument."], "Speaker": ["internal", "function", "(name=NotSet, kind=None, **properties)", "", "", ""], "GamepadCalibrate": ["internal", "function", "()", "", "Action", "An action that invokes the gamepad calibration routine."], "build.change_icon_i686": ["build", "build.change_icon_i686", " = True", "", "var", "If True, and icon.ico exists, the icon of the 32-bit Windows executable will be changed. If False, the icon will not be changed. Setting this to False may prevent some antivirus programs from producing a false positive for your game."], "RotateMatrix": ["internal", "class", "(x, y, z)", "", "", "A TransformMatrix that returns a matrix that rotates the displayable around the origin.\n\n`x`, `y`, `z` The amount to rotate around the origin, in degrees.\n\n The rotations are applied in order:\n\n* A clockwise rotation by `x` degrees in the Y/Z plane. * A clockwise rotation by `y` degrees in the Z/X plane. * A clockwise rotation by `z` degrees in the X/Y plane."], "PauseAudio": ["internal", "class", "(channel, value=True)", "", "Action", "Sets the pause flag for `channel`.\n\nIf `value` is True, the channel is paused. If False, the channel is unpaused. If \\toggle\\, the pause flag will be toggled."], "moveinright": ["transitions", "moveinright", "", "", "var", "Also: **moveinleft, moveintop, moveinbottom**\n\nThese move entering images onto the screen from the appropriate side, taking 0.5 seconds to do so."], "LayeredImage": ["internal", "class", "(attributes, at=[], name=None, image_format=None, format_function=None, attribute_function=None, **kwargs)", "", "", "Get the set of attributes that are incompatible with those in attributes."], "MultipleTransition": ["transitions", "function", "(args)", "", "", "Returns a transition that allows multiple transitions to be displayed, one after the other.\n\n`args` A *list* containing an odd number of items. The first, third, and other odd-numbered items must be scenes, and the even items must be transitions. A scene can be one of:\n\n* A displayable. * False, to use the old scene. * True, to use the new scene.\n\nAlmost always, the first argument will be False and the last True.\n\nThe transitions in `args` are applied in order. For each transition, the old scene is the screen preceding it, and the new scene is the scene following it. For example\n```\ndefine logodissolve = MultipleTransition([\nFalse, Dissolve(0.5),\n\\logo.jpg\\, Pause(1.0),\n\\logo.jpg\\, dissolve,\nTrue])\n\nThis example will dissolve to logo.jpg, wait 1 second, and then\ndissolve to the new scene.\n```"], "updater.get_installed_packages": ["internal", "function", "(base=None)", "", "", "Returns a list of installed DLC package names.\n\n`base` The base directory to update. Defaults to the current project's base directory."], "Play": ["internal", "class", "(channel, file, selected=None, **kwargs)", "", "Action", "Causes an audio file to be played on a given channel.\n\n `channel` The channel to play the sound on. `file` The file to play. `selected` If True, buttons using this action will be marked as selected if the file is playing on the channel. If False, this action will not cause the button to start playing. If None, the button is marked selected if the channel is a music channel, and not otherwise.\n\n Any other keyword arguments are passed to :func:`renpy.music.play`."], "vpunch": ["transitions", "vpunch", "", "", "var", "When invoked, this transition shakes the screen vertically for a quarter second."], "MoveTransition": ["internal", "function", "()", "", "", ""], "NVLCharacter": ["internal", "class", "(who=NotSet, kind=None, **properties)", "", "", ""], "im.Flip": ["obsolete", "class", "(im, horizontal=False, vertical=False, **properties)", "", "", "An image manipulator that flips `im` (an image manipulator) vertically or horizontally. `vertical` and `horizontal` control the directions in which the image is flipped.\n```\nimage eileen flip = im.Flip(\\eileen_happy.png\\, vertical=True)\n\nThe same effect can now be achieved by setting\n:tpref:`xzoom` (for horizontal flip)\nor :tpref:`yzoom` (for vertical flip) to a negative value.\n```"], "ui.ChoiceJump": ["obsolete", "class", "(label, value, location=None, block_all=None, sensitive=True, args=None, kwargs=None)", "", "", "A menu choice action that returns `value`, while managing the button state in a manner consistent with fixed rollback. (See block_all for a description of the behavior.)\n\n `label` The label text of the button. For imagebuttons and hotspots this can be anything. This label is used as a unique identifier of the options within the current screen. Together with `location` it is used to store whether this option has been chosen.\n\n`value` The location to jump to.\n\n`location` A unique location identifier for the current choices screen.\n\n`block_all` If false, the button is given the selected role if it was the chosen choice, and insensitive if it was not selected.\n\nIf true, the button is always insensitive during fixed rollback.\n\nIf None, the value is taken from the :var:`config.fix_rollback_without_choice` variable.\n\nWhen true is given to all items in a screen, it will become unclickable (rolling forward will still work). This can be changed by calling :func:`ui.saybehavior` before the call to :func:`ui.interact`."], "Preference": ["internal", "function", "(name, value=None, range=None)", "", "Action", "This constructs the appropriate action or value from a preference. The preference name should be the name given in the standard menus, while the value should be either the name of a choice, \\toggle\\ to cycle through choices, a specific value, or left off in the case of buttons.\n\n Actions that can be used with buttons and hotspots are:\n\n * Preference(\\display\\, \\fullscreen\\) - displays in fullscreen mode. * Preference(\\display\\, \\window\\) - displays in windowed mode at 1x normal size. * Preference(\\display\\, 2.0) - displays in windowed mode at 2.0x normal size. * Preference(\\display\\, \\any window\\) - displays in windowed mode at the previous size. * Preference(\\display\\, \\toggle\\) - toggle display mode.\n\n * Preference(\\transitions\\, \\all\\) - show all transitions. * Preference(\\transitions\\, \\none\\) - do not show transitions. * Preference(\\transitions\\, \\toggle\\) - toggle transitions.\n\n * Preference(\\video sprites\\, \\show\\) - show all video sprites. * Preference(\\video sprites\\, \\hide\\) - fall back to images where possible. * Preference(\\video sprites\\, \\toggle\\) - toggle image fallback behavior.\n\n * Preference(\\show empty window\\, \\show\\) - Allow the \\window show\\ and \\window auto\\ statement to show an empty window outside of the say statement. * Preference(\\show empty window\\, \\hide\\) - Prevent the above. * Preference(\\show empty window\\, \\toggle\\) - Toggle the above.\n\n * Preference(\\text speed\\, 0) - make text appear instantaneously. * Preference(\\text speed\\, 142) - set text speed to 142 characters per second.\n\n * Preference(\\joystick\\) - Show the joystick preferences.\n\n * Preference(\\skip\\, \\seen\\) - Only skip seen messages. * Preference(\\skip\\, \\all\\) - Skip unseen messages. * Preference(\\skip\\, \\toggle\\) - Toggle between skip seen and skip all.\n\n * Preference(\\begin skipping\\) - Starts skipping.\n\n * Preference(\\after choices\\, \\skip\\) - Skip after choices. * Preference(\\after choices\\, \\stop\\) - Stop skipping after choices. * Preference(\\after choices\\, \\toggle\\) - Toggle skipping after choices.\n\n * Preference(\\auto-forward time\\, 0) - Set the auto-forward time to infinite. * Preference(\\auto-forward time\\, 10) - Set the auto-forward time (unit is seconds per 250 characters).\n\n * Preference(\\auto-forward\\, \\enable\\) - Enable auto-forward mode. * Preference(\\auto-forward\\, \\disable\\) - Disable auto-forward mode. * Preference(\\auto-forward\\, \\toggle\\) - Toggle auto-forward mode.\n\n * Preference(\\auto-forward after click\\, \\enable\\) - Remain in auto-forward mode after a click. * Preference(\\auto-forward after click\\, \\disable\\) - Disable auto-forward mode after a click. * Preference(\\auto-forward after click\\, \\toggle\\) - Toggle auto-forward after click.\n\n * Preference(\\automatic move\\, \\enable\\) - Enable automatic mouse mode. * Preference(\\automatic move\\, \\disable\\) - Disable automatic mouse mode. * Preference(\\automatic move\\, \\toggle\\) - Toggle automatic mouse mode.\n\n * Preference(\\wait for voice\\, \\enable\\) - Wait for the currently playing voice to complete before auto-forwarding. * Preference(\\wait for voice\\, \\disable\\) - Do not wait for the currently playing voice to complete before auto-forwarding. * Preference(\\wait for voice\\, \\toggle\\) - Toggle wait voice.\n\n * Preference(\\voice sustain\\, \\enable\\) - Sustain voice past the current interaction. * Preference(\\voice sustain\\, \\disable\\) - Don't sustain voice past the current interaction. * Preference(\\voice sustain\\, \\toggle\\) - Toggle voice sustain.\n\n * Preference(\\music mute\\, \\enable\\) - Mute the music mixer. * Preference(\\music mute\\, \\disable\\) - Un-mute the music mixer. * Preference(\\music mute\\, \\toggle\\) - Toggle music mute.\n\n * Preference(\\sound mute\\, \\enable\\) - Mute the sound mixer. * Preference(\\sound mute\\, \\disable\\) - Un-mute the sound mixer. * Preference(\\sound mute\\, \\toggle\\) - Toggle sound mute.\n\n * Preference(\\voice mute\\, \\enable\\) - Mute the voice mixer. * Preference(\\voice mute\\, \\disable\\) - Un-mute the voice mixer. * Preference(\\voice mute\\, \\toggle\\) - Toggle voice mute.\n\n * Preference(\\mixer mute\\, \\enable\\) - Mute the specified mixer. * Preference(\\mixer mute\\, \\disable\\) - Unmute the specified mixer. * Preference(\\mixer mute\\, \\toggle\\) - Toggle mute of specified mixer.\n\n * Preference(\\all mute\\, \\enable\\) - Mute all mixers. * Preference(\\all mute\\, \\disable\\) - Unmute all mixers. * Preference(\\all mute\\, \\toggle\\) - Toggle mute of all mixers.\n\n * Preference(\\music volume\\, 0.5) - Set the music volume. * Preference(\\sound volume\\, 0.5) - Set the sound volume. * Preference(\\voice volume\\, 0.5) - Set the voice volume. * Preference(\\mixer volume\\, 0.5) - Set the specified mixer volume.\n\n * Preference(\\emphasize audio\\, \\enable\\) - Emphasize the audio channels found in :var:`config.emphasize_audio_channels`. * Preference(\\emphasize audio\\, \\disable\\) - Do not emphasize audio channels. * Preference(\\emphasize audio\\, \\toggle\\) - Toggle emphasize audio.\n\n * Preference(\\self voicing\\, \\enable\\) - Enables self-voicing. * Preference(\\self voicing\\, \\disable\\) - Disable self-voicing. * Preference(\\self voicing\\, \\toggle\\) - Toggles self-voicing.\n\n * Preference(\\self voicing volume drop\\, 0.5) - Drops the volume of non-voice mixers when self voicing is active.\n\n * Preference(\\clipboard voicing\\, \\enable\\) - Enables clipboard-voicing. * Preference(\\clipboard voicing\\, \\disable\\) - Disable clipboard-voicing. * Preference(\\clipboard voicing\\, \\toggle\\) - Toggles clipboard-voicing.\n\n * Preference(\\debug voicing\\, \\enable\\) - Enables self.-voicing debug * Preference(\\debug voicing\\, \\disable\\) - Disable self-voicing debug. * Preference(\\debug voicing\\, \\toggle\\) - Toggles self-voicing debug.\n\n * Preference(\\rollback side\\, \\left\\) - Touching the left side of the screen causes rollback. * Preference(\\rollback side\\, \\right\\) - Touching the right side of the screen causes rollback. * Preference(\\rollback side\\, \\disable\\) - Touching the screen will not cause rollback.\n\n * Preference(\\gl powersave\\, True) - Drop framerate to allow for power savings. * Preference(\\gl powersave\\, False) - Do not drop framerate to allow for power savings. * Preference(\\gl powersave\\, \\auto\\) - Enable powersave when running on battery.\n\n * Preference(\\gl framerate\\, None) - Runs at the display framerate. * Preference(\\gl framerate\\, 60) - Runs at the given framerate.\n\n * Preference(\\gl tearing\\, True) - Tears rather than skipping frames. * Preference(\\gl tearing\\, False) - Skips frames rather than tearing.\n\n * Preference(\\font transform\\, \\opendyslexic\\) - Sets the accessibility font transform to opendyslexic. * Preference(\\font transform\\, \\dejavusans\\) - Sets the accessibility font transform to deja vu sans. * Preference(\\font transform\\, None) - Disables the accessibility font transform.\n\n * Preference(\\font size\\, 1.0) - Sets the accessibility font size scaling factor. * Preference(\\font line spacing\\, 1.0) - Sets the accessibility font vertical spacing scaling factor.\n\n * Preference(\\system cursor\\, \\enable\\) - Use system cursor ignoring config.mouse. * Preference(\\system cursor\\, \\disable\\) - Use cursor defined in config.mouse. * Preference(\\system cursor\\, \\toggle\\) - Toggle system cursor.\n\n Values that can be used with bars are:\n\n * Preference(\\text speed\\) * Preference(\\auto-forward time\\) * Preference(\\music volume\\) * Preference(\\sound volume\\) * Preference(\\voice volume\\) * Preference(\\mixer volume\\) * Preference(\\self voicing volume drop\\) * Preference(\\font size\\) * Preference(\\font line spacing\\)\n\n The `range` parameter can be given to give the range of certain bars. For \\text speed\\, it defaults to 200 cps. For \\auto-forward time\\, it defaults to 30.0 seconds per chunk of text. (These are maximums, not defaults.)\n\n Actions that can be used with buttons are:\n\n * Preference(\\renderer menu\\) - Show the renderer menu. * Preference(\\accessibility menu\\) - Show the accessibility menu.\n\n These screens are intended for internal use, and are not customizable."], "absolute": ["internal", "class", "()", "", "", ""], "FilePageNext": ["internal", "class", "(max=None, wrap=False, auto=True, quick=True)", "", "Action", "Goes to the next file page.\n\n`max` If set, this should be an integer that gives the number of the maximum file page we can go to.\n\n`wrap` If true, we can go to the first page when on the last file page if `max` is set.\n\n`auto` If true and wrap is set, this can bring the player to the page of automatic saves.\n\n`quick` If true and wrap is set, this can bring the player to the page of automatic saves."], "iap.is_deferred": ["internal", "function", "(product)", "", "", "Returns True if the user has asked to purchase `product`, but that request has to be approved by a third party, such as a parent or guardian."], "DynamicImage": ["internal", "class", "(name)", "", "", "A DynamicImage is a displayable that has text interpolation performed on it to yield a string giving a new displayable. Such interpolation is performed at the start of each interaction."], "NVLSpeaker": ["internal", "class", "(who=NotSet, kind=None, **properties)", "", "", ""], "Matrix.rotate": ["matrix", "Matrix.rotate", "(x, y, z)", "", "function", "Returns a matrix that rotates the displayable around the origin.\n\n`x`, `y`, `z` The amount to rotate around the origin, in degrees.\n\n The rotations are applied in order:\n\n* A clockwise rotation by `x` degrees in the Y/Z plane. * A clockwise rotation by `y` degrees in the Z/X plane. * A clockwise rotation by `z` degrees in the X/Y plane."], "Lexer.name": ["cds", "name", "()", "Lexer", "method", "Matches a name. This does not match built-in keywords."], "im.math": ["obsolete", "function", "()", "", "", "This module is always available. It provides access to the mathematical functions defined by the C standard."], "preferences.text_cps": ["preferences", "preferences.text_cps", " = 0", "", "var", "The speed of text display. 0 is infinite, otherwise this is the number of characters per second to show. The equivalent of the \\text speed\\ preference."], "director": ["internal", "function", "()", "", "", ""], "gui.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "ui.hotbar": ["obsolete", "ui.hotbar", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "Model": ["internal", "class", "(size=None, **properties)", "", "", "This is a displayable that causes Ren'Py to create a 2D or 3D model for use with the model-based renderer, that will be drawn in a single operation with the shaders given here, or selected by an enclosing Transform or Displayable.\n\n`size` If not None, this should be a width, height tuple, that's used to give the size of the Model. If not given, the model is the size of the area provided to it. The fit parameter to a texture takes precedence.\n\nIf no mesh method is called, a mesh that sets a_position and a_tex_coord to match the way Ren'Py loads textures is created if at least one texture is supplied. Otherwise, a mesh that only sets a_position is used.\n\nAll methods on this calls return the displayable the method is called on, making it possible to chain calls."], "gui.interface_text_font": ["gui", "gui.interface_text_font", " = \"ArchitectsDaughter.ttf\"", "", "var", "The font used for text for user interface elements, like the main and game menus, buttons, and so on."], "im.expands_bounds": ["obsolete", "function", "(bounds, size, amount)", "", "", "This expands the rectangle bounds by amount, while ensure it fits inside size."], "TextButton": ["internal", "function", "(text, style=u'button', text_style=u'button_text', clicked=None, **properties)", "", "", ""], "AddToSet": ["internal", "class", "(set, value)", "", "Action", "Adds `value` to `set`.\n\n`set` The set to add to. This may be a Python set or list, in which case the value is appended to the list. `value` The value to add or append."], "sv": ["internal", "function", "(what, interact=True)", "", "", ""], "gui.confirm_frame_borders": ["gui", "gui.confirm_frame_borders", " = Borders(60, 60, 60, 60)", "", "var", "The borders applied to the fame used in the confirm screen."], "gui.slider_size": ["gui", "gui.slider_size", " = 64", "", "var", "The height of horizontal sliders, and width of vertical sliders."], "gui.math": ["internal", "function", "()", "", "", "This module is always available. It provides access to the mathematical functions defined by the C standard."], "director.audio_channels": ["director", "director.audio_channels", " = [ \"music\", \"sound\", \"audio\" ]", "", "var", "The name of the audio channels that can be used with the play, show and stop statements."], "iap.restore": ["internal", "function", "(interact=True)", "", "", "Contacts the app store and restores any missing purchases.\n\n`interact` If True, renpy.pause will be called while waiting for the app store to respond."], "RollForward": ["internal", "class", "(*args, **kwargs)", "", "Action", "This behavior implements rollforward."], "QuickLoad": ["internal", "function", "(confirm=True)", "", "Action", "Performs a quick load.\n\n`confirm` If true and not at the main menu, prompt for confirmation before loading the file."], "Hide": ["internal", "class", "(screen, transition=None, _layer=None)", "", "Action", "This causes the screen named `screen` to be hidden, if it is shown.\n\n`transition` If not None, a transition that occurs when hiding the screen.\n\n`_layer` This is passed as the layer argument to :func:`renpy.hide_screen`."], "gui.notify_ypos": ["gui", "gui.notify_ypos", " = 68", "", "var", "The vertical position of the notify message, in pixels from the top of the window."], "layeredimage.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "Action.unhovered": ["screen_python", "unhovered", "(self)", "Action", "method", "When the action is used as the `hovered` parameter to a button (or similar object), this method is called when the object loses focus."], "move": ["transitions", "move", "", "", "var", "Takes 0.5 seconds to the move images that have changed location to their new locations. An instance of the :func:`MoveTransition` transition class."], "gui.nvl_thought_xpos": ["gui", "gui.nvl_thought_xpos", " = 0.5", "", "var", "The positioning of character names, dialogue text, and thought/narration text, relative to the left side of the entry. This can be a number of pixels, or 0.5 to represent the center of the entry."], "ui.layer": ["obsolete", "function", "(name)", "", "", "Adds displayables to the layer named `name`. The later must be closed with :func:`ui.close`."], "SetScreenVariable": ["internal", "class", "(name, value)", "", "Action", "Causes the variable `name` associated with the current screen to be set to `value`."], "Style.clear": ["style", "clear", "()", "Style", "method", "This removes all style properties from this object. Values will be inherited from this object's parent."], "achievement.Sync": ["internal", "class", "()", "", "Action", "An action that calls achievement.sync(). This is only sensitive if achievements are out of sync."], "gui.idle_color": ["gui", "gui.idle_color", " = '#606060'", "", "var", "The color used for most buttons when not focused or selected."], "style": ["internal", "function", "()", "", "", "The object exported as style in the store."], "FieldInputValue": ["internal", "class", "(object, field, default=True, returnable=False)", "", "Action", "An input value that updates `field` on `object`.\n\n`field` A string giving the name of the field.\n\n`default` If true, this input can be editable by default.\n\n`returnable` If true, the value of this input will be returned when the user presses enter."], "im.threading": ["obsolete", "function", "()", "", "", "Thread module emulating a subset of Java's threading model."], "Matrix.inverse": ["internal", "function", "()", "", "", "Returns the inverse of this matrix."], "updater.python_object": ["internal", "class", "()", "", "", "The most base type"], "FileLoadable": ["internal", "function", "(name, page=None, slot=False)", "", "Action", "This is a function that returns true if the file is loadable, and false otherwise."], "fade": ["transitions", "fade", "", "", "var", "Takes 0.5 seconds to fade to black, and then 0.5 seconds to fade to the new screen. An instance of the :func:`Fade` transition class."], "ToggleField": ["internal", "class", "(object, field, true_value=None, false_value=None)", "", "Action", "Toggles `field` on `object`. Toggling means to invert the boolean value of that field when the action is performed.\n\n `true_value` If not None, then this is the true value we use. `false_value` If not None, then this is the false value we use."], "Lexer.require": ["cds", "require", "(thing, name=None)", "Lexer", "method", "Tries to parse `thing`, and reports an error if it cannot be done.\n\nIf `thing` is a string, tries to parse it using :func:`match`. Otherwise, thing must be a other method on this lexer object, which is called without arguments. If `name` is not specified, the name of the method will be used in the message (or `thing` if it's a string), otherwise the `name` will be used."], "wipeleft": ["transitions", "wipeleft", "", "", "var", "Also: **wiperight, wipeup, wipedown**\n\nWipes the scene in the given direction. Instances of the :func:`CropMove` transition class."], "achievement.clear": ["internal", "function", "(name)", "", "", "Clears the achievement with `name`."], "anim": ["internal", "function", "()", "", "", ""], "ToggleScreen": ["internal", "class", "(screen, transition=None, *args, **kwargs)", "", "Action", "This toggles the visibility of `screen`. If it is not currently shown, the screen is shown with the provided arguments. Otherwise, the screen is hidden.\n\nIf not None, `transition` is use to show and hide the screen."], "HistoryEntry.window_args": ["history", "window_args", "", "HistoryEntry", "attribute", "A dictionary giving the properties that were supplied to the dialogue window when the dialogue was originally shown."], "Solid": ["internal", "class", "(color, **properties)", "", "", "A displayable that fills the area its assigned with `color`.\n```\nimage white = Solid(\\#fff\\)\n\n```"], "BarValue.get_adjustment": ["screen_python", "get_adjustment", "(self)", "BarValue", "method", "This method is called to get an adjustment object for the bar. It should create the adjustment with :func:`ui.adjustment`, and then return the object created this way.\n\nThis method must be overridden, as the default method will raise NotImplemented (and hence cause Ren'Py to report an error)."], "im.zipfile": ["obsolete", "function", "()", "", "", "Read and write ZIP files."], "Motion": ["internal", "function", "()", "", "", "This is used to move a child displayable around the screen. It works by supplying a time value to a user-supplied function, which is in turn expected to return a pair giving the x and y location of the upper-left-hand corner of the child, or a 4-tuple giving that and the xanchor and yanchor of the child.\n\nThe time value is a floating point number that ranges from 0 to 1. If repeat is True, then the motion repeats every period sections. (Otherwise, it stops.) If bounce is true, the time value varies from 0 to 1 to 0 again.\n\nThe function supplied needs to be pickleable, which means it needs to be defined as a name in an init block. It cannot be a lambda or anonymous inner function. If you can get away with using Pan or Move, use them instead.\n\nPlease note that floats and ints are interpreted as for xpos and ypos, with floats being considered fractions of the screen."], "updater.UpdateCancelled": ["internal", "class", "()", "", "", "Used to report the update being cancelled."], "Action.periodic": ["screen_python", "periodic", "(st)", "Action", "method", "This method is called once at the start of each interaction, and then is called periodically thereafter. If it returns a number, it will be called before that many seconds elapse, but it might be called sooner.\n\nThe main use of this is to call :func:`renpy.restart_interaction` if the value of get_selected or get_sensitive should change.\n\nIt takes one argument:\n\n`st` The number of seconds since the screen or displayable this action is associated with was first shown."], "sorted": ["internal", "function", "(*args, **kwargs)", "", "", ""], "gui.dialogue_ypos": ["gui", "gui.dialogue_ypos", " = 75", "", "var", "The horizontal and vertical positions of the actual dialogue. These are usually a number of pixels from the left or top side of the textbox. Setting a variable to 0.5 centers the dialogue in the textbox (see below)."], "ui.transform": ["obsolete", "ui.transform", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "ui.Many": ["obsolete", "class", "(displayable, imagemap, style_prefix)", "", "", "A widget that takes many children."], "Help": ["internal", "class", "(help=None)", "", "Action", "Displays help.\n\nIf a screen named ``help`` is defined, that screen is displayed using :func:`ShowMenu` and `help` is ignored.\n\n`help` A string that is used to find help. This is used in the following way:\n\n* If a label with this name exists, the label is called in a new context. * Otherwise, this is interpreted as giving the name of a file that should be opened in a web browser.\n\nIf `help` is None, :var:`config.help` is used as the default value."], "build.exclude_empty_directories": ["build", "build.exclude_empty_directories", " = True", "", "var", "If true, empty directories (including directories left empty by file archiving) will be removed from generated packages. If false, empty directories will be included."], "Skip": ["internal", "class", "(fast=False, confirm=False)", "", "Action", "Causes the game to begin skipping. If the game is in a menu context, then this returns to the game. Otherwise, it just enables skipping.\n\n `fast` If true, skips directly to the next menu choice.\n\n `confirm` If true, asks for confirmation before beginning skipping."], "preferences.skip_after_choices": ["preferences", "preferences.skip_after_choices", " = False", "", "var", "If True, skipping will resume after a choice. If False, a choice will prevent Ren'Py from skipping. The equivalent of the \\after choices\\ preference."], "InputValue.Enable": ["screen_python", "Enable", "()", "InputValue", "method", "Returns an action that enables text editing on the input."], "MultiPersistent.save": ["persistent", "save", "()", "MultiPersistent", "method", "Saves the multipersistent data to disk. This must be called after the data is modified."], "MoveFactory": ["internal", "function", "()", "", "", ""], "gui.history_text_xpos": ["gui", "gui.history_text_xpos", " = 0.5", "", "var", "The horizontal positions of the name label and dialogue text. These can be a number of pixels from the left side of the history entry, or 0.5 to center."], "ui.textbutton": ["obsolete", "ui.textbutton", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "SetCharacterVolume": ["internal", "function", "(voice_tag, volume=None)", "", "", "This allows the volume of each characters to be adjusted. If `volume` is None, this returns a BarValue that controls the value of `voice_tag`. Otherwise, this set it to `volume`.\n\n`volume` is a number between 0.0 and 1.0, and is interpreted as a fraction of the mixer volume for `voice` channel."], "gui.vbar_borders": ["gui", "gui.vbar_borders", " = Borders(10, 10, 10, 10)", "", "var", "The borders that are used with the Frames containing the bar images."], "with_statement": ["internal", "function", "()", "", "", ""], "topright": ["internal", "function", "()", "", "", ""], "gui.button_image_extension": ["gui", "gui.button_image_extension", " = \".png\"", "", "var", "The extension for button images. This could be changed to .webp to use WEBP button images instead of png ones."], "ADVSpeaker": ["internal", "class", "(name=NotSet, kind=None, **properties)", "", "", ""], "build.display_name": ["internal", "function", "()", "", "", "The name that will be displayed in the title bar."], "Lexer.label_name": ["cds", "label_name", "(declare=False)", "Lexer", "method", "Matches a label name, either absolute or relative. If `declare` is true, then the global label name is set. (Note that this does not actually declare the label - the statement is required to do that by returning it from the `label` function.)"], "MouseDisplayable": ["internal", "class", "(cursor, x, y)", "", "", "A displayable that wraps a mouse cursor displayable, and causes it to move across the screen when the player moves their mouse.\n\n`cursor` A displayable that is used to draw the mouse.\n\n`x`, `y` The coordinates of the hotspot, relative to the upper left corner of the mouse, in virtual pixels.\n\n.. method MouseDisplayable.add(name, cursor, x, y)\n\nThis adds a second cursor, that is used when the `name` mouse is displayed. This returns the MouseDisplayable, so that calls to this method can be chained."], "toggle_skipping": ["internal", "function", "()", "", "", ""], "StaticValue": ["internal", "class", "(value=0.0, range=1.0)", "", "Action", "This allows a value to be specified statically.\n\n `value` The value itself, a number.\n\n `range` The range of the value."], "gui.button_text_size": ["gui", "gui.button_text_size", " = gui.interface_text_size", "", "var", "The font and size of the button text."], "slideleft": ["transitions", "slideleft", "", "", "var", "Also: **slideright, slideup, slidedown**\n\nSlides the new scene in the given direction. Instances of the :func:`CropMove` transition class."], "DictValue": ["internal", "class", "(dict, key, range, max_is_zero=False, style=u'bar', offset=0, step=None, action=None, force_step=False)", "", "Action", "A value that allows the user to adjust the value of a key in a dict.\n\n `dict` The dict. `key` The key. `range` The range to adjust over. `max_is_zero` If True, then when the value of a key is zero, the value of the bar will be range, and all other values will be shifted down by 1. This works both ways - when the bar is set to the maximum, the value of a key is set to 0.\n\n `style` The styles of the bar created. `offset` An offset to add to the value. `step` The amount to change the bar by. If None, defaults to 1/10th of the bar. `action` If not None, an action to call when the field has changed."], "pixellate": ["transitions", "pixellate", "", "", "var", "Pixellates the old scene for .5 seconds, and the new scene for another .5 seconds. An instance of the :func:`Pixellate` transition class."], "print": ["internal", "function", "(*args, **kwargs)", "", "", ""], "Revolve": ["internal", "function", "()", "", "", ""], "squares": ["transitions", "squares", "", "", "var", "Transitions the screen in a squares effect lasting 1 second."], "DictInputValue": ["internal", "class", "(dict, key, default=True, returnable=False)", "", "Action", "An input value that updates `key` in `dict`.\n\n`default` If true, this input can be editable by default.\n\n`returnable` If true, the value of this input will be returned when the user presses enter."], "OpacityMatrix": ["internal", "class", "(value=1.0)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to change the opacity of an image, while leaving color channels alone.\n\n`value` The amount the alpha channel should be multiplied by, a number between 0.0 and 1.0."], "moveoutright": ["transitions", "moveoutright", "", "", "var", "Also: **moveoutleft, moveouttop, moveoutbottom**\n\nThese move leaving images off the screen via the appropriate side, taking 0.5 seconds to do so."], "ScaleMatrix": ["internal", "class", "(x, y, z)", "", "", "A TransformMatrix that returns a matrix that scales the displayable.\n\n`x`, `y`, `z` The factor to scale each axis by."], "VoiceInfo": ["internal", "class", "(self)", "", "", "An object returned by VoiceInfo and get_voice_info()."], "nvl_menu": ["internal", "function", "(items)", "", "", "A Python function that displays a menu in NVL style. This is rarely used directly. Instead, it's assigned to the :var:`menu` variable, using something like\n```\ndefine menu = nvl_menu\n```"], "tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "main_menu": ["store_variables", "main_menu", " = False", "store", "var", "Ren'Py sets this variable to True while in the main menu. This can be used to have screens display differently while in the main menu."], "SetVariable": ["internal", "function", "(name, value)", "", "Action", "Causes the variable with `name` to be set to `value`.\n\nThe `name` argument must be a string, and can be a simple name like \\strength\\, or one with dots separating the variable from fields, like \\hero.strength\\ or \\persistent.show_cutscenes\\."], "topleft": ["internal", "function", "()", "", "", ""], "ui.text": ["obsolete", "ui.text", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "gui.history_text_ypos": ["gui", "gui.history_text_ypos", " = 60", "", "var", "The vertical positions of the name label and dialogue text, relative to the top of a history entry, in pixels."], "Action.__call__": ["screen_python", "__call__", "(self)", "Action", "method", "This is the method that is called when the action is activated. In many cases, returning a non-None value from the action will cause the current interaction to end.\n\nThis method must be overridden, as the default method will raise NotImplemented (and hence cause Ren'Py to report an error)."], "Jump": ["internal", "class", "(label)", "", "Action", "Causes control to transfer to `label`."], "swing": ["internal", "function", "()", "", "", ""], "SideImage": ["internal", "function", "()", "", "", "Returns the side image associated with the currently speaking character, or a Null displayable if no such side image exists."], "MultiPersistent": ["internal", "function", "(name, save_on_quit=False)", "", "", ""], "InvertSelected": ["internal", "class", "(action)", "", "Action", "This inverts the selection state of the provided action, while proxying over all of the other methods."], "gui.python_object": ["internal", "class", "()", "", "", "The most base type"], "Pause": ["transitions", "function", "(delay)", "", "", "Returns a transition that only displays the new screen for `delay` seconds. It can be useful as part of a MultipleTransition."], "build.executable_name": ["build", "build.executable_name", " = \"...\"", "", "var", "This variable controls the name of the executables that the user clicks on to start the game.\n\nThis variable should not contain special characters like spaces, colons, and semicolons. If not set, it defaults to :var:`build.name`.\n\nFor example, if this is set to \\mygame\\, the user will be able to run mygame.exe on Windows, mygame.app on Macintosh, and mygame.sh on Linux."], "gui.nvl_thought_ypos": ["gui", "gui.nvl_thought_ypos", " = 0", "", "var", "The position of character names, dialogue text, and thought/narration text, relative to the top of the entry. This should be a number of pixels from the top."], "gui.namebox_tile": ["gui", "gui.namebox_tile", " = False", "", "var", "These variables control the display of the frame containing the namebox."], "Start": ["internal", "class", "(label=u'start')", "", "Action", "Causes Ren'Py to jump out of the menu context to the named label. The main use of this is to start a new game from the main menu. Common uses are:\n\n * Start() - Start at the start label. * Start(\\foo\\) - Start at the \\foo\\ label."], "gui.interface_text_color": ["gui", "gui.interface_text_color", " = '#404040'", "", "var", "The color used by static text in the game interface, such as text on the help and about screens."], "Tile": ["internal", "class", "(child, style=u'tile', **properties)", "", "", "Tiles `child` until it fills the area allocated to this displayable.\n```\nimage bg tile = Tile(\\bg.png\\)\n\n```"], "preferences.mobile_rollback_side": ["preferences", "preferences.mobile_rollback_side", " = \"disable\"", "", "var", "When on a mobile platform, touches or clicks to this side of the window cause rollback to occur. One of \\left\\, \\right\\, or \\disable\\. This is the equivalend of the \\rollback side\\ preference when on a mobile platform."], "open": ["internal", "function", "(*args, **kwargs)", "", "", ""], "easeintop": ["transitions", "easeintop", "", "", "var", "Also: **easeinright, easeinleft, ease, easeinbottom, easeoutright, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "Lexer.expect_noblock": ["cds", "expect_noblock", "(stmt)", "Lexer", "method", "Called to indicate this statement does not expect a block. If a block is found, raises an error. `stmt` should be a string, it will be added to the message with an error."], "FileSaveName": ["internal", "function", "(name, empty=u'', page=None, slot=False)", "", "Action", "Return the save_name that was in effect when the file was saved, or `empty` if the file does not exist."], "Screenshot": ["internal", "class", "(*args, **kwargs)", "", "Action", "Takes a screenshot."], "preferences.mouse_move": ["preferences", "preferences.mouse_move", " = False", "", "var", "If True, the mouse will automatically move to a selected button. If False, it will not. The equivalent of the \\automatic mouse move\\ preference."], "top": ["internal", "function", "()", "", "", "Raises this displayable to the top of its drag_group."], "Lexer.renpy_block": ["cds", "renpy_block", "(empty=False)", "Lexer", "method", "This parses all of the remaining lines in the current block as Ren'Py script, and returns a SubParse corresponding to the first statement in the block. The block is chained together such that all statements in the block are run, and then control is transferred to the statement after this creator-defined statement.\n\nNote that this parses the current block. In the more likely case that you'd like to parse the subblock of the current statement, the correct way to do that is\n```\ndef mystatement_parse(l):\n\nl.require(':')\nl.expect_eol()\nl.expect_block(\\mystatement\\)\n\nchild = l.subblock_lexer().renpy_block()\n\nreturn { \\child\\ : child }\n\n`empty`\nIf True, allows an empty block to be parsed. (An empty block\nis equivalent to a block with a single ``pass`` statement.)\n\nIf False, an empty block triggers an error.\n\n\n```"], "layeredimage": ["internal", "function", "()", "", "", ""], "build.python_object": ["internal", "class", "()", "", "", "The most base type"], "im.Recolor": ["obsolete", "class", "(im, rmul=255, gmul=255, bmul=255, amul=255, force_alpha=False, **properties)", "", "", "This adjusts the colors of the image that is its child. It takes as an argument 4 numbers between 0 and 255, and maps each channel of the image linearly between 0 and the supplied color."], "HueMatrix": ["internal", "class", "(value=1.0)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to rotate the hue by `value` degrees. While `value` can be any number, positive or negative, 360 degrees makes a complete rotation. The alpha channel is left alone."], "iap": ["internal", "function", "()", "", "", ""], "slidedown": ["transitions", "slidedown", "", "", "var", "Also: **slideright, slideup, slideleft**\n\nSlides the new scene in the given direction. Instances of the :func:`CropMove` transition class."], "ui.ChoiceReturn": ["obsolete", "class", "(label, value, location=None, block_all=None, sensitive=True, args=None, kwargs=None)", "", "", "A menu choice action that returns `value`, while managing the button state in a manner consistent with fixed rollback. (See block_all for a description of the behavior.)\n\n `label` The label text of the button. For imagebuttons and hotspots this can be anything. This label is used as a unique identifier of the options within the current screen. Together with `location` it is used to store whether this option has been chosen.\n\n`value` The value this is returned when the choice is chosen.\n\n`location` A unique location identifier for the current choices screen.\n\n`block_all` If false, the button is given the selected role if it was the chosen choice, and insensitive if it was not selected.\n\nIf true, the button is always insensitive during fixed rollback.\n\nIf None, the value is taken from the :var:`config.fix_rollback_without_choice` variable.\n\nWhen true is given to all items in a screen, it will become unclickable (rolling forward will still work). This can be changed by calling :func:`ui.saybehavior` before the call to :func:`ui.interact`."], "im.Color": ["obsolete", "function", "(im, color)", "", "", "This recolors the supplied image, mapping colors such that black is black and white is the supplied color."], "gui.nvl_thought_width": ["gui", "gui.nvl_thought_width", " = 740", "", "var", "The width of each kind of text, in pixels."], "Language": ["internal", "class", "(language)", "", "Action", "Changes the language of the game to `language`.\n\n`language` A string giving the language to translate to, or None to use the default language of the game script."], "updater.os": ["internal", "function", "()", "", "", "OS routines for NT or Posix depending on what system we're on.\n\nThis exports: - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc. - os.path is one of the modules posixpath, or ntpath - os.name is 'posix', 'nt', 'os2', 'ce' or 'riscos' - os.curdir is a string representing the current directory ('.' or ':') - os.pardir is a string representing the parent directory ('..' or '\n```\n')\n - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\\\')\n - os.extsep is the extension separator ('.' or '/')\n - os.altsep is the alternate pathname separator (None or '/')\n - os.pathsep is the component separator used in $PATH etc\n - os.linesep is the line separator in text files ('\\r' or '\\n' or '\\r\\n')\n - os.defpath is the default search path for executables\n - os.devnull is the file path of the null device ('/dev/null', etc.)\n\nPrograms that import and use 'os' stand a better chance of being\nportable between different platforms. Of course, they must then\nonly use functions that are defined by all platforms (e.g., unlink\nand opendir), and leave all pathname manipulation to os.path\n(e.g., split and join).\n```"], "centered": ["internal", "function", "()", "", "", ""], "gui.text_size": ["gui", "gui.text_size", " = 33", "", "var", "Sets the size of the dialogue text. This may need to be increased or decreased to fit the selected font in the space allotted."], "ImageDissolve": ["transitions", "function", "(image, time, ramplen=8, reverse=False, alpha=True, time_warp=None)", "", "", "Returns a transition that dissolves the old scene into the new scene, using an image to control the dissolve process. This means that white pixels will dissolve in first, and black pixels will dissolve in last.\n\n`image` A control image to use. If `config.gl2` is True, then this can be any displayable, else it needs to be either an image file or an image manipulator. The control image should be the size of the scenes being dissolved, and if `reverse=True`, it should be fully opaque.\n\n`time` The time the dissolve will take.\n\n`ramplen` The length of the ramp to use. This must be an integer power of 2. When this is the default value of 8, when a white pixel is fully dissolved, a pixel 8 shades of gray darker will have completed one step of dissolving in.\n\n`reverse` If True, black pixels will dissolve in before white pixels.\n\n`alpha` Ignored.\n\n`time_warp` A function that adjusts the timeline. If not None, this should be a function that takes a fractional time between 0.0 and 1.0, and returns a number in the same range.\n```\ndefine circirisout = ImageDissolve(\\circiris.png\\, 1.0)\ndefine circirisin = ImageDissolve(\\circiris.png\\, 1.0, reverse=True)\ndefine circiristbigramp = ImageDissolve(\\circiris.png\\, 1.0, ramplen=256)\n```"], "preferences.afm_after_click": ["preferences", "preferences.afm_after_click", " = False", "", "var", "If True, auto-forward move will be continue after a click. If False, a click will end auto-forward mode. The equivalent of the \\auto-forward after click\\ preference."], "DictEquality": ["internal", "class", "(*args, **kwargs)", "", "", "Declares two objects equal if their types are the same, and their internal dictionaries are equal."], "updater.zsync_path": ["internal", "function", "(command)", "", "", "Returns the full platform-specific path to command, which is one of zsync or zsyncmake. If the file doesn't exists, returns the command so the system-wide copy is used."], "LayeredImageProxy": ["internal", "class", "(name, transform=None)", "", "", "This is an image-like object that proxies attributes passed to it to another layered image.\n\n`name` A string giving the name of the layered image to proxy to.\n\n`transform` If given, a transform or list of transforms that are applied to the image after it has been proxied."], "InputValue.set_text": ["screen_python", "set_text", "(s)", "InputValue", "method", "Called when the text of the input is changed, with the new text. This must be implemented."], "Stop": ["internal", "class", "(channel, **kwargs)", "", "Action", "Causes an audio channel to be stopped.\n\n `channel` The channel to stop the sound on.\n\n Any keyword arguments are passed to :func:`renpy.music.stop`"], "nvl_clear_next": ["internal", "function", "()", "", "", ""], "ToggleMute": ["internal", "class", "(mixer)", "", "Action", "Toggles the mute status of one or more mixers.\n\n`mixer` Either a single string giving a mixer name, or a list of strings giving a list of mixers. The strings should be mixer names, usually \\music\\, \\sfx\\, or \\voice\\."], "translate_define": ["translating_renpy", "translate_define", "(language, define, value, help=None)", "", "function", "This is used to set a define when generating a game. For example, it can be used to change the size of a font.\n\n`language` The language involved.\n\n`define` The name of the define.\n\n`value` A string giving the value the define should be set to. (ie. \\10\\, \\False\\, or \\'Font.ttf'\\).\n\n`comment` If not None, a comment that will be generated before the define. The comment will only be generated if the define does not exist in gui.rpy. There is no need to use \\## \\, as the comment will be added and wrapped automatically.\n\nFor example, the following changes the size of dialogue text\n```\ntranslate_define(\\martian\\, \\gui.text_size\\, 12)\n\n```"], "voice_can_replay": ["internal", "function", "()", "", "", "Returns true if it's possible to replay the current voice."], "Set": ["internal", "class", "(*args)", "", "", ""], "gui.dialogue_width": ["gui", "gui.dialogue_width", " = 1116", "", "var", "This variable gives the maximum width of a line of dialogue, in pixels. When dialogue reaches this width, it will be wrapped by Ren'Py."], "ui.interact": ["obsolete", "function", "(roll_forward=None, mouse='default')", "", "", "Causes an interaction with the user, and returns the result of that interaction. This causes Ren'Py to redraw the screen and begin processing input events. When a displayable returns a value in response to an event, that value is returned from ui.interact, and the interaction ends.\n\nThis function is rarely called directly. It is usually called by other parts of Ren'Py, including the say statement, menu statement, with statement, pause statement, call screen, :func:`renpy.input`, among many other functions. However, it can be called directly if necessary.\n\nWhen an interaction ends, the transient layer and all screens shown with transient=True are cleared from the scene lists.\n\nThe following arguments are documented. As other, undocumented arguments exist for Ren'Py's internal use, please pass all arguments as keyword arguments.\n\n`roll_forward` The information that will be returned by this function when a roll forward occurs. (If None, the roll forward is ignored.) This should usually be passed the result of the :func:`renpy.roll_forward_info` function.\n\n`mouse` The style of mouse cursor to use during this function."], "zoomout": ["transitions", "zoomout", "", "", "var", "This zooms out leaving images, taking 0.5 seconds to do so."], "voice_sustain": ["internal", "function", "(ignored=u'', **kwargs)", "", "", "The equivalent of the voice sustain statement."], "RestartStatement": ["internal", "class", "(*args, **kwargs)", "", "Action", "This action causes Ren'Py to rollback to before the current statement, and then re-run the current statement. This may be used when changing a persistent variable that affects how the statement is displayed.\n\nIf run in a menu context, this waits until the player exits to a top-level context before performing the rollback."], "iap.init_android": ["internal", "function", "()", "", "", "Initialize IAP on Android."], "ImageReference": ["internal", "class", "(name, **properties)", "", "", "ImageReference objects are used to reference images by their name, which is a tuple of strings corresponding to the name used to define the image in an image statment."], "ContrastMatrix": ["internal", "class", "(value=1.0)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to change the brightness of an image, while leaving the Alpha channel alone.\n\n`value` The contrast value. Values between 0.0 and 1.0 decrease the contrast, while values above 1.0 increase the contrast."], "RevolveInOut": ["internal", "function", "()", "", "", ""], "iap.get_store_name": ["internal", "function", "()", "", "", "Returns the name of the enabled store for in-app purchase. This currently returns one of \\amazon\\, \\play\\ (for Google Play), \\ios\\ or None if no store is available."], "build.pattern_list": ["internal", "function", "(l)", "", "", "Apply file_lists to the second argument of each tuple in a list."], "ui.fixed": ["obsolete", "ui.fixed", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "preferences.get_volume": ["preferences", "preferences.get_volume", "(mixer):", "", "function", "Gets the volume for `mixer`. If the mixer is muted, this returns 0.0."], "Movie": ["internal", "class", "(fps=24, size=None, channel=u'movie', play=None, mask=None, mask_channel=None, image=None, play_callback=None, side_mask=False, loop=True, start_image=None, **properties)", "", "", "This is a displayable that shows the current movie.\n\n`fps` The framerate that the movie should be shown at. (This is currently ignored, but the parameter is kept for backwards compatibility. The framerate is auto-detected.)\n\n`size` This should be specified as either a tuple giving the width and height of the movie, or None to automatically adjust to the size of the playing movie. (If None, the displayable will be (0, 0) when the movie is not playing.)\n\n`channel` The audio channel associated with this movie. When a movie file is played on that channel, it will be displayed in this Movie displayable. If this is left at the default of \\movie\\, and `play` is provided, a channel name is automatically selected, using :var:`config.single_movie_channel` and :var:`config.auto_movie_channel`.\n\n`play` If given, this should be the path to a movie file. The movie file will be automatically played on `channel` when the Movie is shown, and automatically stopped when the movie is hidden.\n\n`side_mask` If true, this tells Ren'Py to use the side-by-side mask mode for the Movie. In this case, the movie is divided in half. The left half is used for color information, while the right half is used for alpha information. The width of the displayable is half the width of the movie file.\n\nWhere possible, `side_mask` should be used over `mask` as it has no chance of frames going out of sync.\n\n`mask` If given, this should be the path to a movie file that is used as the alpha channel of this displayable. The movie file will be automatically played on `movie_channel` when the Movie is shown, and automatically stopped when the movie is hidden.\n\n`mask_channel` The channel the alpha mask video is played on. If not given, defaults to `channel`\\ _mask. (For example, if `channel` is \\sprite\\, `mask_channel` defaults to \\sprite_mask\\.)\n\n`start_image` An image that is displayed when playback has started, but the first frame has not yet been decoded.\n\n`image` An image that is displayed when `play` has been given, but the file it refers to does not exist. (For example, this can be used to create a slimmed-down mobile version that does not use movie sprites.) Users can also choose to fall back to this image as a preference if video is too taxing for their system. The image will also be used if the video plays, and then the movie ends.\n\n`play_callback` If not None, a function that's used to start the movies playing. (This may do things like queue a transition between sprites, if desired.) It's called with the following arguments:\n\n`old` The old Movie object, or None if the movie is not playing. `new` The new Movie object.\n\nA movie object has the `play` parameter available as ``_play``, while the ``channel``, ``loop``, ``mask``, and ``mask_channel`` fields correspond to the given parameters.\n\nGenerally, this will want to use :func:`renpy.music.play` to start the movie playing on the given channel, with synchro_start=True. A minimal implementation is\n```\ndef play_callback(old, new):\n\nrenpy.music.play(new._play, channel=new.channel, loop=new.loop, synchro_start=True)\n\nif new.mask:\nrenpy.music.play(new.mask, channel=new.mask_channel, loop=new.loop, synchro_start=True)\n\n`loop`\nIf False, the movie will not loop. If `image` is defined, the image\nwill be displayed when the movie ends. Otherwise, the displayable will\nbecome transparent.\n```"], "gui.button_borders": ["gui", "gui.button_borders", " = Borders(10, 10, 10, 10)", "", "var", "The borders surrounding a button, in left, top, right, bottom order."], "Particles": ["internal", "class", "(factory, **properties)", "", "", "Supports particle motion, using the old API."], "DynamicDisplayable": ["internal", "class", "(function, *args, **kwargs)", "", "", "A displayable that can change its child based on a Python function, over the course of an interaction.\n\n`function` A function that is called with the arguments:\n\n* The amount of time the displayable has been shown for. * The amount of time any displayable with the same tag has been shown for. * Any positional or keyword arguments supplied to DynamicDisplayable.\n\nand should return a (d, redraw) tuple, where:\n\n* `d` is a displayable to show. * `redraw` is the amount of time to wait before calling the function again, or None to not call the function again before the start of the next interaction.\n\n`function` is called at the start of every interaction.\n\nAs a special case, `function` may also be a python string that evaluates to a displayable. In that case, function is run once per interaction.\n```\n# Shows a countdown from 5 to 0, updating it every tenth of\n# a second until the time expires.\ninit python:\n\ndef show_countdown(st, at):\nif st > 5.0:\nreturn Text(\\0.0\\), None\nelse:\nd = Text(\\{:.1f}\\.format(5.0 - st))\nreturn d, 0.1\n\nimage countdown = DynamicDisplayable(show_countdown)\n```"], "Window": ["internal", "class", "(child=None, style=u'window', **properties)", "", "", "A window that has padding and margins, and can place a background behind its child. `child` is the child added to this displayable. All other properties are as for the :ref:`Window` screen language statement."], "Animation": ["internal", "function", "(*args, **kwargs)", "", "", ""], "Call": ["internal", "class", "(label, *args, **kwargs)", "", "Action", "Ends the current statement, and calls `label`. Arguments and keyword arguments are passed to :func:`renpy.call`."], "VariableValue": ["internal", "function", "(variable, range, max_is_zero=False, style=u'bar', offset=0, step=None, action=None, force_step=False)", "", "Action", "A bar value that allows the user to adjust the value of a variable in the default store.\n\n `variable` A string giving the name of the variable to adjust. `range` The range to adjust over. `max_is_zero` If True, then when the field is zero, the value of the bar will be range, and all other values will be shifted down by 1. This works both ways - when the bar is set to the maximum, the field is set to 0.\n\n This is used internally, for some preferences. `style` The styles of the bar created. `offset` An offset to add to the value. `step` The amount to change the bar by. If None, defaults to 1/10th of the bar. `action` If not None, an action to call when the field has changed."], "Pan": ["internal", "function", "()", "", "", ""], "gui.kind_text_size": ["gui", "gui.kind_text_size", "", "", "var", "If present, the size of the text."], "moveoutleft": ["transitions", "moveoutleft", "", "", "var", "Also: **moveoutright, moveouttop, moveoutbottom**\n\nThese move leaving images off the screen via the appropriate side, taking 0.5 seconds to do so."], "Rollback": ["internal", "class", "(*args, **kwargs)", "", "Action", "Allows the state of the game to be rolled back to the point just before a node began executing.\n\n@ivar context: A shallow copy of the context we were in before we started executing the node. (Shallow copy also includes a copy of the associated SceneList.)\n\n@ivar objects: A list of tuples, each containing an object and a token of information that, when passed to the rollback method on that object, causes that object to rollback.\n\n@ivar store: A list of updates to store that will cause the state of the store to be rolled back to the start of node execution. This is a list of tuples, either (key, value) tuples representing a value that needs to be assigned to a key, or (key,) tuples that mean the key should be deleted.\n\n@ivar checkpoint: True if this is a user-visible checkpoint, false otherwise.\n\n@ivar purged: True if purge_unreachable has already been called on this Rollback, False otherwise.\n\n@ivar random: A list of random numbers that were generated during the execution of this element."], "Sprite": ["internal", "class", "()", "", "", "This represents a sprite that is managed by the SpriteManager. It contains fields that control the placement of the sprite on the screen. Sprites should not be created directly. Instead, they should be created by calling :meth:`SpriteManager.create`.\n\nThe fields of a sprite object are:\n\n`x`, `y` The x and y coordinates of the upper-left corner of the sprite, relative to the SpriteManager.\n\n`zorder` An integer that's used to control the order of this sprite in the relative to the other sprites in the SpriteManager. The larger the number is, the closer to the viewer the sprite is.\n\n`events` If True, then events are passed to child. If False, the default, the children ignore events (and hence don't spend time processing them).\n\nThe methods of a Sprite object are:"], "updater.hashlib": ["internal", "function", "()", "", "", "hashlib module - A common interface to many hash functions.\n\nnew(name, string='') - returns a new hash object implementing the given hash function; initializing the hash using the given string data.\n\nNamed constructor functions are also available, these are much faster than using new():\n\nmd5(), sha1(), sha224(), sha256(), sha384(), and sha512()\n\nMore algorithms may be available on your platform but the above are guaranteed to exist. See the algorithms_guaranteed and algorithms_available attributes to find out what algorithm names can be passed to new().\n\nNOTE: If you want the adler32 or crc32 hash functions they are available in the zlib module.\n\nChoose your hash function wisely. Some have known collision weaknesses. sha384 and sha512 will be slow on 32 bit platforms.\n\nHash objects have these methods: - update(arg): Update the hash object with the string arg. Repeated calls are equivalent to a single call with the concatenation of all the arguments. - digest(): Return the digest of the strings passed to the update() method so far. This may contain non-ASCII characters, including NUL bytes. - hexdigest(): Like digest() except the digest is returned as a string of double length, containing only hexadecimal digits. - copy(): Return a copy (clone) of the hash object. This can be used to efficiently compute the digests of strings that share a common initial substring.\n\nFor example, to obtain the digest of the string 'Nobody inspects the spammish repetition':\n\n >>> import hashlib >>> m = hashlib.md5() >>> m.update(\\Nobody inspects\\) >>> m.update(\\ the spammish repetition\\) >>> m.digest() '\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9'\n\nMore condensed:\n\n >>> hashlib.sha224(\\Nobody inspects the spammish repetition\\).hexdigest() 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'"], "iap.register": ["internal", "function", "(product, identifier=None, amazon=None, google=None, ios=None, consumable=False)", "", "", "Registers a product with the in-app purchase system.\n\n`product` A string giving the high-level name of the product. This is the string that will be passed to :func:`iap.purchase`, :func:`iap.Purchase`, and :func:`iap.has_purchased` to represent this product.\n\n`identifier` A string that's used to identify the product internally. Once used to represent a product, this must never change. These strings are generally of the form \\com.domain.game.product\\.\n\nIf None, defaults to `product`.\n\n`amazon` A string that identifies the product in the Amazon app store. If not given, defaults to `identifier`.\n\n`google` A string that identifies the product in the Google Play store. If not given, defaults to `identifier`.\n\n`ios` A string that identifies the product in the Apple App store for iOS. If not given, defaults to `identifier`.\n\n`consumable` True if this is a consumable purchase. Right now, consumable purchases are only supported on iOS."], "im.io": ["obsolete", "function", "()", "", "", "The io module provides the Python interfaces to stream handling. The builtin open function is defined in this module.\n\nAt the top of the I/O hierarchy is the abstract base class IOBase. It defines the basic interface to a stream. Note, however, that there is no separation between reading and writing to streams; implementations are allowed to raise an IOError if they do not support a given operation.\n\nExtending IOBase is RawIOBase which deals simply with the reading and writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide an interface to OS files.\n\nBufferedIOBase deals with buffering on a raw byte stream (RawIOBase). Its subclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer streams that are readable, writable, and both respectively. BufferedRandom provides a buffered interface to random access streams. BytesIO is a simple stream of in-memory bytes.\n\nAnother IOBase subclass, TextIOBase, deals with the encoding and decoding of streams into text. TextIOWrapper, which extends it, is a buffered text interface to a buffered raw stream (`BufferedIOBase`). Finally, StringIO is an in-memory stream for text.\n\nArgument names are not part of the specification, and only the arguments of open() are intended to be used as keyword arguments.\n\ndata:\n\nDEFAULT_BUFFER_SIZE\n\n An int containing the default buffer size used by the module's buffered I/O classes. open() uses the file's blksize (as obtained by os.stat) if possible."], "HistoryEntry.who": ["history", "who", "", "HistoryEntry", "attribute", "A string giving the name of the character that is speaking, or None if no such string exists."], "RotoZoom": ["internal", "function", "()", "", "", ""], "Editor.open": ["editor", "open", "( filename, line=None, **kwargs)", "Editor", "method", "Opens a `filename` in the editor.\n\nIf `line` is not None, attempts to position the editing cursor at `line`."], "build": ["internal", "function", "()", "", "", ""], "BarValue.replaces": ["screen_python", "replaces", "(other)", "BarValue", "method", "This is called when a BarValue replaces another BarValue, such as when a screen is updated. It can be used to update this BarValue from the other. It is called before get_adjustment.\n\nNote that `other` is not necessarily the same type as `self`."], "gui.label_text_size": ["gui", "gui.label_text_size", " = 45", "", "var", "The size of section labels in the game's user interface."], "Alpha": ["internal", "function", "()", "", "", ""], "With": ["internal", "class", "(transition)", "", "Action", "Causes `transition` to occur."], "Gallery": ["internal", "class", "(self)", "", "", "Toggles slideshow mode."], "HistoryEntry.kind": ["history", "kind", "", "HistoryEntry", "attribute", "The kind of character that created this history. Ren'Py sets this to either \\adv\\ or \\nvl\\."], "Button": ["internal", "class", "(child=None, style=u'button', clicked=None, hovered=None, unhovered=None, action=None, role=None, time_policy=None, keymap={}, alternate=None, selected=None, sensitive=None, keysym=None, alternate_keysym=None, **properties)", "", "", ""], "object": ["internal", "class", "(*args, **kwargs)", "", "", ""], "nvl_erase": ["internal", "function", "()", "", "", ""], "FileCurrentScreenshot": ["internal", "class", "(empty=None, **properties)", "", "", "A displayable that shows the screenshot that will be saved with the current file, if a screenshot has been taken when entering a menu or with :func:`FileTakeScreenshot`.\n\nIf there is no current screenshot, `empty` is shown in its place. (If `empty` is None, it defaults to :func:`Null`.)"], "director.spacing": ["director", "director.spacing", " = 1", "", "var", "The spacing between a director (scene, show, hide, with, play, queue, and voice) line and a non-director line, or vice versa. These spacings should be 0 or 1 lines, a higher spacing may not work."], "build.directory_name": ["build", "build.directory_name", " = \"...\"", "", "var", "This is used to create the names of directories in the archive files. For example, if this is set to \\mygame-1.0\\, the Linux version of the project will unpack to \\mygame-1.0-linux\\.\n\nThis is also used to determine the name of the directory in which the package files are placed. For example, if you set build.directory_name to mygame-1.0, the archive files will be placed in mygame-1.0-dists in the directory above the base directory.\n\nThis variable should not contain special characters like spaces, colons, and semicolons. If not set, it defaults to :var:`build.name` a dash, and :var:`config.version`."], "GetCharacterVolume": ["internal", "function", "(voice_tag)", "", "Action", "This returns the volume associated with voice tag, a number between 0.0 and 1.0, which is interpreted as a fraction of the mixer volume for the `voice` channel."], "achievement": ["internal", "function", "()", "", "", "The Achievement module allows the developer to grant achievements to the player, to clear achievements, and to determine if an achievement has been granted. It also allows the recording of progress towards an achievement.\n\nBy default, the achievement stores information in the persistent file. If Steam support is available and enabled, achievement information is automatically synchronized with Steam."], "icon": ["internal", "function", "()", "", "", ""], "ui.close": ["obsolete", "function", "()", "", "", "Closes a displayable created with by a UI function. When a displayable is closed, we add new displayables to its parent, or to the layer if no displayable is open."], "audio": ["internal", "function", "()", "", "", ""], "gui.REGULAR_BOLD": ["translating_renpy", "gui.REGULAR_BOLD", " = False", "", "var", "If True, heavy-weight text is bolded."], "doc": ["internal", "function", "()", "", "", ""], "shaderdoc": ["internal", "function", "()", "", "", ""], "easeinright": ["transitions", "easeinright", "", "", "var", "Also: **ease, easeinleft, easeintop, easeinbottom, easeoutright, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "InputValue.Toggle": ["screen_python", "Toggle", "()", "InputValue", "method", "Returns an action that toggles text editing on the input."], "Crop": ["internal", "function", "(rect, child, **properties)", "", "", "This creates a displayable by cropping `child` to `rect`, where `rect` is an (x, y, width, height) tuple.\n```\nimage eileen cropped = Crop((0, 0, 300, 300), \\eileen happy\\)\n```"], "Sample": ["language_basics", "Sample", "(name, delay, position=(0, 0), **properties)", "", "function", "A sample function that doesn't actually exist in Ren'Py, but is used only in documentation."], "Lexer.rest": ["cds", "rest", "()", "Lexer", "method", "Skips whitespace, then returns the rest of the line."], "YScrollValue": ["internal", "class", "(viewport)", "", "Action", "The value of an adjustment that vertically scrolls the viewport with the given id, on the current screen. The viewport must be defined before a bar with this value is."], "ADVCharacter": ["internal", "class", "(name=NotSet, kind=None, **properties)", "", "", "The character object contains information about a character. When passed as the first argument to a say statement, it can control the name that is displayed to the user, and the style of the label showing the name, the text of the dialogue, and the window containing both the label and the dialogue."], "iap.Product": ["internal", "class", "(product, identifier, google, amazon, ios, consumable)", "", "", "A data object representing a product."], "left": ["internal", "function", "()", "", "", ""], "ui.timer": ["obsolete", "ui.timer", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "gui.accent_color": ["gui", "gui.accent_color", " = '#000060'", "", "var", "The accent color is used in many places in the GUI, including titles and labels."], "layeredimage.OrderedDict": ["internal", "class", "(*args, **kwds)", "", "", "Dictionary that remembers insertion order\n\nInitialize an ordered dictionary. The signature is the same as regular dictionaries, but keyword arguments are not recommended because their insertion order is arbitrary."], "Character": ["internal", "function", "(name=..., kind=adv, **args)", "", "", "Creates and returns a Character object, which controls the look and feel of dialogue and narration.\n\n`name` If a string, the name of the character for dialogue. When `name` is None, display of the name is omitted, as for narration. If no name is given, the name is taken from `kind`, and otherwise defaults to None.\n\n`kind` The Character to base this Character off of. When used, the default value of any argument not supplied to this Character is the value of that argument supplied to ``kind``. This can be used to define a template character, and then copy that character with changes.\n\n**Linked Image.** An image tag may be associated with a Character. This allows a say statement involving this character to display an image with the tag, and also allows Ren'Py to automatically select a side image to show when this character speaks.\n\n`image` A string giving the image tag that is linked with this character.\n\n**Voice Tag.** If a voice tag is assign to a Character, the voice files that are associated with it, can be muted or played in the preference screen.\n\n`voice_tag` A String that enables the voice file associated with the Character to be muted or played in the 'voice' channel.\n\n**Prefixes and Suffixes.** These allow a prefix and suffix to be applied to the name of the character, and to the text being shown. This can be used, for example, to add quotes before and after each line of dialogue.\n\n`what_prefix` A string that is prepended to the dialogue being spoken before it is shown.\n\n`what_suffix` A string that is appended to the dialogue being spoken before it is shown.\n\n`who_prefix` A string that is prepended to the name of the character before it is shown.\n\n`who_suffix` A string that is appended to the name of the character before it is shown.\n\n**Changing Name Display.** These options help to control the display of the name.\n\n`dynamic` If true, then `name` should either be a string containing a Python expression, a function, or a callable object. If it's a string, That string will be evaluated before each line of dialogue, and the result used as the name of the character. Otherwise, the function or callable object will be called with no arguments before each line of dialogue, and the return value of the call will be used as the name of the character.\n\n**Controlling Interactions.** These options control if the dialogue is displayed, if an interaction occurs, and the mode that is entered upon display.\n\n`condition` If given, this should be a string containing a Python expression. If the expression is false, the dialogue does not occur, as if the say statement did not happen.\n\n`interact` If true, the default, an interaction occurs whenever the dialogue is shown. If false, an interaction will not occur, and additional elements can be added to the screen.\n\n`advance` If true, the default, the player can click to advance through the statement, and other means of advancing (such as skip and auto-forward mode) will also work. If false, the player will be unable to move past the say statement unless an alternate means (such as a jump hyperlink or screen) is provided.\n\n`mode` A string giving the mode to enter when this character speaks. See the section on :ref:`modes ` for more details.\n\n`callback` A function that is called when events occur while the character is speaking. See the section on :ref:`character-callbacks` fore more information.\n\n**Click-to-continue.** A click-to-continue indicator is displayed once all the text has finished displaying, to prompt the user to advance.\n\n`ctc` A displayable to use as the click-to-continue indicator, unless a more specific indicator is used.\n\n`ctc_pause` A displayable to use a the click-to-continue indicator when the display of text is paused by the {p} or {w} text tags.\n\n`ctc_timedpause` A displayable to use a the click-to-continue indicator when the display of text is paused by the {p=} or {w=} text tags. When None, this takes its default from `ctc_pause`, use ``Null()`` when you want a `ctc_pause` but no `ctc_timedpause`.\n\n`ctc_position` Controls the location of the click-to-continue indicator. If ``\\nestled\\``, the indicator is displayed as part of the text being shown, immediately after the last character. ``\\nestled-close\\`` is similar, except a break is not allowed between the text and the CTC indicator. If ``\\fixed\\``, the indicator is added to the screen, and its position is controlled by the position style properties.\n\n **Screens.** The display of dialogue uses a :ref:`screen `. These arguments allow you to select that screen, and to provide arguments to it.\n\n`screen` The name of the screen that is used to display the dialogue.\n\nKeyword arguments beginning with ``show_`` have the prefix stripped off, and are passed to the screen as arguments. For example, the value of ``show_myflag`` will become the value of the ``myflag`` variable in the screen. (The ``myflag`` variable isn't used by default, but can be used by a custom say screen.)\n\nOne show variable is, for historical reasons, handled by Ren'Py itself:\n\n`show_layer` If given, this should be a string giving the name of the layer to show the say screen on.\n\n**Styling Text and Windows.** Keyword arguments beginning with ``who_``, ``what_``, and ``window_`` have their prefix stripped, and are used to :ref:`style ` the character name, the spoken text, and the window containing both, respectively.\n\nFor example, if a character is given the keyword argument ``who_color=\\#c8ffc8\\``, the color of the character's name is changed, in this case to green. ``window_background=\\frame.png\\`` sets the background of the window containing this character's dialogue.\n\nThe style applied to the character name, spoken text, and window can also be set this way, using the ``who_style``, ``what_style``, and ``window_style`` arguments, respectively.\n\nSetting :var:`config.character_id_prefixes` makes it possible to style other displayables as well. For example, when the default GUI is used, styles prefixed with ``namebox_`` are used to style the name of the speaking character."], "updater.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "build.include_old_themes": ["build", "build.include_old_themes", " = True", "", "var", "When true, files required to support themes that existed before Ren'Py 6.99.9 will be included in the build. When false, such files are excluded.\n\nThis is set to False when :func:`gui.init` is called."], "FilePage": ["internal", "class", "(page)", "", "Action", "Sets the file page to `page`, which should be one of \\auto\\, \\quick\\, or an integer."], "iap.missing_products": ["internal", "function", "()", "", "", "Determines if any products are missing from persistent._iap_purchases"], "ui.add": ["obsolete", "ui.add", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "ui.screen_id": ["obsolete", "function", "(id_, d)", "", "", "Assigns the displayable `d` the screen widget id `id_`, as if it had been created by a screen statement with that id."], "mouse_visible": ["store_variables", "mouse_visible", " = True", "store", "var", "Controls if the mouse is visible. This is automatically set to true when entering the standard game menus."], "gui.interface_text_size": ["gui", "gui.interface_text_size", " = 36", "", "var", "The size of static text in the game's user interface, and the default size of button text in the game's interface."], "preferences.afm_time": ["preferences", "preferences.afm_time", " = 15", "", "var", "The amount of time to wait for auto-forward mode. Bigger numbers are slower, though the conversion to wall time is complicated, as the speed takes into account line length. The equivalent of the \\auto-forward\\ preference."], "theme": ["internal", "function", "()", "", "", ""], "black": ["internal", "image", " = Solid(\"#000\")", "", "image", ""], "gui.SetPreference": ["internal", "class", "(name, value, rebuild=True)", "", "Action", "This Action sets the gui preference with `name` to `value`.\n\n`rebuild` If true, the default, :func:`gui.rebuild` is called to make the changes take effect. This should generally be true, except in the case of multiple gui.SetPreference actions, in which case it should be False in all but the last one.\n\nThis is a very slow action, and probably not suitable for use when a button is hovered."], "blinds": ["transitions", "blinds", "", "", "var", "Transitions the screen in a vertical blinds effect lasting 1 second. An instance of the :func:`ImageDissolve` transition class."], "director.audio_channel_patterns": ["director", "director.audio_channel_patterns", " = { }", "", "var", "A map from a channel name to the list of audio patterns that are available in that audio channel. For example, if this is set to ``{ 'sound' : [ 'sound/*.opus' ], 'music' : [ 'music/*.opus' ] }`` the music and sound channels get their own lists of patterns."], "ToggleDict": ["internal", "class", "(dict, key, true_value=None, false_value=None)", "", "Action", "Toggles the value of `key` in `dict`. Toggling means to invert the value when the action is performed.\n\n `true_value` If not None, then this is the true value used. `false_value` If not None, then this is the false value used."], "im.Alpha": ["obsolete", "function", "(image, alpha, **properties)", "", "", "Returns an alpha-mapped version of the image. Alpha is the maximum alpha that this image can have, a number between 0.0 (fully transparent) and 1.0 (opaque).\n\nIf an image already has an alpha channel, values in that alpha channel are reduced as appropriate."], "reset": ["internal", "function", "()", "", "", "Resets the RNG, removing all of the pushbacked numbers."], "iap.has_purchased": ["internal", "function", "(product)", "", "", "Returns True if the user has purchased `product` in the past, and False otherwise."], "gui.insensitive_color": ["gui", "gui.insensitive_color", " = '#8888887f'", "", "var", "The color used by the text of buttons that are insensitive to user input. (For example, the rollback button when no rollback is possible.)"], "AudioPositionValue": ["internal", "class", "(channel=u'music', update_interval=0.1)", "", "Action", "A value that shows the playback position of the audio file playing in `channel`.\n\n`update_interval` How often the value updates, in seconds."], "Lexer.checkpoint": ["cds", "checkpoint", "()", "Lexer", "method", "Returns an opaque object representing the current state of the lexer."], "HistoryEntry.rollback_identifier": ["history", "rollback_identifier", "", "HistoryEntry", "attribute", "This is an identifier that can be passed to the :func:`RollbackToIdentifier` action, to cause a rollback to the line of script that generated this history entry. The rollback only occurs if the location is still in the script log, otherwise the action is insensitive."], "iap.AndroidBackend": ["internal", "class", "(store, store_name)", "", "", "Waits for a result.\n\n`interact` If true, waits interactively. If false, waits using renpy.pause."], "im.Data": ["obsolete", "class", "(data, filename, **properties)", "", "", "This image manipulator loads an image from binary data.\n\n`data` A string of bytes, giving the compressed image data in a standard file format.\n\n`filename` A \\filename\\ associated with the image. This is used to provide a hint to Ren'Py about the format of `data`. (It's not actually loaded from disk.)"], "build.remove": ["internal", "function", "(l, pattern)", "", "", "Removes the pattern from the list."], "default_transition": ["internal", "function", "()", "", "", ""], "ToggleVariable": ["internal", "function", "(variable, true_value=None, false_value=None)", "", "Action", "Toggles `variable`.\n\nThe `variable` argument must be a string, and can be a simple name like \\strength\\, or one with dots separating the variable from fields, like \\hero.strength\\ or \\persistent.show_cutscenes\\.\n\n `true_value` If not None, then this is the true value used. `false_value` If not None, then this is the false value used."], "im.Image": ["obsolete", "class", "(filename, **properties)", "", "", "This image manipulator loads an image from a file."], "Matrix.perspective": ["matrix", "Matrix.perspective", "(w, h, n, p, f)", "", "function", "Returns a matrix suitable for the perspective projection of an image in the Ren'Py coordinate system. This is a view into the a coordinate system where, where when z=0, (0, 0) corresponds to the top-left corner of the screen, and (w, h) corresponds to the bottom-right corner of the screen.\n\n`w`, `h` The width and height of the input plane, in pixels.\n\n`n` The distance of the near plane from the camera.\n\n`p` How far the z=0 plane is from the camera. This is also where one virtual pixel is one coordinate unit in x and y.\n\n`f` The distance of the far plane from the camera."], "voice_replay": ["internal", "function", "()", "", "", "Replays the current voice, if possible."], "Move": ["internal", "function", "()", "", "", ""], "irisout": ["transitions", "irisout", "", "", "var", "Also: **irisin**\n\nUse a rectangular iris to display the new screen, or hide the old screen. Instances of the :func:`CropMove` transition class."], "Editor": ["editor", "Editor", "", "", "class", ""], "updater.tarfile": ["internal", "function", "()", "", "", "Read from and write to tar format archives."], "build.tobytes": ["internal", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "BrightnessMatrix": ["internal", "class", "(value=1.0)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to change the brightness of an image, while leaving the Alpha channel alone.\n\n`value` The amount of change in image brightness. This should be a number between -1 and 1, with -1 the darkest possible image and 1 the brightest."], "gui.preference": ["internal", "function", "(name, default=not_set)", "", "", "This function returns the value of the gui preference with `name`.\n\n`default` If given, this value becomes the default value of the gui preference. The default value should be given the first time the preference is used."], "irisin": ["transitions", "irisin", "", "", "var", "Also: **irisout**\n\nUse a rectangular iris to display the new screen, or hide the old screen. Instances of the :func:`CropMove` transition class."], "Lexer.expect_eol": ["cds", "expect_eol", "()", "Lexer", "method", "If we are not at the end of the line, raise an error."], "build.name": ["build", "build.name", " = \"...\"", "", "var", "This is used to automatically generate build.directory_name and build.executable_name, if neither is set. This should not contain spaces, colons, or semicolons."], "set": ["internal", "class", "(*args)", "", "", ""], "director.other_spacing": ["director", "director.other_spacing", " = 0", "", "var", "The spacing between two consecutive non-director lines."], "ui.hbox": ["obsolete", "ui.hbox", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "slideawayleft": ["transitions", "slideawayleft", "", "", "var", "Also: **slideawayright, slideawayup, slideawaydown**\n\nSlides the old scene in the given direction. Instances of the :func:`CropMove` transition class."], "MixerValue": ["internal", "class", "(mixer)", "", "Action", "The value of an audio mixer.\n\n `mixer` The name of the mixer to adjust. This is usually one of \\music\\, \\sfx\\, or \\voice\\, but creators can create new mixers."], "Action.get_sensitive": ["screen_python", "get_sensitive", "(self)", "Action", "method", "This is called to determine if the button with this action should be sensitive. It should return true if the button is sensitive.\n\nNote that __call__ can be called, even if this returns False.\n\nThe default implementation returns True."], "AlphaDissolve": ["transitions", "function", "(control, delay=0.0, alpha=False, reverse=False)", "", "", "Returns a transition that uses a control displayable (almost always some sort of animated transform) to transition from one screen to another. The transform is evaluated. The new screen is used where the transform is opaque, and the old image is used when it is transparent.\n\n`control` The control transform.\n\n`delay` The time the transition takes, before ending.\n\n`alpha` Ignored.\n\n`reverse` If true, the alpha channel is reversed. Opaque areas are taken from the old image, while transparent areas are taken from the new image."], "easeouttop": ["transitions", "easeouttop", "", "", "var", "Also: **easeinright, easeinleft, easeintop, easeinbottom, easeoutright, easeoutleft, ease, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "InputValue.default": ["screen_python", "default", "", "InputValue", "attribute", "If true, the input is eligible to be editable by default. (That is, it may be given the caret when the screen is shown.)"], "slideawaydown": ["transitions", "slideawaydown", "", "", "var", "Also: **slideawayright, slideawayup, slideawayleft**\n\nSlides the old scene in the given direction. Instances of the :func:`CropMove` transition class."], "nvl_hide": ["internal", "function", "(with_)", "", "", "The Python equivalent of the ``nvl hide`` statement.\n\n`with_` The transition to use to hide the NVL-mode window."], "nvl_clear": ["internal", "function", "()", "", "", "The Python equivalent of the ``nvl clear`` statement."], "achievement.clear_all": ["internal", "function", "()", "", "", "Clears all achievements."], "Input": ["internal", "class", "(default=u'', length=None, style=u'input', allow=None, exclude=None, prefix=u'', suffix=u'', changed=None, button=None, replaces=None, editable=True, pixel_width=None, value=None, copypaste=False, caret_blink=None, **properties)", "", "", "This is a Displayable that takes text as input."], "slideright": ["transitions", "slideright", "", "", "var", "Also: **slideleft, slideup, slidedown**\n\nSlides the new scene in the given direction. Instances of the :func:`CropMove` transition class."], "FileCurrentPage": ["internal", "function", "()", "", "Action", "Returns the current file page as a string."], "EndReplay": ["internal", "class", "(confirm=True)", "", "Action", "Ends the current replay.\n\n`confirm` If true, prompts the user for confirmation before ending the replay."], "extend": ["internal", "function", "(what, interact=True, *args, **kwargs)", "", "", ""], "Matrix.scale": ["matrix", "Matrix.scale", "(x, y, z)", "", "function", "Returns a matrix that scales the displayable.\n\n`x`, `y`, `z` The factor to scale each axis by."], "Style.take": ["style", "take", "(other)", "Style", "method", "This takes all style properties from `other`. `other` must be a style object."], "FileTime": ["internal", "function", "(name, format=u'%b %d, %H:%M', empty=u'', page=None, slot=False)", "", "Action", "Returns the time the file was saved, formatted according to the supplied `format`. If the file is not found, `empty` is returned.\n\n The return value is a string."], "Queue": ["internal", "class", "(channel, file, **kwargs)", "", "Action", "Causes an audio file to be queued on a given channel.\n\n `channel` The channel to play the sound on. `file` The file to play.\n\n Any keyword arguments are passed to :func:`renpy.music.queue`"], "RemoveFromSet": ["internal", "class", "(set, value)", "", "Action", "Removes `value` from `set`.\n\n`set` The set to remove from. This may be a set or list. `value` The value to remove."], "gui.navigation_button_width": ["gui", "gui.navigation_button_width", " = 290", "", "var", "Increases the width of navigation buttons."], "InputValue.get_text": ["screen_python", "get_text", "(self)", "InputValue", "method", "Returns the default text of the input. This must be implemented."], "blend_parameter": ["live2d", "blend_parameter", "(name, blend, value, weight=1.0)", "", "method", "This method blends the current value of the parameter with passed. This have no effect outside of `update_function`.\n\n`name` Name of parameter to change defined for this model.\n\n`blend` One of \\Add\\, \\Multiply\\ or \\Overwrite\\. The blend kind that will be used.\n\n`value` The value to be used.\n\n`weight` Float from 0.0 to 1.0, the weight by which the new value will change the current value."], "HBox": ["internal", "function", "(*args, **properties)", "", "", "A box that lays out its members from left to right."], "Lexer.image_name_component": ["cds", "image_name_component", "()", "Lexer", "method", "Matches an image name component. Unlike a word, a image name component can begin with a number."], "ui.vbar": ["obsolete", "ui.vbar", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "store": ["internal", "function", "()", "", "", ""], "preferences.set_mute": ["preferences", "preferences.set_mute", "(mixer, mute):", "", "function", "Sets the mute setting for `mixer`. If `mute` is true, the mixer is muted."], "moveoutbottom": ["transitions", "moveoutbottom", "", "", "var", "Also: **moveoutleft, moveouttop, moveoutright**\n\nThese move leaving images off the screen via the appropriate side, taking 0.5 seconds to do so."], "gui.bar_size": ["gui", "gui.bar_size", " = 64", "", "var", "The height of horizontal bars, and width of vertical bars."], "gui.slot_button_height": ["gui", "gui.slot_button_height", " = 309", "", "var", "The width and height of the save slot button."], "gui.notify_text_size": ["gui", "gui.notify_text_size", " = 24", "", "var", "The size of notification text."], "iap.init": ["internal", "function", "()", "", "", "Called to initialize the IAP system."], "Scroll": ["internal", "class", "(id, direction, amount=u'step')", "", "Action", "Causes a Bar, Viewport, or Vpgrid to scroll.\n\n`id` The id of a bar, viewport, or vpgrid in the current screen.\n\n`direction` For a vbar, one of \\increase\\ or \\decrease\\. For a viewport or vpgrid, one of \\horizontal increase\\, \\vertical increase\\, \\horizontal decrease\\, or \\vertical decrease\\.\n\n`amount` The amount to scroll by. This can be a number of pixels, or else \\step\\ or \\page\\."], "gui.text_font": ["gui", "gui.text_font", " = \"ArchitectsDaughter.ttf\"", "", "var", "This sets the font that is used for dialogue text, menus, inputs, and other in-game text. The font file should exist in the game directory."], "name_only": ["internal", "function", "()", "", "", ""], "color": ["internal", "class", "()", "", "", ""], "Transform": ["internal", "class", "(child=None, function=None, style=u'default', focus=None, default=False, _args=None, **kwargs)", "", "", "A transform applies operations such as cropping, rotation, scaling, and alpha-blending to its child. A transform object has fields corresponding to the transform properties, which it applies to its child."], "gui.hover_color": ["gui", "gui.hover_color", " = '#3284d6'", "", "var", "The color used by focused items in the GUI, including the text of of buttons and the thumbs (movable areas) of sliders and scrollbars."], "updater.UpdateError": ["internal", "class", "()", "", "", "Used to report known errors."], "FileSave": ["internal", "class", "(name, confirm=True, newest=True, page=None, cycle=False, slot=False)", "", "Action", "Saves the file.\n\n The button with this slot is selected if it's marked as the newest save file.\n\n `name` The name of the slot to save to. If None, an unused slot (a large number based on the current time) will be used.\n\n `confirm` If true, then we will prompt before overwriting a file.\n\n `newest` Ignored.\n\n `page` The name of the page that the slot is on. If None, the current page is used.\n\n `cycle` If true, then saves on the supplied page will be cycled before being shown to the user. :var:`config.quicksave_slots` slots are used in the cycle.\n\n `slot` If True, `name` is taken to be a slot name, and `page` is ignored."], "Borders": ["internal", "class", "(left, top, right, bottom, pad_left=0, pad_top=0, pad_right=0, pad_bottom=0)", "", "", "This object provides border size and tiling information to a :func:`Frame`. It can also provide padding information that can be supplied to the :propref:`padding` style property of a window or frame.\n\n`left`, `top`, `right`, `bottom` These provide the size of the insets used by a frame, and are added to the padding on each side. They should zero or a positive integer.\n\n`pad_left`, `pad_top`, `pad_right`, `pad_bottom` These are added to the padding on each side, and may be positive or negative. (For example, if `left` is 5 and `pad_left` is -3, the final padding is 2.)\n\nThe padding information is supplied via a field:\n\n.. attribute\n```\npadding\n\nThis is a four-element tuple containing the padding on each of the\nfour sides.\n```"], "im.Twocolor": ["obsolete", "class", "(im, white, black, force_alpha=False, **properties)", "", "", "This takes as arguments two colors, white and black. The image is mapped such that pixels in white have the white color, pixels in black have the black color, and shades of gray are linearly interpolated inbetween. The alpha channel is mapped linearly between 0 and the alpha found in the white color, the black color's alpha is ignored."], "ShowMenu": ["internal", "class", "(screen=None, *args, **kwargs)", "", "Action", "Causes us to enter the game menu, if we're not there already. If we are in the game menu, then this shows a screen or jumps to a label.\n\n `screen` is usually the name of a screen, which is shown using the screen mechanism. If the screen doesn't exist, then \\_screen\\ is appended to it, and that label is jumped to.\n\n If the optional keyword argument `_transition` is given, the menu will change screens using the provided transition. If not manually specified, the default transition is `config.intra_transition`.\n\n * ShowMenu(\\load\\) * ShowMenu(\\save\\) * ShowMenu(\\preferences\\)\n\n This can also be used to show user-defined menu screens. For example, if one has a \\stats\\ screen defined, one can show it as part of the game menu using:\n\n * ShowMenu(\\stats\\)\n\n ShowMenu without an argument will enter the game menu at the default screen, taken from _game_menu_screen.\n\n Extra arguments and keyword arguments are passed on to the screen"], "ConditionSwitch": ["internal", "function", "(*args, predict_all=None, **properties)", "", "", "This is a displayable that changes what it is showing based on Python conditions. The positional arguments should be given in groups of two, where each group consists of:\n\n* A string containing a Python condition. * A displayable to use if the condition is true.\n\nThe first true condition has its displayable shown, at least one condition should always be true.\n\nThe conditions uses here should not have externally-visible side-effects.\n\n`predict_all` If True, all of the possible displayables will be predicted when the displayable is shown. If False, only the current condition is predicted. If None, :var:`config.conditionswitch_predict_all` is used.\n```\nimage jill = ConditionSwitch(\n\\jill_beers > 4\\, \\jill_drunk.png\\,\n\\True\\, \\jill_sober.png\\)\n```"], "BarValue.periodic": ["screen_python", "periodic", "(st)", "BarValue", "method", "This method is called once at the start of each interaction. If it returns a number of seconds, it will be called before that many seconds elapse, but it might be called sooner. It is called after get_adjustment.\n\nIt can be used to update the value of the bar over time, like :func:`AnimatedValue` does. To do this, get_adjustment should store the adjustment, and periodic should call the adjustment's changed method."], "Style.set_parent": ["style", "set_parent", "(parent)", "Style", "method", "Sets the parent of this style object to `parent`."], "gui.button_text_xalign": ["gui", "gui.button_text_xalign", " = 0.0", "", "var", "The horizontal alignment of the button text. 0.0 is left-aligned, 0.5 is centered, and 1.0 is right-aligned."], "FileLoad": ["internal", "class", "(name, confirm=True, page=None, newest=True, cycle=False, slot=False)", "", "Action", "Loads the file.\n\n `name` The name of the slot to load from. If None, an unused slot will be used, and hence the file will not be loadable.\n\n `confirm` If true and not at the main menu, prompt for confirmation before loading the file.\n\n `page` The page that the file will be loaded from. If None, the current page is used.\n\n `newest` If true, the button is selected if this is the newest file.\n\n `cycle` Ignored.\n\n `slot` If True, `name` is taken to be a slot name, and `page` is ignored."], "im.MatrixColor": ["obsolete", "class", "(im, matrix, **properties)", "", "", "An image operator that uses `matrix` to linearly transform the image manipulator `im`.\n\n`Matrix` should be a list, tuple, or :func:`im.matrix` that is 20 or 25 elements long. If the object has 25 elements, then elements past the 20th are ignored.\n\nWhen the four components of the source color are R, G, B, and A, which range from 0.0 to 1.0; the four components of the transformed color are R', G', B', and A', with the same range; and the elements of the matrix are named::\n\n[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]\n\nthe transformed colors can be computed with the formula::\n\nR' = (a * R) + (b * G) + (c * B) + (d * A) + e G' = (f * R) + (g * G) + (h * B) + (i * A) + j B' = (k * R) + (l * G) + (m * B) + (n * A) + o A' = (p * R) + (q * G) + (r * B) + (s * A) + t\n\nThe components of the transformed color are clamped to the range [0.0, 1.0]."], "gui.name_text_size": ["gui", "gui.name_text_size", " = 45", "", "var", "Sets the size of character names."], "slideawayup": ["transitions", "slideawayup", "", "", "var", "Also: **slideawayright, slideawayleft, slideawaydown**\n\nSlides the old scene in the given direction. Instances of the :func:`CropMove` transition class."], "gui.nvl_spacing": ["gui", "gui.nvl_spacing", " = 15", "", "var", "The spacing between entries when gui.nvl_height is None, and the spacing between NVL-mode menu buttons."], "gui.init": ["internal", "function", "(width, height, fov=75)", "", "", "Initializes the gui.\n\n`width` The width of the default window.\n\n`height` The height of the default window.\n\n`fov` The field of view of the 3d stage."], "build.clear": ["internal", "function", "()", "", "", "Clears the list of patterns used to classify files."], "define": ["internal", "function", "()", "", "", ""], "gui.hover_muted_color": ["gui", "gui.hover_muted_color", " = '#8080f0'", "", "var", "Muted colors, used for the sections of bars, scrollbars, and sliders that do not represent the value or visible area. (These are only used when generating images, and will not take effect until images are regenerated in the launcher.)"], "makeJson": ["internal", "function", "()", "", "", ""], "gui.unscrollable": ["gui", "gui.unscrollable", " = \"hide\"", "", "var", "This controls what to do if the bar is unscrollable. \\hide\\ hides the bar, while None keeps it shown."], "im.free_memory": ["obsolete", "function", "()", "", "", "Frees some memory."], "im.Tile": ["obsolete", "class", "(im, size=None, **properties)", "", "", "An image manipulator that tiles the image manipulator `im`, until it is `size`.\n\n`size` If not None, a (width, height) tuple. If None, this defaults to (:var:`config.screen_width`, :var:`config.screen_height`).\n\nThe same effect can now be achieved with Tile(im, size=size)"], "Action": ["internal", "class", "()", "", "", "This can be passed to the clicked method of a button or hotspot. It is called when the action is selected. The other methods determine if the action should be displayed insensitive or disabled."], "im.FactorScale": ["obsolete", "class", "(im, width, height=None, bilinear=True, **properties)", "", "", "An image manipulator that scales `im` (a second image manipulator) to `width` times its original `width`, and `height` times its original height. If `height` is omitted, it defaults to `width`.\n\nIf `bilinear` is true, then bilinear interpolation is used for the scaling. Otherwise, nearest neighbor interpolation is used.\n```\nimage logo doubled = im.FactorScale(\\logo.png\\, 1.5)\n\nThe same effect can now be achieved with the :tpref:`zoom` or the\n:tpref:`xzoom` and :tpref:`yzoom` transform properties.\n```"], "preferences.system_cursor": ["preferences", "preferences.system_cursor", " = False", "", "var", "If True, the system cursor is forced to be used, ignoring the :var:`config.mouse` value. If False, it will not. The equivalent of the \\system cursor\\ preference."], "MoveOut": ["internal", "function", "()", "", "", ""], "preferences.emphasize_audio": ["preferences", "preferences.emphasize_audio", " = False", "", "var", "If True, Ren'Py will emphasize the audio channels found in :var:`config.emphasize_audio_channels` by reducing the volume of other channels. (For example, reducing the music volume when voice is playing.) If False, this doesn't happen."], "iap.purchase": ["internal", "function", "(product, interact=True)", "", "", "This function requests the purchase of `product`.\n\nIt returns true if the purchase succeeds, or false if the purchase fails. If the product has been registered as consumable, the purchase is consumed before this call returns."], "im.Sepia": ["obsolete", "function", "(im, **properties)", "", "", "An image manipulator that creates a sepia-toned version of the image manipulator `im`.\n\nThe same effect can now be achieved by supplying SepiaMatrix() to the :tpref:`matrixcolor` transform property."], "StylePreference": ["internal", "class", "(preference, alternative)", "", "Action", "An action that causes `alternative` to become the selected alternative for the given style preference.\n\n`preference` A string giving the name of the style preference.\n\n`alternative` A string giving the name of the alternative."], "ui.null": ["obsolete", "ui.null", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "Action.get_tooltip": ["screen_python", "get_tooltip", "(self)", "Action", "method", "This gets a default tooltip for this button, if a specific tooltip is not assigned. It should return the tooltip value, or None if a tooltip is not known.\n\nThis defaults to returning None."], "DragGroup": ["internal", "class", "(*children, **properties)", "", "", "Represents a group of Drags. A Drag is limited to the boundary of its DragGroup. Dropping only works between Drags that are in the same DragGroup. Drags may only be raised when they are inside a DragGroup.\n\nA DragGroup is laid out like a :func:`Fixed`.\n\nAll positional parameters to the DragGroup constructor should be Drags, that are added to the DragGroup.\n\n `min_overlap` An integer which means the minimum number of pixels at the overlap so that drop will be allow."], "gui.idle_small_color": ["gui", "gui.idle_small_color", " = '#404040'", "", "var", "The color used for small text (like the date and name of a save slot, and quick menu buttons) when not hovered. This color often needs to be a bit lighter or darker than idle_color to compensate for the smaller size of the font."], "gui.textbox_height": ["gui", "gui.textbox_height", " = 278", "", "var", "The height of the textbox. This should be the same height as the height of gui/textbox.png."], "save_name": ["internal", "function", "()", "", "", "A save name that is included with saves."], "im.Crop": ["obsolete", "class", "(im, rect)", "", "", "An image manipulator that crops `rect`, a (x, y, width, height) tuple, out of `im`, an image manipulator.\n```\nimage logo crop = im.Crop(\\logo.png\\, (0, 0, 100, 307))\n\nThe same effect can now be achieved by setting the :tpref:`crop` transform property.\n```"], "gui.FONT_SCALE": ["translating_renpy", "gui.FONT_SCALE", " = 1.0", "", "var", "A scaling factor that is applied to all text in the launcher."], "achievement.sync": ["internal", "function", "()", "", "", "Synchronizes registered achievements between local storage and other backends. (For example, Steam.)"], "SetLocalVariable": ["internal", "function", "(name, value)", "", "Action", "Causes the variable `name` to be set to `value` in the current local context.\n\nThis function is only useful in a screen that has been use by another scene, as it provides a way of setting the value of a variable inside the used screen. In all other cases, :func:`SetScreenVariable` should be preferred, as it allows more of the screen to be cached.\n\nThis must be created in the context that the variable is set in - it can't be passed in from somewhere else."], "director.tag_blacklist": ["director", "director.tag_blacklist", " = { \"black\", \"text\", \"vtext\" }", "", "var", "A blacklist of tags that will not be shown for the show, scene, or hide statements."], "PlayCharacterVoice": ["internal", "class", "(voice_tag, sample, selected=False)", "", "Action", "This plays `sample` on the voice channel, as if said by a character with `voice_tag`.\n\n`sample` The full path to a sound file. No voice-related handling of this file is done.\n\n`selected` If True, buttons using this action will be marked as selected while the sample is playing."], "ui.prefixed_style": ["obsolete", "function", "(style_suffix)", "", "", "Combines the default style prefix with a style suffix."], "Fade": ["transitions", "function", "(out_time, hold_time, in_time, color=\"#000\")", "", "", "Returns a transition that takes `out_time` seconds to fade to a screen filled with `color`, holds at that screen for `hold_time` seconds, and then takes `in_time` to fade to then new screen.\n```\n# Fade to black and back.\ndefine fade = Fade(0.5, 0.0, 0.5)\n\n# Hold at black for a bit.\ndefine fadehold = Fade(0.5, 1.0, 0.5)\n\n# Camera flash - quickly fades to white, then back to the scene.\ndefine flash = Fade(0.1, 0.0, 0.5, color=\\#fff\\)\n```"], "ui.vbox": ["obsolete", "ui.vbox", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "GetTooltip": ["internal", "function", "(screen=None)", "", "Action", "Returns the tooltip of the currently focused displayable, or None if no displatable is focused.\n\n`screen` If not None, this should be the name or tag of a screen. If given, this function only returns the tooltip if the focused displayable is part of the screen."], "unicode_literals": ["internal", "function", "()", "", "", ""], "build.mac_info_plist": ["build", "build.mac_info_plist", " = { }", "", "var", "This is a dictionary mapping strings to strings, that can be used to add or override keys in the mac's Info.plist file."], "ShowingSwitch": ["internal", "function", "(*args, predict_all=None, **properties)", "", "", "This is a displayable that changes what it is showing based on the images are showing on the screen. The positional argument should be given in groups of two, where each group consists of:\n\n* A string giving an image name, or None to indicate the default. * A displayable to use if the condition is true.\n\nA default image should be specified.\n\n`predict_all` If True, all of the possible displayables will be predicted when the displayable is shown. If False, only the current condition is predicted. If None, :var:`config.conditionswitch_predict_all` is used.\n\nOne use of ShowingSwitch is to have images change depending on the current emotion of a character. For example\n```\nimage emotion_indicator = ShowingSwitch(\n\\eileen concerned\\, \\emotion_indicator concerned\\,\n\\eileen vhappy\\, \\emotion_indicator vhappy\\,\nNone, \\emotion_indicator happy\\)\n\n```"], "gui.button_tile": ["gui", "gui.button_tile", " = True", "", "var", "If true, the sides and center of the button background are tiled to increase or decrease their size. If false, the sides and center are scaled."], "im": ["obsolete", "function", "()", "", "", "An image manipulator is a displayable that takes an image or image manipulator, and either loads it or performs an operation on it. Image manipulators can only take images or other image manipulators as input.\n\nWith the few exceptions listed below, the use of image manipulators is historic. A number of image manipulators that had been documented in the past should no longer be used, as they suffer from inherent problems. In any case except for im.Data, the Transform() displayable provides similar functionality in a more general manner, while fixing the problems, although it sometimes requires gl2 to be enabled."], "achievement.register": ["internal", "function", "(name, **kwargs)", "", "", "Registers an achievement. Achievements are not required to be registered, but doing so allows one to pass information to the backends.\n\n`name` The name of the achievement to register.\n\nThe following keyword parameters are optional.\n\n`steam` The name to use on steam. If not given, defaults to `name`.\n\n`stat_max` The integer value of the stat at which the achievement unlocks.\n\n`stat_modulo` If the progress modulo `stat_max` is 0, progress is displayed to the user. For example, if stat_modulo is 10, progress will be displayed to the user when it reaches 10, 20, 30, etc. If not given, this defaults to 0."], "pushdown": ["transitions", "pushdown", "", "", "var", "Also: **pushleft, pushup, pushright**\n\nThese use the new scene to slide the old scene out the named side. Instances of the :func:`PushMove` transition class."], "Bar": ["internal", "class", "(range=None, value=None, width=None, height=None, changed=None, adjustment=None, step=None, page=None, bar=None, style=None, vertical=False, replaces=None, hovered=None, unhovered=None, **properties)", "", "", "Implements a bar that can display an integer value, and respond to clicks on that value."], "ConditionGroup": ["internal", "class", "(conditions)", "", "", "Combines a list of conditions into a single ConditionSwitch."], "FieldValue": ["internal", "class", "(object, field, range, max_is_zero=False, style=u'bar', offset=0, step=None, action=None, force_step=False)", "", "Action", "A bar value that allows the user to adjust the value of a field on an object.\n\n `object` The object. `field` The field, a string. `range` The range to adjust over. `max_is_zero` If True, then when the field is zero, the value of the bar will be range, and all other values will be shifted down by 1. This works both ways - when the bar is set to the maximum, the field is set to 0.\n\n This is used internally, for some preferences. `style` The styles of the bar created. `offset` An offset to add to the value. `step` The amount to change the bar by. If None, defaults to 1/10th of the bar. `action` If not None, an action to call when the field has changed."], "gui.button_properties": ["internal", "function", "(kind)", "", "", "Given a `kind` of button, returns a dictionary giving standard style properties for that button. This sets:\n\n:propref:`background` As described below.\n\n:propref:`padding` To gui.kind_borders.padding (if it exists).\n\n:propref:`xsize` To gui.kind_width (if it exists).\n\n:propref:`ysize` To gui.kind_height (if it exists).\n\n(Note that if `kind` is the string \\nvl_button\\, this will look for the gui.nvl_button_background variable.)\n\nThe background is a frame that takes its background picture from the first existing one of:\n\n* gui/button/kind_[prefix\\_].background.png * gui/button/[prefix\\_].background.png\n\nIf a gui variables named gui.kind_borders exists, it's used. Otherwise, :var:`gui.button_borders` is used. If gui.kind_tile exists, it determines if the borders are tiled, else :var:`gui.button_tile` controls tiling.\n\nFor what [prefix\\_] means, check out the :ref:`style prefix search ` documentation."], "Return": ["internal", "class", "(value=None)", "", "Action", "Causes the current interaction to return the supplied value, which must not be None. This is often used with menus and imagemaps, to select what the return value of the interaction is. If the screen was called using the ``call screen`` statement, the return value is placed in the `_return` variable.\n\n When in a menu, this returns from the menu. (The value should be None in this case.)"], "gui.choice_text_hover_color": ["gui", "gui.choice_text_hover_color", " = '#0066cc'", "", "var", "The color used for the text of focused choice buttons."], "NullAction": ["internal", "class", "(*args, **kwargs)", "", "Action", "Does nothing.\n\nThis can be used to make a button responsive to hover/unhover events, without actually doing anything."], "ui.grid": ["obsolete", "ui.grid", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "achievement.has": ["internal", "function", "(name)", "", "", "Returns true if the player has been granted the achievement with `name`."], "build.executable": ["internal", "function", "(pattern)", "", "", "Adds a pattern marking files as executable on platforms that support it. (Linux and Macintosh)"], "define.old_move_transitions": ["internal", "function", "(delay=1.0, vertical=False, reverse=False, background=\"#000\", flatten=True)", "", "", "A transitions that rotates the old scene 90 degrees around an axis, so that it is edge on with the viewer, switches to the new scene, and then rotates that scene another 90 degrees to show the new scene to the viewer.\n\n`delay` How long the transition should take.\n\n`vertical` If true, the scene is rotate around the x-axis (pixels move vertically). If false, the scene is roated around the y axis, pixels moving horizontally.\n\n`reverse` When true, the rotation occurs in the reverse direction.\n\n`background` A displayable that is placed behind the scene as it rotates.\n\n`flatten` If true, the scenes are flattened into images the size of the screen before being rotated. Use this if images being not entirely on the screen causes undesired effects."], "HistoryEntry": ["history", "HistoryEntry", "", "", "class", "Instances of this object are used to represent history entries in _history_list."], "RollbackToIdentifier": ["internal", "class", "(identifier)", "", "Action", "This causes a rollback to an identifier to occur. Rollback identifiers are returned as part of HistoryEntry objects."], "SaturationMatrix": ["internal", "class", "(value, desat=(0.2126, 0.7152, 0.0722))", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` that alters the saturation of an image, while leaving the alpha channel alone.\n\n`value` The amount of saturation in the resulting image. 1.0 is the unaltered image, while 0.0 is grayscale.\n\n`desat` This is a 3-element tuple that controls how much of the red, green, and blue channels will be placed into all three channels of a fully desaturated image. The default is based on the constants used for the luminance channel of an NTSC television signal. Since the human eye is mostly sensitive to green, more of the green channel is kept then the other two channels."], "MouseMove": ["internal", "class", "(x, y, duration=0)", "", "Action", "Move the mouse pointer to `x`, `y`. If the device does not have a mouse pointer or _preferences.mouse_move is False, this does nothing.\n\n`duration` The time it will take to perform the move, in seconds. During this time, the mouse may be unresponsive."], "Lexer.simple_expression": ["cds", "simple_expression", "()", "Lexer", "method", "Matches a simple Python expression, returns it as a string. This is often used when you expect a variable name. It is not recommended to change the result. The correct action is to evaluate the result in the future."], "PushMove": ["transitions", "function", "(time, mode=\"pushright\")", "", "", "Returns a transition that works by taking the new scene and using it to \\push\\ the old scene off the screen.\n\n`time` The time the transition takes.\n\n`mode` There are four possible modes: \\pushright\\, \\pushleft\\, \\pushup\\, and \\pushdown\\, which push the old scene off the screen in the direction indicated.\n```\ndefine pushright = PushMove(1.0, \\pushright\\)\ndefine pushleft = PushMove(1.0, \\pushleft\\)\ndefine pushup = PushMove(1.0, \\pushup\\)\ndefine pushdown = PushMove(1.0, \\pushdown\\)\n```"], "ui.imagemap": ["obsolete", "ui.imagemap", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "FileScreenshot": ["internal", "function", "(name, empty=None, page=None, slot=False)", "", "Action", "Returns the screenshot associated with the given file. If the file is not loadable, then `empty` is returned, unless it's None, in which case, a Null displayable is created.\n\n The return value is a displayable."], "iap.NoneBackend": ["internal", "class", "(*args, **kwargs)", "", "", "Called at init time to do any initialization required."], "IdentityMatrix": ["internal", "class", "()", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` that does not change the color or alpha of what is supplied to it.\n\n`value` Is ignored."], "im.AlphaMask": ["obsolete", "class", "(base, mask, **properties)", "", "", "An image manipulator that takes two image manipulators, `base` and `mask`, as arguments. It replaces the alpha channel of `base` with the red channel of `mask`.\n\nThis is used to provide an image's alpha channel in a second image, like having one jpeg for color data, and a second one for alpha. In some cases, two jpegs can be smaller than a single png file.\n\nNote that this takes different arguments from :func:`AlphaMask`, which uses the mask's alpha channel."], "zoomin": ["transitions", "zoomin", "", "", "var", "This zooms in entering images, taking 0.5 seconds to do so."], "ui.input": ["obsolete", "ui.input", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "center": ["internal", "function", "()", "", "", ""], "director.transitions": ["director", "director.transitions", " = [ \"dissolve\", \"pixellate\" ]", "", "var", "A list of transitions that are available to the with statement. Since transitions can't be auto-detected, these must be added manually."], "SelectedIf": ["internal", "class", "(expression)", "", "Action", "This indicates that one action in a list of actions should be used to determine if a button is selected. This only makes sense when the button has a list of actions. For example\n```\n# The button is selected only if mars_flag is True\ntextbutton \\Marsopolis\\:\naction [ SelectedIf(SetVariable(\\mars_flag\\, True)), SetVariable(\\on_mars\\, True) ]\n\nThe action inside SelectedIf is run normally when the button is clicked.\n```"], "pygame_sdl2": ["internal", "function", "()", "", "", ""], "SensitiveIf": ["internal", "class", "(expression)", "", "Action", "This indicates that one action in a list of actions should be used to determine if a button is sensitive. This only makes sense when the button has a list of actions. For example\n```\n# The button is sensitive only if mars_flag is True\ntextbutton \\Marsopolis\\:\naction [ SensitiveIf(SetVariable(\\mars_flag\\, True)), SetVariable(\\on_mars\\, True) ]\n\nThe action inside SensitiveIf is run normally when the button is clicked.\n```"], "hyperlink_function": ["internal", "function", "(target)", "", "", ""], "gui.button_height": ["gui", "gui.button_height", " = 64", "", "var", "The width and height of a button, in pixels. If None, the size is automatically determined based on the size of the text inside a button, and the borders given below."], "ui.button": ["obsolete", "ui.button", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "gui.check_button_borders": ["gui", "gui.check_button_borders", " = Borders(40, 10, 10, 10)", "", "var", "Increases the width of radio and check button borders, leaving extra space on the left for the check mark."], "build.destination": ["build", "build.destination", " = \"{directory_name}-dists\"", "", "var", "Gives the path to the directory the archive files will be placed in. This may be an absolute or a relative path. A relative path is considered to be relative to the projects directory.\n\nThe following values are substituted in using Python's ``str.format`` function.\n\n``{directory_name}`` The value of build.directory_name.\n\n``{executable_name}`` The value of build.executable_name.\n\n``{version}`` The value of build.version."], "offscreenright": ["internal", "function", "()", "", "", ""], "If": ["internal", "function", "(expression, true=None, false=None)", "", "Action", "This returns `true` if `expression` is true, and `false` otherwise. Use this to select an action based on an expression. Note that the default, None, can be used as an action that causes a button to be disabled."], "iap.Purchase": ["internal", "class", "(product, success=None)", "", "Action", "An action that attempts the purchase of `product`. This action is sensitive if and only if the product is purchasable (a store is enabled, and the product has not already been purchased.)\n\n`success` If not None, this is an action or list of actions that are run when the purchase succeeds."], "ui.side": ["obsolete", "ui.side", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "ToggleSetMembership": ["internal", "class", "(set, value)", "", "Action", "Toggles the membership of `value` in `set`. If the value is not in the set, it's added. Otherwise, it is removed.\n\nButtons with this action are marked as selected if and only if the value is in the set.\n\n`set` The set to add to or remove from. This may be a set or list. In the case of a list, new items are appended. `value` The value to add or append."], "nvl_variant": ["internal", "function", "()", "", "", ""], "absolute_import": ["internal", "function", "()", "", "", ""], "iap.get_price": ["internal", "function", "(product)", "", "", "Returns a string giving the price of the `product` in the user's local currency. Returns None if the price of the product is unknown - which indicates the product cannot be purchased."], "InputValue": ["internal", "class", "()", "", "", ""], "iap.Restore": ["internal", "class", "()", "", "Action", "An Action that contacts the app store and restores any missing purchases."], "config": ["internal", "function", "()", "", "", "Configuration variables control the behavior of Ren'Py's implementation, allowing Ren'Py itself to be customized in a myriad of ways. These range from the common (such as changing the screen size) to the obscure (adding new kinds of archive files).\n\nRen'Py's implementation makes the assumption that, once the GUI system has initialized, configuration variables will not change. Changing configuration variables outside of init blocks can lead to undefined behavior. Configuration variables are not part of the save data."], "ZoomInOut": ["internal", "function", "()", "", "", ""], "XScrollValue": ["internal", "class", "(viewport)", "", "Action", "The value of an adjustment that horizontally scrolls the viewport with the given id, on the current screen. The viewport must be defined before a bar with this value is."], "adv": ["internal", "function", "()", "", "", ""], "nvl": ["internal", "function", "()", "", "", ""], "AlphaMask": ["internal", "class", "(child, mask, **properties)", "", "", "This displayable takes its colors from `child`, and its alpha channel from the multiplication of the alpha channels of `child` and `mask`. The result is a displayable that has the same colors as `child`, is transparent where either `child` or `mask` is transparent, and is opaque where `child` and `mask` are both opaque.\n\nThe `child` and `mask` parameters may be arbitrary displayables. The size of the AlphaMask is the size of `child`.\n\nNote that this takes different arguments from :func:`im.AlphaMask`, which uses the mask's red channel."], "SubTransition": ["internal", "function", "()", "", "", "Applies a transition to a subset of the screen. Not documented."], "ease": ["transitions", "ease", "", "", "var", "Also: **easeinright, easeinleft, easeintop, easeinbottom, easeoutright, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "gui.nvl_button_xalign": ["gui", "gui.nvl_button_xalign", " = 0.5", "", "var", "The position and alignment of NVL-mode menu buttons."], "updater.struct": ["internal", "function", "()", "", "", "Functions to convert between Python values and C structs represented as Python strings. It uses format strings (explained below) as compact descriptions of the lay-out of the C structs and the intended conversion to/from Python values.\n\nThe optional first format char indicates byte order, size and alignment: @: native order, size & alignment (default) =: native order, std. size & alignment <: little-endian, std. size & alignment >: big-endian, std. size & alignment !: same as >\n\nThe remaining chars indicate types of args and must match exactly; these can be preceded by a decimal repeat count: x: pad byte (no data); c:char; b:signed byte; B:unsigned byte; ?: _Bool (requires C99; if not available, char is used instead) h:short; H:unsigned short; i:int; I:unsigned int; l:long; L:unsigned long; f:float; d:double. Special cases (preceding decimal count indicates length): s:string (array of char); p: pascal string (with count byte). Special case (only available in native format): P:an integer type that is wide enough to hold a pointer. Special case (not in native mode unless 'long long' in platform C): q:long long; Q:unsigned long long Whitespace between formats is ignored.\n\nThe variable struct.error is an exception raised on errors."], "updater.codecs": ["internal", "function", "()", "", "", "codecs -- Python Codec Registry, API and helpers.\n\n Written by Marc-Andre Lemburg (mal@lemburg.com).\n\n(c) Copyright CNRI, All Rights Reserved. NO WARRANTY."], "gui.frame_borders": ["gui", "gui.frame_borders", " = Borders(15, 15, 15, 15)", "", "var", "The borders applied to frame windows."], "FileAction": ["internal", "function", "(name, page=None, **kwargs)", "", "Action", "\\Does the right thing\\ with the file. This means loading it if the load screen is showing (current screen is named \\load\\), and saving otherwise.\n\n `name` The name of the slot to save to or load from. If None, an unused slot (a large number based on the current time) will be used.\n\n `page` The page that the file will be saved to or loaded from. If None, the current page is used.\n\n Other keyword arguments are passed to FileLoad or FileSave."], "SizeZoom": ["internal", "function", "()", "", "", ""], "easeoutleft": ["transitions", "easeoutleft", "", "", "var", "Also: **easeinright, easeinleft, easeintop, easeinbottom, easeoutright, ease, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "SplineMotion": ["internal", "function", "(points, time, child=None, anchors=(0.5, 0.5), repeat=False, bounce=False, anim_timebase=False, style=u'default', time_warp=None, **properties)", "", "", ""], "vcentered": ["internal", "function", "()", "", "", ""], "HistoryEntry.what_args": ["history", "what_args", "", "HistoryEntry", "attribute", "A dictionary giving the properties that were supplied to the what text widget when the dialogue was originally shown."], "NoRollback": ["internal", "class", "()", "", "", "Instances of classes inheriting from this class do not participate in rollback. Objects reachable through an instance of a NoRollback class only participate in rollback if they are reachable through other paths."], "OpenURL": ["internal", "class", "(url)", "", "Action", "Causes `url` to be opened in a web browser."], "preferences.language": ["preferences", "preferences.language", " = None", "", "var", "The language that the player has selected to use when running the game. This is None for the default language or a string containing a language the game is translated to.\n\nThis can be used to set the default language, and can be read to determine the current language. The :func:`Language` action can be used to change the language."], "Show": ["internal", "class", "(screen, transition=None, *args, **kwargs)", "", "Action", "This causes another screen to be shown. `screen` is a string giving the name of the screen. The arguments are passed to the screen being shown.\n\n If not None, `transition` is use to show the new screen."], "ImageButton": ["internal", "class", "(idle_image, hover_image=None, insensitive_image=None, activate_image=None, selected_idle_image=None, selected_hover_image=None, selected_insensitive_image=None, selected_activate_image=None, style=u'image_button', clicked=None, hovered=None, **properties)", "", "", "Used to implement the guts of an image button."], "HistoryEntry.who_args": ["history", "who_args", "", "HistoryEntry", "attribute", "A dictionary giving the properties that were supplied to the who text widget when the dialogue was originally shown."], "updater.can_update": ["internal", "function", "(base=None)", "", "", "Returns true if it's possible that an update can succeed. Returns false if updating is totally impossible. (For example, if the update directory was deleted.)\n\n Note that this does not determine if an update is actually available. To do that, use :func:`updater.UpdateVersion`."], "ui.window": ["obsolete", "ui.window", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "ToggleVoiceMute": ["internal", "class", "(voice_tag, invert=False)", "", "Action", "Toggles the muting of `voice_tag`. This is selected if the given voice tag is muted, unless `invert` is true, in which case it's selected if the voice is unmuted."], "gui.name_xalign": ["gui", "gui.name_xalign", " = 0.0", "", "var", "The horizontal alignment of the character's name. This can be 0.0 for left- aligned, 0.5 for centered, and 1.0 for right-aligned. (It's almost always 0.0 or 0.5.) This is used for both the position of the namebox relative to gui.name_xpos, and to select the side of of the namebox that is aligned with xpos."], "Transform.set_child": ["trans_trans_python", "set_child", "(child)", "Transform", "method", "Call this method with a new `child` to change the child of this transform."], "moveinleft": ["transitions", "moveinleft", "", "", "var", "Also: **moveinright, moveintop, moveinbottom**\n\nThese move entering images onto the screen from the appropriate side, taking 0.5 seconds to do so."], "QuickSave": ["internal", "function", "(message=u'Quick save complete.', newest=False)", "", "Action", "Performs a quick save.\n\n`message` A message to display to the user when the quick save finishes.\n\n`newest` Set to true to mark the quicksave as the newest save."], "Quit": ["internal", "class", "(confirm=None)", "", "Action", "Quits the game.\n\n `confirm` If true, prompts the user if he wants to quit, rather than quitting directly. If None, asks if and only if the user is not at the main menu."], "right": ["internal", "function", "()", "", "", ""], "build.itch_project": ["build", "build.itch_project", " = None", "", "var", "Setting this allows the Ren'Py launcher to upload your project to itch.io. This should be set to the name of a project registered with itch. (For example, \\renpytom/the-question\\).\n\nOnce this is set, after the distributions have been built, you can click \\Build distributions\\, \\Upload to itch.io\\ to cause an upload to occur."], "ui.drag": ["obsolete", "ui.drag", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "FilePageName": ["internal", "function", "(auto=u'a', quick=u'q')", "", "Action", "Returns the name of the current file page, as a string. If a normal page, this returns the page number. Otherwise, it returns `auto` or `quick`."], "Confirm": ["internal", "class", "(prompt, yes, no=None, confirm_selected=False)", "", "Action", "Prompts the user for confirmation of an action. If the user clicks yes, the yes action is performed. Otherwise, the `no` action is performed.\n\n`prompt` The prompt to display to the user.\n\n`confirm_selected` If true, the prompt will be displayed even if the `yes` action is already selected. If false (the default), the prompt will not be displayed if the `yes` action is selected.\n\nThe sensitivity and selectedness of this action match those of the `yes` action."], "python_object": ["internal", "class", "()", "", "", "The most base type"], "library": ["internal", "function", "()", "", "", ""], "preferences.gl_powersave": ["preferences", "preferences.gl_powersave", " = \"auto\"", "", "var", "This determines how often Ren'Py will redraw an unchanging screen. If True, Ren'Py will only draw the screen 5 times a second. If False, it will always draw at the full framerate possible. If \\auto\\, it will draw at full speed when the device is powered, and 5hz when it is running on battery."], "Pixellate": ["transitions", "function", "(time, steps)", "", "", "Returns a transition that pixellates out the old screen, and then pixellates in the new screen.\n\n`time` The total time the transition will take, in seconds.\n\n`steps` The number of steps that will occur, in each direction. Each step creates pixels about twice the size of those in the previous step, so a 5-step pixellation will create 32x32 pixels."], "SepiaMatrix": ["internal", "function", "(tint=u'#ffeec2', desat=(0.2126, 0.7152, 0.0722))", "", "", "A function that returns a ColorMatrix that can be used with :tpref:`matrixcolor` to sepia-tone a displayable. This is the equivalent of\n```\nTintMatrix(tint) * SaturationMatrix(0.0, desat)\n```"], "director.show_tags": ["director", "director.show_tags", " = set()", "", "var", "If not empty, only the tags present in this set will be presented for the show statement."], "im.Blur": ["obsolete", "class", "(im, xrad, yrad=None, **properties)", "", "", "An image manipulator that blurs the image manipulator `im` using an elliptical kernel described by `xrad` and optionally `yrad`.\n\nIf `yrad` is None, it will take the value of `xrad` resulting in a circular kernel being used.\n```\nimage logo blurred = im.Blur(\\logo.png\\, 1.5)\n\nThe same effect can now be achieved with the :tpref:`blur` transform property.\n```"], "ToggleLocalVariable": ["internal", "function", "(name, true_value=None, false_value=None)", "", "Action", "Toggles the value of `name` in the current local context.\n\nThis function is only useful in a screen that has been use by another scene, as it provides a way of setting the value of a variable inside the used screen. In all other cases, :func:`ToggleScreenVariable` should be preferred, as it allows more of the screen to be cached.\n\nThis must be created in the context that the variable is set in - it can't be passed in from somewhere else.\n\n`true_value` If not None, then this is the true value used. `false_value` If not None, then this is the false value used."], "bord": ["internal", "function", "(s)", "", "", ""], "Lexer.keyword": ["cds", "keyword", "(s)", "Lexer", "method", "Matches `s` as a keyword."], "gui.history_text_width": ["gui", "gui.history_text_width", " = 1110", "", "var", "The width of the name label and dialogue text, in pixels."], "iap.python_object": ["internal", "class", "()", "", "", "The most base type"], "DynamicCharacter": ["internal", "function", "(name_expr, **properties)", "", "", ""], "ParameterizedText": ["internal", "class", "(style=u'default', **properties)", "", "", "This is a displayable that can be shown with an additional string parameter, which then shows that string as if it was an image. This is usually used as part of the pre-defined ``text`` image.\n\nFor example, one can do::\n\nshow text \\Hello, World\\ at truecenter with dissolve pause 1 hide text with dissolve\n\nYou can use ParameterizedText directly to define similar images with different style properties. For example, one can write::\n\nimage top_text = ParameterizedText(xalign=0.5, yalign=0.0)"], "easeoutright": ["transitions", "easeoutright", "", "", "var", "Also: **easeinright, easeinleft, easeintop, easeinbottom, ease, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "Notify": ["internal", "class", "(message)", "", "Action", "Displays `message` using :func:`renpy.notify`."], "Transform.hide_request": ["trans_trans_python", "hide_request", "", "Transform", "attribute", "This is set to true when the function is called, to indicate that the transform is being hidden."], "FileSlotName": ["internal", "function", "(slot, slots_per_page, auto=u'a', quick=u'q', format=u'%s%d')", "", "Action", "Returns the name of the numbered slot. This assumes that slots on normal pages are numbered in a linear order starting with 1, and that page numbers start with 1. When slot is 2, and slots_per_page is 10, and the other variables are the defaults:\n\n * When the first page is showing, this returns \\2\\. * When the second page is showing, this returns \\12\\. * When the auto page is showing, this returns \\a2\\. * When the quicksave page is showing, this returns \\q2\\.\n\n `slot` The number of the slot to access.\n\n `slots_per_page` The number of slots per page.\n\n `auto` A prefix to use for the auto page.\n\n `quick` A prefix to use for the quick page.\n\n `format` The formatting code to use. This is given two arguments: A string giving the page prefix, and an integer giving the slot number."], "im.tobytes": ["obsolete", "function", "(s)", "", "", "Encodes to latin-1 (where the first 256 chars are the same as ASCII.)"], "OldMoveTransition": ["internal", "function", "()", "", "", ""], "Lexer.error": ["cds", "error", "(msg)", "Lexer", "method", "Adds a `msg` (with the current position) in the list of detected parsing errors. This interrupts the parsing of the current statement, but does not prevent further parsing."], "ui.hotspot": ["obsolete", "ui.hotspot", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "easeoutbottom": ["transitions", "easeoutbottom", "", "", "var", "Also: **easeinright, easeinleft, easeintop, easeinbottom, easeoutright, easeoutleft, easeouttop, ease**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "Lexer": ["cds", "Lexer", "", "", "class", "The parse method of renpy.register_statement takes a Lexer object"], "ui.detached": ["obsolete", "function", "()", "", "", "Do not add the next displayable to any later or container. Use this if you want to assign the result of a ui function to a variable."], "gui.kind_text_font": ["gui", "gui.kind_text_font", "", "", "var", "If present, the font used for the text."], "BarValue.get_style": ["screen_python", "get_style", "(self)", "BarValue", "method", "This is used to determine the style of bars that use this value. It should return a tuple of two style names or style objects. The first is used for a bar, and the second for vbar.\n\nThis defaults to (\\bar\\, \\vbar\\)."], "CropMove": ["transitions", "function", "(time, mode=\"slideright\", startcrop=(0.0, 0.0, 0.0, 1.0), startpos=(0.0, 0.0), endcrop=(0.0, 0.0, 1.0, 1.0), endpos=(0.0, 0.0), topnew=True)", "", "", "Returns a transition that works by cropping a scene and positioning it on the screen. This can be used to implement a variety of effects, all of which involve changing rectangular slices of scenes.\n\n`time` The time the transition takes.\n\n`mode` The name of the mode of the transition. There are three groups of modes: wipes, slides, and other. This can also be \\custom\\, to allow a custom mode to be defined.\n\nIn a wipe, the image stays fixed, and more of it is revealed as the transition progresses. For example, in \\wiperight\\, a wipe from left to right, first the left edge of the image is revealed at the left edge of the screen, then the center of the image, and finally the right side of the image at the right of the screen. Other supported wipes are \\wipeleft\\, \\wipedown\\, and \\wipeup\\.\n\nIn a slide, the image moves. So in a \\slideright\\, the right edge of the image starts at the left edge of the screen, and moves to the right as the transition progresses. Other slides are \\slideleft\\, \\slidedown\\, and \\slideup\\.\n\nThere are also slideaways, in which the old image moves on top of the new image. Slideaways include \\slideawayright\\, \\slideawayleft\\, \\slideawayup\\, and \\slideawaydown\\.\n\nWe also support a rectangular iris in with \\irisin\\ and a rectangular iris out with \\irisout\\.\n\nThe following parameters are only respected if the mode is \\custom\\. Positions are relative to the size of the screen, while the crops are relative to the size of the image. So a crop of (0.25, 0.0, 0.5, 1.0) takes the middle half of an image.\n\n`startcrop` The starting rectangle that is cropped out of the top image. A 4-element tuple containing x, y, width, and height.\n\n`startpos` The starting place that the top image is drawn to the screen at, a 2-element tuple containing x and y.\n\n`endcrop` The ending rectangle that is cropped out of the top image. A 4-element tuple containing x, y, width, and height.\n\n`endpos` The ending place that the top image is drawn to the screen at, a 2-element tuple containing x and y.\n\n`topnew` If true, the scene that is cropped and moved (and is on top of the other scene) is the new scene. If false, it is the old scene.\n```\ndefine wiperight = CropMove(1.0, \\wiperight\\)\ndefine wipeleft = CropMove(1.0, \\wipeleft\\)\ndefine wipeup = CropMove(1.0, \\wipeup\\)\ndefine wipedown = CropMove(1.0, \\wipedown\\)\n\ndefine slideright = CropMove(1.0, \\slideright\\)\ndefine slideleft = CropMove(1.0, \\slideleft\\)\ndefine slideup = CropMove(1.0, \\slideup\\)\ndefine slidedown = CropMove(1.0, \\slidedown\\)\n\ndefine slideawayright = CropMove(1.0, \\slideawayright\\)\ndefine slideawayleft = CropMove(1.0, \\slideawayleft\\)\ndefine slideawayup = CropMove(1.0, \\slideawayup\\)\ndefine slideawaydown = CropMove(1.0, \\slideawaydown\\)\n\ndefine irisout = CropMove(1.0, \\irisout\\)\ndefine irisin = CropMove(1.0, \\irisin\\)\n```"], "gui.nvl_borders": ["gui", "gui.nvl_borders", " = Borders(0, 15, 0, 30)", "", "var", "The borders around the background of the NVL-mode. Since the background is not a frame, this is only used to pad out the NVL-mode to prevent it from pressing up against the sides of the screen."], "Lexer.subblock_lexer": ["cds", "subblock_lexer", "()", "Lexer", "method", "Return a Lexer for the block associated with the current line."], "Swing": ["transitions", "function", "(delay=1.0, vertical=False, reverse=False, background=\"#000\", flatten=True)", "", "", "A transitions that rotates the old scene 90 degrees around an axis, so that it is edge on with the viewer, switches to the new scene, and then rotates that scene another 90 degrees to show the new scene to the viewer.\n\n`delay` How long the transition should take.\n\n`vertical` If true, the scene is rotate around the x-axis (pixels move vertically). If false, the scene is roated around the y axis, pixels moving horizontally.\n\n`reverse` When true, the rotation occurs in the reverse direction.\n\n`background` A displayable that is placed behind the scene as it rotates.\n\n`flatten` If true, the scenes are flattened into images the size of the screen before being rotated. Use this if images being not entirely on the screen causes undesired effects."], "print_function": ["internal", "function", "()", "", "", ""], "ui.draggroup": ["obsolete", "ui.draggroup", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "gui.history_height": ["gui", "gui.history_height", " = 210", "", "var", "The height of a history entry, in pixels. This can be None to allow the height of a history entry to vary at the cost of performance \u2013 config.history_length may need to be lowered significantly when this is None."], "preferences.skip_unseen": ["preferences", "preferences.skip_unseen", " = False", "", "var", "When True, Ren'Py will skip all text. When False, Ren'Py will only skip text that has been read by the player in any session. The equivalent of the \\skip\\ preference."], "updater.Updater": ["internal", "class", "(url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, check_only=False, confirm=True)", "", "", "Cleans the file named fn from the updates directory."], "Image": ["internal", "function", "(arg, loose=False, **properties)", "", "", "Loads an image from a file. filename is a string giving the name of the file.\n\nfilename should be a JPEG or PNG file with an appropriate extension.\n\nIf optimize_bounds is True, only the portion of the image inside the bounding box of non-transparent pixels is loaded into GPU memory. (The only reason to set this to False is when using an image as input to a shader.)"], "achievement.get_progress": ["internal", "function", "(name)", "", "", "Returns the current progress towards the achievement identified with `name`, or 0 if no progress has been registered for it or if the achievement is not known."], "ui.bar": ["obsolete", "ui.bar", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "range": ["internal", "function", "(*args)", "", "", ""], "Replay": ["internal", "class", "(label, scope={}, locked=None)", "", "Action", "An action that starts `label` as a replay.\n\n`scope` A dictionary mapping variable name to value. These variables are set when entering the replay.\n\n`locked` If true, this replay is locked. If false, it is unlocked. If None, the replay is locked if the label has not been seen in any playthrough."], "FontGroup": ["internal", "class", "()", "", "", "A group of fonts that can be used as a single font."], "ui.spritemanager": ["obsolete", "ui.spritemanager", "", "", "ui", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "updater.UpdateVersion": ["internal", "function", "(url, check_interval=3600*6, simulate=None, **kwargs)", "", "", "This function contacts the server at `url`, and determines if there is a newer version of software available at that url. If there is, this function returns the new version. Otherwise, it returns None.\n\nSince contacting the server can take some time, this function launches a thread in the background, and immediately returns the version from the last time the server was contacted, or None if the server has never been contacted. The background thread will restart the current interaction once the server has been contacted, which will cause screens that call this function to update.\n\nEach url will be contacted at most once per Ren'Py session, and not more than once every `check_interval` seconds. When the server is not contacted, cached data will be returned.\n\nAdditional keyword arguments (including `simulate`) are passed to the update mechanism as if they were given to :func:`updater.update`."], "build.version": ["internal", "function", "()", "", "", "The version used by the build system."], "gui.kind_text_color": ["gui", "gui.kind_text_color", "", "", "var", "If present, the color of the text."], "FileDelete": ["internal", "class", "(name, confirm=True, page=None, slot=False)", "", "Action", "Deletes the file.\n\n `name` The name of the slot to delete.\n\n `confirm` If true and not at the main menu, prompt for confirmation before loading the file.\n\n `page` The page that the file will be loaded from. If None, the current page is used.\n\n `slot` If True, `name` is taken to be a slot name, and `page` is ignored."], "bchr": ["internal", "function", "(s)", "", "", ""], "im.matrix": ["obsolete", "class", "()", "", "", "Constructs an im.matrix object from `matrix`. im.matrix objects support The operations supported are matrix multiplication, scalar multiplication, element-wise addition, and element-wise subtraction. These operations are invoked using the standard mathematical operators (\\\\*, \\\\*, +, and -, respectively). If two im.matrix objects are multiplied, matrix multiplication is performed, otherwise scalar multiplication is used.\n\n`matrix` is a 20 or 25 element list or tuple. If it is 20 elements long, it is padded with (0, 0, 0, 0, 1) to make a 5x5 matrix, suitable for multiplication."], "gui.frame_tile": ["gui", "gui.frame_tile", " = True", "", "var", "If true, the sides and center of the confirm screen are tiled. If false, they are scaled."], "HistoryEntry.show_args": ["history", "show_args", "", "HistoryEntry", "attribute", "A dictionary giving the properties that were supplied to the say screen when the dialogue was originally shown."], "Editor.end": ["editor", "end", "(**kwargs)", "Editor", "method", "Ends a transaction."], "build.make_file_lists": ["internal", "function", "(s)", "", "", "Turns `s` into a (perhaps empty) list of file_lists.\n\nIf `s` is a list or None, then returns it. If it's a string, splits it on whitespace. Otherwise, errors out."], "preferences.transitions": ["preferences", "preferences.transitions", " = 2", "", "var", "Determines which transitions should be shown. 2 shows all transitions, 0 shows no transitions. (1 is reserved.) The equivalent of the \\transitions\\ preference."], "Transform.update": ["trans_trans_python", "update", "()", "Transform", "method", "This should be called when a transform property field is updated outside of the callback method, to ensure that the change takes effect."], "zoominout": ["transitions", "zoominout", "", "", "var", "This zooms in entering images and zooms out leaving images, taking 0.5 seconds to do so."], "Lexer.eol": ["cds", "eol", "()", "Lexer", "method", "True if the lexer is at the end of the line."], "iap.IOSBackend": ["internal", "class", "(self)", "", "", "Waits for a result.\n\n`interact` If true, waits interactively. If false, waits using renpy.pause."], "gui.REGULAR_FONT": ["translating_renpy", "gui.REGULAR_FONT", " = \"Roboto-Regular.ttf\"", "", "var", "The path to the font used for heavy-weight text in the launcher."], "Lexer.revert": ["cds", "revert", "(o)", "Lexer", "method", "When `o` is the object returned from checkpoint(), reverts the state of the lexer to what it was when checkpoint() was called. (This is used for backtracking.)"], "Editor.begin": ["editor", "begin", "(new_window=False, **kwargs)", "Editor", "method", "Starts an editor transaction.\n\nIf `new_window` is true, the editor should attempt to open a new window. Otherwise, it should attempt to perform the transaction in an existing editor window."], "gui.rebuild": ["internal", "function", "()", "", "", "Rebuilds the GUI.\n\nNote: This is a very slow function."], "Composite": ["internal", "function", "(size, *args, **properties)", "", "", "This creates a new displayable of `size`, by compositing other displayables. `size` is a (width, height) tuple.\n\nThe remaining positional arguments are used to place images inside the Composite. The remaining positional arguments should come in groups of two, with the first member of each group an (x, y) tuple, and the second member of a group is a displayable that is composited at that position.\n\nDisplayables are composited from back to front.\n```\nimage eileen composite = Composite(\n(300, 600),\n(0, 0), \\body.png\\,\n(0, 0), \\clothes.png\\,\n(50, 50), \\expression.png\\)\n```"], "ui.One": ["obsolete", "class", "(displayable, style_prefix)", "", "", "A widget that expects exactly one child."], "preferences.fullscreen": ["preferences", "preferences.fullscreen", " = False", "", "var", "This is True when Ren'Py is in fullscreen mode, and False when it is running in a window. The equivalent of the \\display\\ preference."], "build.include_i686": ["build", "build.include_i686", " = True", "", "var", "If true, files necessary to run on 32-bit x86 processors will be included in the Linux and Mac builds. If False, these files will not be included."], "Action.get_selected": ["screen_python", "get_selected", "(self)", "Action", "method", "This should return true if the button should be rendered as a selected button, and false otherwise.\n\nThe default implemention returns False."], "HideInterface": ["internal", "class", "(*args, **kwargs)", "", "Action", "Causes the interface to be hidden until the user clicks."], "Condition": ["internal", "class", "(condition, image, **kwargs)", "", "", "This is used to represent a layer of an LayeredImage that is controlled by a condition. When the condition is true, the layer is displayed. Otherwise, nothing is displayed.\n\n`condition` This should be a string giving a Python condition that determines if the layer is displayed.\n\n`image` If not None, this should be a displayable that is displayed when the condition is true.\n\n`if_all` An attribute or list of attributes. The condition is only evaluated if all of these are showing.\n\n`if_any` An attribute or list of attributes. If not empty, the condition is only evaluated if any of these are showing.\n\n`if_not` An attribute or list of attributes. The condition is only evaluated if none of these are showing.\n\n`at` A transform or list of transforms that are applied to the image.\n\nOther keyword arguments are interpreted as transform properties. If any are present, a transform is created that wraps the image. (For example, pos=(100, 200) can be used to offset the image by 100 pixels horizontally and 200 vertically.)"], "persistent._clear": ["persistent", "persistent._clear", "(progress=False)", "", "function", "Resets the persistent data.\n\n`progress` If true, also resets progress data that Ren'Py keeps.\n\nNote that this will delete all persistent data, and will not re-apply defaults until Ren'Py restarts."], "director.button": ["director", "director.button", " = True", "", "var", "If True, the director displays a screen with a button to access the director window. If False, the game can provide it's own access, by making available the director.Start action."], "Lexer.expect_block": ["cds", "expect_block", "(stmt)", "Lexer", "method", "Called to indicate that the statement requires that a non-empty block is present. `stmt` should be a string, it will be added to the message with an error."], "QueueEvent": ["internal", "class", "(event, up=False)", "", "Action", "Queues the given event using :func:`renpy.queue_event`."], "translate_font": ["translating_renpy", "translate_font", "(language, font)", "", "function", "This is used to set a font for `language`. The font is used in the launcher, and also used to in games generated in that language. The font file should be placed in game/fonts.\n\n`font` A string giving the name of the font file."], "wipeup": ["transitions", "wipeup", "", "", "var", "Also: **wiperight, wipeleft, wipedown**\n\nWipes the scene in the given direction. Instances of the :func:`CropMove` transition class."], "preferences.gl_tearing": ["preferences", "preferences.gl_tearing", " = False", "", "var", "This determines if tearing (True) or frameskip (False) is the preferred behavior when the game can't keep up with its intended framerate."], "updater.threading": ["internal", "function", "()", "", "", "Thread module emulating a subset of Java's threading model."], "gui.nvl_height": ["gui", "gui.nvl_height", " = 173", "", "var", "The height of a single NVL-mode entry. Setting this to a fixed height makes it possible to have NVL-mode without paging, showing a fixed number of entries at once. Setting this to None allows entries to be of a variable size."], "gui.vscrollbar_borders": ["gui", "gui.vscrollbar_borders", " = Borders(6, 10, 6, 10)", "", "var", "The borders that are used with the Frame containing the bar image."], "updater.zlib": ["internal", "function", "()", "", "", "The functions in this module allow compression and decompression using the zlib library, which is based on GNU zip.\n\nadler32(string[, start]) -- Compute an Adler-32 checksum. compress(string[, level]) -- Compress string, with compression level in 0-9. compressobj([level]) -- Return a compressor object. crc32(string[, start]) -- Compute a CRC-32 checksum. decompress(string,[wbits],[bufsize]) -- Decompresses a compressed string. decompressobj([wbits]) -- Return a decompressor object.\n\n'wbits' is window buffer size and container format. Compressor objects support compress() and flush() methods; decompressor objects support decompress() and flush()."], "gui.scrollbar_tile": ["gui", "gui.scrollbar_tile", " = True", "", "var", "If true, the frame containing the bar of a scrollbar is tiled. If False, if it scaled."], "default": ["internal", "function", "()", "", "", ""], "Attribute": ["internal", "class", "(group, attribute, image=None, default=False, group_args={}, **kwargs)", "", "", "This is used to represent a layer of an LayeredImage that is controlled by an attribute. A single attribute can control multiple layers, in which case all layers corresponding to that attribute will be displayed.\n\n`group` A string giving the group the attribute is part of. This may be None, in which case a group with the same name as the attribute is created.\n\n`attribute` A string giving the name of the attribute.\n\n`image` If not None, this should be a displayable that is displayed when this attribute is shown.\n\n`default` If True, and no other attribute for the group is selected, this attribute is.\n\nThe following keyword arguments are also known:\n\n`at` A transform or list of transforms that are applied to the image.\n\n`if_all` An attribute or list of attributes. The displayable is only shown if all of these are showing.\n\n`if_any` An attribute or list of attributes. if not empty, the displayable is only shown if any of these are showing.\n\n`if_not` An attribute or list of attributes. The displayable is only shown if none of these are showing.\n\nOther keyword arguments are interpreted as transform properties. If any are present, a transform is created that wraps the image. (For example, pos=(100, 200) can be used to offset the image by 100 pixels horizontally and 200 vertically.)\n\nIf the `image` parameter is omitted or None, and the LayeredImage has been given the `image_format` parameter, the image_format is used to generate an image filename."], "preferences.video_image_fallback": ["preferences", "preferences.video_image_fallback", " = False", "", "var", "If True, images are displayed instead of videosprites. If False, video sprites are displayed normally. The equivalent (inverted) of the \\video sprites\\ preference."], "ui.Detached": ["obsolete", "class", "(style_prefix)", "", "", "Used to indicate a widget is detached from the stack."], "preferences.wait_voice": ["preferences", "preferences.wait_voice", " = True", "", "var", "If True, auto-forward mode will wait for voice files and self-voicing to finish before advancing. If False, it will not. The equivalent of the \\wait for voice\\ preference."], "ui": ["obsolete", "function", "()", "", "", "**Note**\n\nThe implementation of Ren'Py has changed, and UI functions that create displayables can now be far slower than their screen language equivalents.\n\nThe UI functions are Python equivalents of the screen language statements. For each screen language statement, there is a ui function with the same name. For example, ui.text corresponds to the text statement, and ui.add corresponds to the add statement."], "HistoryEntry.what": ["history", "what", "", "HistoryEntry", "attribute", "A string giving the dialogue text."], "gui.text_color": ["gui", "gui.text_color", " = \"#402000\"", "", "var", "This sets the color of the dialogue text."], "gui.LIGHT_FONT": ["translating_renpy", "gui.LIGHT_FONT", " = \"Roboto-Light.ttf\"", "", "var", "The path to the font used for normal text in the launcher."], "achievement.grant": ["internal", "function", "(name)", "", "", "Grants the achievement with `name`, if it has not already been granted."], "gui.choice_button_text_idle_color": ["gui", "gui.choice_button_text_idle_color", " = '#888888'", "", "var", "The color used for the text of unfocused choice buttons."], "HistoryEntry.image_tag": ["history", "image_tag", "", "HistoryEntry", "attribute", "The image tag given to the :func:`Character`, or None if no such tag was given."], "ui.ChoiceActionBase": ["obsolete", "class", "(label, value, location=None, block_all=None, sensitive=True, args=None, kwargs=None)", "", "Action", "Base class for choice actions. The choice is identified by a label and value. The class will automatically determine the rollback state and supply correct \\sensitive\\ and \\selected\\ information to the widget. If a location is supplied, it will check whether the choice was previously visited and mark it so if it is chosen."], "persistent": ["internal", "function", "()", "", "", "The persistent variable allows access to the Persistent object's fields, which contains saved data that is not associated with a particular point in a game."], "ScreenVariableValue": ["internal", "class", "(variable, range, max_is_zero=False, style=u'bar', offset=0, step=None, action=None, force_step=False)", "", "Action", "A bar value that adjusts the value of a variable in a screen.\n\n`variable` A string giving the name of the variable to adjust. `range` The range to adjust over. `max_is_zero` If True, then when the field is zero, the value of the bar will be range, and all other values will be shifted down by 1. This works both ways - when the bar is set to the maximum, the field is set to 0.\n\nThis is used internally, for some preferences. `style` The styles of the bar created. `offset` An offset to add to the value. `step` The amount to change the bar by. If None, defaults to 1/10th of the bar. `action` If not None, an action to call when the field has changed."], "file": ["internal", "function", "(*args, **kwargs)", "", "", "Returns a read-only file-like object that accesses the file named `fn`. The file is accessed using Ren'Py's standard search method, and may reside in an RPA archive. or as an Android asset.\n\nThe object supports a wide subset of the fields and methods found on Python's standard file object, opened in binary mode. (Basically, all of the methods that are sensible for a read-only file.)"], "gui.text_properties": ["internal", "function", "(kind=None, accent=False)", "", "", "Given a `kind` of button, returns a dictionary giving standard style properties for that button. This sets:\n\n:propref:`font` To gui.kind_text_font, if it exists.\n\n:propref:`size` To gui.kind_text_size, if it exists.\n\n:propref:`xalign` To gui.kind_text_xalign, if it exists.\n\n:propref:`text_align` To gui.kind_text_xalign, if it exists.\n\n:propref:`layout` To \\subtitle\\ if gui.kind_text_xalign is greater than zero and less than one.\n\nThere are also a number of variables that set the text :propref:`color` style property:\n\ncolor To gui.kind_text_color, if it exists. If the variable is not set, and `accent` is True, sets the text color to the default accent color.\n\ninsensitive_color To gui.kind_text_insensitive_color, if it exists.\n\nidle_color To gui.kind_text_idle_color, if it exists.\n\nhover_color To gui.kind_text_hover_color, if it exists.\n\nselected_color To gui.kind_text_selected_color, if it exists.\n\nAll other :ref:`text style properties ` are also available. For example, gui.kind_text_outlines sets the outlines style property, gui.kind_text_kerning sets kerning, and so on."], "Lexer.delimited_python": ["cds", "delimited_python", "(delim)", "Lexer", "method", "Matches a Python expression that ends in a `delim`, for example ':'. This is often used when you expect a condition until the delimiter. It is not recommended to change the result. The correct action is to evaluate the result in the future. This raises an error if end of line is reached before the delimiter."], "narrator": ["internal", "function", "()", "", "", ""], "Lexer.advance": ["cds", "advance", "()", "Lexer", "method", "In a subblock lexer, advances to the next line. This must be called before the first line, so the first line can be parsed. Returns True if we've successfully advanced to a line in the block, or False if we have advanced beyond all lines in the block."], "GamepadExists": ["internal", "function", "(developer=True)", "", "Action", "A function that returns true if a gamepad is present, and false otherwise.\n\n`developer` Forces this function to always return true while :var:`config.developer` is true."], "im.Grayscale": ["obsolete", "function", "(im, **properties)", "", "", "An image manipulator that creates a desaturated version of the image manipulator `im`.\n\nThe same effect can now be achieved by supplying SaturationMatrix(0) to the :tpref:`matrixcolor` transform property."], "preferences.afm_enable": ["preferences", "preferences.afm_enable", " = False", "", "var", "If True, auto-forward move is enabled, otherwise False. The equivalent of the \\auto-forward\\ preference."], "Function": ["internal", "class", "(callable, *args, **kwargs)", "", "Action", "This Action calls `callable` with `args` and `kwargs`.\n\n`callable` Callable object. `args` position arguments to be passed to `callable`. `kwargs` keyword arguments to be passed to `callable`.\n\nThis Action takes an optional _update_screens keyword argument, which defaults to true. When it is true, the interaction restarts and the screens are updated after the function returns.\n\nIf the function returns a non-None value, the interaction stops and returns that value. (When called using the call screen statement, the result is placed in the `_return` variable.)"], "updater.io": ["internal", "function", "()", "", "", "The io module provides the Python interfaces to stream handling. The builtin open function is defined in this module.\n\nAt the top of the I/O hierarchy is the abstract base class IOBase. It defines the basic interface to a stream. Note, however, that there is no separation between reading and writing to streams; implementations are allowed to raise an IOError if they do not support a given operation.\n\nExtending IOBase is RawIOBase which deals simply with the reading and writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide an interface to OS files.\n\nBufferedIOBase deals with buffering on a raw byte stream (RawIOBase). Its subclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer streams that are readable, writable, and both respectively. BufferedRandom provides a buffered interface to random access streams. BytesIO is a simple stream of in-memory bytes.\n\nAnother IOBase subclass, TextIOBase, deals with the encoding and decoding of streams into text. TextIOWrapper, which extends it, is a buffered text interface to a buffered raw stream (`BufferedIOBase`). Finally, StringIO is an in-memory stream for text.\n\nArgument names are not part of the specification, and only the arguments of open() are intended to be used as keyword arguments.\n\ndata:\n\nDEFAULT_BUFFER_SIZE\n\n An int containing the default buffer size used by the module's buffered I/O classes. open() uses the file's blksize (as obtained by os.stat) if possible."], "Lexer.string": ["cds", "string", "()", "Lexer", "method", "Matches a Ren'Py string."], "pushup": ["transitions", "pushup", "", "", "var", "Also: **pushleft, pushright, pushdown**\n\nThese use the new scene to slide the old scene out the named side. Instances of the :func:`PushMove` transition class."], "slideawayright": ["transitions", "slideawayright", "", "", "var", "Also: **slideawayleft, slideawayup, slideawaydown**\n\nSlides the old scene in the given direction. Instances of the :func:`CropMove` transition class."], "moveinbottom": ["transitions", "moveinbottom", "", "", "var", "Also: **moveinleft, moveintop, moveinright**\n\nThese move entering images onto the screen from the appropriate side, taking 0.5 seconds to do so."], "Lexer.renpy_statement": ["cds", "renpy_statement", "()", "Lexer", "method", "When called, this parses the current line as a Ren'Py script statement, generating an error if this is not possible. This method returns an opaque object that can be returned from get_next() or passed to :func:`renpy.jump` or :func:`renpy.call`. This object should not be stored except as part of the parse result of the statement.\n\nWhen the statement returned from this completes, control is transfered to the statement after the creator-defined statement. (Which might be the statement created using post_execute)."], "HistoryEntry.voice": ["history", "voice", "", "HistoryEntry", "attribute", "This is the object returned from :func:`_get_voice_info`, storing information about the voice that is being played."], "Placeholder": ["internal", "class", "(base=None, full=False, flip=None, **properties)", "", "", "Gets the child image to use."], "gui.nvl_thought_xalign": ["gui", "gui.nvl_thought_xalign", " = 0.5", "", "var", "The alignment of the text. This controls both the alignment of the text, and the side of the text that is placed at xpos. This can be 0.0 for left, 0.5 for center, and 1.0 for right."], "InvertMatrix": ["internal", "class", "(value=1.0)", "", "", "A ColorMatrix that can be used with :tpref:`matrixcolor` to invert each of the color channels. The alpha channel is left alone.\n\n`value` The amount to inverty by. 0.0 is not inverted, 1.0 is fully inverted. Used to animate inversion."], "easeinleft": ["transitions", "easeinleft", "", "", "var", "Also: **easeinright, ease, easeintop, easeinbottom, easeoutright, easeoutleft, easeouttop, easeoutbottom**\n\nThese are similar to the move- family of transitions, except that they use a cosine-based curve to slow down the start and end of the transition."], "gui.vslider_borders": ["gui", "gui.vslider_borders", " = Borders(6, 6, 6, 6)", "", "var", "The borders that are used with the Frame containing the bar image."], "gui.notify_frame_borders": ["gui", "gui.notify_frame_borders", " = Borders(24, 8, 60, 8)", "", "var", "The borders of the frame that is used by the notify screen."], "ui.BarValue": ["obsolete", "class", "()", "", "", "This can be passed to the value method of bar and hotbar."], "updater.Update": ["internal", "class", "(*args, **kwargs)", "", "Action", "An action that calls :func:`updater.update`. All arguments are stored and passed to that function."], "gui.name_ypos": ["gui", "gui.name_ypos", " = 0", "", "var", "The horizontal and vertical positions of the name and namebox. These are usually a number of pixels from the left or top side of the textbox. Setting a variable to 0.5 centers the name in the textbox (see below). These numbers can also be negative \u2013 for example, setting gui.name_ypos to -22 causes it to be places 22 pixels above the top of the textbox."], "eval": ["internal", "function", "(code, globals=None, locals=None)", "", "", ""], "MoveIn": ["internal", "function", "()", "", "", ""], "hyperlink_sensitive": ["internal", "function", "(target)", "", "", "Returns true of the hyperlink is sensitive, False otherwise."], "Dissolve": ["transitions", "function", "(time, alpha=False, time_warp=None)", "", "", "Returns a transition that dissolves from the old scene to the new scene.\n\n`time` The time the dissolve will take.\n\n`alpha` Ignored.\n\n`time_warp` A function that adjusts the timeline. If not None, this should be a function that takes a fractional time between 0.0 and 1.0, and returns a number in the same range."], "LiveComposite": ["obsolete", "function", "(size, *args, **properties)", "", "", "LiveComposite is now :func:`Composite`."], "hyperlink_styler": ["internal", "function", "(target)", "", "", ""], "AudioData": ["internal", "class", "(data, filename)", "", "", "This class wraps a bytes object containing audio data, so it can be passed to the audio playback system. The audio data should be contained in some format Ren'Py supports. (For examples RIFF WAV format headers, not unadorned samples.)\n\n`data` A bytes object containing the audio file data.\n\n`filename` A synthetic filename associated with this data. It can be used to suggest the format `data` is in, and is reported as part of error messages.\n\nOnce created, this can be used wherever an audio filename is allowed. For example\n```\ndefine audio.easteregg = AudioData(b'...', 'sample.wav')\nplay sound easteregg\n```"], "Color": ["internal", "class", "(color=None, hls=None, hsv=None, rgb=None, alpha=1.0)", "", "", "The Color class is used to represent and manipulate colors and convert between various color spaces. It also represents opacity in the form of an alpha.\n\nWhen creating a Color, at one of the `color`, `hls`, `hsv`, or `rgb` arguments should be supplied. (If all are None, None is returned.)\n\n`color` The color, in one of the standard formats Ren'Py understands. These are:\n\n* A Color object. * An (r, g, b) or (r, g, b, a) tuple, in which all the numbers are between 0 and 255. * A string giving a hexadecimal color, in the form \\#rgb\\, \\#rgba\\, \\#rrggbb\\, or \\#rrggbbaa\\.\n\n`hls` A color in the hue-lightness-saturation color space. This should be supplied a three-component tuple, where each component is between 0.0 and 1.0.\n\n`hsv` A color in the hue-saturation-value color space. This should be supplied a three-component tuple, where each component is between 0.0 and 1.0.\n\n`rgb` A color in the red-green-blue color space. This should be supplied a three-component tuple, where each component is between 0.0 and 1.0.\n\nIf the supplied color does not contain an alpha value, `alpha` is used. `alpha` must be between 0.0 and 1.0.\n\nColor objects can be used as 4-component tuples, where the components are (red, green, blue, and alpha). When used as a tuple, the value of each component is between 0 and 255.\n\nColor objects support the +, -, and * operators, representing component-wise addition, subtraction, and multiplication. Some uses of these operators can cause the creation of colors with components that are not in the supported range. Such colors should not be passed to other parts of Ren'Py. (The normalize method can be called to return a new color with the components limited to the proper range.)\n\nA Color object has the following properties:\n\n.. attribute:: hls\n\nReturns the color as a tuple of three floating point numbers giving hue, lightness, and saturation. Each component ranges between 0.0 and 1.0.\n\n.. attribute:: hsv\n\nReturns the color as a tuple of three floating point numbers giving hue, saturation, and value. Each component ranges between 0.0 and 1.0.\n\n.. attribute:: rgb\n\nReturns the color as a tuple of three floating point numbers giving the red, green, and blue components. Each component ranges between 0.0 and 1.0.\n\n.. attribute:: rgba\n\nReturns the color as a tuple of four floating point numbers giving the red, green, blue and alpha components as 0.0 to 1.0 values.\n\n.. attribute:: alpha\n\nReturns the alpha (opacity) of this Color as a number between 0.0 and 1.0, where 0.0 is transparent and 1.0 is opaque.\n\n.. attribute:: hexcode\n\nReturns a string containing a hex color code of the form #rrggbbaa or #rrggbb.\n\nColor objects have the following methods. Since Colors are immutable, these methods always return a new Color object."], "preferences.desktop_rollback_side": ["preferences", "preferences.desktop_rollback_side", " = \"disable\"", "", "var", "When on a desktop platform, touches or clicks to this side of the window cause rollback to occur. One of \\left\\, \\right\\, or \\disable\\. This is the equivalend of the \\rollback side\\ preference when on a desktop platform."], "Matrix.screen_projection": ["internal", "function", "()", "", "", "This generates a matrix that projects the Ren'Py space, where (0, 0) is the top left and (`w`, `h`) is the bottom right, into the OpenGL viewport, where (-1.0, 1.0) is the top left and (1.0, -1.0) is the bottom.\n\nGenerates the matrix that projects the Ren'Py screen to the OpenGL screen."], "InputValue.Disable": ["screen_python", "Disable", "()", "InputValue", "method", "Returns an action that disables text editing on the input."], "moveintop": ["transitions", "moveintop", "", "", "var", "Also: **moveinleft, moveinright, moveinbottom**\n\nThese move entering images onto the screen from the appropriate side, taking 0.5 seconds to do so."], "gui.history_text_xalign": ["gui", "gui.history_text_xalign", " = 0.5", "", "var", "This controls the alignment of text and the side of the text that is aligned with xpos. 0.0 is left-aligned, 0.5 is center-aligned, 1.0 is right-aligned."], "At": ["internal", "function", "(d, *args)", "", "", "Given a displayable `d`, applies each of the transforms in `args` to it. The transforms are applied in left-to-right order, so that the outermost transform is the rightmost argument.\n```\ntransform birds_transform:\n xpos -200\n linear 10 xpos 800\n pause 20\n repeat\n\nimage birds = At(\\birds.png\\, birds_transform)\n```"], "build.package": ["internal", "function", "(name, format, file_lists, description=None, update=True, dlc=False, hidden=False)", "", "", "Declares a package that can be built by the packaging tool.\n\n`name` The name of the package.\n\n`format` The format of the package. A string containing a space separated list of:\n\nzip A zip file. tar.bz2 A tar.bz2 file. directory A directory containing the files. dmg A Macintosh DMG containing the files. app-zip A zip file containing a macintosh application. app-directory A directory containing the mac app. app-dmg A macintosh drive image containing a dmg. (Mac only.)\n\nThe empty string will not build any package formats (this makes dlc possible).\n\n`file_lists` A list containing the file lists that will be contained within the package.\n\n`description` An optional description of the package to be built.\n\n`update` If true and updates are being built, an update will be built for this package.\n\n`dlc` If true, any zip or tar.bz2 file will be built in standalone DLC mode, without an update directory.\n\n`hidden` If true, this will be hidden from the list of packages in the launcher."], "gui.glyph_font": ["gui", "gui.glyph_font", " = \"DejaVuSans.ttf\"", "", "var", "A font used for certain glyphs, such as the arrow glyphs used by the skip indicator. DejaVuSans is a reasonable default for these glyphs, and is automatically included with every Ren'Py game."]}} \ No newline at end of file diff --git a/src/renpyauto.json b/src/renpyauto.json new file mode 100644 index 0000000..c2f0470 --- /dev/null +++ b/src/renpyauto.json @@ -0,0 +1,126 @@ +{ + "window" : "auto|hide|show", + "play" : "music|sound|voice|audio|{channels}", + "stop" : "music|sound|voice|audio|{channels}", + "queue" : "music|sound|voice|audio|{channels}", + "fadein" : "0.5|1.0|1.5|2.0", + "fadeout" : "0.5|1.0|1.5|2.0", + "volume" : "0.5|0.75|1.0", + "parent.play" : "fadeout|fadein|loop|noloop|if_changed|volume", + "parent.queue" : "loop|noloop|volume|fadein", + "parent.stop" : "fadeout", + "image.contains" : "{displayable}", + "screen.add" : "None|{displayable!q}", + "focus_mask" : "None|True|{displayable!q}", + "idle" : "None|{displayable!q}", + "hover" : "None|{displayable!q}", + "selected" : "None|{displayable!q}", + "insensitive" : "None|{displayable!q}", + "selected_idle" : "None|{displayable!q}", + "selected_hover" : "None|{displayable!q}", + "selected_insensitive" : "None|{displayable!q}", + "background" : "None|{displayable!q}", + "idle_background" : "idle_button.png", + "hover_background" : "None|{displayable!q}", + "selected_background" : "None|{displayable!q}", + "insensitive_background" : "None|{displayable!q}", + "selected_idle_background" : "None|{displayable!q}", + "selected_hover_background" : "None|{displayable!q}", + "selected_insensitive_background" : "None|{displayable!q}", + "foreground" : "None|{displayable!q}", + "idle_foreground" : "None|{displayable!q}", + "hover_foreground" : "None|{displayable!q}", + "selected_foreground" : "None|{displayable!q}", + "insensitive_foreground" : "None|{displayable!q}", + "selected_idle_foreground" : "None|{displayable!q}", + "selected_hover_foreground" : "None|{displayable!q}", + "selected_insensitive_foreground" : "None|{displayable!q}", + "xfill" : "True|False", + "yfill" : "True|False", + "fit" : "None|\"contain\"|\"cover\"|\"fill\"|\"scale-down\"|\"scale-up\"", + "fit_first" : "True|\"width\"|\"height\"", + "xfit" : "True|False", + "yfit" : "True|False", + "mipmap" : "True|False|None", + "antialias" : "True|False", + "bold" : "True|False", + "adjust_spacing" : "True|False|\"horizontal\"|\"vertical\"", + "italic" : "True|False", + "justify" : "True|False", + "newline_indent" : "True|False", + "slow_abortable" : "True|False", + "strikethrough" : "True|False", + "underline" : "True|False", + "vertical" : "True|False", + "outlines" : "{outlines}", + "text_outlines" : "{outlines}", + "what_outlines" : "{outlines}", + "who_outlines" : "{outlines}", + "text_antialias" : "True|False", + "text_bold" : "True|False", + "text_adjust_spacing" : "True|False|\"horizontal\"|\"vertical\"", + "text_italic" : "True|False", + "text_justify" : "True|False", + "text_newline_indent" : "True|False", + "text_slow_abortable" : "True|False", + "text_strikethrough" : "True|False", + "text_underline" : "True|False", + "text_vertical" : "True|False", + "text_align" : "0.0|0.5|1.0", + "modal" : "True|False", + "keyboard_focus" : "True|False", + "key_events" : "True|False", + "bar_vertical" : "True|False", + "bar_invert" : "True|False", + "bar_resizing" : "True|False", + "box_reverse" : "True|False", + "box_wrap" : "True|False", + "order_reverse" : "True|False", + "repeat" : "True|False", + "subpixel" : "True|False", + "matrixcolor" : "None|BrightnessMatrix|ColorizeMatrix|HueMatrix|IdentityMatrix|InvertMatrix|OpacityMatrix|SaturationMatrix|SepiaMatrix|TintMatrix", + "action" : "{action}", + "alternate" : "{action}", + "hovered" : "{action}", + "unhovered" : "{action}", + "unscrollable" : "None|\"insensitive\"|\"hide\"", + "vscrollbar_unscrollable" : "None|\"insensitive\"|\"hide\"", + "hscrollbar_unscrollable" : "None|\"insensitive\"|\"hide\"", + "alpha" : "0.0|0.25|0.5|0.75|1.0", + "zoom" : "0.5|0.75|1.0|1.5", + "pos" : "(0, 0)|(xpos, ypos)", + "anchor" : "(0, 0)|(xanchor, yanchor)", + "xanchor" : "0.0|0.5|1.0", + "yanchor" : "0.0|0.5|1.0", + "matrixanchor" : "(0.5, 0.5)|(xanchor, yanchor)", + "align" : "(0.0, 0.0)|(0.5, 0.0)|(1.0, 0.0)|(0.5, 0.5)|(0.0, 1.0)|(0.5, 1.0)|(1.0, 1.0)|(xalign, yalign)", + "xalign" : "0.0|0.5|1.0", + "yalign" : "0.0|0.5|1.0", + "offset" : "(0, 0)|(xoffset, yoffset)", + "maximum" : "(0, 0)|(maximum, ymaximum)", + "minimum" : "(0, 0)|(xminimum, yminimum)", + "xysize" : "(0, 0)|(xsize, ysize)", + "size" : "(0, 0)|(xsize, ysize)", + "area" : "(0, 0, 1, 1)|(xpos, ypos, width, height)", + "alt" : "\"string\"|None", + "layer" : "{layer}", + "onlayer" : "{layer}", + "screen" : "expression|{screens}", + "screen.use" : "{screens}", + "transform.on" : "show|hide|hover|idle|appear|selected_idle|selected_hover", + "screen.layer" : "{layer!q}", + "screen.on" : "\"show\"|\"hide\"|\"replace\"|\"replaced\"", + "screen.has" : "fixed|grid|hbox|side|vbox", + "variant" : "\"large\"|\"medium\"|\"small\"|\"tablet\"|\"phone\"|\"touch\"|\"tv\"|\"ouya\"|\"firetv\"|\"android\"|\"ios\"|\"mobile\"|\"pc\"|\"web\"|None", + "hover_sound" : "{audio}", + "activate_sound" : "{audio}", + "font" : "gui.text_font|gui.name_text_font|gui.interface_text_font|{fonts!q}", + "text_font" : "gui.text_font|gui.name_text_font|gui.interface_text_font|{fonts!q}", + "function" : "{function!3}", + "format_function" : "{function}", + "attribute_function" : "{function}", + "perspective" : "True|False|(0, 0, 0)", + "gl_depth" : "True|False", + "matrixtransform" : "None|OffsetMatrix|RotateMatrix|ScaleMatrix", + "zzoom" : "True|False" +} \ No newline at end of file diff --git a/src/semantics.ts b/src/semantics.ts new file mode 100644 index 0000000..0dfa43b --- /dev/null +++ b/src/semantics.ts @@ -0,0 +1,224 @@ +// Semantic Tokens +'use strict'; + +import { Position, Range, SemanticTokens, SemanticTokensBuilder, SemanticTokensLegend, TextDocument } from "vscode"; +import { Navigation, splitParameters, rangeAsString } from "./navigation"; +import { NavigationData } from "./navigationdata"; +import { stripWorkspaceFromFile } from "./workspace"; + +export function getSemanticTokens(document: TextDocument, legend: SemanticTokensLegend): SemanticTokens { + const tokensBuilder = new SemanticTokensBuilder(legend); + const rxKeywordList = /\s*(screen|label|transform|def)\s+/; + const rxParameterList = /\s*(screen|label|transform|def)\s+([a-zA-Z0-9_]+)\((.*)\):/s; + const filename = stripWorkspaceFromFile(document.uri.path); + let parent = ''; + let parent_line = 0; + let parent_type = ''; + let parent_args: string[] = []; + let parent_local: string[][] = []; + let parent_defaults: { [key: string]: Navigation } = {}; + let indent_level = 0; + let append_line = 0; + + for (let i = 0; i < document.lineCount; ++i) { + let line = document.lineAt(i).text; + + // check if we've outdented out of the parent block + if (line.length > 0 && line.length - line.trimLeft().length <= indent_level) { + parent = ''; + parent_args = []; + parent_local = []; + parent_defaults = {}; + parent_line = 0; + parent_type = ''; + } + + // if (line.indexOf('def trophy_count') > 0) { + // console.log('stop'); + // } + + append_line = i; + if (line.match(rxKeywordList)) { + // check for unterminated parenthesis for multiline declarations + let no_string = NavigationData.filterStringLiterals(line); + let open_count = (no_string.match(/\(/g)||[]).length; + let close_count = (no_string.match(/\)/g)||[]).length; + while (open_count > close_count && append_line < document.lineCount - 1) { + append_line++; + line = line + document.lineAt(append_line).text + '\n'; + no_string = NavigationData.filterStringLiterals(line); + open_count = (no_string.match(/\(/g)||[]).length; + close_count = (no_string.match(/\)/g)||[]).length; + } + } + + const matches = line.match(rxParameterList); + if (matches) { + // this line has a parameter list - tokenize the parameter ranges + if (matches[3] && matches[3].length > 0 && matches[2] !== '_') { + indent_level = line.length - line.trimLeft().length; + parent = matches[2]; + parent_type = matches[1]; + parent_line = i + 1; + let start = line.indexOf('(') + 1; + const split = splitParameters(matches[3], false); + for (let m of split) { + const offset = m.length - m.trimLeft().length; + let length = m.length; + if (m.indexOf('=') > 0) { + length = m.split('=')[0].trimRight().length; + } + const range = new Range(i, start + offset, i, start + length); + tokensBuilder.push(range, 'parameter', ['declaration']); + parent_args.push(line.substr(start + offset, length - offset)); + parent_defaults[m.substring(offset, length)] = new Navigation("parameter", m.substring(offset, length), filename, i + 1, "", m.trim(), "", start + offset); + // create a Navigation dictionary entry for this token range + const key = rangeAsString(filename, range); + const docs = `${parent_type} ${parent}()`; + const navigation = new Navigation("parameter", matches[1], filename, parent_line, docs, "", parent_type, start + offset); + NavigationData.gameObjects['semantic'][key] = navigation; + start += m.length + 1; + } + } else if (matches[1] === 'screen' || matches[1] === 'def') { + // parent screen or function def with no parameters + indent_level = line.length - line.trimLeft().length; + parent = matches[2]; + parent_type = matches[1]; + parent_line = i; + parent_args = []; + parent_local = []; + parent_defaults = {}; + } + } else if (parent !== '') { + // we are still inside a parent block + // check if this line has any tokens that are parameters + if (parent_args.length > 0) { + for (let a of parent_args) { + try { + const token = a.replace(/\./g, '\\.').replace(/\*/g, '\\*'); + const rx = RegExp(`[^a-zA-Z_](${token})($|[^a-zA-Z_])`, 'g'); + let matches; + while ((matches = rx.exec(line)) !== null) { + const offset = matches[0].indexOf(matches[1]); + let length = matches[1].length; + if (NavigationData.positionIsCleanForCompletion(line, new Position(i, matches.index + offset))) { + // push the token into the token builder + const range = new Range(i, matches.index + offset, i, matches.index + offset + length); + tokensBuilder.push(range, 'parameter'); + // create a Navigation dictionary entry for this token range + const key = rangeAsString(filename, range); + const docs = `${parent_type} ${parent}()`; + const parent_nav = parent_defaults[matches[1]]; + const navigation = new Navigation("parameter", matches[1], filename, parent_line, docs, "", parent_type, parent_nav.character); + NavigationData.gameObjects['semantic'][key] = navigation; + } + } + } catch (error) { + console.log(error); + } + } + } + // tokenize any local variables + if (parent_local.length > 0) { + for (let a of parent_local) { + try { + const token = a[0].replace(/\./g, '\\.').replace(/\*/g, '\\*'); + const rx = RegExp(`[^a-zA-Z_](${token})($|[^a-zA-Z_])`, 'g'); + let matches; + while ((matches = rx.exec(line)) !== null) { + const offset = matches[0].indexOf(matches[1]); + let length = matches[1].length; + if (NavigationData.positionIsCleanForCompletion(line, new Position(i, matches.index + offset))) { + // push the token into the token builder + const range = new Range(i, matches.index + offset, i, matches.index + offset + length); + tokensBuilder.push(range, 'variable'); + // create a Navigation dictionary entry for this token range + const key = rangeAsString(filename, range); + const parent_nav = parent_defaults[`${parent_type}.${parent}.${matches[1]}`]; + let nav_source = 'variable'; + if (a[1] === 'sv') { + nav_source = 'screen variable'; + } else if (a[1] === 'g') { + nav_source = 'global variable'; + } + const navigation = new Navigation(nav_source, matches[1], filename, parent_nav.location, parent_nav.documentation, "", parent_nav.type, parent_nav.character); + NavigationData.gameObjects['semantic'][key] = navigation; + } + } + } catch (error) { + console.log(error); + } + } + } + + // check if this line is a default and we're in a screen + // mark the token as a screen variable + if (parent_type === 'screen') { + const rxDefault = /^\s*(default)\s+(\w*)\s*=\s*([\w'"`\[{]*)/; + const matches = rxDefault.exec(line); + if (matches) { + parent_local.push([matches[2], 'sv']); + // push the token into the token builder + const offset = matches[0].indexOf(matches[2]); + const range = new Range(i, matches.index + offset, i, matches.index + offset + matches[2].length); + tokensBuilder.push(range, 'variable', ['declaration']); + // create a Navigation dictionary entry for this token range + const key = rangeAsString(filename, range); + const docs = `${parent_type} ${parent}()\n ${line.trim()}`; + const navigation = new Navigation("screen variable", matches[2], filename, i + 1, docs, "", parent_type, matches.index + offset); + NavigationData.gameObjects['semantic'][key] = navigation; + parent_defaults[`${parent_type}.${parent}.${matches[2]}`] = navigation; + } + } else if (parent_type === 'def') { + // check if this line is a global variable declaration in a function + // mark the token as a variable + const rxPatterns = [/^\s*(global)\s+(\w*)/g, /\s*(for)\s+([a-zA-Z_]+)\s+in\s+/g, /(\s*)([a-zA-Z_, ]+)\s+=[a-zA-Z_\s]/g]; + for (let rx of rxPatterns) { + let matches; + while ((matches = rx.exec(line)) !== null) { + try { + let start = line.indexOf(matches[2]); + const split = matches[2].split(','); + for (let m of split) { + const offset = m.length - m.trimLeft().length; + if (parent_args.includes(m.substr(offset))) { + continue; + } + + let length = m.length; + let source = 'variable'; + if (matches[1] === 'global') { + source = 'global variable'; + } + if (!parent_local.some(e => e[0] === m.substr(offset))) { + if (matches[1] === 'global') { + parent_local.push([m.substr(offset), 'g']); + } else { + parent_local.push([m.substr(offset), 'v']); + } + } else { + continue; + } + + // push the token into the token builder + const range = new Range(i, start + offset, i, start + length); + tokensBuilder.push(range, 'variable', ['declaration']); + // create a Navigation dictionary entry for this token range + const key = rangeAsString(filename, range); + const docs = `${parent_type} ${parent}()\n ${line.trim()}`; + const navigation = new Navigation(source, m.substr(offset), filename, i + 1, docs, "", parent_type, start + offset); + NavigationData.gameObjects['semantic'][key] = navigation; + parent_defaults[`${parent_type}.${parent}.${m.substr(offset)}`] = navigation; + start += m.length + 1; + } + } catch (error) { + console.log(error); + } + } + } + } + } + } + + return tokensBuilder.build(); +} \ No newline at end of file diff --git a/src/workspace.ts b/src/workspace.ts new file mode 100644 index 0000000..890e078 --- /dev/null +++ b/src/workspace.ts @@ -0,0 +1,122 @@ +// Workspace and file functions +'use strict'; + +import { Location, Position, TextDocument, workspace } from "vscode"; +import * as fs from 'fs'; +import { NavigationData } from "./navigationdata"; + +/** + * Returns the filename.extension for the given fully qualified path + * @param str - The full path and filename of the file + * @returns The filename.ext of the filepath + */ +export function extractFilename(str: string) { + if (str) { + str = str.replace(/\\/g, '/'); + return str.split('/').pop(); + } + return null; +} + +/** + * Returns the filename without the path and extension for the given fully qualified path + * @param str - The full path and filename of the file + * @returns The filename of the filepath + */ +export function extractFilenameWithoutExtension(str: string) { + if (str) { + str = str.replace(/\\/g, '/'); + let filename = str.split('/').pop(); + if (filename) { + return filename.replace(/\.[^/.]+$/, ''); + } + } + return null; +} + +/** + * Strips the workspace path from the file, leaving the path relative to the workspace plus filename (e.g., `game/script.rpy`) + * @param str - The full path and filename of the file + * @returns The filename of the filepath (e.g., `game/script.rpy`) + */ +export function stripWorkspaceFromFile(str: string) { + const wf = getWorkspaceFolder(); + + let filename = cleanUpPath(str); + if (filename.toLowerCase().startsWith(wf.toLowerCase())) { + filename = filename.substr(wf.length + 1); + } + + while (filename.startsWith('/')) { + filename = filename.substr(1); + } + return filename; +} + +/** + * Gets the workspace folder path (i.e., the Ren'Py base folder) + * @returns The path of the workspace (i.e., the Ren'Py base folder) + */ +export function getWorkspaceFolder() { + if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { + let wf = workspace.workspaceFolders[0].uri.path; + wf = cleanUpPath(wf); + return wf; + } + return ""; +} + +/** + * Gets the full path and filename of the file with invalid characters removed + * @remarks + * This removes the leading `/` character that appears before the path on Windows systems (e.g., `/c:/user/Documents/renpy/game/script.rpy`) + * @param path - The full path and filename of the file + * @returns The full path and filename of the file with invalid characters removed + */ +export function cleanUpPath(path: string): string { + if (path.startsWith('/') && path.substr(2, 2) === ":/") { + // windows is reporting the path as "/c:/xxx" + path = path.substr(1); + } + return path; +} + +/** + * Returns the filename path including the workspace folder + * @param filename - The filename + * @returns The filename path including the workspace folder + */ +export function getFileWithPath(filename: string) { + let wf = getWorkspaceFolder(); + if (wf && wf.length > 0) { + if (filename.startsWith(wf)) { + return filename; + } + let path = wf + '/game/' + filename; + if (!fs.existsSync(path)) { + path = wf + '/' + filename; + } + return path; + } else { + return filename; + } +} + +export function findReferenceMatches(keyword: string, document: TextDocument): Location[] { + let locations: Location[] = []; + const rx = RegExp(`[^a-zA-Z_](${keyword.replace('.','/.')})[^a-zA-Z_]`, 'g'); + + let index = 0; + while (index < document.lineCount) { + let line = NavigationData.filterStringLiterals(document.lineAt(index).text); + let matches = rx.exec(line); + if (matches) { + let position = new Position(index, matches.index); + const loc = new Location(document.uri, position); + locations.push(loc); + } + index++; + } + + return locations; +} \ No newline at end of file diff --git a/syntaxes/renpy.tmLanguage.json b/syntaxes/renpy.tmLanguage.json index a76e2af..7abedce 100644 --- a/syntaxes/renpy.tmLanguage.json +++ b/syntaxes/renpy.tmLanguage.json @@ -147,7 +147,7 @@ } }, { - "match": "^\\s*(screen)\\s+([a-zA-Z_][a-zA-Z_0-9]*)", + "match": "^\\s*(use)\\s+([a-zA-Z_][a-zA-Z_0-9]*)", "captures": { "1": { "name": "keyword.python.renpy" }, "2": { @@ -155,6 +155,60 @@ } } }, + { + "begin": "^\\s*(screen)\\s+(?=[A-Za-z_][A-Za-z0-9_]*\\s*\\()", + "beginCaptures": { + "1": { "name": "keyword.python.renpy" }, + "2": { + "name": "entity.name.class.python.renpy.screen.renpy" + } + }, + "end": "(\\))\\s*(?:(\\:)|(.*$\\n?))", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.python.renpy.screen.renpy" + }, + "2": { + "name": "punctuation.section.function.begin.python.renpy.screen.renpy" + }, + "3": { + "name": "invalid.illegal.missing-section-begin.python.renpy.screen.renpy" + } + }, + "name": "meta.function.python.renpy.screen.renpy", + "patterns": [ + { + "begin": "(?=[A-Za-z_][A-Za-z0-9_]*)", + "contentName": "entity.name.function.python.renpy.screen.renpy", + "end": "(?![A-Za-z0-9_])", + "patterns": [ { "include": "#entity_name_function" } ] + }, + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.parameters.begin.python.renpy.screen.renpy" + } + }, + "contentName": "meta.function.parameters.python.renpy.screen.renpy", + "end": "(?=\\)\\s*\\:)", + "patterns": [ + { "include": "#keyword_arguments" }, + { + "captures": { + "1": { + "name": "variable.parameter.function.python.renpy.screen.renpy" + }, + "2": { + "name": "punctuation.separator.parameters.python.renpy.screen.renpy" + } + }, + "match": "\\b([a-zA-Z_][a-zA-Z_0-9]*)\\s*(?:(,)|(?=[\\n\\)]))" + } + ] + } + ] + }, { "match": "^\\s*(image)\\s+([a-zA-Z_0-9 ]*)", "captures": {