diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 1dd680d8..5bce422e 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Test build and run tests +name: Build and test status on: push: @@ -24,8 +24,8 @@ jobs: python-version: "3.12" - name: Test by decompiling a script and building un.rpyc run: | - ./unrpyc.py --clobber testcases/script.rpyc - diff -u testcases/script.orig.rpy testcases/script.rpy + ./unrpyc.py --clobber --init-offset "testcases/compiled/**/*.rpyc" + diff -ur testcases/expected testcases/compiled -x "*.rpyc" cd un.rpyc; ./compile.py -p 1 cd .. diff --git a/.gitignore b/.gitignore index dc30ec79..43d103b4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ unrpyc.sublime-* !testcases/originals/**/*.rpy +!testcases/expected/**/*.rpy !testcases/compiled/**/*.rpyc diff --git a/testcases/.gitignore b/testcases/.gitignore deleted file mode 100644 index 8ae0154f..00000000 --- a/testcases/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -compiled/*.rpy -stripped/* - -!originals/*.rpy -!compiled/*.rpyc \ No newline at end of file diff --git a/testcases/README.md b/testcases/README.md new file mode 100644 index 00000000..0ef2b0f9 --- /dev/null +++ b/testcases/README.md @@ -0,0 +1,11 @@ +The folders in this folder contain the following items: + +* `originals`: contains several original .rpy files to be used in for testing the decompiler +* `compiled`: contains the compiled `.rpyc` files corresponding to the files in `originals` +* `expected`: contains the expected output of decompiling the `.rpyc` files to `.rpy` files + +The contents in `expected` have been manually verified to match `originals`, as `.rpyc` files unfortunately do not contain enough data to reconstruct the original file perfectly. + +To make this verification easier, a test script (`validate_expected.py`) has been provided that strips out comments and empty lines. Running it with the --update option will cause it to update the `expected` folder with decompiled `.rpy` files found in the `compiled` folder. + +Licenses for the files can be found in the corresponding `originals` folder for each dataset. \ No newline at end of file diff --git a/testcases/compiled/the_question-8.2/gui.rpyc b/testcases/compiled/the_question-8.2/gui.rpyc new file mode 100644 index 00000000..5f367d04 Binary files /dev/null and b/testcases/compiled/the_question-8.2/gui.rpyc differ diff --git a/testcases/compiled/the_question-8.2/options.rpyc b/testcases/compiled/the_question-8.2/options.rpyc new file mode 100644 index 00000000..093b517d Binary files /dev/null and b/testcases/compiled/the_question-8.2/options.rpyc differ diff --git a/testcases/compiled/the_question-8.2/screens.rpyc b/testcases/compiled/the_question-8.2/screens.rpyc new file mode 100644 index 00000000..d8c7fe0a Binary files /dev/null and b/testcases/compiled/the_question-8.2/screens.rpyc differ diff --git a/testcases/compiled/the_question-8.2/script.rpyc b/testcases/compiled/the_question-8.2/script.rpyc new file mode 100644 index 00000000..e552b7b8 Binary files /dev/null and b/testcases/compiled/the_question-8.2/script.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/01director_support.rpyc b/testcases/compiled/tutorial-8.2/01director_support.rpyc new file mode 100644 index 00000000..63139f2a Binary files /dev/null and b/testcases/compiled/tutorial-8.2/01director_support.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/01example.rpyc b/testcases/compiled/tutorial-8.2/01example.rpyc new file mode 100644 index 00000000..ddb3f686 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/01example.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/examples.rpyc b/testcases/compiled/tutorial-8.2/examples.rpyc new file mode 100644 index 00000000..4240b245 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/examples.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/gui.rpyc b/testcases/compiled/tutorial-8.2/gui.rpyc new file mode 100644 index 00000000..03a40380 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/gui.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_character.rpyc b/testcases/compiled/tutorial-8.2/indepth_character.rpyc new file mode 100644 index 00000000..0c2b00c1 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_character.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_displayables.rpyc b/testcases/compiled/tutorial-8.2/indepth_displayables.rpyc new file mode 100644 index 00000000..20ea6b73 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_displayables.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_minigame.rpyc b/testcases/compiled/tutorial-8.2/indepth_minigame.rpyc new file mode 100644 index 00000000..edd16767 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_minigame.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_style.rpyc b/testcases/compiled/tutorial-8.2/indepth_style.rpyc new file mode 100644 index 00000000..681824a4 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_style.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_text.rpyc b/testcases/compiled/tutorial-8.2/indepth_text.rpyc new file mode 100644 index 00000000..d81c6524 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_text.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_transitions.rpyc b/testcases/compiled/tutorial-8.2/indepth_transitions.rpyc new file mode 100644 index 00000000..a0abf437 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_transitions.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/indepth_translations.rpyc b/testcases/compiled/tutorial-8.2/indepth_translations.rpyc new file mode 100644 index 00000000..9850c4e8 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/indepth_translations.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/options.rpyc b/testcases/compiled/tutorial-8.2/options.rpyc new file mode 100644 index 00000000..9d6329a2 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/options.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/screens.rpyc b/testcases/compiled/tutorial-8.2/screens.rpyc new file mode 100644 index 00000000..b5d37009 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/screens.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/script.rpyc b/testcases/compiled/tutorial-8.2/script.rpyc new file mode 100644 index 00000000..1baebc9c Binary files /dev/null and b/testcases/compiled/tutorial-8.2/script.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/testcases.rpyc b/testcases/compiled/tutorial-8.2/testcases.rpyc new file mode 100644 index 00000000..3b741287 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/testcases.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_atl.rpyc b/testcases/compiled/tutorial-8.2/tutorial_atl.rpyc new file mode 100644 index 00000000..521ed981 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_atl.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_director.rpyc b/testcases/compiled/tutorial-8.2/tutorial_director.rpyc new file mode 100644 index 00000000..eccc105d Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_director.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_distribute.rpyc b/testcases/compiled/tutorial-8.2/tutorial_distribute.rpyc new file mode 100644 index 00000000..9527bd00 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_distribute.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_nvlmode.rpyc b/testcases/compiled/tutorial-8.2/tutorial_nvlmode.rpyc new file mode 100644 index 00000000..ab07eaa3 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_nvlmode.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_playing.rpyc b/testcases/compiled/tutorial-8.2/tutorial_playing.rpyc new file mode 100644 index 00000000..05b86d9e Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_playing.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_quickstart.rpyc b/testcases/compiled/tutorial-8.2/tutorial_quickstart.rpyc new file mode 100644 index 00000000..32f9fc48 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_quickstart.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_screen_displayables.rpyc b/testcases/compiled/tutorial-8.2/tutorial_screen_displayables.rpyc new file mode 100644 index 00000000..9b153662 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_screen_displayables.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_screens.rpyc b/testcases/compiled/tutorial-8.2/tutorial_screens.rpyc new file mode 100644 index 00000000..f3e66679 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_screens.rpyc differ diff --git a/testcases/compiled/tutorial-8.2/tutorial_video.rpyc b/testcases/compiled/tutorial-8.2/tutorial_video.rpyc new file mode 100644 index 00000000..9ec3f674 Binary files /dev/null and b/testcases/compiled/tutorial-8.2/tutorial_video.rpyc differ diff --git a/testcases/expected/the_question-8.2/gui.rpy b/testcases/expected/the_question-8.2/gui.rpy new file mode 100644 index 00000000..b76584af --- /dev/null +++ b/testcases/expected/the_question-8.2/gui.rpy @@ -0,0 +1,167 @@ +init offset = -2 +init python: + gui.init(1280, 720) +define gui.accent_color = '#cc6600' +define gui.idle_color = '#555555' +define gui.idle_small_color = '#aaaaaa' +define gui.hover_color = '#e0a366' +define gui.selected_color = '#ffffff' +define gui.insensitive_color = '#5555557f' +define gui.muted_color = '#512800' +define gui.hover_muted_color = '#7a3d00' +define gui.text_color = '#ffffff' +define gui.interface_text_color = '#ffffff' +define gui.text_font = "DejaVuSans.ttf" +define gui.name_text_font = "DejaVuSans.ttf" +define gui.interface_text_font = "DejaVuSans.ttf" +define gui.text_size = 22 +define gui.name_text_size = 30 +define gui.interface_text_size = 24 +define gui.label_text_size = 28 +define gui.notify_text_size = 16 +define gui.title_text_size = 50 +define gui.main_menu_background = "gui/main_menu.png" +define gui.game_menu_background = "gui/game_menu.png" +define gui.main_menu_text_color = "#ffaa22" +define gui.textbox_height = 185 +define gui.textbox_yalign = 1.0 +define gui.name_xpos = 240 +define gui.name_ypos = 0 +define gui.name_xalign = 0.0 +define gui.namebox_width = None +define gui.namebox_height = None +define gui.namebox_borders = Borders(5, 5, 5, 5) +define gui.namebox_tile = False +define gui.dialogue_xpos = 268 +define gui.dialogue_ypos = 50 +define gui.dialogue_width = 744 +define gui.dialogue_text_xalign = 0.0 +define gui.button_width = None +define gui.button_height = 36 +define gui.button_borders = Borders(4, 4, 4, 4) +define gui.button_tile = False +define gui.button_text_font = gui.interface_text_font +define gui.button_text_size = gui.interface_text_size +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 +define gui.button_text_xalign = 0.0 +define gui.radio_button_borders = Borders(25, 4, 4, 4) +define gui.check_button_borders = Borders(25, 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 +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.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 config.thumbnail_width = 256 +define config.thumbnail_height = 144 +define gui.file_slot_cols = 3 +define gui.file_slot_rows = 2 +define gui.navigation_xpos = 40 +define gui.skip_ypos = 10 +define gui.notify_ypos = 45 +define gui.choice_spacing = 22 +define gui.navigation_spacing = 4 +define gui.pref_spacing = 10 +define gui.pref_button_spacing = 0 +define gui.page_spacing = 0 +define gui.slot_spacing = 10 +define gui.main_menu_text_xalign = 0.0 +define gui.frame_borders = Borders(4, 4, 4, 4) +define gui.confirm_frame_borders = Borders(40, 40, 40, 40) +define gui.skip_frame_borders = Borders(16, 5, 50, 5) +define gui.notify_frame_borders = Borders(16, 5, 40, 5) +define gui.frame_tile = False +define gui.bar_size = 36 +define gui.scrollbar_size = 12 +define gui.slider_size = 30 +define gui.bar_tile = False +define gui.scrollbar_tile = False +define gui.slider_tile = False +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) +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) +define gui.unscrollable = "hide" +define config.history_length = 250 +define gui.history_height = 140 +define gui.history_name_xpos = 150 +define gui.history_name_ypos = 0 +define gui.history_name_width = 150 +define gui.history_name_xalign = 1.0 +define gui.history_text_xpos = 170 +define gui.history_text_ypos = 5 +define gui.history_text_width = 740 +define gui.history_text_xalign = 0.0 +define gui.nvl_borders = Borders(0, 10, 0, 20) +define gui.nvl_height = 115 +define gui.nvl_spacing = 10 +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 +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 +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 +define gui.nvl_button_xpos = 450 +define gui.nvl_button_xalign = 0.0 +define gui.language = "unicode" +init python: + @gui.variant + def touch(): + gui.quick_button_borders = Borders(40, 14, 40, 0) + @gui.variant + def small(): + gui.text_size = 30 + gui.name_text_size = 36 + gui.notify_text_size = 25 + gui.interface_text_size = 36 + gui.button_text_size = 34 + gui.label_text_size = 36 + gui.textbox_height = 240 + gui.name_xpos = 80 + gui.dialogue_xpos = 90 + gui.dialogue_width = 1100 + gui.choice_button_width = 1240 + gui.navigation_spacing = 20 + gui.pref_button_spacing = 10 + gui.history_height = 190 + gui.history_text_width = 690 + gui.file_slot_cols = 2 + gui.file_slot_rows = 2 + 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 + gui.quick_button_text_size = 20 +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/the_question-8.2/options.rpy b/testcases/expected/the_question-8.2/options.rpy new file mode 100644 index 00000000..e4ff03a0 --- /dev/null +++ b/testcases/expected/the_question-8.2/options.rpy @@ -0,0 +1,39 @@ +define config.name = _("The Question") +define gui.show_name = True +define config.version = "7.0" +define gui.about = _("""Character Art: Deji. +Original Character Art: derik. +Background Art: Mugenjohncel. +Original Background Art: DaFool +Music By: Alessio +Written By: mikey""") +define build.name = "the_question" +define build.version = "7.0" +define config.has_sound = True +define config.has_music = True +define config.has_voice = False +define config.enter_transition = dissolve +define config.exit_transition = dissolve +define config.after_load_transition = None +define config.end_game_transition = None +define config.window = "auto" +define config.window_show_transition = Dissolve(.2) +define config.window_hide_transition = Dissolve(.2) +default preferences.text_cps = 0 +default preferences.afm_time = 15 +define config.save_directory = "the_question-7" +define config.window_icon = "gui/window_icon.png" +init python: + config.searchpath.append(config.renpy_base + "/sdk-fonts") + build.classify_renpy("sdk-fonts/**", "all") + build._sdk_fonts = True + build.classify('**~', None) + build.classify('**.bak', None) + build.classify('**/.**', None) + build.classify('**/#**', None) + build.classify('**/thumbs.db', None) + build.documentation('*.html') + build.documentation('*.txt') +define build.itch_project = "renpytom/the-question" +define config.console = True +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/the_question-8.2/screens.rpy b/testcases/expected/the_question-8.2/screens.rpy new file mode 100644 index 00000000..323c4c19 --- /dev/null +++ b/testcases/expected/the_question-8.2/screens.rpy @@ -0,0 +1,896 @@ +init offset = -1 +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) +screen say(who, what): + style_prefix "say" + window: + id "window" + if who is not None: + window: + style "namebox" + text who id "who" + text what id "what" + if not renpy.variant("small"): + add SideImage() xalign 0.0 yalign 1.0 +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 + adjust_spacing False +screen input(prompt): + style_prefix "input" + window: + has 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 +screen choice(items): + style_prefix "choice" + vbox: + for i in items: + textbutton i.caption action i.action +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.text_properties("choice_button") +screen quick_menu(): + 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') +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.text_properties("quick_button") +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")): + textbutton _("Help") action ShowMenu("help") + if renpy.variant("pc"): + 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.text_properties("navigation_button") +screen main_menu(): + tag menu + style_prefix "main_menu" + add gui.main_menu_background + frame + use navigation + if gui.show_name: + vbox: + text "[config.name!t]": + style "main_menu_title" + text _("Ren'Py 7+ Edition"): + 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 + xsize 960 + 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") +screen game_menu(title, scroll=None): + style_prefix "game_menu" + if main_menu: + add gui.main_menu_background + else: + add gui.game_menu_background + frame: + style "game_menu_outer_frame" + has hbox + frame: + style "game_menu_navigation_frame" + frame: + style "game_menu_content_frame" + if scroll == "viewport": + viewport: + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + side_yfill True + has vbox + transclude + elif scroll == "vpgrid": + vpgrid: + cols 1 + yinitial 1.0 + 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 +screen about(): + tag menu + use game_menu(_("About"), scroll="viewport"): + style_prefix "about" + vbox: + label "[config.name!t]" + text _("[config.version!t]\n") + hbox: + spacing 15 + text _("Updated Character Art") style "about_small" + text _("Deji") + hbox: + spacing 15 + text _("Original Character Art") style "about_small" + text _("Derik") + null height 15 + hbox: + spacing 15 + text _("Updated Background Art") style "about_small" + text _("Mugenjohncel") + hbox: + spacing 15 + text _("Original Background Art") style "about_small" + text _("DaFool") + null height 15 + hbox: + spacing 15 + text _("Music By") style "about_small" + text _("Alessio") + null height 15 + hbox: + spacing 15 + text _("Update Written By") style "about_small" + text _("Lore") + hbox: + spacing 15 + text _("Originally Written By ") style "about_small" + text _("mikey (ATP Projects)") + text _("\nMade with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only]") + null height 15 + text _("[renpy.license!t]") size 20 +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 +style about_small: + size 20 + minwidth 260 + textalign 1.0 + yalign 0.9 +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: + order_reverse True + button: + style "page_label" + key_events True + xalign 0.5 + action page_name_value.Toggle() + input: + style "page_label_text" + value page_name_value + 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) + 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") + 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: + textalign 0.5 + layout "subtitle" + hover_color gui.hover_color +style page_button: + properties gui.button_properties("page_button") +style page_button_text: + properties gui.text_properties("page_button") +style slot_button: + properties gui.button_properties("slot_button") +style slot_button_text: + properties gui.text_properties("slot_button") +screen preferences(): + tag menu + if renpy.mobile: + $ cols = 2 + else: + $ cols = 4 + 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 "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")) + vbox: + style_prefix "radio" + label _("Language") + textbutton "English" text_font "DejaVuSans.ttf" action Language(None) + textbutton "Česky" text_font "DejaVuSans.ttf" action Language("czech") + textbutton "Dansk" text_font "DejaVuSans.ttf" action Language("danish") + textbutton "Français" text_font "DejaVuSans.ttf" action Language("french") + textbutton "Bahasa Melayu" text_font "DejaVuSans.ttf" action Language("malay") + textbutton "Русский" text_font "DejaVuSans.ttf" action Language("russian") + vbox: + style_prefix "radio" + label _(" ") + textbutton "Español" text_font "DejaVuSans.ttf" action Language("spanish") + textbutton "Українська" text_font "DejaVuSans.ttf" action Language("ukrainian") + textbutton "日本語" text_font "SourceHanSansLite.ttf" action Language("japanese") + textbutton "한국어" text_font "SourceHanSansLite.ttf" action Language("korean") + textbutton "简体中文" text_font "SourceHanSansLite.ttf" action Language("schinese") + textbutton "繁體中文" text_font "SourceHanSansLite.ttf" action Language("tchinese") + 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.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.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.text_properties("slider_button") +style slider_vbox: + xsize 450 +screen history(): + tag menu + predict False + use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport")): + style_prefix "history" + for h in _history_list: + window: + has fixed + yfit True + if h.who: + label h.who: + style "history_name" + substitute False + 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.") +define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } +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 + textalign 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 + textalign 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 +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}.") + hbox: + label "Shift+A" + text _("Opens the accessibility menu.") +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") + 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.text_properties("help_button") +style help_label: + xsize 250 + right_padding 20 +style help_label_text: + size gui.text_size + xalign 1.0 + textalign 1.0 +screen confirm(message, yes_action, no_action): + modal True + zorder 200 + style_prefix "confirm" + add "gui/overlay/confirm.png" + frame: + has 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 + 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: + textalign 0.5 + layout "subtitle" +style confirm_button: + properties gui.button_properties("confirm_button") +style confirm_button_text: + properties gui.text_properties("confirm_button") +screen skip_indicator(): + zorder 100 + style_prefix "skip" + frame: + has 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" +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: + font "DejaVuSans.ttf" +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") +screen nvl(dialogue, items=None): + window: + style "nvl_window" + has vbox + spacing gui.nvl_spacing + if gui.nvl_height: + vpgrid: + cols 1 + yinitial 1.0 + use nvl_dialogue(dialogue) + else: + use nvl_dialogue(dialogue) + 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 + has 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 +define config.nvl_list_length = 6 +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 + textalign 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 + textalign 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 + textalign 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.text_properties("nvl_button") +style pref_vbox: + variant "medium" + xsize 450 +screen quick_menu(): + variant "touch" + zorder 100 + 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 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 slider_pref_vbox: + variant "small" + xsize None +style slider_pref_slider: + variant "small" + xsize 600 +style main_menu_vbox: + variant "small" + xsize 900 +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/the_question-8.2/script.rpy b/testcases/expected/the_question-8.2/script.rpy new file mode 100644 index 00000000..f41df926 --- /dev/null +++ b/testcases/expected/the_question-8.2/script.rpy @@ -0,0 +1,130 @@ +define s = Character(_("Sylvie"), color="#c8ffc8") +define m = Character(_("Me"), color="#c8c8ff") +default book = False +label start: + play music "illurock.opus" + scene bg lecturehall + with fade + "It's only when I hear the sounds of shuffling feet and supplies being put away that I realize that the lecture's over." + "Professor Eileen's lectures are usually interesting, but today I just couldn't concentrate on it." + "I've had a lot of other thoughts on my mind...thoughts that culminate in a question." + "It's a question that I've been meaning to ask a certain someone." + scene bg uni + with fade + "When we come out of the university, I spot her right away." + show sylvie green normal + with dissolve + "I've known Sylvie since we were kids. She's got a big heart and she's always been a good friend to me." + "But recently... I've felt that I want something more." + "More than just talking, more than just walking home together when our classes end." + menu: + "As soon as she catches my eye, I decide..." + "To ask her right away.": + jump rightaway + "To ask her later.": + jump later +label rightaway: + show sylvie green smile + s "Hi there! How was class?" + m "Good..." + "I can't bring myself to admit that it all went in one ear and out the other." + m "Are you going home now? Wanna walk back with me?" + s "Sure!" + scene bg meadow + with fade + "After a short while, we reach the meadows just outside the neighborhood where we both live." + "It's a scenic view I've grown used to. Autumn is especially beautiful here." + "When we were children, we played in these meadows a lot, so they're full of memories." + m "Hey... Umm..." + show sylvie green smile + with dissolve + "She turns to me and smiles. She looks so welcoming that I feel my nervousness melt away." + "I'll ask her...!" + m "Ummm... Will you..." + m "Will you be my artist for a visual novel?" + show sylvie green surprised + "Silence." + "She looks so shocked that I begin to fear the worst. But then..." + show sylvie green smile + menu: + s "Sure, but what's a \"visual novel?\"" + "It's a videogame.": + jump game + "It's an interactive book.": + jump book +label game: + m "It's a kind of videogame you can play on your computer or a console." + m "Visual novels tell a story with pictures and music." + m "Sometimes, you also get to make choices that affect the outcome of the story." + s "So it's like those choose-your-adventure books?" + m "Exactly! I've got lots of different ideas that I think would work." + m "And I thought maybe you could help me...since I know how you like to draw." + m "It'd be hard for me to make a visual novel alone." + show sylvie green normal + s "Well, sure! I can try. I just hope I don't disappoint you." + m "You know you could never disappoint me, Sylvie." + jump marry +label book: + $ book = True + m "It's like an interactive book that you can read on a computer or a console." + show sylvie green surprised + s "Interactive?" + m "You can make choices that lead to different events and endings in the story." + s "So where does the \"visual\" part come in?" + m "Visual novels have pictures and even music, sound effects, and sometimes voice acting to go along with the text." + show sylvie green smile + s "I see! That certainly sounds like fun. I actually used to make webcomics way back when, so I've got lots of story ideas." + m "That's great! So...would you be interested in working with me as an artist?" + s "I'd love to!" + jump marry +label marry: + scene black + with dissolve + "And so, we become a visual novel creating duo." + scene bg club + with dissolve + "Over the years, we make lots of games and have a lot of fun making them." + if book: + "Our first game is based on one of Sylvie's ideas, but afterwards I get to come up with stories of my own, too." + "We take turns coming up with stories and characters and support each other to make some great games!" + "And one day..." + show sylvie blue normal + with dissolve + s "Hey..." + m "Yes?" + show sylvie blue giggle + s "Will you marry me?" + m "What? Where did this come from?" + show sylvie blue surprised + s "Come on, how long have we been dating?" + m "A while..." + show sylvie blue smile + s "These last few years we've been making visual novels together, spending time together, helping each other..." + s "I've gotten to know you and care about you better than anyone else. And I think the same goes for you, right?" + m "Sylvie..." + show sylvie blue giggle + s "But I know you're the indecisive type. If I held back, who knows when you'd propose?" + show sylvie blue normal + s "So will you marry me?" + m "Of course I will! I've actually been meaning to propose, honest!" + s "I know, I know." + m "I guess... I was too worried about timing. I wanted to ask the right question at the right time." + show sylvie blue giggle + s "You worry too much. If only this were a visual novel and I could pick an option to give you more courage!" + scene black + with dissolve + "We get married shortly after that." + "Our visual novel duo lives on even after we're married...and I try my best to be more decisive." + "Together, we live happily ever after even now." + "{b}Good Ending{/b}." + return +label later: + "I can't get up the nerve to ask right now. With a gulp, I decide to ask her later." + scene black + with dissolve + "But I'm an indecisive person." + "I couldn't ask her that day and I end up never being able to ask her." + "I guess I'll never know the answer to my question now..." + "{b}Bad Ending{/b}." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/01director_support.rpy b/testcases/expected/tutorial-8.2/01director_support.rpy new file mode 100644 index 00000000..99a32088 --- /dev/null +++ b/testcases/expected/tutorial-8.2/01director_support.rpy @@ -0,0 +1,12 @@ +default _director_enable = False +python early hide: + import shutil + fn1 = os.path.join(renpy.config.gamedir, "tutorial_director.rpym") + fn2 = os.path.join(renpy.config.gamedir, "tutorial_director.rpy") + try: + if not renpy.session.get("director", False): + shutil.copy(fn1, fn2) + store.director_readonly = False + except Exception: + store.director_readonly = True +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/01example.rpy b/testcases/expected/tutorial-8.2/01example.rpy new file mode 100644 index 00000000..dfdb321f --- /dev/null +++ b/testcases/expected/tutorial-8.2/01example.rpy @@ -0,0 +1,427 @@ +python early: + examples = { } + example_size = "small" + example_location = "top" + example_screen = None + example_transition = None + def reset_example(): + """ + Called to reset the example code to the defaults. + """ + global example_size + global example_location + global example_screen + example_size = "small" + example_location = "top" + example_screen = None + def show_example_screen(name): + global example_screen + example_screen = name + renpy.show_screen(name) + def hide_example_screen(): + global example_screen + if example_screen is not None: + renpy.hide_screen(example_screen) + example_screen = None + OUTDENT_KEYWORDS = { "define", "default", "image", "screen", "init", "transform", "label", "style" } + def read_example(name, fn, line, outdent): + """ + This reads an example from an example statement, and places it into + the examples dictionary. + """ + fn = fn.replace("game/", "") + with renpy.notl_file(fn) as f: + data = f.read().decode("utf-8") + rawlines = [ i.rstrip() for i in data.split("\n") ] + lines = [ ] + base_indent = 0 + while True: + if line >= len(rawlines): + raise Exception("Example open at end of {}.".format(fn)) + l = rawlines[line] + line += 1 + if not l: + lines.append(l) + continue + indent = len(l) - len(l.lstrip()) + if base_indent == 0: + base_indent = indent + lines.append(l[4:]) + elif indent >= base_indent: + lines.append(l[4:]) + else: + break + if outdent == "auto": + for i in lines: + l = i.strip().split() + if not l: + continue + if l[0] in OUTDENT_KEYWORDS: + outdent = True + else: + outdent = False + break + if outdent: + lines = [ i[base_indent - 4:] for i in lines ] + if name in examples: + examples[name].append('') + examples[name].extend(lines) + else: + examples[name] = lines + def parse_example(l): + """ + This parses the example statement. + """ + name = None + hide = False + bottom = False + small = False + top = False + large = False + outdent = "auto" + show_screen = True + hide_screen = True + showtrans = False + while True: + if l.match(':'): + break + elif l.keyword('hide'): + hide = True + elif l.keyword('bottom'): + bottom = True + elif l.keyword('small'): + small = True + elif l.keyword('top'): + top = True + elif l.keyword('large'): + large = True + elif l.keyword('outdent'): + outdent = True + elif l.keyword('nooutindent'): + outdent = False + elif l.keyword("noshow"): + show_screen = False + elif l.keyword("nohide"): + hide_screen = False + elif l.keyword("showtrans"): + showtrans = True + else: + if name: + l.error('an example may have at most one name') + name = l.require(l.name) + l.expect_eol() + if name is None: + name = "example_{}_{}".format(l.filename, l.number) + ll = l.subblock_lexer() + ll.advance() + if ll.keyword('screen'): + screen_name = ll.name() + else: + screen_name = None + return { + "name" : name, + "names" : [ name ], + "hide" : hide, + "bottom" : bottom, + "small" : small, + "filename" : l.filename, + "number" : l.number, + "top" : top, + "large" : large, + "outdent" : outdent, + "screen_name" : screen_name, + "show_screen" : show_screen, + "hide_screen" : hide_screen, + "showtrans" : showtrans, + } + def next_example(data, first): + return first + def execute_example(data): + names = data.get("names", [ ]) + hide = data.get("hide", False) + bottom = data.get("bottom", False) + small = data.get("small", False) + top = data.get("top", False) + large = data.get("large", False) + showtrans = data.get("showtrans", False) + global example_location + global example_size + if bottom: + example_location = "bottom" + elif top: + example_location = "top" + if small: + example_size = "small" + elif large: + example_size = "large" + if not hide: + renpy.show_screen("example", names, example_size == "small", example_location == "bottom", showtrans=showtrans) + screen_name = data.get("screen_name", None) + if screen_name is not None: + if data.get("hide_screen", True): + hide_example_screen() + if data.get("show_screen", True): + show_example_screen(screen_name) + renpy.with_statement(example_transition) + def execute_init_example(data): + read_example(data["name"], data["filename"], data["number"], data.get("outdent", "auto")) + def reachable_example(data, is_reachable, this, next, block): + if is_reachable: + return { this, next, block } + else: + return { True, block } + renpy.register_statement( + "example", + parse=parse_example, + execute=execute_example, + execute_init=execute_init_example, + next=next_example, + reachable=reachable_example, + block="script") + def parse_show_example(l): + names = [ ] + bottom = False + small = False + top = False + large = False + showtrans = False + while not l.eol(): + if l.keyword('bottom'): + bottom = True + elif l.keyword('small'): + small = True + elif l.keyword('top'): + top = True + elif l.keyword('large'): + large = True + elif l.keyword('showtrans'): + showtrans = True + else: + names.append(l.require(l.name)) + return { + "names" : names, + "hide" : False, + "bottom" : bottom, + "small" : small, + "top" : top, + "large" : large, + "showtrans" : showtrans, + } + renpy.register_statement("show example", parse=parse_show_example, execute=execute_example) + def parse_hide_example(l): + hide_screen = True + while not l.eol(): + if l.keyword('nohide'): + hide_screen = False + else: + break + l.expect_eol() + return { + "hide_screen" : hide_screen, + } + def execute_hide_example(data): + renpy.hide_screen("example") + if example_screen and data.get("hide_screen", True) and renpy.get_screen(example_screen): + hide_example_screen() + renpy.with_statement(example_transition) + renpy.register_statement("hide example", parse=parse_hide_example, execute=execute_hide_example) +default persistent.show_translation_marker = False +init python: + dialogue_map = { } + for i in renpy.known_languages(): + dialogue_map[i] = renpy.translation.dialogue.create_dialogue_map(i) + import re + import keywords + KEYWORDS = set(keywords.keywords) + PROPERTIES = set(keywords.properties) + regex = r"(?P\b(\$|[_a-zA-Z0-9]+)\b)" + \ + r"|(?P\"([^\"]|\\.)*(?#.*)" + regex = re.compile(regex) + def quote(s): + s = s.replace("{", "{{") + s = s.replace("[", "[[") + return s + def translate(m): + if m.group("string"): + s = eval(m.group(0)) + if __(s) != s: + s = __(s) + elif _preferences.language: + dm = dialogue_map[_preferences.language] + if s in dm: + s = dm[s] + quote = m.group(0)[0] + s = s.replace("\\", "\\\\") + s = s.replace(quote, "\\" + quote) + s = s.replace("\n", "\\n") + s = quote + s + quote + return s + return m.group(0) + def colorize(m): + if m.group("string"): + return "{color=#060}" + m.group(0) + "{/color}" + word = m.group("word") + if word: + if word in KEYWORDS: + return "{color=#840}" + m.group(0) + "{/color}" + elif word in PROPERTIES: + return "{color=#048}" + m.group(0) + "{/color}" + else: + return m.group(0) + if m.group("comment"): + return "{color=#600}" + m.group(0) + "{/color}" + return m.group(0) + def clean_example(lines): + rv = list(lines) + while rv and not rv[0]: + rv.pop(0) + while rv and not rv[-1]: + rv.pop(-1) + return rv + def example_code(blocks, raw=False, showtrans=False): + if not isinstance(blocks, list): + blocks = [ blocks ] + lines1 = [ ] + for i in blocks: + if i not in examples: + lines1.append('Example {} not found.'.format(i)) + else: + lines1.extend(clean_example(examples[i])) + lines1.append('') + last_blank = False + lines = [ ] + for i in lines1: + if not i and last_blank: + continue + last_blank = not i + i = regex.sub(translate, i) + if not (persistent.show_translation_marker or showtrans): + i = re.sub(r'__?\((".*?")\)', r'\1', i) + i = re.sub(r"__?\(('.*?')\)", r'\1', i) + i = i.replace("!t]", "]") + if not raw: + i = quote(i) + i = regex.sub(colorize, i) + lines.append(i) + while not lines[-1]: + lines.pop() + return "\n".join(lines) + "\n " + class CopyCode(Action): + def __init__(self, s): + self.s = s + def __call__(self): + import pygame.scrap + pygame.scrap.put(pygame.SCRAP_TEXT, self.s.encode("utf-8")) + renpy.notify(_("Copied the example to the clipboard.")) + example_transition = dissolve + import os + SHOW_EXAMPLES = ("RENPY_LESS_EXAMPLES" not in os.environ) +transform example_transform(height, ypos): + ypos ypos + yanchor 1.0 + xalign 0.5 + on replace: + crop (0, 0, 1280, height) + on show: + crop (0, 0, 1280, 0) + linear .5 crop (0, 0, 1280, height) + on hide: + linear .5 crop (0, 0, 1280, 0) +screen example(blocks, small=False, bottom=False, showtrans=False): + zorder 10 + default raw_code = example_code(blocks, raw=True, showtrans=showtrans) + default code = example_code(blocks, showtrans=showtrans) + if small: + $ height = 80 + else: + $ height = 160 + if bottom: + $ ypos = 720 + else: + $ ypos = 540 + if SHOW_EXAMPLES: + frame: + style "empty" + background "#fffc" + foreground Solid("#aaac", xsize=1, xpos=178) + xfill True + yfill True + ymaximum height + at example_transform(height, ypos) + viewport: + side_xmaximum 1098 + side_xpos 180 + child_size (2000, 2000) + ymaximum height + draggable True + mousewheel True + scrollbars "vertical" + vscrollbar_xsize 5 + vscrollbar_base_bar "#aaac" + vscrollbar_unscrollable "hide" + text code: + alt "" + size 16 + color "#000" + textbutton _("copy"): + style "empty" + text_style "quick_button_text" + text_textalign 0.5 + text_minwidth 180 + text_size 16 + action CopyCode(raw_code) +init python hide: + import os.path + import re + files = [ ] + for i in renpy.list_files(): + if i.endswith(".rpy"): + files.append(i) + for fn in files: + try: + with open(os.path.join(renpy.config.gamedir, fn), "r") as f: + lines = f.readlines() + except Exception: + lines = [ ] + open_examples = set() + for l in lines: + l = l.rstrip() + l = l.lstrip("\ufeff") + m = re.match("\s*#begin (\w+)", l) + if m: + example = m.group(1) + if example in examples: + raise Exception("Example %r is defined in two places.", example) + open_examples.add(example) + examples[example] = [ ] + continue + m = re.match("\s*#end (\w+)", l) + if m: + example = m.group(1) + if example not in open_examples: + raise Exception("Example %r is not open.", example) + open_examples.remove(example) + continue + for i in open_examples: + examples[i].append(l) + if open_examples: + raise Exception("Examples %r remain open at the end of %r" % (open_examples, fn)) +init python: + def lint_stats_callback(): + print("The game contains {} examples.".format(len(examples))) + config.lint_stats_callbacks.append(lint_stats_callback) + import base64 +image _finaleimage = im.Data(base64.b64decode(""" + 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 + """), "finale.png") +image _finale: + "_finaleimage" + yalign 0.3 + xanchor 1.0 xpos 0.0 + pause 90.0 + easein 1.5 xanchor 0.0 xpos .25 + pause 3.0 + easeout 1.5 xanchor 1.0 xpos 0.0 + repeat +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/examples.rpy b/testcases/expected/tutorial-8.2/examples.rpy new file mode 100644 index 00000000..8fa6ceb3 --- /dev/null +++ b/testcases/expected/tutorial-8.2/examples.rpy @@ -0,0 +1 @@ +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/gui.rpy b/testcases/expected/tutorial-8.2/gui.rpy new file mode 100644 index 00000000..44de1307 --- /dev/null +++ b/testcases/expected/tutorial-8.2/gui.rpy @@ -0,0 +1,166 @@ +init offset = -2 +init python: + gui.init(1280, 720) +define gui.accent_color = '#0099cc' +define gui.idle_color = '#888888' +define gui.idle_small_color = '#aaaaaa' +define gui.hover_color = '#66c1e0' +define gui.selected_color = '#ffffff' +define gui.insensitive_color = '#5555557f' +define gui.muted_color = '#003d51' +define gui.hover_muted_color = '#005b7a' +define gui.text_color = '#ffffff' +define gui.interface_text_color = '#ffffff' +define gui.text_font = "DejaVuSans.ttf" +define gui.name_text_font = "DejaVuSans.ttf" +define gui.interface_text_font = "DejaVuSans.ttf" +define gui.text_size = 22 +define gui.name_text_size = 30 +define gui.interface_text_size = 24 +define gui.label_text_size = 28 +define gui.notify_text_size = 16 +define gui.title_text_size = 50 +define gui.main_menu_background = "gui/main_menu.jpg" +define gui.game_menu_background = "images/bg washington.jpg" +define gui.textbox_height = 185 +define gui.textbox_yalign = 1.0 +define gui.name_xpos = 240 +define gui.name_ypos = 0 +define gui.name_xalign = 0.0 +define gui.namebox_width = None +define gui.namebox_height = None +define gui.namebox_borders = Borders(5, 5, 5, 5) +define gui.namebox_tile = False +define gui.dialogue_xpos = 268 +define gui.dialogue_ypos = 50 +define gui.dialogue_width = 744 +define gui.dialogue_text_xalign = 0.0 +define gui.button_width = None +define gui.button_height = None +define gui.button_borders = Borders(4, 4, 4, 4) +define gui.button_tile = False +define gui.button_text_font = gui.interface_text_font +define gui.button_text_size = gui.interface_text_size +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 +define gui.button_text_xalign = 0.0 +define gui.radio_button_borders = Borders(25, 4, 4, 4) +define gui.check_button_borders = Borders(25, 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 +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.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 config.thumbnail_width = 256 +define config.thumbnail_height = 144 +define gui.file_slot_cols = 3 +define gui.file_slot_rows = 2 +define gui.navigation_xpos = 40 +define gui.skip_ypos = 10 +define gui.notify_ypos = 45 +define gui.choice_spacing = 8 +define gui.navigation_spacing = 4 +define gui.pref_spacing = 10 +define gui.pref_button_spacing = 0 +define gui.page_spacing = 0 +define gui.slot_spacing = 10 +define gui.main_menu_text_xalign = 0.0 +define gui.frame_borders = Borders(20, 8, 20, 8) +define gui.confirm_frame_borders = Borders(40, 40, 40, 40) +define gui.skip_frame_borders = Borders(16, 5, 50, 5) +define gui.notify_frame_borders = Borders(16, 5, 40, 5) +define gui.frame_tile = False +define gui.bar_size = 36 +define gui.scrollbar_size = 12 +define gui.slider_size = 30 +define gui.bar_tile = False +define gui.scrollbar_tile = False +define gui.slider_tile = False +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) +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) +define gui.unscrollable = "hide" +define config.history_length = 250 +define gui.history_height = 140 +define gui.history_name_xpos = 150 +define gui.history_name_ypos = 0 +define gui.history_name_width = 150 +define gui.history_name_xalign = 1.0 +define gui.history_text_xpos = 170 +define gui.history_text_ypos = 5 +define gui.history_text_width = 740 +define gui.history_text_xalign = 0.0 +define gui.nvl_borders = Borders(0, 10, 0, 20) +define gui.nvl_height = 115 +define gui.nvl_spacing = 10 +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 +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 +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 +define gui.nvl_button_xpos = 450 +define gui.nvl_button_xalign = 0.0 +define gui.language = "unicode" +init python: + @gui.variant + def touch(): + gui.quick_button_borders = Borders(40, 14, 40, 0) + @gui.variant + def small(): + gui.text_size = 30 + gui.name_text_size = 36 + gui.notify_text_size = 25 + gui.interface_text_size = 36 + gui.button_text_size = 34 + gui.label_text_size = 36 + gui.textbox_height = 240 + gui.name_xpos = 80 + gui.dialogue_xpos = 90 + gui.dialogue_width = 1100 + gui.choice_button_width = 1240 + gui.navigation_spacing = 20 + gui.pref_button_spacing = 10 + gui.history_height = 190 + gui.history_text_width = 690 + gui.file_slot_cols = 2 + gui.file_slot_rows = 2 + 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 + gui.quick_button_text_size = 20 +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_character.rpy b/testcases/expected/tutorial-8.2/indepth_character.rpy new file mode 100644 index 00000000..4669eb4b --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_character.rpy @@ -0,0 +1,89 @@ +init python: + config.searchpath.append("../launcher/game/fonts") +label demo_character: + show example characters + e "We've already seen how to define a Character in Ren'Py. But I want to go into a bit more detail as to what a Character is." + example: + define e_shout = Character("Eileen", who_color="#c8ffc8", what_size=34) + define e_whisper = Character("Eileen", who_color="#c8ffc8", what_size=18) + e "Here are couple of additional characters." + e "Each statement creates a Character object, and gives it a single argument, a name. If the name is None, no name is displayed." + e "This can be followed by named arguments that set properties of the character. A named argument is a property name, an equals sign, and a value." + e "Multiple arguments should be separated with commas, like they are here. Let's see those characters in action." + example: + e_shout "I can shout!" + e_whisper "And I can speak in a whisper." + e "This example shows how the name Character is a bit of a misnomer. Here, we have multiple Characters in use, but you see it as me speaking." + e "It's best to think of a Character as repesenting a name and style, rather than a single person." + hide example + e "There are a lot of properties that can be given to Characters, most of them prefixed styles." + e "Properties beginning with window apply to the textbox, those with what apply to the the dialogue, and those with who to the name of Character speaking." + e "If you leave a prefix out, the style customizes the name of the speaker." + e "There are quite a few different properties that can be set this way. Here are some of the most useful." + example: + define e1 = Character("Eileen", window_background="gui/startextbox.png") + e1 "The window_background property sets the image that's used for the background of the textbox, which should be the same size as the default in gui/textbox.png." + example: + define e1a = Character("Eileen", window_background=None) + e1a "If it's set to None, the textbox has no background window." + example: + define e2 = Character("Eileen", who_color="#c8ffc8", what_color="#ffc8c8") + e2 "The who_color and what_color properties set the color of the character's name and dialogue text, respectively." + e2 "The colors are strings containing rgb hex codes, the same sort of colors understood by a web browser." + example: + define e3 = Character("Eileen", who_font="Roboto-Regular.ttf", what_font="Roboto-Light.ttf") + e3 "Similarly, the who_font and what_font properties set the font used by the different kinds of text." + example: + define e4 = Character("Eileen", who_bold=True, what_italic=True, what_size=20) + e4 "Setting the who_bold, what_italic, and what_size properties makes the name bold, and the dialogue text italic at a size of 20 pixels." + e4 "Of course, the what_bold, who_italic and who_size properties also exist, even if they're not used here." + example: + define e5 = Character("Eileen", what_outlines=[( 1, "#008000", 0, 0 )] ) + e5 "The what_outlines property puts an outline around the text." + e5 "It's a little complicated since it takes a list with a tuple in it, with the tuple being four things in parenthesis, and the list the square brackets around them." + e5 "The first number is the size of the outline, in pixels. That's followed by a string giving the hex-code of the color of the outline, and the x and y offsets." + example: + define e6 = Character("Eileen", what_outlines=[( 0, "#808080", 2, 2 )] ) + e6 "When the outline size is 0 and the offsets are given, what_outlines can also act as a drop-shadow behind the text." + example: + define e7 = Character("Eileen", what_xalign=0.5, what_textalign=0.5, what_layout='subtitle') + e7 "The what_xalign and what_textalign properties control the alignment of text, with 0.0 being left, 0.5 being center, and 1.0 being right." + e7 "The what_xalign property controls where all the text itself is placed within the textbox, while what_textalign controls where rows of text are placed relative to each other." + e7 "Generally you'll want to to set them both what_xalign and what_textalign to the same value." + e7 "Setting what_layout to 'subtitle' puts Ren'Py in subtitle mode, which tries to even out the length of every line of text in a block." + hide example + e8 "These properties can be combined to achieve many different effects." + example large: + define e8 = Character( + None, + window_background = None, + what_size=28, + what_outlines=[( 1, "#008000", 0, 0 )], + what_xalign=0.5, + what_textalign=0.5, + what_layout='subtitle') + e8 "This example hides the background and shows dialogue centered and outlined, as if the game is being subtitled." + hide example + with dissolve + example small: + define e9 = Character("Eileen", what_prefix='"', what_suffix='"') + e9 "There are two interesting non-style properties, what_prefix and what_suffix. These can put text at the start and end of a line of dialogue." + example: + define l8 = Character(kind=e8, what_outlines=[( 1, "#c00000", 0, 0 )] ) + e "By using kind, you can copy properties from one character to another, changing only what you need to." + hide example + with dissolve + scene bg cave + show lucy happy + with slideleft + l8 "Like this! Finally I get some more dialogue around here." + scene bg washington + show eileen happy + with slideawayleft + example: + define narrator = Character(what_italic=True) + e "The last thing you have to know is that there's a special character, narrator, that speaks narration. Got it?" + "I think I do." + hide example + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_displayables.rpy b/testcases/expected/tutorial-8.2/indepth_displayables.rpy new file mode 100644 index 00000000..d2cfa915 --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_displayables.rpy @@ -0,0 +1,89 @@ +image logo blink: + "logo base" + pause 0.5 + linear .5 alpha 0.0 + pause 0.5 + linear .5 alpha 1.0 + repeat +transform logopos: + xalign .5 + ypos 50 +label simple_displayables: + e "Ren'Py has the concept of a displayable, which is something like an image that can be shown and hidden." + example: + image logo base = "logo base.png" + show logo base at logopos + e "The image statement is used to give an image name to a displayable. The easy way is to simply give an image filename." + example: + image logo alias = "logo base" + show logo alias at logopos + e "But that's not the only thing that an image can refer to. When the string doesn't have a dot in it, Ren'Py interprets that as a reference to a second image." + hide logo with dissolve + example: + image bg red = "#c00" + image bg blue = "#0000cc" + image overlay red = "#c008" + image overlay blue = "#0000cc88" + show bg blue with dissolve + e "The string can also contain a color code, consisting of hexadecimal digits, just like the colors used by web browsers." + e "Three or six digit colors are opaque, containing red, green, and blue values. The four and eight digit versions append alpha, allowing translucent colors." + show bg washington with dissolve + example: + image logo rotated = Transform("logo base", rotate=45) + show logo rotated at logopos + with dissolve + e "The Transform displayable takes a displayable and can apply transform properties to it." + e "Notice how, since it takes a displayable, it can take another image. In fact, it can take any displayable defined here." + example: + image logo solidexample = Solid("#0000cc", xysize=(200, 200)) + show logo solidexample at logopos + with dissolve + e "There's a more complete form of Solid, that can take style properties. This lets us change the size of the Solid, where normally it fills the screen." + example: + image logo text = Text(_("This is a text displayable."), size=30) + show logo text at logopos + with dissolve + e "The Text displayable lets Ren'Py treat text as if it was an image." + example: + image logo text rotate = Transform(Text(_("This is a text displayable."), size=30), rotate=45) + show logo text rotate at logopos + with dissolve + e "This means that we can apply other displayables, like Transform, to Text in the same way we do to images." + example: + image logo composite = Composite((240, 460), + (0, 0), "logo blink", + (0, 50), "logo base.png", + (0, 100), "logo base.png") + show logo composite at logopos + with dissolve + e "The Composite displayable lets us group multiple displayables together into a single one, from bottom to top." + hide logo + example: + image ninepatch frame = Frame("ninepatch", 40, 40, 40, 40) + show ninepatch frame at logopos: + size (120, 120) + e "Some displayables are often used to customize the Ren'Py interface, with the Frame displayable being one of them. The frame displayable takes another displayable, and the size of the left, top, right, and bottom borders." + show ninepatch frame at logopos: + size (120, 120) + linear 3.0 size (360, 360) + pause 1.0 + linear 3.0 size (120, 120) + pause 1.0 + repeat + e "The Frame displayable expands or shrinks to fit the area available to it. It does this by scaling the center in two dimensions and the sides in one, while keeping the corners the same size." + example: + image ninepatch frame tiled = Frame("ninepatch", 40, 40, 40, 40, tile=True) + show ninepatch frame tiled + e "A Frame can also tile sections of the displayable supplied to it, rather than scaling." + example: + image ninepatch paper tiled = Frame("ninepatch paper", 40, 40, 40, 40, tile=True) + show ninepatch paper tiled + with dissolve + e "Frames might look a little weird in the abstract, but when used with a texture, you can see how we create scalable interface components." + hide ninepatch + hide example + with dissolve + e "These are just the simplest displayables, the ones you'll use directly the most often." + e "You can even write custom displayables for minigames, if you're proficient at Python. But for many visual novels, these will be all you'll need." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_minigame.rpy b/testcases/expected/tutorial-8.2/indepth_minigame.rpy new file mode 100644 index 00000000..d5ccaf05 --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_minigame.rpy @@ -0,0 +1,155 @@ +example minigame: + init python: + class PongDisplayable(renpy.Displayable): + def __init__(self): + renpy.Displayable.__init__(self) + self.PADDLE_WIDTH = 12 + self.PADDLE_HEIGHT = 95 + self.PADDLE_X = 240 + self.BALL_WIDTH = 15 + self.BALL_HEIGHT = 15 + self.COURT_TOP = 129 + self.COURT_BOTTOM = 650 + self.paddle = Solid("#ffffff", xsize=self.PADDLE_WIDTH, ysize=self.PADDLE_HEIGHT) + self.ball = Solid("#ffffff", xsize=self.BALL_WIDTH, ysize=self.BALL_HEIGHT) + self.stuck = True + self.playery = (self.COURT_BOTTOM - self.COURT_TOP) / 2 + self.computery = self.playery + self.computerspeed = 380.0 + self.bx = self.PADDLE_X + self.PADDLE_WIDTH + 10 + self.by = self.playery + self.bdx = .5 + self.bdy = .5 + self.bspeed = 350.0 + self.oldst = None + self.winner = None + def visit(self): + return [ self.paddle, self.ball ] + def render(self, width, height, st, at): + r = renpy.Render(width, height) + if self.oldst is None: + self.oldst = st + dtime = st - self.oldst + self.oldst = st + speed = dtime * self.bspeed + oldbx = self.bx + if self.stuck: + self.by = self.playery + else: + self.bx += self.bdx * speed + self.by += self.bdy * speed + cspeed = self.computerspeed * dtime + if abs(self.by - self.computery) <= cspeed: + self.computery = self.by + else: + self.computery += cspeed * (self.by - self.computery) / abs(self.by - self.computery) + ball_top = self.COURT_TOP + self.BALL_HEIGHT / 2 + if self.by < ball_top: + self.by = ball_top + (ball_top - self.by) + self.bdy = -self.bdy + if not self.stuck: + renpy.sound.play("pong_beep.opus", channel=0) + ball_bot = self.COURT_BOTTOM - self.BALL_HEIGHT / 2 + if self.by > ball_bot: + self.by = ball_bot - (self.by - ball_bot) + self.bdy = -self.bdy + if not self.stuck: + renpy.sound.play("pong_beep.opus", channel=0) + def paddle(px, py, hotside): + pi = renpy.render(self.paddle, width, height, st, at) + r.blit(pi, (int(px), int(py - self.PADDLE_HEIGHT / 2))) + if py - self.PADDLE_HEIGHT / 2 <= self.by <= py + self.PADDLE_HEIGHT / 2: + hit = False + if oldbx >= hotside >= self.bx: + self.bx = hotside + (hotside - self.bx) + self.bdx = -self.bdx + hit = True + elif oldbx <= hotside <= self.bx: + self.bx = hotside - (self.bx - hotside) + self.bdx = -self.bdx + hit = True + if hit: + renpy.sound.play("pong_boop.opus", channel=1) + self.bspeed *= 1.10 + paddle(self.PADDLE_X, self.playery, self.PADDLE_X + self.PADDLE_WIDTH) + paddle(width - self.PADDLE_X - self.PADDLE_WIDTH, self.computery, width - self.PADDLE_X - self.PADDLE_WIDTH) + ball = renpy.render(self.ball, width, height, st, at) + r.blit(ball, (int(self.bx - self.BALL_WIDTH / 2), + int(self.by - self.BALL_HEIGHT / 2))) + if self.bx < -50: + self.winner = "eileen" + renpy.timeout(0) + elif self.bx > width + 50: + self.winner = "player" + renpy.timeout(0) + renpy.redraw(self, 0) + return r + def event(self, ev, x, y, st): + import pygame + if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1: + self.stuck = False + renpy.restart_interaction() + y = max(y, self.COURT_TOP) + y = min(y, self.COURT_BOTTOM) + self.playery = y + if self.winner: + return self.winner + else: + raise renpy.IgnoreEvent() + screen pong(): + default pong = PongDisplayable() + add "bg pong field" + add pong + text _("Player"): + xpos 240 + xanchor 0.5 + ypos 25 + size 40 + text _("Eileen"): + xpos (1280 - 240) + xanchor 0.5 + ypos 25 + size 40 + if pong.stuck: + text _("Click to Begin"): + xalign 0.5 + ypos 50 + size 40 +label demo_minigame: + e "You may want to mix Ren'Py with other forms of gameplay. There are a couple of ways to do this." + e "The first is with the screen system, which can be used to display data and create button and menu based interfaces." + e "Screens will work for many simulation-style games and RPGs." + e "When screens are not enough, you can write a creator-defined displayable to extend Ren'Py itself. A creator-defined displayable can process raw events and draw to the screen." id demo_minigame_a92baa6b + e "That makes it possible to create all kinds of minigames. Would you like to play some pong?" +example minigame hide: + label play_pong: + window hide + $ quick_menu = False + call screen pong + $ quick_menu = True + window show + show eileen vhappy + if _return == "eileen": + e "I win!" + else: + e "You won! Congratulations." +label pong_done: + show eileen happy + menu: + e "Would you like to play again?" + "Sure.": + jump play_pong + "No thanks.": + pass + show example minigame large + e "Here's the source code for the minigame. It's very complex, and assumes you understand Python well." + e "I won't go over it in detail here. You can read more about it in the {a=https://www.renpy.org/doc/html/udd.html}Creator-Defined Displayable documentation{/a}." + hide example + e "Minigames can spice up your visual novel, but be careful – not every visual novel player wants to be good at arcade games." + e "Part of the reason Ren'Py works well is that it's meant for certain types of games, like visual novels and life simulations." + e "The further afield you get from those games, the more you'll find yourself fighting Ren'Py. At some point, it makes sense to consider other engines." + show eileen vhappy + e "And that's fine with us. We'll always be here for you when you're making visual novels." + show eileen happy + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_style.rpy b/testcases/expected/tutorial-8.2/indepth_style.rpy new file mode 100644 index 00000000..bc1ae269 --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_style.rpy @@ -0,0 +1,694 @@ +transform examplepos: + xalign 0.5 + ypos 50 +screen style0(): + frame: + at examplepos + xpadding 20 + ypadding 20 + has vbox + spacing 10 + textbutton _("Button 1") action Return(True) + textbutton _("Button 2"): + style "empty" + text_style "empty" + xminimum 240 + ypadding 2 + background RoundRect("#003c78") + hover_background RoundRect("#0050a0") + text_color "#c8ffff" + text_xalign 0.5 + action Return(True) +label new_gui: + e "When you create a new project, Ren'Py will automatically create a GUI - a Graphical User Interface - for it." + e "It defines the look of both in-game interface, like this text box, and out-of-game interface like the main and game menus." + e "The default GUI is meant to be nice enough for a simple project. With a few small changes, it's what you're seeing in this game." + e "The GUI is also meant to be easy for an intermediate creator to customize. Customizing the GUI consists of changing the image files in the gui directory, and changing variables in gui.rpy." + e "At the same time, even when customized, the default GUI might be too recognizable for an extremely polished game. That's why we've made it easy to totally replace." + e "We've put an extensive guide to customizing the GUI on the Ren'Py website. So if you want to learn more, visit the {a=https://www.renpy.org/doc/html/gui.html}GUI customization guide{/a}." + return +label styles: + show eileen happy at left + e "Ren'Py has a powerful style system that controls what displayables look like." + e "While the default GUI uses variables to provide styles with sensible defaults, if you're replacing the GUI or creating your own screens, you'll need to learn about styles yourself." +label styles_menu: + $ reset_example() + menu: + e "What would you like to know about styles?" + "Style basics.": + call style_basics from _call_style_basics + "General style properties.": + call style_general from _call_style_general + "Text style properties.": + call style_text from _call_style_text + "Window and Button style properties.": + call style_button from _call_style_button + "Bar style properties.": + call style_bar from _call_style_bar + "Box, Grid, and Fixed style properties.": + call style_box from _call_style_box + "The Displayable Inspector.": + call style_inspector from _call_style_inspector + "That's all I want to know.": + return + jump styles_menu +label style_basics: + e "Styles let a displayable look different from game to game, or even inside the same game." + show screen style0 + with dissolve + e "Both of these buttons use the same displayables. But since different styles have been applied, the buttons look different from each other." + hide screen style0 + with dissolve + e "Styles are a combination of information from four different places." + example: + screen style1(): + text _("This text is colored green."): + style "my_text" + color "#c0ffc0" + at examplepos + show screen style1 + with dissolve + e "The first place Ren'Py can get style information from is part of a screen. Each displayable created by a screen can take a style name and style properties." + example: + screen textstyle(): + frame: + textbutton _("Danger"): + text_color "#c04040" + text_hover_color "#ff0000" + action Return(True) + at examplepos + hide screen style1 + show screen textstyle + with dissolve + e "When a screen displayable contains text, style properties prefixed with text_ apply to that text." + example: + image style2 = Text(_("This text is colored red."), color="#ffc0c0") + show style2 at examplepos + with dissolve + hide screen textstyle + with dissolve + e "The next is as part of a displayable created in an image statement. Style properties are just arguments to the displayable." + hide style2 + with dissolve + example: + define egreen = Character("Eileen", who_color="#c8ffc8", who_bold=True, what_color="#c8ffc8") + egreen "Style properties can also be given as arguments when defining a character." + egreen "Arguments beginning with who_ are style properties applied to the character's name, while those beginning with what_ are applied to the character's dialogue." + egreen "Style properties that don't have a prefix are also applied to the character's name." + example: + style blue_text: + color "#c0c0ff" + image style3 = Text(_("This text is colored blue."), style="blue_text") + show style3 at examplepos + e "Finally, there is the the style statement, which creates or changes a named style. By giving Text the style argument, we tell it to use the blue_text style." + hide style3 + hide example + with dissolve + e "A style property can inherit from a parent. If a style property is not given in a style, it comes from the parent of that style." + e "By default the parent of the style has the same name, with the prefix up to the the first underscore removed. If the style does not have an underscore in its name, 'default' is used." + e "For example, blue_text inherits from text, which in turn inherits from default. The default style defines all properties, so it doesn't inherit from anything." + example: + style blue_text is text: + color "#c0c0ff" + e "The parent can be explicitly changed by giving the style statement an 'is' clause. In this case, we're explictly setting the style to the parent of text." + hide example + e "Each displayable has a default style name. By default, it's usually the lower-case displayable name, like 'text' for Text, or 'button' for buttons." + e "In a screen, a displayable can be given the style_prefix property to give a prefix for that displayable and its children." id style_basics_35db9a05 + e "For example, a text displayable with a style_prefix of 'help' will be given the style 'help_text'." + e "Lastly, when a displayable is a button, or inside a button, it can take style prefixes." + e "The prefixes idle_, hover_, and insensitive_ are used when the button is unfocused, focused, and unfocusable." + e "These can be preceded by selected_ to change how the button looks when it represents a selected value or screen." + example: + style styled_button_text: + idle_color "#c0c0c0" + hover_color "#ffffff" + insensitive_color "#303030" + selected_idle_color "#e0e080" + selected_hover_color "#ffffc0" + screen style4(): + default result = 1 + frame: + style_prefix "styled" + xpadding 20 + ypadding 20 + at examplepos + vbox: + textbutton "Button 1" action SetScreenVariable("result", 1) + textbutton "Button 2" action SetScreenVariable("result", 2) + textbutton "Button 3" action None + show screen style4 + with dissolve + e "This screen shows the style prefixes in action. You can click on a button to select it, or click outside to advance." + hide screen style4 + with dissolve + hide example + e "Those are the basics of styles. If GUI customization isn't enough for you, styles let you customize just about everything in Ren'Py." + return +screen general(style): + frame: + style style + text _("Orbiting Earth in the spaceship, I saw how beautiful our planet is.\n–Yuri Gagarin") +label style_general: + e "The first group of style properties that we'll go over are the general style properties. These work with every displayable, or at least many different ones." + example: + style general is frame: + xalign 0.5 + yalign 0.2 + show screen general("general") + with dissolve + e "Every displayable takes the position properties, which control where it can be placed on screen. Since I've already mentioned them, I won't repeat them here." + example: + style minmax_general: + xmaximum 400 + yminimum 200 + show screen general("minmax_general") + with dissolve + e "The xmaximum and ymaximum properties set the maximum width and height of the displayable, respectively. This will cause Ren'Py to shrink things, if possible." + e "Sometimes, the shrunken size will be smaller than the size given by xmaximum and ymaximum." + e "Similarly, the xminimum and yminimum properties set the minimum width and height. If the displayable is smaller, Ren'Py will try to make it bigger." + example: + style xysize_general: + xsize 400 + ysize 200 + show screen general("xysize_general") + e "The xsize and ysize properties set the minimum and maximum size to the same value, fixing the size." + e "These only works for displayables than can be resized. Some displayables, like images, can't be made bigger or smaller." + example: + style area_general: + area (600, 20, 400, 200) + show screen general("area_general") + e "The area property takes a tuple - a parenthesis bounded list of four items. The first two give the position, and the second two the size." + example: + style alt_general: + alt _("\"Orbiting Earth in the spaceship, I saw how beautiful our planet is.\" Said by Yuri Gagarin.") + show screen general("alt_general") + e "Finally, the alt property changes the text used by self-voicing for the hearing impaired." + hide screen general + hide example + with dissolve + return +screen text(style, vertical=False): + frame: + xalign 0.5 + ypos 50 + if vertical: + left_padding 20 + right_padding 35 + bottom_padding 35 + text _("Vertical") style style + else: + xsize 400 + text _("Far better it is to dare mighty things, to win glorious triumphs, even though checkered by failure, than to rank with those poor spirits who neither enjoy nor suffer much, because they live in the gray twilight that knows not victory nor defeat.\n\n–Theodore Roosevelt"): + style style +label style_text: + e "The text style properties apply to text and input displayables." + e "Text displayables can be created implicitly or explicitly. For example, a textbutton creates a text displayable with a style ending in button_text." + e "These can also be set in gui.rpy by changing or defining variables with names like gui.button_text_size." + example: + style bold_text: + bold True + show screen text("bold_text") + e "The bold style property makes the text bold. This can be done using an algorithm, rather than a different version of the font." + example: + style color_text: + color "#c0ffc0" + show screen text("color_text") + e "The color property changes the color of the text. It takes hex color codes, just like everything else in Ren'Py." + example: + style first_indent_text: + first_indent 40 + show screen text("first_indent_text") + e "The first_indent style property determines how far the first line is indented." + example: + style font_text: + font "DejaVuSans-Bold.ttf" + show screen text("font_text") + e "The font style property changes the font the text uses. Ren'Py takes TrueType and OpenType fonts, and you'll have to include the font file as part of your visual novel." + example: + style size_text: + size 14 + show screen text("size_text") + e "The size property changes the size of the text." + example: + style italic_text: + italic True + show screen text("italic_text") + e "The italic property makes the text italic. Again, this is better done with a font, but for short amounts of text Ren'Py can do it for you." + example: + style justify_text: + justify True + show screen text("justify_text") + e "The justify property makes the text justified, lining all but the last line up on the left and the right side." + example: + style kerning_text: + kerning -0.5 + show screen text("kerning_text") + e "The kerning property kerns the text. When it's negative, characters are closer together. When positive, characters are farther apart." + example: + style leading_spacing_text: + line_leading 5 + line_spacing 7 + show screen text("leading_spacing_text") + e "The line_leading and line_spacing properties put spacing before each line, and between lines, respectively." + example: + style outlines_text: + outlines [ (1, "#408040", 0, 0) ] + show screen text("outlines_text") + e "The outlines property puts outlines around text. This takes a list of tuples, which is a bit complicated." + e "But if you ignore the brackets and parenthesis, you have the width of the outline, the color, and then horizontal and vertical offsets." + example: + style rest_indent_text: + rest_indent 40 + show screen text("rest_indent_text") + e "The rest_indent property controls the indentation of lines after the first one." + example: + style center_text: + textalign 0.5 + show screen text("center_text") + e "The textalign property controls the positioning of multiple lines of text inside the text displayable. For example, 0.5 means centered." id style_text_430c1959 + e "It doesn't change the position of the text displayable itself. For that, you'll often want to set the textalign and xalign to the same value." id style_text_19aa0833 + example: + style right_text: + textalign 1.0 + yalign 1.0 + show screen text("right_text") + e "When both textalign and xalign are set to 1.0, the text is properly right-justified." id style_text_efc3c392 + example: + style underline_text: + underline True + show screen text("underline_text") + e "The underline property underlines the text." + hide screen text + hide example + with dissolve + e "Those are the most common text style properties, but not the only ones. Here are a few more that you might need in special circumstances." + example: + style antialias_text: + antialias False + show screen text("antialias_text") + e "By default, text in Ren'Py is antialiased, to smooth the edges. The antialias property can turn that off, and make the text a little more jagged." + example: + style adjust_true_text: + adjust_spacing True + show screen text("adjust_true_text") + e "The adjust_spacing property is a very subtle one, that only matters when a player resizes the window. When True, characters will be shifted a bit so the Text has the same relative spacing." + example: + style adjust_true_text: + adjust_spacing False + show screen text("adjust_false_text") + e "When False, the text won't jump around as much. But it can be a little wider or narrower based on screen size." + example: + style layout_nobreak_text: + layout "nobreak" + show screen text("layout_nobreak_text") + e "The layout property has a few special values that control where lines are broken. The 'nobreak' value disables line breaks entirely, making the text wider." + example: + style layout_subtitle_text: + layout "subtitle" + xalign 0.5 + textalign 0.5 + show screen text("layout_subtitle_text") + e "When the layout property is set to 'subtitle', the line breaking algorithm is changed to try to make all lines even in length, as subtitles usually are." + example: + style strikethrough_text: + strikethrough True + show screen text("strikethrough_text") + e "The strikethrough property draws a line through the text. It seems pretty unlikely you'd want to use this one." + example: + style vertical_text: + vertical True + size 18 + show screen text("vertical_text", True) + e "The vertical style property places text in a vertical layout. It's meant for Asian languages with special fonts." + hide screen text + hide example + with dissolve + e "And those are the text style properties. There might be a lot of them, but we want to give you a lot of control over how you present text to your players." + return +screen button(style): + default selected = "top" + frame: + xalign 0.5 + ypos 50 + background "#0004" + xsize 350 + has vbox + xalign 0.5 + textbutton _("Top Choice"): + style style + action SetScreenVariable("selected", "top") + text_style "example_button_text" + textbutton _("Bottom Choice"): + style style + action SetScreenVariable("selected", "bottom") + text_style "example_button_text" +example example_button_text: + style example_button_text: + xalign 0.5 + color "#404040" + hover_color "#4040c0" +label style_button: + e "Next up, we have the window and button style properties. These apply to windows like the text window at the bottom of this screen and frames like the ones we show examples in." + e "These properties also apply to buttons, in-game and out-of-game. To Ren'Py, a button is a window you can click." + example example_button large: + style example_button is default: + idle_background Frame("idle_background.png", 10, 10, tile=True) + hover_background Frame("hover_background.png", 10, 10, tile=True) + xalign 0.5 + show screen button('example_button') + with dissolve + e "I'll start off with this style, which everything will inherit from. To make our lives easier, it inherits from the default style, rather than the customized buttons in this game's GUI." id style_button_9b53ce93 + e "The first style property is the background property. It adds a background to a button or window. Since this is a button, idle and hover variants choose different backgrounds when focused." id style_button_aece4a8c + e "We also center the two buttons, using the xalign position property." + show example example_button_text + e "We've also customized the style of the button's text, using this style. It centers the text and makes it change color when hovered." + example: + style oddly_padded_button is example_button: + left_padding 10 + right_padding 40 + top_padding 10 + bottom_padding 5 + show screen button('oddly_padded_button') + e "Without any padding around the text, the button looks odd. Ren'Py has padding properties that add space inside the button's background." + example: + style padded_button is example_button: + xpadding 40 + ypadding 10 + show screen button('padded_button') + e "More commonly used are the xpadding and ypadding style properties, which add the same padding to the left and right, or the top and bottom, respectively." + example: + style margin_button is padded_button: + ymargin 10 + show screen button('margin_button') + e "The margin style properties work the same way, except they add space outside the background. The full set exists: left_margin, right_margin, top_margin, bottom_margin, xmargin, and ymargin." + example: + style sized_button is margin_button: + size_group "example" + show screen button('sized_button') + e "The size_group style property takes a string. Ren'Py will make sure that all the windows or buttons with the same size_group string are the same size." + example: + style xfill_button is margin_button: + xfill True + show screen button('xfill_button') + e "Alternatively, the xfill and yfill style properties make a button take up all available space in the horizontal or vertical directions." + example: + style foreground_button is xfill_button: + foreground "check_foreground.png" + selected_foreground "check_selected_foreground.png" + show screen button('foreground_button') + e "The foreground property gives a displayable that is placed on top of the contents and background of the window or button." + e "One way to use it is to provide extra decorations to a button that's serving as a checkbox. Another would be to use it with a Frame to provide a glossy shine that overlays the button's contents." + example: + style beep_button is foreground_button: + hover_sound "pong_beep.opus" + activate_sound "pong_boop.opus" + show screen button('beep_button') + e "There are also a few style properties that only apply to buttons. The hover_sound and activate_sound properties play sound files when a button is focused and activated, respectively." + example: + style focus_mask_button is beep_button: + focus_mask True + show screen button('focus_mask_button') + e "Finally, the focus_mask property applies to partially transparent buttons. When it's set to True, only areas of the button that aren't transparent cause a button to focus." + hide example + hide screen button + with dissolve + return +screen bar(style): + default measure = 42.0 + frame: + xalign 0.5 + ypos 50 + ypadding 20 + has vbox + spacing 20 + bar: + value ScreenVariableValue("measure", range=100.0) + style style + xsize 400 + text "[measure:.0f]/100": + xalign 0.5 +screen vbar(style): + default measure = 42.0 + frame: + xalign 0.5 + ypos 20 + ypadding 20 + xsize 140 + has vbox + spacing 20 + xfill True + bar: + value ScreenVariableValue("measure", range=100.0) + style style + ysize 250 + xalign 0.5 + text "[measure:.0f]/100": + xalign 0.5 + textalign 0.5 + min_width 100 +image bar empty idle vertical = Transform("bar empty idle", rotate=90, rotate_pad=False) +image bar empty hover vertical = Transform("bar empty hover", rotate=90, rotate_pad=False) +image bar full idle vertical = Transform("bar full idle", rotate=90, rotate_pad=False) +image bar full hover vertical = Transform("bar full hover", rotate=90, rotate_pad=False) +style empty_only is default: + left_bar Frame("bar empty idle.png", 4, 0) + hover_left_bar Frame("bar empty hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 +style full_only is default: + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar full idle.png", 4, 0) + hover_right_bar Frame("bar full hover.png", 4, 0) + ysize 30 +label style_bar: + show screen bar("empty_only") + with dissolve + e "To demonstrate styles, let me first show two of the images we'll be using. This is the image we're using for parts of the bar that are empty." + show screen bar("full_only") + e "And here's what we use for parts of the bar that are full." + example large: + style example_bar is default: + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + show screen bar('example_bar') + e "The left_bar and right_bar style properties, and their hover variants, give displayables for the left and right side of the bar. By default, the value is shown on the left." + e "Also by default, both the left and right displayables are rendered at the full width of the bar, and then cropped to the appropriate size." + e "We give the bar the ysize property to set how tall it is. We could also give it xsize to choose how wide, but here it's limited by the width of the frame it's in." + example: + style invert_bar is default: + bar_invert True + left_bar Frame("bar empty idle.png", 4, 0) + hover_left_bar Frame("bar empty hover.png", 4, 0) + right_bar Frame("bar full idle.png", 4, 0) + hover_right_bar Frame("bar full hover.png", 4, 0) + ysize 30 + show screen bar('invert_bar') + e "When the bar_invert style property is True, the bar value is displayed on the right side of the bar. The left_bar and right_bar displayables might also need to be swapped." + example: + style resizing_bar is default: + bar_resizing True + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + show screen bar('resizing_bar') + e "The bar_resizing style property causes the bar images to be resized to represent the value, rather than being rendered at full size and cropped." + example: + style thumb_bar is default: + thumb "bar thumb idle.png" + hover_thumb "bar thumb hover.png" + base_bar Frame("bar empty idle.png", 4, 0) + hover_base_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + show screen bar('thumb_bar') + e "The thumb style property gives a thumb image, that's placed based on the bar's value. In the case of a scrollbar, it's resized if possible." id style_bar_7d361bac + e "Here, we use it with the base_bar style property, which sets both bar images to the same displayable." + example: + style gutter_bar is default: + left_gutter 4 + right_gutter 4 + thumb "bar thumb idle.png" + hover_thumb "bar thumb hover.png" + base_bar Frame("bar empty idle.png", 4, 0) + hover_base_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + show screen bar('gutter_bar') + e "The left_gutter and right_gutter properties set a gutter on the left or right size of the bar. The gutter is space the bar can't be dragged into, that can be used for borders." + example: + style vertical_bar is default: + bar_vertical True + top_bar Frame("bar empty idle vertical", 4, 0) + hover_top_bar Frame("bar empty hover vertical", 4, 0) + bottom_bar Frame("bar full idle vertical", 4, 0) + hover_bottom_bar Frame("bar full hover vertical", 4, 0) + xsize 30 + hide screen bar + show screen vbar('vertical_bar') + with dissolve + e "The bar_vertical style property displays a vertically oriented bar. All of the other properties change names - left_bar becomes top_bar, while right_bar becomes bottom_bar." + hide screen vbar + with dissolve + e "Finally, there's one style we can't show here, and it's unscrollable. It controls what happens when a scrollbar can't be moved at all." + e "By default, it's shown. But if unscrollable is 'insensitive', the bar becomes insensitive. If it's 'hide', the bar is hidden, but still takes up space." + hide example + e "That's it for the bar properties. By using them, a creator can customize bars, scrollbars, and sliders." + return +screen hbox(style, wide, highlight="#fff2"): + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + has frame + style "empty" + background highlight + hbox: + style style + text _("First Child") color "#ffe0e0" + text _("Second Child") color "#e0ffe0" + text _("Third Child") color "#e0e0ff" + if wide: + text _("Fourth Child") color "#ffffe0" + text _("Fifth Child") color "#e0ffff" + text _("Sixth Child") color "#ffe0ff" +screen vbox(style, highlight="#fff2"): + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + has frame + style "empty" + background highlight + vbox: + style style + text _("First Child") xalign 0.5 color "#ffe0e0" + text _("Second Child") xalign 0.5 color "#e0ffe0" + text _("Third Child") xalign 0.5 color "#e0e0ff" +screen grid(style): + frame: + xalign 0.5 + ypos 50 + ypadding 20 + has grid 3 2 + style style + text _("First Child") color "#ffe0e0" + text _("Second Child") color "#e0ffe0" + text _("Third Child") color "#e0e0ff" + text _("Fourth Child") color "#ffffe0" + text _("Fifth Child") color "#e0ffff" + text _("Sixth Child") color "#ffe0ff" +screen fixed(style): + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + ysize 400 + has frame + style "empty" + background "#fff2" + fixed: + style style + add Transform("logo base", zoom=.9): + xpos 10 + ypos 10 + text "Ren'Py": + font "DejaVuSans.ttf" + xpos 150 + ypos 220 + size 80 + outlines [ (2, "#333", 0, 0) ] +label style_box: + show screen hbox('hbox', False, None) + with dissolve + e "The hbox displayable is used to lay its children out horizontally. By default, there's no spacing between children, so they run together." + hide screen hbox + show screen vbox('vbox', None) + with dissolve + e "Similarly, the vbox displayable is used to lay its children out vertically. Both support style properties that control placement." + show screen vbox('vbox') + with dissolve + e "To make the size of the box displayable obvious, I'll add a highlight to the box itself, and not the frame containing it." + example: + style fill_vbox: + xfill True + show screen vbox('fill_vbox') + e "Boxes support the xfill and yfill style properties. These properties make a box expand to fill the available space, rather than the space of the largest child." + example: + style spacing_vbox: + spacing 10 + xfill True + show screen vbox('spacing_vbox') + e "The spacing style property takes a value in pixels, and adds that much spacing between each child of the box." + example: + style first_spacing_vbox is vbox: + first_spacing 10 + xfill True + show screen vbox('first_spacing_vbox') + e "The first_spacing style property is similar, but it only adds space between the first and second children. This is useful when the first child is a title that needs different spacing." + example: + style reverse_vbox: + box_reverse True + xfill True + show screen vbox('reverse_vbox') + e "The box_reverse style property reverses the order of entries in the box." + example: + style spacing_hbox: + spacing 20 + xfill True + hide screen vbox + show screen hbox("spacing_hbox", False) + with dissolve + e "We'll switch back to a horizontal box for our next example." + example: + style wrap_hbox: + box_wrap True + spacing 5 + xfill True + show screen hbox("wrap_hbox", True) + e "The box_wrap style property fills the box with children until it's full, then starts again on the next line." + hide screen hbox + show screen grid('spacing_grid') + with dissolve + example: + style spacing_grid: + xspacing 20 + yspacing 50 + e "Grids bring with them two more style properties. The xspacing and yspacing properties control spacing in the horizontal and vertical directions, respectively." + hide example + hide screen grid + show screen fixed('fixed') + with dissolve + e "Lastly, we have the fixed layout. The fixed layout usually expands to fill all space, and shows its children from back to front." + e "But of course, we have some style properties that can change that." + example: + style fit_fixed: + xfit True + yfit True + show screen fixed('fit_fixed') + e "When the xfit style property is True, the fixed lays out all its children as if it was full size, and then shrinks in width to fit them. The yfit style works the same way, but in height." + example: + style reverse_fixed: + order_reverse True + show screen fixed('reverse_fixed') + e "The order_reverse style property changes the order in which the children are shown. Instead of back-to-front, they're displayed front-to-back." + hide screen fixed + hide example + with dissolve + return +label style_inspector: + e "Sometimes it's hard to figure out what style is being used for a particular displayable. The displayable inspector can help with that." + e "To use it, place the mouse over a portion of the Ren'Py user interface, and hit shift+I. That's I for inspector." + e "Ren'Py will pop up a list of displayables the mouse is over. Next to each is the name of the style that displayable uses." + e "You can click on the name of the style to see where it gets its properties from." + e "By default, the inspector only shows interface elements like screens, and not images. Type shift+alt+I if you'd like to see images as well." + e "You can try the inspector right now, by hovering this text and hitting shift+I." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_text.rpy b/testcases/expected/tutorial-8.2/indepth_text.rpy new file mode 100644 index 00000000..955842d3 --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_text.rpy @@ -0,0 +1,61 @@ +init 1: + define eslow = Character(kind=e, what_slow_cps=20) +label a_label: + e "You just clicked to jump to a label." + jump after_a_label +label text: + e "Sometimes, when showing text, we'll want to change the way some of the text is displayed." + example tags1 hide: + e "For example, we might want to have text that is {b}bold{/b}, {i}italic{/i}, {s}struckthrough{/s}, or {u}underlined{/u}." + e "That's what text tags are for." + show example tags1 + e "Text tags are contained in braces, like the {{b} tag above. When a text tag takes a closing tag, the closing tag begins with a slash, like {{/b} does." + e "We've already seen the b, i, s, and u tags, but there are lot more than those. I'll show you the rest of them." + example: + e "The a text tag can {a=https://www.renpy.org}link to a website{/a} or {a=jump:a_label}jump to a label{/a}." +label after_a_label: + example: + e "The alpha text tag makes text {alpha=.5}translucent{/alpha}." + example: + e "The color text tag changes the {color=#0080c0}color{/color} of the text." + example: + e "The cps text tag {cps=25}makes text type itself out slowly{/cps}, even if slow text is off." + e "The cps tag can also be relative to the default speed, {cps=*2}doubling{/cps} or {cps=*0.5}halving{/cps} it." + example: + e "The font tag changes the font, for example to {font=DejaVuSans-Bold.ttf}DejaVuSans-Bold.ttf{/font}." + e "Sometimes, changing to a bold font looks better than using the {{b} tag." + example: + e "The k tag changes kerning. It can space the letters of a word {k=-.5}closer together{/k} or {k=.5}farther apart{/k}." + example: + e "The size tag changes the size of text. It can make text {size=+10}bigger{/size} or {size=-10}smaller{/size}, or set it to a {size=30}fixed size{/size}." + example: + e "The space tag {space=30} adds horizontal space in text.{vspace=30}The vspace tag adds vertical space between lines." + hide example + e "There are a few text tags that only makes sense in dialogue." + example: + e "The p tag breaks a paragraph,{p}and waits for the player to click." + e "If it is given a number as an argument,{p=1.5}it waits that many seconds." + example: + e "The w tag also waits for a click,{w} except it doesn't break lines,{w=.5} the way p does." + example: + eslow "The nw tag causes Ren'Py to continue past slow text,{nw}" + with flashbulb + extend " to the next statement." + example: + e "To break a line without pausing,\none can write \\n. \\' and \\\" include quotes in the text." + hide example + e "The interpolation feature takes a variable name in square brackets, and inserts it into text." + example: + $ variable = _("{i}variable value{/i}") + e "For example, this displays the [variable!t]." + example: + e "When the variable name is followed by !q, special characters are quoted. This displays the raw [variable!q!t], including the italics tags." + example showtrans: + $ translatable = _("translatable text") + e "When the variable name is followed by !t, it is translated to [variable!t]. It could be something else in a different language." + example: + e "Finally, certain characters are special. [[, {{, and \\ need to be doubled if included in text. The %% character should be doubled if used in dialogue." + hide example + pause .5 + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_transitions.rpy b/testcases/expected/tutorial-8.2/indepth_transitions.rpy new file mode 100644 index 00000000..eaa10417 --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_transitions.rpy @@ -0,0 +1,327 @@ +example slow_dissolve: + define slow_dissolve = Dissolve(1.0) +example flashbulb: + define flashbulb = Fade(0.2, 0.0, 0.8, color='#fff') +example circleirisout: + define circleirisout = ImageDissolve("imagedissolve circleiris.png", 1.0, 8) +example circleirisin: + define circleirisin = ImageDissolve("imagedissolve circleiris.png", 1.0, 8 , reverse=True) +example circlewipe: + define circlewipe = ImageDissolve("imagedissolve circlewipe.png", 1.0, 8) +example dream: + define dream = ImageDissolve("imagedissolve dream.png", 2.0, 64) +example teleport: + define teleport = ImageDissolve("imagedissolve teleport.png", 1.0, 0) +image bg circleiris = "imagedissolve circleiris" +image bg teleport = "imagedissolve teleport" +example alphadissolve: + image alpha_control: + "spotlight.png" + anchor (.5, .5) + parallel: + zoom 0 + linear .5 zoom .75 + pause 2 + linear 1.0 zoom 4.0 + parallel: + xpos 0.0 ypos .6 + linear 1.5 xpos 1.0 + linear 1.0 xpos .5 ypos .2 + pause .5 + repeat + define alpha_example = AlphaDissolve("alpha_control", delay=3.5) +label demo_transitions: + e "Ren'Py ships with a large number of built-in transitions, and also includes classes that let you define your own." + menu demo_transitions_menu: + e "What kind of transitions would you like demonstrated?" + "Simple Transitions": + call demo_simple_transitions from _call_demo_simple_transitions_1 + "ImageDissolve Transitions": + call demo_imagedissolve_transitions from _call_demo_imagedissolve_transitions_1 + "MoveTransition Transitions": + call demo_movetransition from _call_demo_movetransition_1 + "CropMove Transitions": + call demo_cropmove_transitions from _call_demo_cropmove_transitions_1 + "PushMove Transitions": + call demo_pushmove_transitions from _call_demo_pushmove_transitions_1 + "AlphaDissolve Transitions": + call demo_alphadissolve from _call_demo_alphadissolve + "How about something else?": + return + jump demo_transitions_menu +label demo_simple_transitions: + e "Okay, I can tell you about simple transitions. We call them simple because they don't take much in the way of configuration." + e "But don't let that get you down, since they're the transitions you'll probably use the most in a game." + example: + show bg whitehouse + with dissolve + e "The 'dissolve' transition is probably the most useful, blending one scene into another." + example slow_dissolve small: + show bg washington + with slow_dissolve + e "The 'Dissolve' function lets you create your own dissolves, taking a different amount of time." + example: + show bg whitehouse + with fade + e "The 'fade' transition fades to black, and then fades back in to the new scene." + e "If you're going to stay at a black screen, you'll probably want to use 'dissolve' rather than 'fade'." + example flashbulb small: + with flashbulb + e "You can use 'Fade' to define your own fades. By changing the timing and the color faded to, you can use this for special effects, like flashbulbs." + example: + show bg washington + with pixellate + e "The 'pixellate' transition pixellates out the old scene, switches to the new scene, and then unpixellates that." + e "It's probably not appropriate for most games, but we think it's kind of neat." + e "You can use 'Pixellate' to change the details of the pixellation." + e "Motions can also be used as transitions." + "..." + "......" + example: + play audio "punch.opus" + with vpunch + e "Hey! Pay attention." + e "I was about to demonstrate 'vpunch'... well, I guess I just did." + example: + play audio "punch.opus" + with hpunch + e "We can also shake the screen horizontally, with 'hpunch'. These were defined using the 'Move' function." + e "There's also the 'move' transition, which is confusingly enough defined using the 'MoveTransition' function." + example: + show eileen happy at right + with move + show eileen happy at center + with move + e "The 'move' transition finds images that have changed placement, and slides them to their new place. It's an easy way to get motion in your game." + hide example + e "That's it for the simple transitions." + return +label demo_imagedissolve_transitions: + e "Perhaps the most flexible kind of transition is the ImageDissolve, which lets you use an image to control a dissolve." + e "This lets us specify very complex transitions, fairly simply. Let's try some, and then I'll show you how they work." + e "There are two ImageDissolve transitions built into Ren'Py." + example: + scene black + with blinds + scene bg washington + show eileen happy + with blinds + e "The 'blinds' transition opens and closes what looks like vertical blinds." + example: + scene black + with squares + scene bg washington + show eileen happy + with squares + e "The 'squares' transition uses these squares to show things." + e "I'm not sure why anyone would want to use it, but it was used in some translated games, so we added it." + hide example + e "The most interesting transitions aren't in the standard library." + e "These ones require an image the size of the screen, and so we couldn't include them as the size of the screen can change from game to game." + example circleirisin small: + scene black + with circleirisin + e "We can hide things with a 'circleirisin'..." + example circleirisout small: + scene bg washington + with circleirisout + e "... and show them again with a 'circleirisout'." + example circlewipe small: + show bg whitehouse + with circlewipe + e "The 'circlewipe' transitions changes screens using a circular wipe effect." + example dream small: + scene bg washington + with dream + e "The 'dream' transition does this weird wavy dissolve, and does it relatively slowly." + example teleport small: + show eileen happy + with teleport + e "The 'teleport' transition reveals the new scene one line at a time." + show example circleirisout small + scene bg circleiris + with dissolve + e "This is the background picture used with the circleirisout transition." + e "When we use an ImageDissolve, the white will dissolve in first, followed by progressively darker colors. Let's try it." + show bg washington + with circleirisout + show example circleirisout small + e "If we give ImageDissolve the 'reverse' parameter, black areas will dissolve in first." + show bg circleiris + with circleirisin + e "This lets circleirisin and circleirisout use the same image." + show example teleport small + show bg teleport + with dissolve + e "The teleport transition uses a different image, one that dissolves things in one line at a time." + show bg washington + with teleport + e "A dissolve only seems to affect parts of the scene that have changed..." + show eileen happy + with teleport + e "... which is how we apply the teleport effect to a single character." + hide example + return +label demo_cropmove_transitions: + e "The CropMove transition class provides a wide range of transition effects. It's not used very much in practice, though." + show eileen happy at offscreenleft + with move + e "I'll stand offscreen, so you can see some of its modes. I'll read out the mode name after each transition." + example: + scene bg whitehouse + with wiperight + e "We first have wiperight..." + example: + scene bg washington + with wipeleft + e "...followed by wipeleft... " + example: + scene bg whitehouse + with wipeup + e "...wipeup..." + example: + scene bg washington + with wipedown + e "...and wipedown." + e "Next, the slides." + example: + scene bg whitehouse + with slideright + e "Slideright..." + example: + scene bg washington + with slideleft + e "...slideleft..." + example: + scene bg whitehouse + with slideup + e "...slideup..." + example: + scene bg washington + with slidedown + e "and slidedown." + e "While the slide transitions slide in the new scene, the slideaways slide out the old scene." + example: + scene bg whitehouse + with slideawayright + e "Slideawayright..." + example: + scene bg washington + with slideawayleft + e "...slideawayleft..." + example: + scene bg whitehouse + with slideawayup + e "...slideawayup..." + example: + scene bg washington + with slideawaydown + e "and slideawaydown." + e "We also have a couple of transitions that use a rectangular iris." + example: + scene bg whitehouse + with irisout + e "There's irisout..." + example: + scene bg washington + show eileen happy + with irisin + e "... and irisin." + hide example + e "It's enough to make you feel a bit dizzy." + return +label demo_pushmove_transitions: + e "The PushMove transitions use the new scene to push the old one out. Let's take a look." + example: + show bg whitehouse + hide eileen + with pushright + "There's pushright..." + example: + show bg washington + with pushleft + "...pushleft..." + example: + show bg whitehouse + with pushdown + "...pushdown..." + example: + show bg washington + show eileen happy + with pushup + "... and pushup. And that's it the for the PushMove-based transitions." + hide example + pause .5 + return +label demo_movetransition: + e "The most common MoveTransition is move, which slides around images that have changed position on the screen." + example move: + show eileen happy at left + with move + e "Just like that." + show example moveinout + e "There are also the moveout and movein transitions." + e "The moveout transitions (moveoutleft, moveoutright, moveouttop, and moveoutbottom) slide hidden images off the appropriate side of the screen." + e "The movein transitions (moveinleft, moveinright, moveintop, and moveinbottom) slide in new images." + e "Let's see them all in action." + hide example + pause .5 + example moveinout hide: + hide eileen happy + with moveoutleft + show eileen happy + with moveinbottom + hide eileen happy + with moveoutbottom + show eileen happy + with moveinright + hide eileen happy + with moveoutright + show eileen happy: + yzoom -1 + xalign 0.5 + yalign 0.0 + with moveintop + hide eileen happy + with moveouttop + show eileen happy + with moveinleft + e "That's it for the moveins and moveouts." + e "Finally, there are the zoomin and zoomout transitions, which show and hide things using a zoom." + example: + hide eileen happy + with zoomout + show eileen happy + with zoomin + e "And that's all there is." + hide example + pause .5 + return +label demo_alphadissolve: + e "The AlphaDissolve transition lets you use one displayable to combine two others. Click, and I'll show you an example." + scene black + with dissolve + example alphadissolve large: + scene bg washington + show eileen happy at center + with alpha_example + e "The AlphaDissolve displayable takes a control displayable, usually an ATL transform." + scene + show alpha_control + e "To be useful, the control displayable should be partially transparent." + e "During an AlphaDissolve, the old screen is used to fill the transparent areas of the image, while the new screen fills the opaque areas." + scene black + e "For our spotlight example, the old screen is this all-black image." + scene bg washington + show eileen happy at center + e "The new screen is me just standing here." + scene black + with dissolve + scene bg washington + show eileen happy at center + with alpha_example + e "By combining them using AlphaDissolve, we can build a complicated effect out of simpler parts." + hide example + pause .5 + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/indepth_translations.rpy b/testcases/expected/tutorial-8.2/indepth_translations.rpy new file mode 100644 index 00000000..33ed9f1a --- /dev/null +++ b/testcases/expected/tutorial-8.2/indepth_translations.rpy @@ -0,0 +1,37 @@ +example translate_font: + translate japanese python: + gui.text_font = "SourceHanSansLite.ttf" + gui.name_text_font = "SourceHanSansLite.ttf" + gui.interface_text_font = "SourceHanSansLite.ttf" + translate japanese style default: + language "japanese-strict" +label translations: + e "Ren'Py includes support for translating your game into languages other than the one it was originally written in." + e "This includes the translation of every string in the game, including dialogue, menu choice, and interface strings, and of images and other assets." + e "While Ren'Py can find dialogue and menu choice strings for you, you'll have to indicate which other strings need translation." + show example characters + e "For example, here is how we define a character and her name." + show example characters showtrans + e "To mark Lucy's name as translatable, we surround it by parentheses preceded by a single underscore." + e "Notice how we don't translate the reddish color that we use for her name. That stays the same for all languages." + hide example + show launcher translate at launcher_place + with moveinleft + e "Once that's done, you can generate the translation files. That's done by going to the launcher, and clicking translate." + e "After you type in the name of the language you'll be translating to, choosing Generate Translations will scan your game and create translation files." + e "The files will be generated in game/tl/language, where language is the name of the language you typed in." + e "You'll need to edit those files to include translations for everything in your game." + e "If you want to localize image files, you can also place them in game/tl/language." + hide launcher + with moveoutleft + show example translate_font large + e "If the default fonts used by the game do not support the language you are translating to, you will have to change them." + e "The translate python statement can be used to set the values of gui variables to change the font." + e "The translate style statement sets style properties more directly." + e "If you need to add new files, such as font files, you can place them into the game/tl/language directory where Ren'Py will find them." + show example language_picker large + e "Finally, you'll have to add support for picking the language of the game. That usually goes into the preferences screen, found in screens.rpy." + e "Here's an excerpt of the preferences screen of this tutorial. The Language action tells Ren'Py to change the language. It takes a string giving a language name, or None." + e "The None language is special, as it's the default language that the visual novel was written in. Since this tutorial was written in English, Language(None) selects English." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/options.rpy b/testcases/expected/tutorial-8.2/options.rpy new file mode 100644 index 00000000..c79d04c9 --- /dev/null +++ b/testcases/expected/tutorial-8.2/options.rpy @@ -0,0 +1,46 @@ +define config.name = _("Ren'Py Tutorial Game") +define gui.show_name = False +define config.version = renpy.version_string +define build.version = renpy.version_only +define gui.about = _("") +define build.name = "tutorial_7" +define config.has_sound = True +define config.has_music = True +define config.has_voice = True +define config.enter_transition = dissolve +define config.exit_transition = dissolve +define config.after_load_transition = None +define config.end_game_transition = None +define config.window = "auto" +define config.window_show_transition = Dissolve(.2) +define config.window_hide_transition = Dissolve(.2) +default preferences.text_cps = 0 +default preferences.afm_time = 15 +define config.save_directory = "tutorial-7" +define config.window_icon = "gui/window_icon.png" +init python: + config.searchpath.append(config.renpy_base + "/sdk-fonts") + build.classify_renpy("sdk-fonts/**", "all") + build._sdk_fonts = True + build.classify('**~', None) + build.classify('**.bak', None) + build.classify('**/.**', None) + build.classify('**/#**', None) + build.classify('**/thumbs.db', None) + build.documentation('*.html') + build.documentation('*.txt') +init python hide: + import datetime + today = datetime.date.today() + if (today.month == 3) and (today.day == 19): + config.mouse = { 'default' : [ + ("gui/mouse0.png", 0, 0), + ("gui/mouse1.png", 0, 0), + ("gui/mouse2.png", 0, 0), + ("gui/mouse1.png", 0, 0), + ] * 2 + [ + ("gui/mouse0.png", 0, 0), + ] * (10 * 20) +} +define config.defer_tl_scripts = True +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/screens.rpy b/testcases/expected/tutorial-8.2/screens.rpy new file mode 100644 index 00000000..d0fb582b --- /dev/null +++ b/testcases/expected/tutorial-8.2/screens.rpy @@ -0,0 +1,869 @@ +init offset = -1 +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) +example say_screen: + screen say(who, what): + window: + id "window" + if who is not None: + text who id "who" + text what id "what" +screen say(who, what): + style_prefix "say" + window: + id "window" + if who is not None: + window: + style "namebox" + text who id "who" + text what id "what" + if not renpy.variant("small"): + add SideImage() xalign 0.0 yalign 1.0 +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 + adjust_spacing False +screen input(prompt): + style_prefix "input" + window: + has 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 +screen choice(items): + style_prefix "choice" + vbox: + for i in items: + textbutton i.caption action i.action +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.text_properties("choice_button") +screen quick_menu(): + 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') +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.text_properties("quick_button") +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")): + textbutton _("Help") action ShowMenu("help") + if renpy.variant("pc"): + 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.text_properties("navigation_button") +screen main_menu(): + tag menu + style_prefix "main_menu" + add gui.main_menu_background + frame + use navigation + text "[renpy.version_string] \"[renpy.version_name]\"" 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: + color "#fff" + yalign .995 + size 18 + kerning -1 + xoffset 290 +style main_menu_version: + variant "small" + xoffset 365 +screen game_menu(title, scroll=None): + style_prefix "game_menu" + if main_menu: + add gui.main_menu_background + else: + add gui.game_menu_background + frame: + style "game_menu_outer_frame" + has hbox + frame: + style "game_menu_navigation_frame" + frame: + style "game_menu_content_frame" + if scroll == "viewport": + viewport: + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + side_yfill True + has vbox + transclude + elif scroll == "vpgrid": + vpgrid: + cols 1 + yinitial 1.0 + 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 +screen about(): + tag menu + use game_menu(_("About"), scroll="viewport"): + style_prefix "about" + vbox: + label "[config.name!t]" + text _("Version [config.version!t]\n") + 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 +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: + order_reverse True + button: + style "page_label" + key_events True + xalign 0.5 + action page_name_value.Toggle() + input: + style "page_label_text" + value page_name_value + 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) + 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") + 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: + textalign 0.5 + layout "subtitle" + hover_color gui.hover_color +style page_button: + properties gui.button_properties("page_button") +style page_button_text: + properties gui.text_properties("page_button") +style slot_button: + properties gui.button_properties("slot_button") +style slot_button_text: + properties gui.text_properties("slot_button") +screen preferences(): + tag menu + if renpy.mobile: + $ cols = 2 + else: + $ cols = 4 + use game_menu(_("Preferences"), scroll="viewport"): + vbox: + hbox: + box_wrap True + if renpy.variant("pc") or renpy.variant("web"): + vbox: + spacing 12 + vbox: + style_prefix "radio" + label _("Display") + textbutton _("Window") action Preference("display", "window") + textbutton _("Fullscreen") action Preference("display", "fullscreen") + vbox: + style_prefix "check" + label _("Examples") + textbutton _("Translations") action ToggleField(persistent, "show_translation_marker") + 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")) + vbox: + style_prefix "radio" + label _("Language") + textbutton "English" text_font "DejaVuSans.ttf" action Language(None) + textbutton "Français" text_font "DejaVuSans.ttf" action Language("french") + textbutton "Русский" text_font "DejaVuSans.ttf" action Language("russian") + textbutton "Español" text_font "DejaVuSans.ttf" action Language("spanish") + textbutton "Українська" text_font "DejaVuSans.ttf" action Language("ukrainian") + vbox: + style_prefix "radio" + label "" + textbutton "한국어" text_font "SourceHanSansLite.ttf" action Language("korean") + textbutton "日本語" text_font "SourceHanSansLite.ttf" action Language("japanese") + textbutton "简体中文" text_font "SourceHanSansLite.ttf" action Language("schinese") + textbutton "Pig Latin" text_font "DejaVuSans.ttf" action Language("piglatin") + 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.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.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.text_properties("slider_button") +style slider_vbox: + xsize 450 +screen history(): + tag menu + predict False + use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport")): + style_prefix "history" + for h in _history_list: + window: + has fixed + yfit True + if h.who: + label h.who: + style "history_name" + substitute False + 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.") +define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } +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 + textalign 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 + textalign 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 +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}.") + hbox: + label "Shift+A" + text _("Opens the accessibility menu.") +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") + 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.text_properties("help_button") +style help_label: + xsize 250 + right_padding 20 +style help_label_text: + size gui.text_size + xalign 1.0 + textalign 1.0 +screen confirm(message, yes_action, no_action): + modal True + zorder 200 + style_prefix "confirm" + add "gui/overlay/confirm.png" + frame: + has 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 + 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: + textalign 0.5 + layout "subtitle" +style confirm_button: + properties gui.button_properties("confirm_button") +style confirm_button_text: + properties gui.text_properties("confirm_button") +screen skip_indicator(): + zorder 100 + style_prefix "skip" + frame: + has 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" +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: + font "DejaVuSans.ttf" +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") +screen nvl(dialogue, items=None): + window: + style "nvl_window" + has vbox + spacing gui.nvl_spacing + if gui.nvl_height: + vpgrid: + cols 1 + yinitial 1.0 + use nvl_dialogue(dialogue) + else: + use nvl_dialogue(dialogue) + 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 + has 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 +define config.nvl_list_length = 6 +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 + textalign 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 + textalign 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 + textalign 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.text_properties("nvl_button") +style pref_vbox: + variant "medium" + xsize 450 +screen quick_menu(): + variant "touch" + zorder 100 + 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 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 slider_pref_vbox: + variant "small" + xsize None +style slider_pref_slider: + variant "small" + xsize 600 +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/script.rpy b/testcases/expected/tutorial-8.2/script.rpy new file mode 100644 index 00000000..74f6deec --- /dev/null +++ b/testcases/expected/tutorial-8.2/script.rpy @@ -0,0 +1,133 @@ +define e = Character(_('Eileen'), color="#c8ffc8") +init python: + tutorials = [ ] + class Section(object): + """ + Represents a section of the tutorial menu. + `title` + The title of the section. This should be a translatable string. + """ + def __init__(self, title): + self.kind = "section" + self.title = title + tutorials.append(self) + class Tutorial(object): + """ + Represents a label that we can jump to. + """ + def __init__(self, label, title, move=True): + self.kind = "tutorial" + self.label = label + self.title = title + if move and (move != "after"): + self.move_before = True + else: + self.move_before = False + if move and (move != "before"): + self.move_after = True + else: + self.move_after = False + tutorials.append(self) + Section(_("Quickstart")) + Tutorial("tutorial_playing", _("Player Experience")) + Tutorial("tutorial_create", _("Creating a New Game")) + Tutorial("tutorial_dialogue", _("Writing Dialogue")) + Tutorial("tutorial_images", _("Adding Images")) + Tutorial("tutorial_simple_positions", _("Positioning Images")) + Tutorial("tutorial_transitions", _("Transitions")) + Tutorial("tutorial_music", _("Music and Sound Effects")) + Tutorial("tutorial_menus", _("Choices and Python")) + Tutorial("tutorial_input", _("Input and Interpolation")) + Tutorial("tutorial_video", _("Video Playback")) + Tutorial("tutorial_nvlmode", _("NVL Mode"), move=None) + Tutorial("director", _("Tools and the Interactive Director")) + Tutorial("distribute", _("Building Distributions")) + Section(_("In Depth")) + Tutorial("text", _("Text Tags, Escapes, and Interpolation")) + Tutorial("demo_character", _("Character Objects")) + Tutorial("simple_displayables", _("Simple Displayables"), move=None) + Tutorial("demo_transitions", _("Transition Gallery")) + Tutorial("tutorial_positions", _("Position Properties")) + Tutorial("tutorial_atl", _("Transforms and Animation")) + Tutorial("transform_properties", _("Transform Properties")) + Tutorial("new_gui", _("GUI Customization")) + Tutorial("styles", _("Styles and Style Properties"), move=None) + Tutorial("tutorial_screens", _("Screen Basics"), move=None) + Tutorial("screen_displayables", _("Screen Displayables"), move=None) + Tutorial("demo_minigame", _("Minigames and CDDs")) + Tutorial("translations", _("Translations")) +screen tutorials(adj): + frame: + xsize 640 + xalign .5 + ysize 485 + ypos 30 + has side "c r b" + viewport: + yadjustment adj + mousewheel True + draggable True + has vbox + for i in tutorials: + if i.kind == "tutorial": + textbutton i.title: + action Return(i) + left_padding 20 + xfill True + else: + null height 10 + text i.title alt "" + null height 5 + bar adjustment adj style "vscrollbar" + textbutton _("That's enough for now."): + xfill True + action Return(False) + top_margin 10 +default tutorials_adjustment = ui.adjustment() +default tutorials_first_time = True +label start: + scene bg washington + show eileen vhappy + with dissolve + play music "sunflower-slow-drag.ogg" + window show + e "Hi! My name is Eileen, and I'd like to welcome you to the Ren'Py tutorial." + show eileen happy + e "In this tutorial, we'll teach you the basics of Ren'Py, so you can make games of your own. We'll also demonstrate many features, so you can see what Ren'Py is capable of." +label tutorials: + show eileen happy at left + with move + if tutorials_first_time: + $ e(_("What would you like to see?"), interact=False) + else: + $ e(_("Is there anything else you'd like to see?"), interact=False) + $ tutorials_first_time = False + $ renpy.choice_for_skipping() + call screen tutorials(adj=tutorials_adjustment) + $ tutorial = _return + if not tutorial: + jump end + if tutorial.move_before: + show eileen happy at center + with move + $ reset_example() + call expression tutorial.label from _call_expression + if tutorial.move_after: + hide example + show eileen happy at left + with move + jump tutorials +label end: + show eileen happy at center + with move + show _finale behind eileen + e "Thank you for viewing this tutorial." + e "If you'd like to see a full Ren'Py game, select \"The Question\" in the launcher." + e "You can download new versions of Ren'Py from {a=https://www.renpy.org/}https://www.renpy.org/{/a}. For help and discussion, check out the {a=https://lemmasoft.renai.us/forums/}Lemma Soft Forums{/a}." + e "We'd like to thank Piroshki for contributing my sprites; Mugenjohncel for Lucy, the band, and drawn backgrounds; and Jake for the magic circle." + e "The background music is \"Sunflower Slow Drag\", by Scott Joplin and Scott Hayden, performed by the United States Marine Band. The concert music is by Alessio." + show eileen vhappy + e "We look forward to seeing what you create with Ren'Py. Have fun!" + window hide + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/testcases.rpy b/testcases/expected/tutorial-8.2/testcases.rpy new file mode 100644 index 00000000..f2e098de --- /dev/null +++ b/testcases/expected/tutorial-8.2/testcases.rpy @@ -0,0 +1,208 @@ +testcase player_experience: + scroll "Bar" until "Player Experience" + click until "Yes." + click + click + click + click button 4 + click button 4 + click button 4 + click button 4 + click button 5 + click button 5 + click button 4 + click button 4 + type PAGEDOWN + type PAGEDOWN + type PAGEUP + type PAGEUP + "No." + click until label tutorials +testcase new_game: + scroll "Bar" until "Creating a New Game" + click until label tutorials +testcase dialogue: + scroll "Bar" until "Writing Dialogue" + click until label tutorials +testcase images: + scroll "Bar" until "Adding Images" + click until label tutorials +testcase transitions: + scroll "Bar" until "Transitions" + click until label tutorials +testcase music: + scroll "Bar" until "Music and Sound Effects" + click until label tutorials +testcase choices: + scroll "Bar" until "Choices and Python" + click until "Yes, I do." + click until "Yes." + click until label tutorials +testcase input: + scroll "Bar" until "Input and Interpolation" + click until "Some games might prompt the player for input." + type "Tom" + type BACKSPACE + type "m" + type LEFT + type RIGHT + type "\n" + click until label tutorials +testcase positioning_images: + scroll "Bar" until "Positioning Images" + click until label tutorials +testcase video: + scroll "Bar" until "Video Playback" + click until label tutorials +testcase nvl_mode: + scroll "Bar" until "NVL Mode" + click until "Yes." + click until label tutorials +testcase tools: + scroll "Bar" until "Tools and the Interactive Director" + click until label tutorials +testcase building: + scroll "Bar" until "Building Distributions" + click until label tutorials +testcase text_tags: + scroll "Bar" until "Text Tags, Escapes, and Interpolation" + click until label tutorials +testcase character_objects: + scroll "Bar" until "Character Objects" + click until label tutorials +testcase simple_displayables: + scroll "Bar" until "Simple Displayables" + click until label tutorials +testcase transition_gallery: + $ _test.transition_timeout = 60.0 + scroll "Bar" until "Transition Gallery" + click until "Simple" + click until "ImageDissolve" + click until "MoveTransition" + click until "CropMove" + click until "PushMove" + click until "AlphaDissolve" + click until "something else" + click until label tutorials + $ _test.transition_timeout = 0.05 +testcase position_properties: + scroll "Bar" until "Position Properties" + click until "xpos .75 ypos .25" + click until label tutorials +testcase transforms: + scroll "Bar" until "Transforms and Animation" + click until label tutorials + scroll "Bar" until "Transform Properties" + click until label tutorials +testcase gui_customization: + scroll "Bar" until "GUI Customization" + click until label tutorials +testcase styles: + scroll "Bar" until "Styles and Style Properties" + click until "Style basics." + click until "General style properties." + click until "Text style properties." + click until "Window and Button style properties." + click until "Bar style properties." + click until "Box, Grid, and Fixed style properties." + click until "The Displayable Inspector." + click until "That's all I want to know." + click until label tutorials +testcase screens: + scroll "Bar" until "Screen Basics" + click until "What screens can do." + click until "Yes." + click until "How to show screens." + click until "you'll have to click" + "Okay" + click until "Passing parameters to screens." + click until "the call screen statement" + "Okay" + click until "Screen properties." + click until "Close This Screen" + pause .5 + "Close This Screen" + click until "Special screen statements." + click until "Using other screens." + click until "That's it." + scroll "Bar" until "Screen Displayables" + click until "Common properties" + click until "Adding images" + click until "Text" + click until "Buttons" + click until "Bars" + click until "Viewports" + click until "Imagemaps" + click until "Science" + click until "That's all" + click until label tutorials +testcase translations: + scroll "Bar" until "Translations" + click until label tutorials +testcase out_of_game: + "Back" + "Back" + "Skip" + "Back" + $ _preferences.self_voicing = False + $ _preferences.afm_time = 1 + "Auto" + scroll "Bar" until "Player Experience" + "Auto" + "History" + pause .5 + "Save" + "Save Slot 1" + "Yes" + "Load" + pause .5 + "Load Slot 1" + "Yes" + "Prefs" + pause .5 + "About" + pause .5 + "Help" + pause .5 + "Main Menu" + "Yes" + "Load" + pause .5 + "Load Slot 1" + click until "Yes." + click until label tutorials +testcase template: + scroll "Bar" until "-" + click until label tutorials +testcase default: + $ _test.transition_timeout = 0.05 + "Start" + click until label tutorials + call player_experience + call new_game + call dialogue + call images + call positioning_images + call transitions + call music + call choices + call input + call video + call nvl_mode + call tools + call building + call text_tags + call character_objects + call simple_displayables + call transition_gallery + call position_properties + call transforms + call gui_customization + call styles + call screens + call translations + call out_of_game + "That's enough for now." + click until "Quit" + pause .5 +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_atl.rpy b/testcases/expected/tutorial-8.2/tutorial_atl.rpy new file mode 100644 index 00000000..b900333d --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_atl.rpy @@ -0,0 +1,612 @@ +image bg band = Transform("concert1", zoom=.75) +image logo small = Transform("logo base", zoom=.66) +image concert: + subpixel True + size (1280, 720) + xalign .5 + yalign .5 + parallel: + "concert2" + pause 1.0 + block: + "concert1" with Dissolve(.1) + pause .4 + "concert2" with Dissolve(.1) + pause .4 + "concert3" with Dissolve(.1) + pause .4 + "concert2" with Dissolve(.1) + pause .4 + repeat + time 43.0 + parallel: + crop (213, 778, 590, 332) + pause 1.0 + easeout .6 crop (286, 818, 469, 264) + crop (87, 370, 590, 332) + easein .8 crop (14, 306, 791, 445) + time 2.9 + crop (1035, 656, 417, 235) + time 3.4 + crop (564, 545, 552, 311) + time 3.9 + crop (1035, 656, 417, 235) + time 4.4 + crop (564, 545, 552, 311) + time 5.0 + linear 4.0 crop (0, 482, 1738, 978) + easein 4.0 crop (267, 91, 2133, 1200) + easeout 4.0 crop (0, 91, 2133, 1200) + time 17.0 + crop (1047, 849, 1132, 637) + linear 4.0 crop (868, 58, 1532, 862) + time 22.25 + block: + choice: + crop (741, 517, 725, 408) + choice: + crop (409, 594, 1157, 651) + choice: + crop (719, 526, 753, 424) + choice: + crop (692, 521, 449, 253) + choice: + crop (468, 903, 1038, 584) + pass + choice: + zoom 1.0 + choice: + zoom 1.3 + choice: + zoom 1.5 + pause .43 + repeat + time 26.97 + zoom 1 + crop (0, 482, 1738, 978) + pause 1.0 + crop (0, 775, 984, 554) + easein 5.5 crop (0, 279, 984, 554) + easeout 4.0 crop (0, 91, 2133, 1200) + easein 4.0 crop (267, 91, 2133, 1200) +image pos: + "target1.png" + block: + rotate 0 + linear 2.0 rotate 360.0 + repeat +image anchor: + "target2.png" + block: + rotate 360.0 + linear 2.0 rotate 0.0 + repeat +example atl_right: + transform right: + xalign 1.0 + yalign 1.0 +example atl_image: + image eileen animated: + "eileen vhappy" + pause .5 + "eileen happy" + pause .5 + repeat +example atl_image1: + image eileen animated twice: + "eileen vhappy" + pause .5 + "eileen happy" + pause .5 + repeat 2 +example atl_image2: + image eileen animated once: + "eileen vhappy" + pause .5 + "eileen happy" +example atl_with: + image bg atl transitions: + "bg washington" + "bg whitehouse" with dissolve + pause 1.0 + "bg washington" with dissolve + pause 1.0 + repeat +example atl_transform: + transform topright: + xalign 1.0 yalign 0.0 +example atl_transform1: + transform move_jump: + xalign 1.0 yalign 0.0 + pause 1.0 + xalign 0.0 + pause 1.0 + repeat +example atl_transform2: + transform move_slide: + xalign 1.0 yalign 0.0 + linear 3.0 xalign 0.0 + pause 1.0 + repeat +transform reset: + xpos 0.5 + xanchor 0.5 + ypos 0.3 + yanchor 0.5 + zoom 1.0 + xzoom 1.0 + yzoom 1.0 + crop None + xsize None + ysize None + fit None + alpha 1.0 + rotate None + rotate_pad True + nearest False + additive 0.0 + xtile 1 + ytile 1 + xpan None + ypan None +label tutorial_positions: + e "In this tutorial, I'll teach you how Ren'Py positions things on the screen. But before that, let's learn a little bit about how Python handles numbers." + e "There are two main kinds of numbers in Python: integers and floating point numbers. An integer consists entirely of digits, while a floating point number has a decimal point." + e "For example, 100 is an integer, while 0.5 is a floating point number, or float for short. In this system, there are two zeros: 0 is an integer, and 0.0 is a float." + e "Ren'Py uses integers to represent absolute coordinates, and floats to represent fractions of an area with known size." + e "When we're positioning something, the area is usually the entire screen." + e "Let me get out of the way, and I'll show you where some positions are." + hide eileen + with moveoutright + show pos: + xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5 + subpixel True + with dissolve + show pos: + linear .5 xpos 0.0 ypos 0.0 + e "The origin is the upper-left corner of the screen. That's where the x position (xpos) and the y position (ypos) are both zero." + show pos: + ypos 0.0 + linear .5 xpos 0.5 + e "When we increase xpos, we move to the right. So here's an xpos of .5, meaning half the width across the screen." + show pos: + linear .5 xpos 1.0 + e "Increasing xpos to 1.0 moves us to the right-hand border of the screen." + show pos: + xpos 1280 + linear .5 xpos 640 + e "We can also use an absolute xpos, which is given in an absolute number of pixels from the left side of the screen. For example, since this window is 1280 pixels across, using an xpos of 640 will return the target to the center of the top row." + e "The y-axis position, or ypos works the same way. Right now, we have a ypos of 0.0." + show pos: + xpos 640 + linear .5 ypos .5 + e "Here's a ypos of 0.5." + show pos: + linear .5 ypos 1.0 + e "A ypos of 1.0 specifies a position at the bottom of the screen. If you look carefully, you can see the position indicator spinning below the text window." + e "Like xpos, ypos can also be an integer. In this case, ypos would give the total number of pixels from the top of the screen." + show pos: + linear .5 xpos .75 ypos .25 + menu: + e "Can you guess where this position is, relative to the screen?" + "xpos 1.0 ypos .5": + e "Sorry, that's wrong. The xpos is .75, and the ypos is .25." + e "In other words, it's 75%% of the way from the left side, and 25%% of the way from the top." + "xpos .75 ypos .25": + e "Good job! You got that position right." + "xpos .25 ypos .33": + e "Sorry, that's wrong. The xpos is .75, and the ypos is .25." + e "In other words, it's 75%% of the way from the left side, and 25%% of the way from the top." + hide pos + show logo solid: + xpos 523 ypos 100 + show anchor: + xanchor 0.5 yanchor 0.5 + xpos 523 ypos 100 + with dissolve + e "The second position we care about is the anchor. The anchor is a spot on the thing being positioned." + e "For example, here we have an xanchor of 0.0 and a yanchor of 0.0. It's in the upper-left corner of the logo image." + show anchor: + linear .5 xpos 757 + e "When we increase the xanchor to 1.0, the anchor moves to the right corner of the image." + show anchor: + linear .5 ypos 460 + e "Similarly, when both xanchor and yanchor are 1.0, the anchor is the bottom-right corner." + show pos: + xanchor .5 yanchor .5 + xpos 957 ypos 460 + e "To place an image on the screen, we need both the position and the anchor." + show logo solid: + linear .5 xpos 723 ypos 100 + show anchor: + linear .5 xpos 957 ypos 460 + e "We then line them up, so that both the position and anchor are at the same point on the screen." + show anchor: + linear .5 xpos 0 ypos 0 + show pos: + linear .5 xpos 0 ypos 0 + show logo solid: + linear .5 xpos 0 ypos 0 + e "When we place both in the upper-left corner, the image moves to the upper-left corner of the screen." + show anchor: + linear .5 xpos 0.5 ypos 0.5 + show pos: + linear .5 xpos 0.5 ypos 0.5 + show logo solid: + linear .5 xalign 0.5 yalign 0.5 + e "With the right combination of position and anchor, any place on the screen can be specified, without even knowing the size of the image." + show logo solid: + linear .5 yalign .3 + with None + hide anchor + hide pos + with dissolve + e "It's often useful to set xpos and xanchor to the same value. We call that xalign, and it gives a fractional position on the screen." + show logo solid: + linear .5 xalign 0.0 + e "For example, when we set xalign to 0.0, things are aligned to the left side of the screen." + show logo solid: + linear .5 xalign 1.0 + e "When we set it to 1.0, then we're aligned to the right side of the screen." + show logo solid: + linear .5 xalign .5 + e "And when we set it to 0.5, we're back to the center of the screen." + e "Setting yalign is similar, except along the y-axis." + e "Remember that xalign is just setting xpos and xanchor to the same value, and yalign is just setting ypos and yanchor to the same value." + show logo solid: + linear .5 xcenter .75 + e "The xcenter and ycenter properties position the center of the image. Here, with xcenter set to .75, the center of the image is three-quarters of the way to the right side of the screen." + show logo solid: + linear 1.0 xcenter 1.0 + e "The difference between xalign and xcenter is more obvious when xcenter is 1.0, and the image is halfway off the right side of the screen." + show logo solid: + linear .5 xalign 0.5 yalign 0.5 + linear .5 xoffset 50 yoffset 20 + pause .5 + e "There are the xoffset and yoffset properties, which are applied after everything else, and offset things to the right or bottom, respectively." + show logo solid: + linear .5 xoffset -50 yoffset -20 + e "Of course, you can use negative numbers to offset things to the left and top." + show logo solid: + linear .5 align (0.5, 0.5) offset (0, 0) + e "Lastly, I'll mention that there are combined properties like align, pos, anchor, and center. Align takes a pair of numbers, and sets xalign to the first and yalign to the second. The others are similar." + hide logo + with dissolve + show eileen happy + with moveinright + e "Once you understand positions, you can use transformations to move things around the Ren'Py screen." + return +label tutorial_atl: + e "Ren'Py uses transforms to animate, manipulate, and place images. We've already seen the very simplest of transforms in use:" + example simple_transform: + show eileen happy at right + with move + e "Transforms can be very simple affairs that place the image somewhere on the screen, like the right transform." + hide example + e "But transforms can also be far more complicated affairs, that introduce animation and effects into the mix. To demonstrate, let's have a Gratuitous Rock Concert!" + stop music fadeout 1.0 + scene concert + with fade + play music "renpyallstars.ogg" noloop + e "But first, let's have... a Gratuitous Rock Concert!" + play music "sunflower-slow-drag.ogg" fadeout 1.0 + scene bg washington + show eileen happy + with dissolve + e "That was a lot of work, but it was built out of small parts." + e "Most transforms in Ren'Py are built using the Animation and Transform Language, or ATL for short." + e "There are currently three places where ATL can be used in Ren'Py." + show example atl_image + show eileen animated + e "The first place ATL can be used is as part of an image statement. Instead of a displayable, an image may be defined as a block of ATL code." + e "When used in this way, we have to be sure that ATL includes one or more displayables to actually show." + show example atl_transform + show eileen happy at right + e "The second way is through the use of the transform statement. This assigns the ATL block to a python variable, allowing it to be used in at clauses and inside other transforms." + example: + show logo base: + xalign .3 yalign .7 + linear 1.0 xalign .7 yalign .3 + linear 1.0 xalign .3 yalign .7 + repeat + with dissolve + e "Finally, an ATL block can be used as part of a show statement, instead of the at clause." id tutorial_atl_da7a7759 + example: + show logo base: + yoffset 10 + e "When ATL is used as part of a show statement, values of properties exist even when the transform is changed. So even though your click stopped the motion, the image remains in the same place." id tutorial_atl_1dd345c6 + hide logo + show eileen happy at center + with moveoutleft + hide screen example + e "The key to ATL is what we call composability. ATL is made up of relatively simple commands, which can be combined together to create complicated transforms." + e "Before I explain how ATL works, let me explain what animation and transformation are." + show eileen animated: + center + e "Animation is when the displayable being shown changes. For example, right now I am changing my expression." + show eileen happy + show magic: + yalign .5 subpixel True + parallel: + xalign .5 + linear 3.0 xalign .75 + linear 6.0 xalign .25 + linear 3.0 xalign .5 + repeat + parallel: + alpha 1.0 zoom 1.0 + linear .75 alpha .5 zoom .9 + linear .75 alpha 1.0 zoom 1.0 + repeat + parallel: + rotate 0 + linear 5 rotate 360 + repeat + with dissolve + e "Transformation involves moving or distorting an image. This includes placing it on the screen, zooming it in and out, rotating it, and changing its opacity." + hide magic + with dissolve + show example atl_image + show eileen animated + e "To introduce ATL, let's start by looking at a simple animation. Here's one that consists of five lines of ATL code, contained within an image statement." id tutorial_atl_fbc9bf83 + e "To change a displayable, simply mention it on a line of ATL. Here, we're switching back and forth between two images." + e "Since we're defining an image, the first line of ATL must give a displayable. Otherwise, there would be nothing to show." + e "The second and fourth lines are pause statements, which cause ATL to wait half a second each before continuing. That's how we give the delay between images." + e "The final line is a repeat statement. This causes the current block of ATL to be restarted. You can only have one repeat statement per block." + show example atl_image1 + show eileen animated twice + e "If we were to write repeat 2 instead, the animation would loop twice, then stop." + show example atl_image1 + show eileen animated once + e "Omitting the repeat statement means that the animation stops once we reach the end of the block of ATL code." + show example atl_with + show bg atl transitions + e "By default, displayables are replaced instantaneously. We can also use a with clause to give a transition between displayables." + show bg washington + with dissolve + hide screen example + e "With animation done, we'll see how we can use ATL to transform images, starting with positioning an image on the screen." + show logo base behind eileen + show logo base at topright + with dissolve + show example atl_transform + e "The simplest thing we can do is to statically position an image. This is done by giving the names of the position properties, followed by the property values." id tutorial_atl_ddc55039 + show example atl_transform1 + show logo base at move_jump + e "With a few more statements, we can move things around on the screen." + e "This example starts the image off at the top-right of the screen, and waits a second. It then moves it to the left side, waits another second, and repeats." + e "The pause and repeat statements are the same statements we used in our animations. They work throughout ATL code." + show example atl_transform2 + show logo base at move_slide + e "Having the image jump around on the screen isn't all that useful. That's why ATL has the interpolation statement." + e "The interpolation statement allows you to smoothly vary the value of a transform property, from an old to a new value." + e "Here, we have an interpolation statement on the second ATL line. It starts off with the name of a time function, in this case linear." + e "That's followed by an amount of time, in this case three seconds. It ends with a list of properties, each followed by its new value." + e "The value of each property is interpolated from its value when the statement starts to the value at the end of the statement. This is done once per frame, allowing smooth animation." + hide example + show eileen happy at right + with move + show logo base: + alignaround (.5, .3) + linear 2.0 clockwise circles 3 xalign .5 yalign .3 + e "ATL supports more complicated move types, like circle and spline motion. But I won't be showing those here." + hide logo with dissolve + e "Apart from displayables, pause, interpolation, and repeat, there are a few other statements we can use as part of ATL." + example large: + show eileen happy: + right + pause 1.25 + left + pause 1.25 + repeat + with dissolve + e "ATL transforms created using the statement become ATL statements themselves. Since the default positions are also transforms, this means that we can use left, right, and center inside of an ATL block." + show eileen happy at center + show logo base behind eileen + with dissolve + example: + show logo base: + xalign 0.0 yalign 0.0 + block: + linear 1.0 xalign 1.0 + linear 1.0 xalign 0.0 + repeat + time 11.5 + linear .5 xalign 1.0 + e "Here, we have two new statements. The block statement allows you to include a block of ATL code. Since the repeat statement applies to blocks, this lets you repeat only part of an ATL transform." + e "We also have the time statement, which runs after the given number of seconds have elapsed from the start of the block. It will run even if another statement is running, stopping the other statement." + e "So this example bounces the image back and forth for eleven and a half seconds, and then moves it to the right side of the screen." + example: + show logo base: + parallel: + linear 1.0 xalign 0.0 + linear 1.0 xalign 1.0 + repeat + parallel: + linear 1.3 yalign 1.0 + linear 1.3 yalign 0.0 + repeat + e "The parallel statement lets us run two blocks of ATL code at the same time." + e "Here, the top block move the image in the horizontal direction, and the bottom block moves it in the vertical direction. Since they're moving at different speeds, it looks like the image is bouncing on the screen." + show logo base: + yalign 0.0 + xalign 0.0 + example: + show logo base: + choice: + linear 1.0 xalign 0.0 + choice: + linear 1.0 xalign 1.0 + repeat + e "Finally, the choice statement makes Ren'Py randomly pick a block of ATL code. This allows you to add some variation as to what Ren'Py shows." + hide logo base + with dissolve + hide example + e "This tutorial game has only scratched the surface of what you can do with ATL. For example, we haven't even covered the on and event statements. For more information, you might want to check out {a=https://renpy.org/doc/html/atl.html}the ATL chapter in the reference manual{/a}." + return +label transform_properties: + e "Ren'Py has quite a few transform properties that can be used with ATL, the Transform displayable, and the add Screen Language statement." + e "Here, we'll show them off so you can see them in action and get used to what each does." + show eileen happy at right + with move + example: + show logo base: + xpos 0.5 + xanchor 0.5 + ypos 0.3 + yanchor 0.5 + with dissolve + e "First off, all of the position properties are also transform properties. These include the pos, anchor, align, center, and offset properties." + hide eileen + hide logo base + show bg band: + xanchor 0 yanchor 0 + xpos 0 ypos -428 + with dissolve + example: + show bg band: + xanchor 0 yanchor 0 + xpos 0 ypos -428 + linear 3.0 xpos -220 ypos -60 + e "The position properties can also be used to pan over a displayable larger than the screen, by giving xpos and ypos negative values." + if False: + example: + show bg band: + subpixel False + linear 60.0 xpos 0 + "The subpixel property controls how things are lined up with the screen. When False, images can be pixel-perfect, but there can be pixel jumping." + example: + show bg band: + subpixel True + linear 60.0 xpos 0 + "When it's set to True, movement is smoother at the cost of blurring images a little." + hide bg + show bg washington + show eileen happy at right + hide logo + example: + show logo small: + anchor (0.5, 0.5) + around (640, 216) + angle 270 + radius 200 + with dissolve + e "Transforms also support polar coordinates. The around property sets the center of the coordinate system to coordinates given in pixels." + example: + show logo small: + linear 1.0 angle 315 + linear 1.0 angle 270 + repeat + e "The angle property gives the angle in degrees. Angles run clockwise, with the zero angle at the top of the screen." + example: + show logo small: + linear 1.0 radius 100 + linear 1.0 radius 200 + repeat + e "The radius property gives the distance in pixels from the anchor of the displayable to the center of the coordinate system." + hide logo small + show logo base at reset + with dissolve + example: + show logo base: + zoom 1.0 + linear 1.0 zoom 1.5 + linear 1.0 zoom 1.0 + repeat + e "There are several ways to resize a displayable. The zoom property lets us scale a displayable by a factor, making it bigger and smaller." + show logo base at reset + example: + show logo base: + xzoom .75 yzoom 1.25 + linear 1.0 xzoom 1.25 yzoom .75 + linear 1.0 xzoom .75 yzoom 1.25 + repeat + with dissolve + e "The xzoom and yzoom properties allow the displayable to be scaled in the X and Y directions independently." + show logo base at reset + example: + show logo base: + linear 1.0 xzoom -1.0 yzoom 1.0 + with dissolve + e "By making xzoom or yzoom a negative number, we can flip the image horizontally or vertically." + show logo base at reset + example: + show logo base: + size (350, 540) + with dissolve + e "Instead of zooming by a scale factor, the size transform property can be used to scale a displayable to a size in pixels." + show logo base at reset + example: + show logo base: + alpha 1.0 + linear 1.0 alpha 0.0 + pause .5 + linear 1.0 alpha 1.0 + pause .5 + repeat + with dissolve + e "The alpha property is used to change the opacity of a displayable. This can make it appear and disappear." + show logo base at reset + example: + show logo base: + xanchor 0.5 yanchor 0.5 + rotate 0 + linear 4.0 rotate 360 + repeat + with dissolve + e "The rotate property rotates a displayable." + example: + show logo base: + xalign 0.0 yalign 0.0 + rotate 0 + linear 4.0 rotate 360 + repeat + with dissolve + e "By default, when a displayable is rotated, Ren'Py will include extra space on all four sides, so the size doesn't change as it rotates. Here, you can see the extra space on the left and top, and it's also there on the right and bottom." + example: + show logo base: + rotate_pad False + xalign 0.0 yalign 0.0 + rotate 0 + linear 4.0 rotate 360 + repeat + with dissolve + e "By setting rotate_pad to False, we can get rid of the space, at the cost of the size of the displayable changing as it rotates." + show logo base at reset + example: + show logo base: + xtile 3 + ytile 2 + with dissolve + e "The tile transform properties, xtile and ytile, repeat the displayable multiple times." + show logo base at reset + example: + show logo base: + crop (0, 0, 117, 360) + with dissolve + e "The crop property crops a rectangle out of a displayable, showing only part of it." + hide logo base + with dissolve + example: + show bg washington: + crop (0, 0, 800, 600) + size (1280, 720) + linear 4.0 crop (451, 437, 409, 230) + with dissolve + e "When used together, crop and size can be used to focus in on specific parts of an image." + hide bg + example: + show bg panorama: + xpan 0 + linear 10.0 xpan 360 + repeat + with dissolve + e "The xpan and ypan properties can be used to pan over a displayable, given an angle in degrees, with 0 being the center." + hide example + scene bg washington + show eileen happy + with dissolve + e "Those are all the transform properties we have to work with. By putting them together in the right order, you can create complex things." + show eileen happy + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_director.rpy b/testcases/expected/tutorial-8.2/tutorial_director.rpy new file mode 100644 index 00000000..aef10799 --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_director.rpy @@ -0,0 +1,41 @@ +define director.show_tags = { "eileen", "lucy" } +label director: + e "There are a few tools you can access by pressing the right commands on the keyboard." + e "Typing Shift+R turns on autoreload mode. When it's enabled, your game will automatically reload when you edit a script file." + e "Shift+O brings you to the console, which lets you enter Ren'Py and Python commands to try them out." + e "Shift+D pops up a developer menu with access to these and other functions." + e "The most powerful tool is the interactive director that lets you add images, music, and voice lines to your game from inside Ren'Py." + e "The idea is that you can use an editor to write the script and logic of your visual novel, and then interactively add images in the right places." + if director_readonly: + show eileen concerned + e "It looks like Ren'Py is installed read-only on your system, so you won't be able to try out the interactive director now." + e "You'll need to make your own project, and try it out there. But I can tell you how to use it." + show eileen happy + else: + e "You can try the interactive director out right now, by using it to change this tutorial game." + e "Be sure to click my dialogue at the bottom of the screen to advance the tutorial." + e "If something goes wrong, don't worry. Quitting and restarting this tutorial will remove your changes and bring everything back to normal." + stop music fadeout 1.0 + scene + with dissolve + $ _director_enable = not director_readonly + e "To get started, let's go back to a blank slate, with no images on the screen." + e "You can show the director at any time by pressing the 'D' key on your keyboard. Ren'Py will reload, and you'll come back here. Try it now." + e "Let's add a background. Click the '+' to pick where to add it, then the 'scene' statement and 'washington' for the image. Finally, click 'Add' to add it." + e "Next, add a sprite. Click '+', then 'show', 'eileen', 'happy', and 'Add'. Once you've added it, dissolve it in by clicking the second '+', then 'with', 'dissolve', and 'Add'." + show eileen happy + e "You can edit or remove statements with the pencil icon. You can move me to the right by editing the show statement, then clicking '(transform)', 'right', and 'Change'." + e "Finally, you can use the play, queue, stop, and voice statements to manage audio. Try adding 'play', 'music', 'sunflower-slow-drag.ogg'." + $ director.state.show_director = False + $ _director_enable = False + if renpy.showing("lucy"): + l "Finally, I get some more screen time!" + queue music "sunflower-slow-drag.ogg" + scene bg washington + show eileen happy + with dissolve + e "The changes you make with the director are permanent. They're saved to the script, and you can rollback or repeat this section to see them." + e "However, we reset this tutorial when the game restarts, so you can try again from a clean slate. That won't happen with your own visual novel." + e "I hope these tools make developing your visual novel that much easier." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_distribute.rpy b/testcases/expected/tutorial-8.2/tutorial_distribute.rpy new file mode 100644 index 00000000..8abe890b --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_distribute.rpy @@ -0,0 +1,20 @@ +label distribute: + e "One thing Ren'Py makes easy is building distributions of your visual novel so you can give it to players." + e "Before you build distributions, you should use the Lint command to check your game for problems." + e "While not every potential problem lint reports is a real issue, they generally are, and you should try to understand what might be wrong." + show launcher distribute at launcher_place + with moveinleft + e "After lint has finished, you can choose Build Distributions to build the Windows, Linux, and Mac distributions of your game." + e "This can be as simple as clicking the Build button, when you're not on a Mac." + e "If you are on a Macintosh, you can have Ren'Py sign the Mac application, which makes it easier for players to run. To enable this, you need to set build.mac_identity in options.rpy." + hide launcher distribute + with moveoutleft + e "Ren'Py supports the mobile platforms, Android and iOS. We also support ChromeOS, through its ability to run Android apps." + e "These mobile platforms can be a bit more complicated. While Android apps can be built everywhere, iOS requires a Mac." + e "Mobile platforms might also require you to change your visual novel a little, due to the smaller limited devices. For example, buttons need to be made large enough for a person to touch." + e "Rather than cover mobile here, I'll point you to the {a=https://www.renpy.org/doc/html/android.html}Android{/a} and {a=https://www.renpy.org/doc/html/ios.html}iOS{/a} documentation, where you can read more." + e "Thanks to the distribution tools Ren'Py ships with, there are thousands of visual novels available." + show eileen vhappy + e "I hope that soon, yours will be one of them!" + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_nvlmode.rpy b/testcases/expected/tutorial-8.2/tutorial_nvlmode.rpy new file mode 100644 index 00000000..696c1d2f --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_nvlmode.rpy @@ -0,0 +1,47 @@ +example nvl1: + define nvle = Character(_("Eileen"), color="#c8ffc8", kind=nvl) +define config.adv_nvl_transition = dissolve +define config.nvl_adv_transition = dissolve +example nvl3a: + define menu = nvl_menu +define menu = renpy.display_menu +define config.lint_ignore_redefine += [ "store.menu" ] +label tutorial_nvlmode: + window hide + nvl clear + nvl show dissolve + nvle "NVL-style games are games that cover the full screen with text, rather then placing it in a window at the bottom of the screen. Like this." + show example nvl1 bottom + nvle "To use NVL-mode, you need to define Characters with a kind=nvl." + example large bottom: + nvle "Then just use that character in a say statement." + nvl clear + nvle "You use 'nvl clear' to clear the screen when that becomes necessary." + nvl hide dissolve + nvl show dissolve + nvle "The 'nvl show' and 'nvl hide' statements use transitions to show and hide the NVL window." + $ menu = nvl_menu + show example nvl3a nvl3 large bottom +example nvl3 hide: + menu: + nvle "NVL-mode also supports showing menus to the player, providing it's the last thing on the screen. Understand?" + "Yes.": + nvl clear + nvle "Good!" + "No.": + nvl clear + nvle "Well, hopefully the code below makes it a little more clear." +label after_nvl_menu: + hide example + nvle "Games can mix NVL-mode and the normal ADV-mode by having some characters that have kind=nvl, and some that do not." + e "You can specify transitions that occur when going from NVL-mode to ADV-mode." + nvle "As well as when going from ADV-mode to NVL-mode." + nvle "Text tags like {{w}{w} work in NVL-mode." + extend " As does the \"extend\" special character." + nvle "And that's it for NVL-mode." + $ menu = renpy.display_menu + nvl hide dissolve + $ _last_say_who = None + window show dissolve + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_playing.rpy b/testcases/expected/tutorial-8.2/tutorial_playing.rpy new file mode 100644 index 00000000..90425682 --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_playing.rpy @@ -0,0 +1,56 @@ +transform popup_place: + xpos 0.1 xanchor 0.0 ypos 0.1 yanchor 0.0 +label tutorial_playing: + e "As someone who has played more than a few visual novels, there are many features that I expect all games to have." + e "Features like saving, loading, changing preferences, and so on." + e "One of the nice things about Ren'Py is that the engine provides many of these features for you. You can spend your time creating your game, and let us provide these things." + e "While you're in the game, you can access the game menu by right clicking or hitting the escape key." + e "You can also access the game menu through some of the quick menu buttons at the bottom of this screen." + show eileen happy at right + show popup save at popup_place + with moveinleft + e "When you first enter the game menu, you'll see the save screen. Clicking on a numbered slot will save the game." + e "Unlike other engines, Ren'Py doesn't limit the number of save slots that you can use. You can keep hitting next until you reach the page you want." + e "Clicking on the title of the page allows you to start typing to change the page name." + e "The load screen looks quite similar to the save screen, and lets you load a game from a save slot." + e "It also lets you load one of the auto-saves that Ren'Py makes for you." + show popup prefs at popup_place + with dissolve + e "The game menu also has the preferences screen." + e "This screen lets you decide how Ren'Py displays, pick what Ren'Py skips, control text speed and auto-click speed, and adjust sound, music, and voice volumes." + e "The game menu also lets you end the game and return to the main menu, or quit Ren'Py entirely." + show popup hrpprefs at popup_place + with dissolve + e "While the default game menus look a bit generic, with a little work they can be customized or even entirely replaced, allowing you to create menus as unique as your game." + hide popup + show eileen happy at center + with moveoutleft + e "While inside the game, there are a few more things you can do." + e "When I'm liking a visual novel, I want to see all the endings. Ren'Py's skip function lets me easily do this, by skipping text that I've already seen." + e "I can skip a few lines by holding down Control, or I can toggle skip mode by clicking the skip button at the bottom of the screen." + e "By default, we only skip read text, so this won't do anything the first time through the game." + e "Clicking the auto button toggles auto-forward mode, which makes the game advance without you clicking." + e "The Q.Save and Q.Load buttons provide a single-click way to make a save, and a fast way to load that save again." + e "Pressing the 's' key saves a screenshot to disk, so I can upload pictures of the game to websites like {a=https://www.renpy.org}renpy.org{/a}." + e "The history button displays a history of read text - but you can also use rollback, which is usually better." + menu: + e "Would you like to hear about rollback?" + "Yes.": + jump tutorial_rollback + "No.": + jump tutorial_rollback_done +label tutorial_rollback: + e "You can invoke a rollback by clicking the 'Back' button, scrolling the mouse wheel up, or by pushing the page up key. That'll bring you back to the previous screen." + e "While at a previous screen, you can roll forward by scrolling the mouse wheel down, or pushing the page down key." + e "Rolling forward through a menu will make the same choice you did last time. But you don't have to do that - Ren'Py's rollback system allows you to make a different choice." + e "You can try it by rolling back through the last menu, and saying 'No'." + e "Click back a few times, press page up, or scroll up the mouse wheel." + show eileen concerned + e "Well, are you going to try it?" + e "Your loss." + e "Moving on." + show eileen happy +label tutorial_rollback_done: + e "By allowing Ren'Py to take care of out-of-game issues like loading and saving, you can focus on making your game, while still giving players the experience they've come to expect when playing visual novels." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_quickstart.rpy b/testcases/expected/tutorial-8.2/tutorial_quickstart.rpy new file mode 100644 index 00000000..7dc56ba8 --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_quickstart.rpy @@ -0,0 +1,338 @@ +define l = Character(_("Lucy"), color="#ffcccc") +define slowdissolve = Dissolve(1.0) +default name = "Player" +transform rightish: + xcenter .6 + ypos 50 +transform launcher_place: + xpos 0.05 + xanchor 0 + ypos 0.1 + yanchor 0 +label tutorial_create: + show launcher step1 at launcher_place + with dissolve + e "When you're ready to use Ren'Py to create your visual novel, the first step is to create a new project." + e "You can create a new project by clicking 'Create New Project' on the front screen of the launcher." + e "If this is your first time using Ren'Py, it'll ask you for the place you want to keep your projects. The best place is always somewhere that's frequently backed up." + show launcher step2 + e "After that, Ren'Py will ask for a name for your project. You'll have to stick to English letters and numbers, as zip files can't handle anything more than that." + show launcher step3 + e "The next thing Ren'Py will ask for is the resolution the visual novel will run at. This controls how large or small you'll have to make your game's artwork." + show launcher step4 + e "Finally, Ren'Py will ask you to select a color scheme. You can change this after the game has been created, so just pick a color that's pleasing." + show launcher step5 + e "Once that's done, Ren'Py will work for a bit and return you to the main menu with the new project selected. Now, when you click Launch, Ren'Py will start your new game." + e "To get back here, you can choose 'Tutorial' to switch to this tutorial game." + e "You'll also need to edit the games script to make changes. To do that, click 'script.rpy' on the front page of the launcher." + e "If it's your first time doing so, Ren'Py will ask you to select a text editor. Atom might be a safe choice, but read the descriptions to be sure." + e "After the text editor is downloaded, the script will open up and you can start to change what characters are saying." + hide launcher + with dissolve + return +label tutorial_dialogue: + e "Probably the most common thing a creator does with Ren'Py is to write dialogue for the player to read." + e "But before I can show you how to write dialogue, let me show you how we present script examples." + example large: + "Eileen" "Examples will show up in a window like the one above. You'll need to click outside of the example window in order to advance the tutorial." + "Eileen" "When an example is bigger than the screen, you can scroll around in it using the mouse wheel or by simply dragging the mouse." + "Eileen" "Script might seem scary at first, but if you look you'll see it's easy to match it up to what I'm saying." + hide example + e "Let's see the simplest possible Ren'Py game." + scene black + with dissolve + example dialogue1 hide: + "Wow, It's really really dark in here." + "Lucy" "Better watch out. You don't want to be eaten by a Grue." + scene bg washington + show eileen happy + with dissolve + show example start dialogue1 + e "I'll show you the script of that example." + e "This script demonstrates two kinds of Ren'Py statements, labels and say statements." + e "The first line is a label statement. The label statement is used to give a name to a place in the program." + e "In this case, we're naming a place \"start\". The start label is special, as it marks the place a game begins running." + e "The next line is a simple say statement. It consists of a string beginning with a double-quote, and ending at the next double-quote." + e "Special characters in strings can be escaped with a backslash. To include \" in a string, we have to write \\\"." + hide example + scene black + with dissolve + "Wow, It's really really dark in here." + scene bg washington + show eileen happy + with dissolve + show example start dialogue1 + e "When Ren'Py sees a single string on a line by itself, it uses the narrator to say that string. So a single string can be used to express a character's thoughts." + hide example + scene black + with dissolve + "Lucy" "Better watch out. You don't want to be eaten by a Grue." + scene bg washington + show eileen happy + with dissolve + show example start dialogue1 + e "When we have two strings separated by a space, the first is used as the character's name, and the second is what the character is saying." + e "This two-argument form of the say statement is used for dialogue, where a character is speaking out loud." + e "If you'd like, you can run this game yourself by erasing everything in your project's script.rpy file, and replacing it with the code in the box above." + e "Be sure to preserve the spacing before lines. That's known as indentation, and it's used to help Ren'Py group lines of script into blocks." + hide example + e "Using a string for a character's name is inconvenient, for two reasons." + e "The first is that's it's a bit verbose. While typing \"Lucy\" isn't so bad, imagine if you had to type \"Eileen Richardson\" thousands of times." + e "The second is that it doesn't leave any place to put styling, which can change the look of a character." + e "To solve these problems, Ren'Py lets you define Characters." + show example characters + e "Here's an example Character definition. It begins with the word \"define\". That tells Ren'Py that we are defining something." + e "Define is followed by a short name for the character, like \"l\". We'll be able to use that short name when writing dialogue." + e "This is followed by an equals sign, and the thing that we're defining. In this case, it's a Character." + e "On the first line, the character's name is given to be \"Lucy\", and her name will be drawn a reddish color." + e "These short names are case-sensitive. Capital L is a different name from lower-case l, so you'll need to be careful about that." + hide example + e "Now that we have a character defined, we can use it to say dialogue." + scene black + with dissolve + example dialogue2 hide: + l "Why are you trying to put words into my mouth? And who are you calling \"it\"?" + l "What's more, what are you going to do about the Grue problem? Are you just going to leave me here?" + scene bg washington + show eileen happy + with dissolve + show example characters start dialogue1 dialogue2 + e "Here's the full game, including the two new lines of dialogue, both of which use the Character we defined to say dialogue." + e "The one-argument form of the say statement is unchanged, but in the two-argument form, instead of the first string we can use a short name." + e "When this say statement is run, Ren'Py will look up the short name, which is really a Python variable. It will then use the associated Character to show the dialogue." + e "The Character object controls who is speaking, the color of their name, and many other properties of the dialogue." + hide example + e "Since the bulk of a visual novel is dialogue, we've tried to make it as easy to write as possible." + e "Hopefully, by allowing the use of short names for characters, we've succeeded." + return +label tutorial_images: + e "A visual novel isn't much without images. So let's add some images to our little game." + e "Before we can show images, we must first choose image names, then place the image files into the images directory." + e "An image name is something like 'bg cave' or 'lucy happy', with one or more parts separated by spaces." + e "Each part should start with a lower-case letter, and then contain lower-case letters, numbers, and underscores." + e "The first part of an image is called the tag. For 'bg cave' the tag is 'bg', while for 'lucy happy' the tag is 'lucy'." + e "You can open the images directory by clicking the appropriate button in the Ren'Py launcher." + e "The files in the images directory should have the same name as the image, followed by an extension like .jpg, .png, or .webp." + e "Our example uses 'bg cave.jpg', 'lucy happy.png', and 'lucy mad.png'." + hide screen example + e "Let's see what those look like in the game." + example images1 hide: + scene bg cave + show lucy happy + l "Now that the lights are on, we don't have to worry about Grues anymore." + show lucy mad at right + l "But what's the deal with me being in a cave? Eileen gets to be out in the sun, and I'm stuck here!" + scene bg washington + show eileen happy + with dissolve + show example start images1 large + e "Here's the script for that scene. Notice how it includes two new statements, the scene and show statement." + e "The scene statement clears the screen, and then adds a background image." + e "The show statement adds a new image on top of all the other images on the screen." + e "If there is already an image with the same tag, the new image is used to replace the old one." + e "Changes to the list of shown images take place instantly, so in the example, the user won't see the background by itself." + e "The second show statement has an at clause, which gives a location on the screen. Common locations are left, right, and center, but you can define many more." + example: + show logo base at rightish behind eileen + e "In this example, we show an image named logo base, and we show it at a creator-defined position, rightish." + e "We also specify that it should be shown behind another image, in this case eileen. That's me." + example: + hide logo + e "Finally, there's the hide statement, which hides the image with the given tag." + e "Since the show statement replaces an image, and the scene statement clears the scene, it's pretty rare to hide an image." + e "The main use is for when a character or prop leaves before the scene is over." + hide example + return +example slightleft: + transform slightleft: + xalign 0.25 + yalign 1.0 +label tutorial_simple_positions: + e "When the standard positions that come with Ren'Py aren't enough for you, you can create your own. Here, I'll show you the easy way to do it." + example: + show eileen happy: + xalign 0.75 + yalign 1.0 + with move + e "The first way to do it is to show an image followed by a colon. Then indented on the next couple of lines are the xalign and yalign transform properties." + e "Each of the transform properties is a name followed by a value. For xalign and yalign, the values are numbers." + e "The xalign transform property is the important one, as it controls where the image is placed horizontally on the screen." + example: + show eileen happy: + xalign 0.0 + yalign 1.0 + with move + e "An xalign of 0.0 is the left side." + example: + show eileen happy: + xalign 0.5 + yalign 1.0 + with move + e "0.5 is the center." + example: + show eileen happy: + xalign 1.0 + yalign 1.0 + with move + e "And 1.0 is the right. The decimal place is important and has to be there. Just 1 by itself won't work the same." + example: + show eileen happy: + xalign 0.75 + yalign 1.0 + with move + e "Of course, you can pick any position in between." + e "The yalign property is the same way, with 0.0 being the top of the screen and 1.0 being the bottom. Since most sprites stick to the bottom, it's almost always 1.0." + hide example + show eileen happy at center + with move + e "While being able to write positions like this is useful, having to repeatedly do so isn't. So Ren'Py lets you define a transform once, and reuse it." + show example slightleft + e "Usually transforms are defined at the top of a file, right after the characters. But it doesn't matter to Ren'Py where you define them." + e "The transform is given a name, slightleft, and then the xalign and yalign properties." + example: + show eileen vhappy at slightleft + with move + e "Once a transform has been defined, you can use it in the at clause of the show statement." + example: + show eileen happy + e "Transforms are sticky. If you replace an image without using a transform, Ren'Py will keep the same transforms it had been using." + hide example + e "Of course, there's a lot more to transforms than this. If you want to learn more, you can read the sections on Position Properties, Transforms and Animation, and Transform Properties." + e "But for many visual novels, xalign and yalign are the only properties that matter." + return +label tutorial_transitions: + e "It can be somewhat jarring for the game to jump from place to place." + scene bg whitehouse + pause .5 + scene bg washington + show eileen happy + e "To help take some of edge off a change in scene, Ren'Py supports the use of transitions. Let's try that scene change again, but this time we'll use transitions." + example trans1 hide: + scene bg whitehouse + with Dissolve(.5) + pause .5 + scene bg washington + show eileen happy + with Dissolve(.5) + show example trans1 large + e "That's much smoother. Here's some example code showing how we include transitions in our game." + e "It uses the with statement. The with statement causes the scene to transition from the last things shown to the things currently being shown." + e "It takes a transition as an argument. In this case, we're using the Dissolve transition. This transition takes as an argument the amount of time the dissolve should take." + e "In this case, each transition takes half a second." + show example slowdissolve + e "We can define a short name for a transition, using the define statement. Here, we're defining slowdissolve to be a dissolve that takes a whole second." + hide example + example trans2 hide: + scene bg whitehouse + with slowdissolve + scene bg washington + show eileen happy + with slowdissolve + show example trans2 + e "Once a transition has been given a short name, we can use it in our game." + hide example + e "Ren'Py defines some transitions for you, like dissolve, fade, and move. For more complex or customized transitions, you'll have to define your own." + e "If you're interested, check out the Transitions Gallery section of this tutorial." + return +label tutorial_music: + e "Another important part of a visual novel or simulation game is the soundtrack." + e "Ren'Py breaks sound up into channels. The channel a sound is played on determines if the sound loops, and if it is saved and restored with the game." + e "When a sound is played on the music channel, it is looped, and it is saved when the game is saved." + e "When the channel named sound is used, the sound is played once and then stopped. It isn't saved." + e "The sounds themselves are stored in audio files. Ren'Py supports the Opus, Ogg Vorbis, and mp3 formats." + e "Let's check out some of the commands that can affect the music channel." + example: + play music "sunflower-slow-drag.ogg" fadeout 1 + e "The play music command replaces the currently playing music, and replaces it with the named filename." + e "If you specify the currently-playing song, it will restart it." + e "If the optional fadeout clause is given, it will fade out the currently playing music before starting the new music." + example: + queue music "sunflower-slow-drag.ogg" + e "The queue statement also adds music to the named channel, but it waits until the currently-playing song is finished before playing the new music." + example: + stop music fadeout 1 + e "The third statement is the stop statement. It stops the music playing on a channel. It too takes the fadeout clause." + example: + play sound "tower_clock.ogg" + e "Unlike the music channel, playing a sound on the sound channel causes it to play only once." + example: + queue sound "tower_clock.ogg" + queue sound "tower_clock.ogg" + queue sound "tower_clock.ogg" + e "You can queue up multiple sounds on the sound channel, but the sounds will only play one at a time." + play music "sunflower-slow-drag.ogg" + hide example + e "Ren'Py has separate mixers for sound, music, and voices, so the player can adjust them as they like." + return +label tutorial_menus: + e "Many visual novels require the player to make choices from in-game menus. These choices can add some challenge to the game, or adjust it to the player's preferences." + e "Do you like to play visual novels with choices in them?" + example menu1 hide: + menu: + "Yes, I do.": + jump choice1_yes + "No, I don't.": + jump choice1_no + label choice1_yes: + $ menu_flag = True + e "While creating a multi-path visual novel can be a bit more work, it can yield a unique experience." + jump choice1_done + label choice1_no: + $ menu_flag = False + e "Games without menus are called kinetic novels, and there are dozens of them available to play." + jump choice1_done + label choice1_done: + show example menu1 large + e "Here, you can see the code for that menu. If you scroll down, you can see the code we run after the menu." + e "Menus are introduced by the menu statement. The menu statement takes an indented block, in which there can be one line of dialogue and multiple choices." + e "Each choice must end with a colon, as each choice has its own block of Ren'Py code, that is run when that choice is selected." + e "Here, each block jumps to a label. While you could put small amounts of Ren'Py code inside a menu label, it's probably good practice to usually jump to a bigger block of code." + e "Scrolling down past the menu, you can see the labels that the menu jumps to. There are three labels here, named choice1_yes, choice1_no, and choice1_done." + e "When the first menu choice is picked, we jump to the choice1_yes, which runs two lines of script before jumping to choice1_done." + e "Similarly, picking the second choice jumps us to choice1_no, which also runs two lines of script." + e "The lines beginning with the dollar sign are lines of python code, which are used to set a flag based on the user's choice." + e "The flag is named menu_flag, and it's set to True or False based on the user's choice. The if statement can be used to test a flag, so the game can remember the user's choices." + example: + if menu_flag: + e "For example, I remember that you plan to use menus in your game." + else: + e "For example, I remember that you're planning to make a kinetic novel, without menus." + e "Here's an example that shows how we can test a flag, and do different things if it is true or not." +example: + menu: + e "Finally, this shows how you can show dialogue and menus at the same time. Understand?" + "Yes.": + e "Great." + "No.": + e "If you look at the example, before the first choice, there's an indented say statement." +label menu3_done: + hide example + e "Although we won't demonstrate it here, Ren'Py supports making decisions based on a combinations of points, flags, and other factors." + e "One of Ren'Py's big advantages is the flexibility using a scripting language like Python provides us. It lets us easily scale from kinetic novels to complex simulation games." + return +example guy: + define g = Character("[name]") +label tutorial_input: + e "Some games might prompt the player for input." + example input hide: + python: + name = renpy.input(_("What's your name?")) + name = name.strip() or __("Guy Shy") + python: + if name.lower() == "shiro": + renpy.run(OpenURL("https://shiro-heartcat.tumblr.com/")) + show example input + e "That's done with Python, and especially the renpy.input function. The first line of this example prompts the player for some texts, and sticks it in the name variable." + e "Often times, you'll want to clean the name up before you use it. The last line does that, by calling the strip method to remove whitespace, and replacing the name with a default if it's missing." + example: + e "To interpolate a variable, write it in square brackets. Isn't that right, [name]?" + show example guy + e "Variable names can also be shown in character names. To do that, just include the variable in square brackets in the character's name. Got it?" + example: + g "I think I do." + example: + $ answer = 42 + $ flag = True + e "Variable interpolation also works with other variables. Here, the answer is [answer] and the flag is [flag]." + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_screen_displayables.rpy b/testcases/expected/tutorial-8.2/tutorial_screen_displayables.rpy new file mode 100644 index 00000000..ec4bf3f5 --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_screen_displayables.rpy @@ -0,0 +1,577 @@ +label screen_displayables: + e "There are quite a few screen displayables. Here, I'll tell you about some of the most important ones." +label screen_displayables_menu: + $ reset_example() + menu: + e "What would you like to know about?" + "Common properties all displayables share.": + call screen_displayable_properties from _call_screen_displayable_properties + "Adding images and other displayables.": + call add_displayable from _call_add_displayable + "Text.": + call text_displayable from _call_text_displayable + "Boxes and other layouts.": + call layout_displayables from _call_layout_displayables + "Windows and frames.": + call window_displayables from _call_window_displayables + "Buttons.": + call button_displayables from _call_button_displayables + "Bars.": + call bar_displayables from _call_bar_displayables + "Viewports.": + call viewport_displayables from _call_viewport_displayables + "Imagemaps.": + call imagemap_displayables from _call_imagemap_displayables + "That's all for now.": + return + jump screen_displayables_menu +label screen_displayable_properties: + e "There are a few properties that every screen language displayable shares. Here, I'll demonstrate them for you." + example large: + screen pos_example(): + frame: + xalign 0.5 ypos 50 + text _("This uses position properties.") + e "First off, every screen language displayable supports the position properties. When the container a displayable is in supports it, you can use properties like align, anchor, pos, and so so on." + example: + screen at_example(): + frame: + xalign 0.5 ypos 50 + text _("And the world turned upside down..."): + at rotated + transform rotated: + rotate 180 rotate_pad False + e "The at property applies a transform to the displayable, the same way the at clause in the show statement does." + hide screen at_example + with dissolve + show example say_screen + e "The id property is mostly used with the say screen, which is used to show dialogue. Outside of the say screen, it isn't used much." + e "It tells Ren'Py which displayables are the background window, 'who' is speaking, and 'what' is being said. This used to apply per-Character styles, and help with auto-forward mode." + example: + screen style_example(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("Flight pressure in tanks.") style "green_text" + text _("On internal power.") + text _("Launch enabled.") + text _("Liftoff!") + style green_text: + color "#c8ffc8" + e "The style property lets you specify the style of a single displayable." + example: + screen style_prefix_example(): + frame: + xalign 0.5 ypos 50 + vbox: + vbox: + style_prefix "green" + text _("Flight pressure in tanks.") + text _("On internal power.") + vbox: + style_prefix "yellow" + text _("Launch enabled.") + text _("Liftoff!") + style yellow_text: + color "#ffffc8" + e "The style_prefix property sets the prefix of the style that's used for a displayable and its children." + e "For example, when the style_prefix property is 'green', the vbox has the 'green_vbox' style, and the text in it has the 'green_text' style." + hide example + e "There are a few more properties than these, and you can find the rest in the documentation. But these are the ones you can expect to see in your game, in the default screens." + return +label add_displayable: + e "Sometimes you'll have a displayable, like an image, that you want to add to a screen." + example large: + screen add_image_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" + e "This can be done using the add statement, which adds an image or other displayable to the screen." + e "There are a few ways to refer to the image. If it's in the images directory or defined with the image statement, you can just put the name inside a quoted string." + example large: + screen add_filename_example(): + frame: + xalign 0.5 ypos 50 + add "images/logo base.png" + e "An image can also be referred to by its filename, relative to the game directory." id add_displayable_8ba81c26 + example large: + screen add_displayable_example(): + frame: + xalign 0.5 ypos 50 + add Solid("#0000ff", xsize=234, ysize=360) + e "Other displayables can also be added using the add statement. Here, we add the Solid displayable, showing a solid block of color." + example large: + screen add_transform_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" zoom 0.7 rotate 43.21 + e "In addition to the displayable, the add statement can be given transform properties. These can place or otherwise transform the displayable being added." + example: + screen add_at_transform_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" at unrotate + transform unrotate: + zoom 0.7 rotate 43.21 + linear 1.0 rotate 0 + e "Of course, the add statement can also take the at property, letting you give it a more complex transform." + hide example + return +label text_displayable: + example large: + screen text_example(): + frame: + xalign 0.5 ypos 50 + text _("This is a text displayable."): + size 30 + e "The screen language text statement adds a text displayable to the screen. It takes one argument, the text to be displayed." + e "In addition to the common properties that all displayables take, text takes the text style properties. For example, size sets the size of the text." + example large: + screen text_interpolation_example(): + $ answer = 42 + frame: + xalign 0.5 ypos 50 + text _("The answer is [answer].") + e "The text displayable can also interpolate values enclosed in square brackets." + e "When text is displayed in a screen using the text statement, variables defined in the screen take precedence over those defined outside it." id text_displayable_32d76ccb + e "Those variables may be parameters given to the screen, defined with the default or python statements, or set using the SetScreenVariable action." + example large: + screen text_tax_example(): + frame: + xalign 0.5 ypos 50 + text _("Text tags {color=#c8ffc8}work{/color} in screens.") + e "There's not much more to say about text in screens, as it works the same way as all other text in Ren'Py." + hide example + return +label layout_displayables: + e "The layout displayables take other displayables and lay them out on the screen." + example large: + screen hbox_example(): + frame: + xalign 0.5 ypos 50 + hbox: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + e "For example, the hbox displayable takes its children and lays them out horizontally." + example: + screen vbox_example(): + frame: + xalign 0.5 ypos 50 + vbox: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + e "The vbox displayable is similar, except it takes its children and arranges them vertically." + e "Both of the boxes take the box style properties, the most useful of which is spacing, the amount of space to leave between children." + example: + screen grid_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + null + e "The grid displayable displays its children in a grid of equally-sized cells. It takes two arguments, the number of columns and the number of rows." + e "The grid has to be full, or Ren'Py will produce an error. Notice how in this example, the empty cell is filled with a null." + e "Like the boxes, grid uses the spacing property to specify the space between cells." + example: + screen grid_transpose_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + transpose True + text "1" + text "2" + text "3" + text "4" + text "5" + null + e "Grid also takes the transpose property, to make it fill top-to-bottom before it fills left-to-right." + example: + screen grid_bigger_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + transpose True + text "1" + text "2" + text "3" + text "4" + text "5" + text _("Bigger") + e "And just to demonstrate that all cells are equally-sized, here's what happens when once child is bigger than the others." + example: + screen fixed_example(): + frame: + xalign 0.5 ypos 50 + fixed: + xsize 400 ysize 300 + text "1" xpos 41 ypos 184 + text "2" xpos 135 ypos 177 + text "3" xpos 92 ypos 3 + text "4" xpos 359 ypos 184 + text "5" xpos 151 ypos 25 + e "The fixed displayable displays the children using Ren'Py's normal placement algorithm. This lets you place displayables anywhere in the screen." + e "By default, the layout expands to fill all the space available to it. To prevent that, we use the xsize and ysize properties to set its size in advance." + example: + screen implicit_fixed_example(): + frame: + xalign 0.5 ypos 50 + xsize 440 ysize 316 + text "1" xpos 41 ypos 184 + text "2" xpos 135 ypos 177 + text "3" xpos 92 ypos 3 + text "4" xpos 359 ypos 184 + text "5" xpos 151 ypos 25 + e "When a non-layout displayable is given two or more children, it's not necessary to create a fixed. A fixed is automatically added, and the children are added to it." + example large: + screen hbox_example(): + frame: + xalign 0.5 ypos 50 + has hbox spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + e "Finally, there's one convenience to save space. When many displayables are nested, adding a layout to each could cause crazy indent levels." + e "The has statement creates a layout, and then adds all further children of its parent to that layout. It's just a convenience to make screens more readable." + hide example + return +label window_displayables: + e "In the default GUI that Ren'Py creates for a game, most user interface elements expect some sort of background." + example large: + screen noframe_example(): + vbox: + xalign 0.5 ypos 50 + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + e "Without the background, text can be hard to read. While a frame isn't strictly required, many screens have one or more of them." + example large: + screen frame_example(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + e "But when I add a background, it's much easier. That's why there are two displayables that are intended to give backgrounds to user interface elements." + e "The two displayables are frame and window. Frame is the one we use above, and it's designed to provide a background for arbitrary parts of the user interface." + show example say_screen + e "On the other hand, the window displayable is very specific. It's used to provide the text window. If you're reading what I'm saying, you're looking at the text window right now." + e "Both frames and windows can be given window style properties, allowing you to change things like the background, margins, and padding around the window." + hide example + return +label button_displayables: + e "One of the most flexible displayables is the button displayable, and its textbutton and imagebutton variants." + example large: + screen button_example(): + frame: + xalign 0.5 ypos 50 + button: + action Notify(_("You clicked the button.")) + text _("Click me.") style "button_text" + e "A button is a displayable that when selected runs an action. Buttons can be selected by clicking with the mouse, by touch, or with the keyboard and controller." + e "Actions can do many things, like setting variables, showing screens, jumping to a label, or returning a value. There are many {a=https://www.renpy.org/doc/html/screen_actions.html}actions in the Ren'Py documentation{/a}, and you can also write your own." + example large: + screen button_hover_example(): + frame: + xalign 0.5 ypos 50 + button: + action Notify(_("You clicked the button.")) + hovered Notify(_("You hovered the button.")) + unhovered Notify(_("You unhovered the button.")) + text _("Click me.") style "button_text" + e "It's also possible to run actions when a button gains and loses focus." + example large: + screen button_heal_example(): + default health = 42 + frame: + xalign 0.5 ypos 50 + button: + action SetScreenVariable("health", 100) + hbox: + spacing 10 + text _("Heal") style "button_text" yalign 0.5 + bar value AnimatedValue(health, 100, 1.0) yalign 0.5 xsize 200 + e "A button takes another displayable as a child. Since that child can be a layout, it can take as many children as you want." id button_displayables_47af4bb9 + example large: + screen textbutton_example(): + frame: + xalign 0.5 ypos 50 + textbutton _("This is a textbutton."): + action Notify(_("You clicked the button.")) + e "In many cases, buttons will be given text. To make that easier, there's the textbutton displayable that takes the text as an argument." + e "Since the textbutton displayable manages the style of the button text for you, it's the kind of button that's used most often in the default GUI." + example large: + screen imagebutton_example(): + frame: + xalign 0.5 ypos 50 + imagebutton: + idle "logo bw" + hover "logo base" + action Notify(_("You clicked the button.")) + e "There's also the imagebutton, which takes displayables, one for each state the button can be in, and displays them as the button." + e "An imagebutton gives you the most control over what a button looks like, but is harder to translate and won't look as good if the game window is resized." + example large: + screen button_inline_style_example(): + frame: + xalign 0.5 ypos 50 + textbutton _("Click me."): + idle_background Frame("button glossy idle", 12, 12) + hover_background Frame("button glossy hover", 12, 12) + xpadding 20 + ypadding 10 + xmargin 5 + ymargin 5 + hover_sound "pong_beep.opus" + text_idle_color "#c0c0c0" + text_hover_color "#ffffff" + action Notify(_("You clicked the button.")) + e "Buttons take Window style properties, that are used to specify the background, margins, and padding. They also take Button-specific properties, like a sound to play on hover." + e "When used with a button, style properties can be given prefixes like idle and hover to make the property change with the button state." + e "A text button also takes Text style properties, prefixed with text. These are applied to the text displayable it creates internally." + example large: + screen button_style_example(): + frame: + xalign 0.5 ypos 50 + has vbox + textbutton _("Click me."): + style "custom_button" + action Notify(_("You clicked the button.")) + textbutton _("Or me."): + style "custom_button" + action Notify(_("You clicked the other button.")) + style custom_button: + idle_background Frame("button glossy idle", 12, 12) + hover_background Frame("button glossy hover", 12, 12) + xpadding 20 + ypadding 10 + xmargin 5 + ymargin 5 + size_group "custom_button" + hover_sound "pong_beep.opus" + style custom_button_text: + idle_color "#c0c0c0" + hover_color "#ffffff" + e "Of course, it's prety rare we'd ever customize a button in a screen like that. Instead, we'd create custom styles and tell Ren'Py to use them." + hide example + return + return +label bar_displayables: + example large: + screen bar_example(): + frame: + xalign 0.5 ypos 50 + xsize 500 + bar: + value StaticValue(66, 100) + e "The bar and vbar displayables are flexible displayables that show bars representing a value. The value can be static, animated, or adjustable by the player." + e "The value property gives a BarValue, which is an object that determines the bar's value and range. Here, a StaticValue sets the range to 100 and the value to 66, making a bar that's two thirds full." + e "A list of all the BarValues that can be used is found {a=https://www.renpy.org/doc/html/screen_actions.html#bar-values}in the Ren'Py documentation{/a}." + e "In this example, we give the frame the xsize property. If we didn't do that, the bar would expand to fill all available horizontal space." + example large: + screen bars_example(): + default n = 66 + frame: + xalign 0.5 ypos 50 + xsize 500 + vbox: + spacing 10 + bar value AnimatedValue(n, 100, 0.5) style "bar" + bar value ScreenVariableValue("n", 100) style "slider" + bar value ScreenVariableValue("n", 100) style "scrollbar" + e "There are a few different bar styles that are defined in the default GUI. The styles are selected by the style property, with the default selected by the value." + e "The top style is the 'bar' style. It's used to display values that the player can't adjust, like a life or progress bar." + e "The middle style is the 'slider' value. It's used for values the player is expected to adjust, like a volume preference." id bar_displayables_c2aa4725 + e "Finally, the bottom style is the 'scrollbar' style, which is used for horizontal scrollbars. When used as a scrollbar, the thumb in the center changes size to reflect the visible area of a viewport." + example large: + screen vbars_example(): + default n = 66 + frame: + xalign 0.5 ypos 50 + ysize 300 + hbox: + spacing 10 + vbar value AnimatedValue(n, 100, 0.5) + vbar value ScreenVariableValue("n", 100) style "vslider" + vbar value ScreenVariableValue("n", 100) style "vscrollbar" + e "The vbar displayable is similar to the bar displayable, except it uses vertical styles - 'vbar', 'vslider', and 'vscrollbar' - by default." + e "Bars take the Bar style properties, which can customize the look and feel greatly. Just look at the difference between the bar, slider, and scrollbar styles." + hide example + return +label imagemap_displayables: + e "Imagemaps use two or more images to show buttons and bars. Let me start by showing you an example of an imagemap in action." + window hide None + example imagemap hide noshow: + screen imagemap_example(): + imagemap: + idle "imagemap ground" + hover "imagemap hover" + hotspot (44, 238, 93, 93) action Jump("swimming") alt "Swimming" + hotspot (360, 62, 93, 93) action Jump("science") alt "Science" + hotspot (726, 106, 93, 93) action Jump("art") alt "Art" + hotspot (934, 461, 93, 93) action Jump("home") alt "Home" + label imagemap_example: + call screen imagemap_example + label swimming: + e "You chose swimming." + e "Swimming seems like a lot of fun, but I didn't bring my bathing suit with me." + jump imagemap_done + label science: + e "You chose science." + e "I've heard that some schools have a competitive science team, but to me research is something that can't be rushed." + jump imagemap_done + label art: + e "You chose art." + e "Really good background art is hard to make, which is why so many games use filtered photographs. Maybe you can change that." + jump imagemap_done + label home: + e "You chose to go home." + jump imagemap_done + label imagemap_done: + e "Anyway..." + window show None + window auto + e "To demonstrate how imagemaps are put together, I'll show you the five images that make up a smaller imagemap." + show imagemap volume idle: + xalign 0.5 ypos 50 + with dissolve + e "The idle image is used for the background of the imagemap, for hotspot buttons that aren't focused or selected, and for the empty part of an unfocused bar." + show imagemap volume hover: + xalign 0.5 ypos 50 + with dissolve + e "The hover image is used for hotspots that are focused but not selected, and for the empty part of a focused bar." + e "Notice how both the bar and button are highlighted in this image. When we display them as part of a screen, only one of them will show up as focused." + show imagemap volume selected_idle: + xalign 0.5 ypos 50 + with dissolve + e "Selected images like this selected_idle image are used for parts of the bar that are filled, and for selected buttons, like the current screen and a checked checkbox." + show imagemap volume selected_hover: + xalign 0.5 ypos 50 + with dissolve + e "Here's the selected_hover image. The button here will never be shown, since it will never be marked as selected." + show imagemap volume insensitive: + xalign 0.5 ypos 50 + with dissolve + e "Finally, an insensitive image can be given, which is used when a hotspot can't be interacted with." + hide imagemap + with dissolve + e "Imagemaps aren't limited to just images. Any displayable can be used where an image is expected." + example large: + screen volume_imagemap_example(): + imagemap: + xalign 0.5 ypos 50 + idle "imagemap volume idle" + hover "imagemap volume hover" + selected_idle "imagemap volume selected_idle" + selected_hover "imagemap volume selected_hover" + insensitive "imagemap volume insensitive" + hotspot (237, 171, 126, 50) action Return(True) + hotbar (51, 96, 498, 52) value Preference("music volume") + e "Here's an imagemap built using those five images. Now that it's an imagemap, you can interact with it if you want to." + example large: + screen volume_imagemap_auto_example(): + imagemap: + xalign 0.5 ypos 50 + auto "imagemap volume %s" + hotspot (237, 171, 126, 50) action Return(True) + hotbar (51, 96, 498, 52) value Preference("music volume") + e "To make this a little more concise, we can replace the five images with the auto property, which replaces '%%s' with 'idle', 'hover', 'selected_idle', 'selected_hover', or 'insensitive' as appropriate." + e "Feel free to omit the selected and insensitive images if your game doesn't need them. Ren'Py will use the idle or hover images to replace them." + e "The hotspot and hotbar statements describe areas of the imagemap that should act as buttons or bars, respectively." + e "Both take the coordinates of the area, in (x, y, width, height) format." + e "A hotspot takes an action that is run when the hotspot is activated. It can also take actions that are run when it's hovered and unhovered, just like a button can." + e "A hotbar takes a BarValue object that describes how full the bar is, and the range of values the bar should display, just like a bar and vbar does." + hide screen volume_imagemap_auto_example + show example imagemap + with dissolve + e "A useful pattern is to define a screen with an imagemap that has hotspots that jump to labels, and call that using the call screen statement." + e "That's what we did in the school example I showed before. Here's the script for it. It's long, but the imagemap itself is fairly simple." + hide example + e "Imagemaps have pluses and minuses. On one hand, they are easy for a designer to create, and can look very good. At the same time, they can be hard to translate, and text baked into images may be blurry when the window is scaled." + e "It's up to you and your team to decide if imagemaps are right for your project." + return +label viewport_displayables: + e "Sometimes, you'll want to display something bigger than the screen. That's what the viewport displayable is for." + example large: + screen viewport_screen(): + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + draggable True + mousewheel True + arrowkeys True + add "bg band" + with dissolve + e "Here's an example of a simple viewport, used to display a single image that's far bigger than the screen. Since the viewport will expand to the size of the screen, we use the xysize property to make it smaller." + e "By default the viewport can't be moved, so we give the draggable, mousewheel, and arrowkeys properties to allow it to be moved in multiple ways." + example large: + screen edgescroll_viewport_screen(): + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + edgescroll (150, 500) + mousewheel True + arrowkeys True + add "bg band" + e "When I give the viewport the edgescroll property, the viewport automatically scrolls when the mouse is near its edges. The two numbers are the size of the edges, and the speed in pixels per second." + example large: + screen scrollbar_viewport_screen(): + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + scrollbars "both" + spacing 5 + draggable True + mousewheel True + arrowkeys True + add "bg band" + with dissolve + e "Giving the viewport the scrollbars property surrounds it with scrollbars. The scrollbars property can take 'both', 'horizontal', and 'vertical' as values." + e "The spacing property controls the space between the viewport and its scrollbars, in pixels." + example large: + screen initial_viewport_screen(): + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + xinitial 0.5 + yinitial 1.0 + scrollbars "both" + spacing 5 + draggable True + mousewheel True + arrowkeys True + add "bg band" + with dissolve + e "The xinitial and yinitial properties set the initial amount of scrolling, as a fraction of the amount that can be scrolled." + example large: + screen vpgrid_screen(): + vpgrid: + cols 6 + rows 4 + xalign 0.5 ypos 50 xysize (700, 300) + child_size (1000, None) + scrollbars "both" + side_spacing 5 + draggable True + mousewheel True + arrowkeys True + for i in range(6 * 4): + add "logo base" + with dissolve + e "Finally, there's the vpgrid displayable. It combines a viewport and a grid into a single displayable, except it's more efficient than either, since it doesn't have to draw every child." + e "It takes the cols and rows properties, which give the number of rows and columns of children. If one is omitted, Ren'Py figures it out from the other and the number of children." + hide example + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_screens.rpy b/testcases/expected/tutorial-8.2/tutorial_screens.rpy new file mode 100644 index 00000000..e1b581bb --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_screens.rpy @@ -0,0 +1,390 @@ +default player_hp = 15 +default player_hp_max = 42 +default eileen_hp = 100 +default eileen_hp_max = 100 +default player_lv = 4 +default eileen_lv = 99 +screen single_stat(name, hp, hp_max, lv, xalign): + frame: + xalign xalign + has vbox + spacing 5 + hbox: + text "[name!t]" min_width 220 + text _(" Lv. [lv]") + hbox: + text _("HP"): + min_width 40 + yalign 0.5 + bar: + value AnimatedValue(hp, hp_max, 1.0) + xmaximum 180 + ysize 26 + text " [hp]/[hp_max]": + yalign 0.5 +screen stats(): + use single_stat(_("Player"), player_hp, player_hp_max, player_lv, 0.0) + use single_stat(_("Eileen"), eileen_hp, eileen_hp_max, eileen_lv, 1.0) +define day_periods = [ _('Morning'), _('Afternoon'), _('Evening') ] +define day_choices = [ _('Study'), _('Exercise'), _('Eat'), _('Drink'), _('Be Merry') ] +default day_plan = { + 'Morning' : 'Eat', + 'Afternoon' : 'Drink', + 'Evening' : 'Be Merry' + } +default stat_strength = 10 +default stat_intelligence = 25 +default stat_moxie = 100 +default stat_chutzpah = 75 +style stat_text is default: + min_width 200 + textalign 1.0 + yalign 0.5 +style stat_hbox is hbox: + spacing 10 +style stat_vbox: + spacing 5 +screen day_planner(): + vbox: + spacing 5 + frame: + style_prefix "stat" + xpadding 150 + xfill True + has vbox + hbox: + text _("Strength") + bar value StaticValue(stat_strength, 100) + hbox: + text _("Intelligence") + bar value StaticValue(stat_strength, 100) + hbox: + text _("Moxie") + bar value StaticValue(stat_strength, 100) + hbox: + text _("Chutzpah") + bar value StaticValue(stat_strength, 100) + grid 3 1: + spacing 5 + xfill True + for p in day_periods: + frame: + xfill True + has vbox + label p + null height 5 + for i in day_choices: + textbutton i action SetDict(day_plan, p, i) + grid 3 1: + xfill True + spacing 5 + null + frame: + textbutton _("Done"): + action Return(True) + xfill True + text_xalign 0.5 + null +label tutorial_screens: + e "Screens are the most powerful part of Ren'Py. Screens let you customize the out-of-game interface, and create new in-game interface components." +label screens_menu: + $ reset_example() + menu: + e "What would you like to know about screens?" + "What screens can do.": + call screens_demo from _call_screens_demo + "How to show screens.": + call screens_showing from _call_screens_showing + "Passing parameters to screens.": + call screens_parameters from _call_screens_parameters + "Screen properties.": + call screens_properties from _call_screens_properties + "Special screen statements.": + call screens_control from _call_screens_control + "Using other screens.": + call screen_use from _call_screen_use + "That's it.": + return + jump screens_menu +label screens_demo: + e "Screens are how we create the user interface in Ren'Py. With the exception of images and transitions, everything you see comes from a screen." + e "When I'm speaking to you, I'm using the 'say' screen. It's responsible for taking dialogue and presenting it to the player." + menu: + e "And when the menu statement displays an in-game choice, the 'choice' screen is used. Got it?" + "Yes.": + pass + "I do.": + pass + e "Text input uses the 'input' screen, NVL mode uses the 'nvl' screen, and so on." + e "More than one screen can be displayed at once. For example, the buttons at the bottom - Back, History, Skip, and so on - are all displayed by a quick_menu screen that's shown all of the time." + e "There are a lot of special screens, like 'main_menu', 'load', 'save', and 'preferences'. Rather than list them all here, I'll {a=https://www.renpy.org/doc/html/screen_special.html}send you to the documentation{/a}." + e "In a newly created project, all these screens live in screens.rpy. You can edit that file in order to change them." + e "You aren't limited to these screens either. In Ren'Py, you can make your own screens, and use them for your game's interface." + $ player_hp = 15 + show screen stats + with dissolve + e "For example, in an RPG like visual novel, a screen can display the player's statistics." + $ player_hp = 42 + e "Which reminds me, I should probably heal you." + hide screen stats + show screen day_planner + with dissolve + e "Complex screens can be the basis of whole game mechanics. A stats screen like this can be the basis of dating and life-sims." + hide screen day_planner + with dissolve + e "While screens might be complex, they're really just the result of a lot of simple parts working together to make something larger than all of them." + return +label screens_showing: + example large noshow: + screen simple_screen(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + e "Here's an example of a very simple screen. The screen statement is used to tell Ren'Py this is a screen, and its name is simple_screen." id screens_showing_1b51e9a4 + e "Inside the screen statement, lines introduces displayables such as frame, vbox, text, and textbutton; or properties like action, xalign, and ypos." + show screen simple_screen + with dissolve + e "I'll work from the inside out to describe the statements. But first, I'll show the screen so you can see it in action." + e "The text statement is used to display the text provided." + e "The textbutton statement introduces a button that can be clicked. When the button is clicked, the provided action is run." + e "Both are inside a vbox, which means vertical box, statement - that places the text on top of the button." + e "And that is inside a frame that provides the background and borders. The frame has an at property that takes a transform giving its position." + hide screen simple_screen + hide example + with dissolve + e "There are a trio of statements that are used to display screens." + example: + show screen simple_screen + e "The first is the show screen statement, which displays a screen and lets Ren'Py keep going." + e "The screen will stay shown until it is hidden." + hide screen simple_screen + e "Hiding a screen is done with the hide screen statement." + show example call_screen + e "The call screen statement stops Ren'Py from executing script until the screen either returns a value, or jumps the script somewhere else." + e "Since we can't display dialogue at the same time, you'll have to click 'Okay' to continue." + hide example + example call_screen hide: + call screen simple_screen + e "When a call screen statement ends, the screen is automatically hidden." + e "Generally, you use show screen to show overlays that are up all the time, and call screen to show screens the player interacts with for a little while." + return +label screens_parameters: + example large noshow: + screen parameter_screen(message, okay=Return(True), cancel=Return(False)): + frame: + xalign 0.5 ypos 50 + vbox: + text "[message!t]" + textbutton _("Okay"): + action okay + textbutton _("Cancel"): + action cancel + example hide show_parameter_screen: + show screen parameter_screen(_("Hello, world."), cancel=Notify(_("You can't cancel this."))) + with dissolve + e "Here's an example of a screen that takes three parameters. The message parameter is a message to show, while the okay and cancel actions are run when the appropriate button is chosen." + e "While the message parameter always has to be supplied, the okay and cancel parameters have default values that are used if no argument is given." + e "Each parameter is a variable that is defined inside the screen. Inside the screen, these variables take priority over those used in the rest of Ren'Py." + show example show_parameter_screen + e "When a screen is shown, arguments can be supplied for each of the parameters. Arguments can be given by position or by name." + example: + show screen parameter_screen(_("Shiro was here.")) + with dissolve + e "Parameters let us change what a screen displays, simply by re-showing it with different arguments." + hide screen parameter_screen + with dissolve + show example call_parameter_screen + e "The call screen statement can also take arguments, much like show screen does." + hide example + example hide call_parameter_screen: + call screen parameter_screen(_("Click either button to continue.")) + return +label screens_properties: + e "There are a few properties that can be applied to a screen itself." + example large: + screen modal_example(): + modal True + frame: + xalign 0.5 ypos 50 + textbutton _("Close This Screen"): + action Hide("modal_example") + e "When the modal property is true, you can't interact with things beneath the screen. You'll have to click 'Close This Screen' before you can continue." + example: + screen a_tag_screen(): + tag tag_screen + frame: + xalign 0.33 ypos 50 + text _("A Tag Screen") + screen b_tag_screen(): + tag tag_screen + frame: + xalign 0.66 ypos 50 + text _("B Tag Screen") + e "When a screen has the tag property, it's treated like the tag part of an image name. Here, I'm showing a_tag_screen." + show screen b_tag_screen + e "When I show b_tag_screen, it replaces a_tag_screen." + e "This is useful in the game and main menus, where you want the load screen to replace the preferences screen. By default, all those screens have tag menu." + show eileen concerned + e "For some reason, tag takes a name, and not an expression. It's too late to change it." + show eileen happy + with None + hide screen b_tag_screen + example noshow: + screen zorder_100_screen(): + zorder 100 + frame: + xalign 0.5 xoffset 50 ypos 70 + text "Zorder 100" + screen zorder_0_screen(): + frame: + xalign 0.5 ypos 50 + text "Zorder 0" + show screen zorder_100_screen + show screen zorder_0_screen + with dissolve + e "The zorder property controls the order in which screens overlap each other. The larger the zorder number, the closer the screen is to the player." + e "By default, a screen has a zorder of 0. When two screens have the same zorder number, the screen that is shown second is closer to the player." + hide screen zorder_100_screen + hide screen zorder_0_screen + example nohide: + screen variant_screen(): + variant "small" + frame: + xalign 0.5 ypos 50 + text _("You're on a small device.") + screen variant_screen(): + frame: + xalign 0.5 ypos 50 + text _("You're not on a small device.") + e "The variant property selects a screen based on the properties of the device it's running on." + e "In this example, the first screen will be used for small devices like telephones, and the other screen will be used for tablets and computers." + example: + screen style_prefix_screen(): + style_prefix "red" + frame: + xalign 0.5 ypos 50 + text _("This text is red.") + style red_frame: + background "#440000d9" + style red_text: + color "#ffc0c0" + e "Finally, the style_prefix property specifies a prefix that's applied to the styles in the screen." + e "When the 'red' prefix is given, the frame gets the 'red_frame' style, and the text gets the 'red_text' style." + e "This can save a lot of typing when styling screens with many displayables in them." + hide example + return +label warp_screen_displayables: + $ renpy.pop_call() + jump screen_displayables +label screens_control: + e "The screen language has a few statements that do things other than show displayables. If you haven't seen the section on {a=jump:warp_screen_displayables}Screen Displayables{/a} yet, you might want to check it out, then come back here." + example large: + screen single_python_screen(): + $ message = _("Hello, World.") + frame: + xalign 0.5 ypos 50 + vbox: + text "[message!t]" + e "The python statement works just about the same way it does in the script. A single line of Python is introduced with a dollar sign. This line is run each time the screen updates." + example large: + screen block_python_screen(): + python: + message1 = _("Hello, World.") + message2 = _("It's good to meet you.") + frame: + xalign 0.5 ypos 50 + vbox: + text "[message1!t]" + text "[message2!t]" + e "Similarly, the python statement introduces an indented block of python statements. But there is one big difference in Python in screens and Python in scripts." + e "The Python you use in screens isn't allowed to have side effects. That means that it can't do things like change the value of a variable." + e "The reason for this is that Ren'Py will run a screen, and the Python in it, during screen prediction." + example large: + screen default_screen(): + default n = 0 + frame: + xalign 0.5 ypos 50 + vbox: + text "n = [n]" + textbutton _("Increase") action SetScreenVariable("n", n + 1) + e "The default statement lets you set the value of a screen variable the first time the screen runs. This value can be changed with the SetScreenVariable and ToggleScreenVariable actions." + e "The default statement differs from the Python statement in that it is only run once. Python runs each time the screen updates, and hence the variable would never change value." + example large: + screen if_screen(): + default n = 0 + frame: + xalign 0.5 ypos 50 + vbox: + if n > 2: + text "n = [n]" color "#cfc" + else: + text "n = [n]" color "#fcc" + textbutton _("Increase") action SetScreenVariable("n", n + 1) + e "The if statement works like it does in script, running one block if the condition is true and another if the condition is false." + example large: + screen for_screen(): + $ landings = [ _("Earth"), _("Moon"), _("Mars") ] + frame: + xalign 0.5 ypos 50 + vbox: + for i in landings: + textbutton "[i!t]" action Return(i) + e "The for statement takes a list of values, and iterates through them, running the block inside the for loop with the variable bound to each list item." + example large: + screen on_key_screen(): + frame: + xalign 0.5 ypos 50 + text _("Now press 'a'.") + on "show" action Notify(_("The screen was just shown.")) + key "a" action Notify(_("You pressed the 'a' key.")) + e "The on and key statements probably only make sense at the top level of the screen." + e "The on statement makes the screen run an action when an event occurs. The 'show' event happens when the screen is first shown, and the 'hide' event happens when it is hidden." + e "The key event runs an event when a key is pressed." + hide example + return +label screen_use: + e "The screen language use statement lets you include a screen inside another. This can be useful to prevent duplication inside screens." + example large: + screen duplicate_stats(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("Health") xalign 0.5 + bar value StaticValue(90, 100) xalign 0.5 xsize 250 + null height 15 + text _("Magic") xalign 0.5 + bar value StaticValue(42, 100) xalign 0.5 xsize 250 + e "Take for example this screen, which shows two stat entries. There's already a lot of duplication there, and if we had more stats, there would be more." + example large: + screen using_stats(): + frame: + xalign 0.5 ypos 50 + vbox: + use stat(_("Health"), 90) + null height 15 + use stat(_("Magic"), 42) + screen stat(name, amount): + text name xalign 0.5 + bar value StaticValue(amount, 100) xalign 0.5 xsize 250 + e "Here, we moved the statements that show the text and bar into a second screen, and the use statement includes that screen in the first one." + e "The name and amount of the stat are passed in as arguments to the screen, just as is done in the call screen statement." + e "By doing it this way, we control the amount of duplication, and can change the stat in one place." + example large: + screen transclusion_example(): + use boilerplate(): + text _("There's not much left to see.") + screen boilerplate(): + frame: + xalign 0.5 ypos 50 + vbox: + transclude + e "The transclude statement goes one step further, by letting the use statement take a block of screen language statements." + e "When the included screen reaches the transclude statement it is replaced with the block from the use statement." + e "The boilerplate screen is included in the first one, and the text from the first screen is transcluded into the boilerplate screen." + e "Use and transclude are complex, but very powerful. If you think about it, 'use boilerplate' is only one step removed from writing your own Screen Language statement." + hide example + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/expected/tutorial-8.2/tutorial_video.rpy b/testcases/expected/tutorial-8.2/tutorial_video.rpy new file mode 100644 index 00000000..e2478319 --- /dev/null +++ b/testcases/expected/tutorial-8.2/tutorial_video.rpy @@ -0,0 +1,25 @@ +example movie_image: + image launch = Movie(play="oa4_launch.webm", pos=(10, 10), anchor=(0, 0)) +label tutorial_video: + e "Ren'Py supports playing movies. There are two ways of doing this." + e "The first way allows you to show a movie as an image, along with every other image that's displayed on the screen." + show example movie_image + e "To do this, we first have to define an image to be a Movie displayable. Movie displayables take a movie to play, and can be given position properties." + stop music fadeout .25 + show example movie_play + pause .25 + example movie_play hide: + show launch behind eileen + e "Then, we can show the movie displayable, which starts the movie playing." + example: + hide launch + e "When we no longer want to play the movie, we can hide it." + show example movie_cutscene + e "The other way to show a movie is with the renpy.movie_cutscene python function. This shows the movie fullscreen, either until it ends or until the user clicks." + hide screen example + example movie_cutscene hide: + $ renpy.movie_cutscene("oa4_launch.webm") + e "A Movie displayable can also take a mask with an alpha channel, which lets you make movie sprites. But that's more complicated, so I'll stop here for now." + play music "sunflower-slow-drag.ogg" + return +# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc diff --git a/testcases/originals/the_question-8.2/gui.rpy b/testcases/originals/the_question-8.2/gui.rpy new file mode 100644 index 00000000..72416db2 --- /dev/null +++ b/testcases/originals/the_question-8.2/gui.rpy @@ -0,0 +1,468 @@ +################################################################################ +## Initialization +################################################################################ + +## The init offset statement causes the init code in this file to run before +## init code 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 = '#cc6600' + +## The color used for a text button when it is neither selected nor hovered. +define gui.idle_color = '#555555' + +## The small color is used for small text, which needs to be brighter/darker to +## achieve the same effect. +define gui.idle_small_color = '#aaaaaa' + +## The color that is used for buttons and bars that are hovered. +define gui.hover_color = '#e0a366' + +## 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 = '#ffffff' + +## The color used for a text button when it cannot be selected. +define gui.insensitive_color = '#5555557f' + +## 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 = '#512800' +define gui.hover_muted_color = '#7a3d00' + +## The colors used for dialogue and menu choice text. +define gui.text_color = '#ffffff' +define gui.interface_text_color = '#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 = 24 + +## The size of labels in the game's user interface. +define gui.label_text_size = 28 + +## 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" + +## The color of the main menu. +define gui.main_menu_text_color = "#ffaa22" + + +## 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 if 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 = 36 + +## 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(25, 4, 4, 4) + +define gui.check_button_borders = Borders(25, 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" + + +## 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 + +## 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 = 0.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 that are introduced by player code. +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 code. + +## The height of horizontal bars, scrollbars, and sliders. The width of vertical +## bars, scrollbars, and sliders. +define gui.bar_size = 36 +define gui.scrollbar_size = 12 +define gui.slider_size = 30 + +## 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 = 150 +define gui.history_name_ypos = 0 +define gui.history_name_width = 150 +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 = 5 +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 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. + @gui.variant + def 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. + @gui.variant + def small(): + + ## Font sizes. + gui.text_size = 30 + gui.name_text_size = 36 + gui.notify_text_size = 25 + gui.interface_text_size = 36 + gui.button_text_size = 34 + gui.label_text_size = 36 + + ## 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 items in the game menu. + gui.choice_button_width = 1240 + + gui.navigation_spacing = 20 + gui.pref_button_spacing = 10 + + gui.history_height = 190 + gui.history_text_width = 690 + + ## 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 + + ## Quick buttons. + gui.quick_button_text_size = 20 diff --git a/testcases/originals/the_question-8.2/options.rpy b/testcases/originals/the_question-8.2/options.rpy new file mode 100644 index 00000000..6816dc59 --- /dev/null +++ b/testcases/originals/the_question-8.2/options.rpy @@ -0,0 +1,220 @@ +## 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 = _("The Question") + + +## 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 = "7.0" + + +## Text that is placed on the game's about screen. To insert a blank line +## between paragraphs, write \n\n. + +define gui.about = _("""Character Art: Deji. +Original Character Art: derik. + +Background Art: Mugenjohncel. +Original Background Art: DaFool + +Music By: Alessio + +Written By: mikey""") + + +## 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 = "the_question" + +## Change the version used by the build system. +define build.version = "7.0" + +## 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 = False + + +## 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 + + +## 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 = "the_question-7" + + +## 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: + + # SDK Fonts. + config.searchpath.append(config.renpy_base + "/sdk-fonts") + build.classify_renpy("sdk-fonts/**", "all") + build._sdk_fonts = True + + ## 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/the-question" + + +# Enable the console. +define config.console = True diff --git a/testcases/originals/the_question-8.2/screens.rpy b/testcases/originals/the_question-8.2/screens.rpy new file mode 100644 index 00000000..c4a0c319 --- /dev/null +++ b/testcases/originals/the_question-8.2/screens.rpy @@ -0,0 +1,1532 @@ +################################################################################ +## 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: + 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 + + +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 + + adjust_spacing False + + +## 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. +## +## http://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. +## +## http://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 + + +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.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.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.text_properties("navigation_button") + + +## Main Menu screen ############################################################ +## +## Used to display the main menu when Ren'Py starts. +## +## http://www.renpy.org/doc/html/screen_special.html#main-menu + +screen main_menu(): + + ## This ensures that any other menu screen is replaced. + tag menu + + style_prefix "main_menu" + + add gui.main_menu_background + + ## This empty frame darkens the main menu. + frame: + pass + + ## 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: + text "[config.name!t]": + style "main_menu_title" + + text _("Ren'Py 7+ Edition"): + 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 + xsize 960 + 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): + + 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: + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + + side_yfill True + + vbox: + transclude + + elif scroll == "vpgrid": + + vpgrid: + cols 1 + yinitial 1.0 + + 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 _("[config.version!t]\n") + + hbox: + spacing 15 + text _("Updated Character Art") style "about_small" + text _("Deji") + + hbox: + spacing 15 + text _("Original Character Art") style "about_small" + text _("Derik") + + null height 15 + + hbox: + spacing 15 + text _("Updated Background Art") style "about_small" + text _("Mugenjohncel") + + hbox: + spacing 15 + text _("Original Background Art") style "about_small" + text _("DaFool") + + null height 15 + + hbox: + spacing 15 + text _("Music By") style "about_small" + text _("Alessio") + + null height 15 + + hbox: + spacing 15 + text _("Update Written By") style "about_small" + text _("Lore") + + hbox: + spacing 15 + text _("Originally Written By ") style "about_small" + text _("mikey (ATP Projects)") + + + text _("\nMade with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only]") + null height 15 + text _("[renpy.license!t]") size 20 + + +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 + +style about_small: + size 20 + minwidth 260 + textalign 1.0 + yalign 0.9 + + +## 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: + textalign 0.5 + layout "subtitle" + hover_color gui.hover_color + +style page_button: + properties gui.button_properties("page_button") + +style page_button_text: + properties gui.text_properties("page_button") + +style slot_button: + properties gui.button_properties("slot_button") + +style slot_button_text: + properties gui.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 + + if renpy.mobile: + $ cols = 2 + else: + $ cols = 4 + + 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 "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. + +#begin language_picker + + vbox: + style_prefix "radio" + label _("Language") + + textbutton "English" text_font "DejaVuSans.ttf" action Language(None) + textbutton "Česky" text_font "DejaVuSans.ttf" action Language("czech") + textbutton "Dansk" text_font "DejaVuSans.ttf" action Language("danish") + textbutton "Français" text_font "DejaVuSans.ttf" action Language("french") + textbutton "Bahasa Melayu" text_font "DejaVuSans.ttf" action Language("malay") + textbutton "Русский" text_font "DejaVuSans.ttf" action Language("russian") + + vbox: + style_prefix "radio" + label _(" ") + + textbutton "Español" text_font "DejaVuSans.ttf" action Language("spanish") + textbutton "Українська" text_font "DejaVuSans.ttf" action Language("ukrainian") + textbutton "日本語" text_font "SourceHanSansLite.ttf" action Language("japanese") + textbutton "한국어" text_font "SourceHanSansLite.ttf" action Language("korean") + textbutton "简体中文" text_font "SourceHanSansLite.ttf" action Language("schinese") + textbutton "繁體中文" text_font "SourceHanSansLite.ttf" action Language("tchinese") + +#end language_picker + + 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.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.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.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")): + + 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.") + +define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } + +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 + textalign 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 + textalign 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}.") + + hbox: + label "Shift+A" + text _("Opens the accessibility menu.") + + +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") + 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.text_properties("help_button") + +style help_label: + xsize 250 + right_padding 20 + +style help_label_text: + size gui.text_size + xalign 1.0 + textalign 1.0 + + + +################################################################################ +## Additional screens +################################################################################ + + +## Confirm screen ############################################################## +## +## The confirm screen is called when Ren'Py wants to ask the player a yes or no +## question. +## +## http://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: + textalign 0.5 + layout "subtitle" + +style confirm_button: + properties gui.button_properties("confirm_button") + +style confirm_button_text: + properties gui.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. +## +## http://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. + 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 = 6 + +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 + textalign 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 + textalign 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 + textalign 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.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 + + 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 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 slider_pref_vbox: + variant "small" + xsize None + +style slider_pref_slider: + variant "small" + xsize 600 + +# Shrink the title. +style main_menu_vbox: + variant "small" + xsize 900 diff --git a/testcases/originals/the_question-8.2/script.rpy b/testcases/originals/the_question-8.2/script.rpy new file mode 100644 index 00000000..ec1766da --- /dev/null +++ b/testcases/originals/the_question-8.2/script.rpy @@ -0,0 +1,252 @@ +# Declare characters used by this game. +define s = Character(_("Sylvie"), color="#c8ffc8") +define m = Character(_("Me"), color="#c8c8ff") + +# This is a variable that is True if you've compared a VN to a book, and False +# otherwise. +default book = False + +# The game starts here. +label start: + + # Start by playing some music. + play music "illurock.opus" + + scene bg lecturehall + with fade + + "It's only when I hear the sounds of shuffling feet and supplies being put away that I realize that the lecture's over." + + "Professor Eileen's lectures are usually interesting, but today I just couldn't concentrate on it." + + "I've had a lot of other thoughts on my mind...thoughts that culminate in a question." + + "It's a question that I've been meaning to ask a certain someone." + + scene bg uni + with fade + + "When we come out of the university, I spot her right away." + + show sylvie green normal + with dissolve + + "I've known Sylvie since we were kids. She's got a big heart and she's always been a good friend to me." + + "But recently... I've felt that I want something more." + + "More than just talking, more than just walking home together when our classes end." + + menu: + + "As soon as she catches my eye, I decide..." + + "To ask her right away.": + + jump rightaway + + "To ask her later.": + + jump later + + +label rightaway: + + show sylvie green smile + + s "Hi there! How was class?" + + m "Good..." + + "I can't bring myself to admit that it all went in one ear and out the other." + + m "Are you going home now? Wanna walk back with me?" + + s "Sure!" + + scene bg meadow + with fade + + "After a short while, we reach the meadows just outside the neighborhood where we both live." + + "It's a scenic view I've grown used to. Autumn is especially beautiful here." + + "When we were children, we played in these meadows a lot, so they're full of memories." + + m "Hey... Umm..." + + show sylvie green smile + with dissolve + + "She turns to me and smiles. She looks so welcoming that I feel my nervousness melt away." + + "I'll ask her...!" + + m "Ummm... Will you..." + + m "Will you be my artist for a visual novel?" + + show sylvie green surprised + + "Silence." + + "She looks so shocked that I begin to fear the worst. But then..." + + show sylvie green smile + + menu: + + s "Sure, but what's a \"visual novel?\"" + + "It's a videogame.": + jump game + + "It's an interactive book.": + jump book + + +label game: + + m "It's a kind of videogame you can play on your computer or a console." + + m "Visual novels tell a story with pictures and music." + + m "Sometimes, you also get to make choices that affect the outcome of the story." + + s "So it's like those choose-your-adventure books?" + + m "Exactly! I've got lots of different ideas that I think would work." + + m "And I thought maybe you could help me...since I know how you like to draw." + + m "It'd be hard for me to make a visual novel alone." + + show sylvie green normal + + s "Well, sure! I can try. I just hope I don't disappoint you." + + m "You know you could never disappoint me, Sylvie." + + jump marry + + +label book: + + $ book = True + + m "It's like an interactive book that you can read on a computer or a console." + + show sylvie green surprised + + s "Interactive?" + + m "You can make choices that lead to different events and endings in the story." + + s "So where does the \"visual\" part come in?" + + m "Visual novels have pictures and even music, sound effects, and sometimes voice acting to go along with the text." + + show sylvie green smile + + s "I see! That certainly sounds like fun. I actually used to make webcomics way back when, so I've got lots of story ideas." + + m "That's great! So...would you be interested in working with me as an artist?" + + s "I'd love to!" + + jump marry + +label marry: + + scene black + with dissolve + + "And so, we become a visual novel creating duo." + + scene bg club + with dissolve + + "Over the years, we make lots of games and have a lot of fun making them." + + if book: + + "Our first game is based on one of Sylvie's ideas, but afterwards I get to come up with stories of my own, too." + + "We take turns coming up with stories and characters and support each other to make some great games!" + + "And one day..." + + show sylvie blue normal + with dissolve + + s "Hey..." + + m "Yes?" + + show sylvie blue giggle + + s "Will you marry me?" + + m "What? Where did this come from?" + + show sylvie blue surprised + + s "Come on, how long have we been dating?" + + m "A while..." + + show sylvie blue smile + + s "These last few years we've been making visual novels together, spending time together, helping each other..." + + s "I've gotten to know you and care about you better than anyone else. And I think the same goes for you, right?" + + m "Sylvie..." + + show sylvie blue giggle + + s "But I know you're the indecisive type. If I held back, who knows when you'd propose?" + + show sylvie blue normal + + s "So will you marry me?" + + m "Of course I will! I've actually been meaning to propose, honest!" + + s "I know, I know." + + m "I guess... I was too worried about timing. I wanted to ask the right question at the right time." + + show sylvie blue giggle + + s "You worry too much. If only this were a visual novel and I could pick an option to give you more courage!" + + scene black + with dissolve + + "We get married shortly after that." + + "Our visual novel duo lives on even after we're married...and I try my best to be more decisive." + + "Together, we live happily ever after even now." + + "{b}Good Ending{/b}." + + return + +label later: + + "I can't get up the nerve to ask right now. With a gulp, I decide to ask her later." + + scene black + with dissolve + + "But I'm an indecisive person." + + "I couldn't ask her that day and I end up never being able to ask her." + + "I guess I'll never know the answer to my question now..." + + "{b}Bad Ending{/b}." + + return diff --git a/testcases/originals/tutorial-8.2/01director_support.rpy b/testcases/originals/tutorial-8.2/01director_support.rpy new file mode 100644 index 00000000..cc28b4bd --- /dev/null +++ b/testcases/originals/tutorial-8.2/01director_support.rpy @@ -0,0 +1,21 @@ +# Disable the director until the director example enables it. +default _director_enable = False + +python early hide: + + + + import shutil + fn1 = os.path.join(renpy.config.gamedir, "tutorial_director.rpym") + fn2 = os.path.join(renpy.config.gamedir, "tutorial_director.rpy") + + try: + + if not renpy.session.get("director", False): + shutil.copy(fn1, fn2) + + store.director_readonly = False + + except Exception: + + store.director_readonly = True diff --git a/testcases/originals/tutorial-8.2/01example.rpy b/testcases/originals/tutorial-8.2/01example.rpy new file mode 100644 index 00000000..2383a0a0 --- /dev/null +++ b/testcases/originals/tutorial-8.2/01example.rpy @@ -0,0 +1,650 @@ +python early: + + # This maps from example name to the text of a fragment. + examples = { } + + # The size of the example - large or small. + example_size = "small" + + # The location of the example - top or bottom. + example_location = "top" + + # The screen in the last example. + example_screen = None + + # A transition used with examples + example_transition = None + + def reset_example(): + """ + Called to reset the example code to the defaults. + """ + + global example_size + global example_location + global example_screen + + example_size = "small" + example_location = "top" + example_screen = None + + def show_example_screen(name): + global example_screen + example_screen = name + renpy.show_screen(name) + + def hide_example_screen(): + global example_screen + + if example_screen is not None: + renpy.hide_screen(example_screen) + + example_screen = None + + + # Keywords that, at the start of an example block, cause it to be + # outdented + OUTDENT_KEYWORDS = { "define", "default", "image", "screen", "init", "transform", "label", "style" } + + + # This defines the example statement. + # + # The example statement takes: + # * An optional name for the example. + # * An optional hide flag. + # * Optional size flags, large or small. + # * Optional position flags, top or bottom. + # + # The statement defines an example with the name, or an anyonymous name if no + # name is given. When run, the example is displayed if the hide flag is not + # given. + + + def read_example(name, fn, line, outdent): + """ + This reads an example from an example statement, and places it into + the examples dictionary. + """ + + fn = fn.replace("game/", "") + + with renpy.notl_file(fn) as f: + data = f.read().decode("utf-8") + + rawlines = [ i.rstrip() for i in data.split("\n") ] + + lines = [ ] + + base_indent = 0 + + while True: + + if line >= len(rawlines): + raise Exception("Example open at end of {}.".format(fn)) + + l = rawlines[line] + line += 1 + + if not l: + lines.append(l) + continue + + indent = len(l) - len(l.lstrip()) + + if base_indent == 0: + base_indent = indent + lines.append(l[4:]) + elif indent >= base_indent: + lines.append(l[4:]) + else: + break + + # Determine if the line should be indented. + if outdent == "auto": + + for i in lines: + l = i.strip().split() + if not l: + continue + + if l[0] in OUTDENT_KEYWORDS: + outdent = True + else: + outdent = False + + break + + # Strip indentation. + if outdent: + lines = [ i[base_indent - 4:] for i in lines ] + + if name in examples: + examples[name].append('') + examples[name].extend(lines) + else: + examples[name] = lines + + def parse_example(l): + """ + This parses the example statement. + """ + + # The name of the example. A string, or None if we don't know yet. + name = None + + # Hide, bottom, and small flags. + hide = False + bottom = False + small = False + top = False + large = False + outdent = "auto" + show_screen = True + hide_screen = True + showtrans = False + + while True: + + if l.match(':'): + break + + elif l.keyword('hide'): + hide = True + + elif l.keyword('bottom'): + bottom = True + + elif l.keyword('small'): + small = True + + elif l.keyword('top'): + top = True + + elif l.keyword('large'): + large = True + + elif l.keyword('outdent'): + outdent = True + + elif l.keyword('nooutindent'): + outdent = False + + elif l.keyword("noshow"): + show_screen = False + + elif l.keyword("nohide"): + hide_screen = False + + elif l.keyword("showtrans"): + showtrans = True + + else: + + if name: + l.error('an example may have at most one name') + + name = l.require(l.name) + + l.expect_eol() + + if name is None: + name = "example_{}_{}".format(l.filename, l.number) + + ll = l.subblock_lexer() + ll.advance() + + if ll.keyword('screen'): + screen_name = ll.name() + else: + screen_name = None + + return { + "name" : name, + "names" : [ name ], + "hide" : hide, + "bottom" : bottom, + "small" : small, + "filename" : l.filename, + "number" : l.number, + "top" : top, + "large" : large, + "outdent" : outdent, + "screen_name" : screen_name, + "show_screen" : show_screen, + "hide_screen" : hide_screen, + "showtrans" : showtrans, + } + + def next_example(data, first): + return first + + def execute_example(data): + names = data.get("names", [ ]) + hide = data.get("hide", False) + bottom = data.get("bottom", False) + small = data.get("small", False) + top = data.get("top", False) + large = data.get("large", False) + showtrans = data.get("showtrans", False) + + global example_location + global example_size + + if bottom: + example_location = "bottom" + elif top: + example_location = "top" + + if small: + example_size = "small" + elif large: + example_size = "large" + + if not hide: + renpy.show_screen("example", names, example_size == "small", example_location == "bottom", showtrans=showtrans) + + screen_name = data.get("screen_name", None) + + if screen_name is not None: + if data.get("hide_screen", True): + hide_example_screen() + + if data.get("show_screen", True): + show_example_screen(screen_name) + renpy.with_statement(example_transition) + + def execute_init_example(data): + read_example(data["name"], data["filename"], data["number"], data.get("outdent", "auto")) + + def reachable_example(data, is_reachable, this, next, block): + if is_reachable: + return { this, next, block } + else: + return { True, block } + + renpy.register_statement( + "example", + parse=parse_example, + execute=execute_example, + execute_init=execute_init_example, + next=next_example, + reachable=reachable_example, + block="script") + + + # The show example statement. + + def parse_show_example(l): + + names = [ ] + + bottom = False + small = False + top = False + large = False + showtrans = False + + while not l.eol(): + + if l.keyword('bottom'): + bottom = True + + elif l.keyword('small'): + small = True + + elif l.keyword('top'): + top = True + + elif l.keyword('large'): + large = True + + elif l.keyword('showtrans'): + showtrans = True + + else: + + names.append(l.require(l.name)) + + return { + "names" : names, + "hide" : False, + "bottom" : bottom, + "small" : small, + "top" : top, + "large" : large, + "showtrans" : showtrans, + } + + renpy.register_statement("show example", parse=parse_show_example, execute=execute_example) + + + # The hide example statement. + + def parse_hide_example(l): + + hide_screen = True + + while not l.eol(): + if l.keyword('nohide'): + hide_screen = False + else: + break + + l.expect_eol() + + return { + "hide_screen" : hide_screen, + } + + def execute_hide_example(data): + renpy.hide_screen("example") + + if example_screen and data.get("hide_screen", True) and renpy.get_screen(example_screen): + hide_example_screen() + renpy.with_statement(example_transition) + + renpy.register_statement("hide example", parse=parse_hide_example, execute=execute_hide_example) + + +# A preference that controls if translations are shown. +default persistent.show_translation_marker = False + +init python: + + dialogue_map = { } + + for i in renpy.known_languages(): + dialogue_map[i] = renpy.translation.dialogue.create_dialogue_map(i) + + + import re + import keywords + + KEYWORDS = set(keywords.keywords) + PROPERTIES = set(keywords.properties) + + regex = r"(?P\b(\$|[_a-zA-Z0-9]+)\b)" + \ + r"|(?P\"([^\"]|\\.)*(?#.*)" + + regex = re.compile(regex) + + def quote(s): + s = s.replace("{", "{{") + s = s.replace("[", "[[") + + return s + + def translate(m): + if m.group("string"): + s = eval(m.group(0)) + + if __(s) != s: + s = __(s) + elif _preferences.language: + + dm = dialogue_map[_preferences.language] + + if s in dm: + s = dm[s] + + quote = m.group(0)[0] + s = s.replace("\\", "\\\\") + s = s.replace(quote, "\\" + quote) + s = s.replace("\n", "\\n") + s = quote + s + quote + + return s + + return m.group(0) + + def colorize(m): + if m.group("string"): + return "{color=#060}" + m.group(0) + "{/color}" + + word = m.group("word") + if word: + if word in KEYWORDS: + return "{color=#840}" + m.group(0) + "{/color}" + elif word in PROPERTIES: + return "{color=#048}" + m.group(0) + "{/color}" + else: + return m.group(0) + + if m.group("comment"): + return "{color=#600}" + m.group(0) + "{/color}" + + return m.group(0) + + def clean_example(lines): + rv = list(lines) + + while rv and not rv[0]: + rv.pop(0) + + while rv and not rv[-1]: + rv.pop(-1) + + return rv + + def example_code(blocks, raw=False, showtrans=False): + + if not isinstance(blocks, list): + blocks = [ blocks ] + + + # Collect the examples we use. + lines1 = [ ] + + for i in blocks: + if i not in examples: + lines1.append('Example {} not found.'.format(i)) + else: + lines1.extend(clean_example(examples[i])) + + lines1.append('') + + + # Strip off doubled blank lines. + last_blank = False + lines = [ ] + + for i in lines1: + + if not i and last_blank: + continue + + last_blank = not i + + i = regex.sub(translate, i) + + if not (persistent.show_translation_marker or showtrans): + i = re.sub(r'__?\((".*?")\)', r'\1', i) + i = re.sub(r"__?\(('.*?')\)", r'\1', i) + i = i.replace("!t]", "]") + + if not raw: + i = quote(i) + i = regex.sub(colorize, i) + + lines.append(i) + + while not lines[-1]: + lines.pop() + + # Join them into a single string. + return "\n".join(lines) + "\n " + + class CopyCode(Action): + def __init__(self, s): + self.s = s + + def __call__(self): + import pygame.scrap + pygame.scrap.put(pygame.SCRAP_TEXT, self.s.encode("utf-8")) + renpy.notify(_("Copied the example to the clipboard.")) + + example_transition = dissolve + + import os + SHOW_EXAMPLES = ("RENPY_LESS_EXAMPLES" not in os.environ) + + +transform example_transform(height, ypos): + ypos ypos + yanchor 1.0 + xalign 0.5 + + on replace: + crop (0, 0, 1280, height) + + on show: + crop (0, 0, 1280, 0) + linear .5 crop (0, 0, 1280, height) + + on hide: + linear .5 crop (0, 0, 1280, 0) + + +screen example(blocks, small=False, bottom=False, showtrans=False): + + zorder 10 + + default raw_code = example_code(blocks, raw=True, showtrans=showtrans) + default code = example_code(blocks, showtrans=showtrans) + + if small: + $ height = 80 + else: + $ height = 160 + + if bottom: + $ ypos = 720 + else: + $ ypos = 540 + + + if SHOW_EXAMPLES: + + frame: + style "empty" + background "#fffc" + foreground Solid("#aaac", xsize=1, xpos=178) + + xfill True + yfill True + ymaximum height + + at example_transform(height, ypos) + + viewport: + side_xmaximum 1098 + side_xpos 180 + child_size (2000, 2000) + ymaximum height + draggable True + mousewheel True + scrollbars "vertical" + + vscrollbar_xsize 5 + vscrollbar_base_bar "#aaac" + vscrollbar_unscrollable "hide" + + text code: + alt "" + size 16 + color "#000" + + textbutton _("copy"): + style "empty" + text_style "quick_button_text" + text_textalign 0.5 + text_minwidth 180 + + text_size 16 + + action CopyCode(raw_code) + + + + +init python hide: + + import os.path + import re + + # A list of files we will be scanning. + files = [ ] + + for i in renpy.list_files(): + if i.endswith(".rpy"): + files.append(i) + + for fn in files: + + try: + with open(os.path.join(renpy.config.gamedir, fn), "r") as f: + lines = f.readlines() + except Exception: + lines = [ ] + + open_examples = set() + + for l in lines: + + l = l.rstrip() + l = l.lstrip("\ufeff") + + m = re.match("\s*#begin (\w+)", l) + if m: + example = m.group(1) + + if example in examples: + raise Exception("Example %r is defined in two places.", example) + + open_examples.add(example) + examples[example] = [ ] + + continue + + m = re.match("\s*#end (\w+)", l) + if m: + example = m.group(1) + + if example not in open_examples: + raise Exception("Example %r is not open.", example) + + open_examples.remove(example) + continue + + for i in open_examples: + examples[i].append(l) + + if open_examples: + raise Exception("Examples %r remain open at the end of %r" % (open_examples, fn)) + + + +init python: + + def lint_stats_callback(): + print("The game contains {} examples.".format(len(examples))) + + config.lint_stats_callbacks.append(lint_stats_callback) + + import base64 + +image _finaleimage = im.Data(base64.b64decode(""" + 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 + """), "finale.png") + + +image _finale: + "_finaleimage" + yalign 0.3 + xanchor 1.0 xpos 0.0 + pause 90.0 + easein 1.5 xanchor 0.0 xpos .25 + pause 3.0 + easeout 1.5 xanchor 1.0 xpos 0.0 + repeat diff --git a/testcases/originals/tutorial-8.2/examples.rpy b/testcases/originals/tutorial-8.2/examples.rpy new file mode 100644 index 00000000..ba2c687d --- /dev/null +++ b/testcases/originals/tutorial-8.2/examples.rpy @@ -0,0 +1,11 @@ +# This file is responsible for displaying code examples. It expects to see +# comments like #begin foo and #end foo a the start of lines. The code is +# then used to create example fragments. +# +# When we see: +# +# show screen example(['foo', 'bar']) +# +# We concatenate fragements foo and bar, highlight them, wrap them into a +# viewport, button and transform, and display them to the user. + diff --git a/testcases/originals/tutorial-8.2/gui.rpy b/testcases/originals/tutorial-8.2/gui.rpy new file mode 100644 index 00000000..d91ee60b --- /dev/null +++ b/testcases/originals/tutorial-8.2/gui.rpy @@ -0,0 +1,465 @@ +################################################################################ +## Initialization +################################################################################ + +## The init offset statement causes the init code in this file to run before +## init code 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 = '#0099cc' + +## The color used for a text button when it is neither selected nor hovered. +define gui.idle_color = '#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 = '#aaaaaa' + +## The color that is used for buttons and bars that are hovered. +define gui.hover_color = '#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 = '#ffffff' + +## The color used for a text button when it cannot be selected. +define gui.insensitive_color = '#5555557f' + +## 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 = '#003d51' +define gui.hover_muted_color = '#005b7a' + +## The colors used for dialogue and menu choice text. +define gui.text_color = '#ffffff' +define gui.interface_text_color = '#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 = 24 + +## The size of labels in the game's user interface. +define gui.label_text_size = 28 + +## 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.jpg" +define gui.game_menu_background = "images/bg washington.jpg" + + +## 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(25, 4, 4, 4) + +define gui.check_button_borders = Borders(25, 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" + + +## 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 + +## 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 = 8 + +## 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 = 0.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 that are introduced by player code. +define gui.frame_borders = Borders(20, 8, 20, 8) + +## 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 code. + +## The height of horizontal bars, scrollbars, and sliders. The width of vertical +## bars, scrollbars, and sliders. +define gui.bar_size = 36 +define gui.scrollbar_size = 12 +define gui.slider_size = 30 + +## 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 = 150 +define gui.history_name_ypos = 0 +define gui.history_name_width = 150 +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 = 5 +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 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. + @gui.variant + def 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. + @gui.variant + def small(): + + ## Font sizes. + gui.text_size = 30 + gui.name_text_size = 36 + gui.notify_text_size = 25 + gui.interface_text_size = 36 + gui.button_text_size = 34 + gui.label_text_size = 36 + + ## 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 items in the game menu. + gui.choice_button_width = 1240 + + gui.navigation_spacing = 20 + gui.pref_button_spacing = 10 + + gui.history_height = 190 + gui.history_text_width = 690 + + ## 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 + + ## Quick buttons. + gui.quick_button_text_size = 20 diff --git a/testcases/originals/tutorial-8.2/indepth_character.rpy b/testcases/originals/tutorial-8.2/indepth_character.rpy new file mode 100644 index 00000000..737f2d26 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_character.rpy @@ -0,0 +1,163 @@ +# This file demonstrates how Character objects can be used to customize the +# display of text. + +init python: + config.searchpath.append("../launcher/game/fonts") + +label demo_character: + + show example characters + + e "We've already seen how to define a Character in Ren'Py. But I want to go into a bit more detail as to what a Character is." + + example: + define e_shout = Character("Eileen", who_color="#c8ffc8", what_size=34) + define e_whisper = Character("Eileen", who_color="#c8ffc8", what_size=18) + + e "Here are couple of additional characters." + + e "Each statement creates a Character object, and gives it a single argument, a name. If the name is None, no name is displayed." + + e "This can be followed by named arguments that set properties of the character. A named argument is a property name, an equals sign, and a value." + + e "Multiple arguments should be separated with commas, like they are here. Let's see those characters in action." + + example: + + e_shout "I can shout!" + + e_whisper "And I can speak in a whisper." + + e "This example shows how the name Character is a bit of a misnomer. Here, we have multiple Characters in use, but you see it as me speaking." + + e "It's best to think of a Character as repesenting a name and style, rather than a single person." + + hide example + + e "There are a lot of properties that can be given to Characters, most of them prefixed styles." + + e "Properties beginning with window apply to the textbox, those with what apply to the the dialogue, and those with who to the name of Character speaking." + + e "If you leave a prefix out, the style customizes the name of the speaker." + + e "There are quite a few different properties that can be set this way. Here are some of the most useful." + + example: + define e1 = Character("Eileen", window_background="gui/startextbox.png") + + e1 "The window_background property sets the image that's used for the background of the textbox, which should be the same size as the default in gui/textbox.png." + + example: + + define e1a = Character("Eileen", window_background=None) + + e1a "If it's set to None, the textbox has no background window." + + example: + define e2 = Character("Eileen", who_color="#c8ffc8", what_color="#ffc8c8") + + e2 "The who_color and what_color properties set the color of the character's name and dialogue text, respectively." + + e2 "The colors are strings containing rgb hex codes, the same sort of colors understood by a web browser." + + example: + + define e3 = Character("Eileen", who_font="Roboto-Regular.ttf", what_font="Roboto-Light.ttf") + + e3 "Similarly, the who_font and what_font properties set the font used by the different kinds of text." + + + example: + + define e4 = Character("Eileen", who_bold=True, what_italic=True, what_size=20) + + e4 "Setting the who_bold, what_italic, and what_size properties makes the name bold, and the dialogue text italic at a size of 20 pixels." + + e4 "Of course, the what_bold, who_italic and who_size properties also exist, even if they're not used here." + + + example: + + define e5 = Character("Eileen", what_outlines=[( 1, "#008000", 0, 0 )] ) + + e5 "The what_outlines property puts an outline around the text." + + e5 "It's a little complicated since it takes a list with a tuple in it, with the tuple being four things in parenthesis, and the list the square brackets around them." + + e5 "The first number is the size of the outline, in pixels. That's followed by a string giving the hex-code of the color of the outline, and the x and y offsets." + + example: + + define e6 = Character("Eileen", what_outlines=[( 0, "#808080", 2, 2 )] ) + + e6 "When the outline size is 0 and the offsets are given, what_outlines can also act as a drop-shadow behind the text." + + + example: + define e7 = Character("Eileen", what_xalign=0.5, what_textalign=0.5, what_layout='subtitle') + + e7 "The what_xalign and what_textalign properties control the alignment of text, with 0.0 being left, 0.5 being center, and 1.0 being right." + + e7 "The what_xalign property controls where all the text itself is placed within the textbox, while what_textalign controls where rows of text are placed relative to each other." + + e7 "Generally you'll want to to set them both what_xalign and what_textalign to the same value." + + e7 "Setting what_layout to 'subtitle' puts Ren'Py in subtitle mode, which tries to even out the length of every line of text in a block." + + hide example + + + e8 "These properties can be combined to achieve many different effects." + + example large: + + define e8 = Character( + None, + window_background = None, + + what_size=28, + what_outlines=[( 1, "#008000", 0, 0 )], + what_xalign=0.5, + what_textalign=0.5, + what_layout='subtitle') + + e8 "This example hides the background and shows dialogue centered and outlined, as if the game is being subtitled." + + hide example + with dissolve + + example small: + + define e9 = Character("Eileen", what_prefix='"', what_suffix='"') + + e9 "There are two interesting non-style properties, what_prefix and what_suffix. These can put text at the start and end of a line of dialogue." + + example: + + define l8 = Character(kind=e8, what_outlines=[( 1, "#c00000", 0, 0 )] ) + + e "By using kind, you can copy properties from one character to another, changing only what you need to." + + hide example + with dissolve + + scene bg cave + show lucy happy + with slideleft + + l8 "Like this! Finally I get some more dialogue around here." + + scene bg washington + show eileen happy + with slideawayleft + + example: + define narrator = Character(what_italic=True) + + e "The last thing you have to know is that there's a special character, narrator, that speaks narration. Got it?" + + "I think I do." + + hide example + + return diff --git a/testcases/originals/tutorial-8.2/indepth_displayables.rpy b/testcases/originals/tutorial-8.2/indepth_displayables.rpy new file mode 100644 index 00000000..edd4b184 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_displayables.rpy @@ -0,0 +1,136 @@ +image logo blink: + "logo base" + pause 0.5 + linear .5 alpha 0.0 + pause 0.5 + linear .5 alpha 1.0 + repeat + +transform logopos: + xalign .5 + ypos 50 + +label simple_displayables: + + e "Ren'Py has the concept of a displayable, which is something like an image that can be shown and hidden." + + example: + image logo base = "logo base.png" + + show logo base at logopos + + e "The image statement is used to give an image name to a displayable. The easy way is to simply give an image filename." + + example: + image logo alias = "logo base" + + show logo alias at logopos + + e "But that's not the only thing that an image can refer to. When the string doesn't have a dot in it, Ren'Py interprets that as a reference to a second image." + + hide logo with dissolve + + example: + image bg red = "#c00" + image bg blue = "#0000cc" + image overlay red = "#c008" + image overlay blue = "#0000cc88" + + show bg blue with dissolve + + e "The string can also contain a color code, consisting of hexadecimal digits, just like the colors used by web browsers." + + e "Three or six digit colors are opaque, containing red, green, and blue values. The four and eight digit versions append alpha, allowing translucent colors." + + show bg washington with dissolve + + example: + image logo rotated = Transform("logo base", rotate=45) + + show logo rotated at logopos + with dissolve + + e "The Transform displayable takes a displayable and can apply transform properties to it." + + e "Notice how, since it takes a displayable, it can take another image. In fact, it can take any displayable defined here." + + example: + image logo solidexample = Solid("#0000cc", xysize=(200, 200)) + + show logo solidexample at logopos + with dissolve + + e "There's a more complete form of Solid, that can take style properties. This lets us change the size of the Solid, where normally it fills the screen." + + + example: + image logo text = Text(_("This is a text displayable."), size=30) + + show logo text at logopos + with dissolve + + e "The Text displayable lets Ren'Py treat text as if it was an image." + + example: + image logo text rotate = Transform(Text(_("This is a text displayable."), size=30), rotate=45) + + show logo text rotate at logopos + with dissolve + + e "This means that we can apply other displayables, like Transform, to Text in the same way we do to images." + + example: + image logo composite = Composite((240, 460), + (0, 0), "logo blink", + (0, 50), "logo base.png", + (0, 100), "logo base.png") + + show logo composite at logopos + with dissolve + + e "The Composite displayable lets us group multiple displayables together into a single one, from bottom to top." + + hide logo + + example: + image ninepatch frame = Frame("ninepatch", 40, 40, 40, 40) + + show ninepatch frame at logopos: + size (120, 120) + + e "Some displayables are often used to customize the Ren'Py interface, with the Frame displayable being one of them. The frame displayable takes another displayable, and the size of the left, top, right, and bottom borders." + + show ninepatch frame at logopos: + size (120, 120) + linear 3.0 size (360, 360) + pause 1.0 + linear 3.0 size (120, 120) + pause 1.0 + repeat + + e "The Frame displayable expands or shrinks to fit the area available to it. It does this by scaling the center in two dimensions and the sides in one, while keeping the corners the same size." + + example: + image ninepatch frame tiled = Frame("ninepatch", 40, 40, 40, 40, tile=True) + + show ninepatch frame tiled + + e "A Frame can also tile sections of the displayable supplied to it, rather than scaling." + + example: + image ninepatch paper tiled = Frame("ninepatch paper", 40, 40, 40, 40, tile=True) + + show ninepatch paper tiled + with dissolve + + e "Frames might look a little weird in the abstract, but when used with a texture, you can see how we create scalable interface components." + + hide ninepatch + hide example + with dissolve + + e "These are just the simplest displayables, the ones you'll use directly the most often." + + e "You can even write custom displayables for minigames, if you're proficient at Python. But for many visual novels, these will be all you'll need." + + return diff --git a/testcases/originals/tutorial-8.2/indepth_minigame.rpy b/testcases/originals/tutorial-8.2/indepth_minigame.rpy new file mode 100644 index 00000000..af0697ec --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_minigame.rpy @@ -0,0 +1,286 @@ +example minigame: + + init python: + + class PongDisplayable(renpy.Displayable): + + def __init__(self): + + renpy.Displayable.__init__(self) + + + # The sizes of some of the images. + self.PADDLE_WIDTH = 12 + self.PADDLE_HEIGHT = 95 + self.PADDLE_X = 240 + self.BALL_WIDTH = 15 + self.BALL_HEIGHT = 15 + self.COURT_TOP = 129 + self.COURT_BOTTOM = 650 + + + # Some displayables we use. + self.paddle = Solid("#ffffff", xsize=self.PADDLE_WIDTH, ysize=self.PADDLE_HEIGHT) + self.ball = Solid("#ffffff", xsize=self.BALL_WIDTH, ysize=self.BALL_HEIGHT) + + # If the ball is stuck to the paddle. + self.stuck = True + + # The positions of the two paddles. + self.playery = (self.COURT_BOTTOM - self.COURT_TOP) / 2 + self.computery = self.playery + + # The speed of the computer. + self.computerspeed = 380.0 + + # The position, delta-position, and the speed of the + # ball. + self.bx = self.PADDLE_X + self.PADDLE_WIDTH + 10 + self.by = self.playery + self.bdx = .5 + self.bdy = .5 + self.bspeed = 350.0 + + # The time of the past render-frame. + self.oldst = None + + # The winner. + self.winner = None + + def visit(self): + return [ self.paddle, self.ball ] + + # Recomputes the position of the ball, handles bounces, and + # draws the screen. + def render(self, width, height, st, at): + + # The Render object we'll be drawing into. + r = renpy.Render(width, height) + + # Figure out the time elapsed since the previous frame. + if self.oldst is None: + self.oldst = st + + dtime = st - self.oldst + self.oldst = st + + # Figure out where we want to move the ball to. + speed = dtime * self.bspeed + oldbx = self.bx + + if self.stuck: + self.by = self.playery + else: + self.bx += self.bdx * speed + self.by += self.bdy * speed + + # Move the computer's paddle. It wants to go to self.by, but + # may be limited by it's speed limit. + cspeed = self.computerspeed * dtime + if abs(self.by - self.computery) <= cspeed: + self.computery = self.by + else: + self.computery += cspeed * (self.by - self.computery) / abs(self.by - self.computery) + + # Handle bounces. + + # Bounce off of top. + ball_top = self.COURT_TOP + self.BALL_HEIGHT / 2 + if self.by < ball_top: + self.by = ball_top + (ball_top - self.by) + self.bdy = -self.bdy + + if not self.stuck: + renpy.sound.play("pong_beep.opus", channel=0) + + # Bounce off bottom. + ball_bot = self.COURT_BOTTOM - self.BALL_HEIGHT / 2 + if self.by > ball_bot: + self.by = ball_bot - (self.by - ball_bot) + self.bdy = -self.bdy + + if not self.stuck: + renpy.sound.play("pong_beep.opus", channel=0) + + # This draws a paddle, and checks for bounces. + def paddle(px, py, hotside): + + # Render the paddle image. We give it an 800x600 area + # to render into, knowing that images will render smaller. + # (This isn't the case with all displayables. Solid, Frame, + # and Fixed will expand to fill the space allotted.) + # We also pass in st and at. + pi = renpy.render(self.paddle, width, height, st, at) + + # renpy.render returns a Render object, which we can + # blit to the Render we're making. + r.blit(pi, (int(px), int(py - self.PADDLE_HEIGHT / 2))) + + if py - self.PADDLE_HEIGHT / 2 <= self.by <= py + self.PADDLE_HEIGHT / 2: + + hit = False + + if oldbx >= hotside >= self.bx: + self.bx = hotside + (hotside - self.bx) + self.bdx = -self.bdx + hit = True + + elif oldbx <= hotside <= self.bx: + self.bx = hotside - (self.bx - hotside) + self.bdx = -self.bdx + hit = True + + if hit: + renpy.sound.play("pong_boop.opus", channel=1) + self.bspeed *= 1.10 + + # Draw the two paddles. + paddle(self.PADDLE_X, self.playery, self.PADDLE_X + self.PADDLE_WIDTH) + paddle(width - self.PADDLE_X - self.PADDLE_WIDTH, self.computery, width - self.PADDLE_X - self.PADDLE_WIDTH) + + # Draw the ball. + ball = renpy.render(self.ball, width, height, st, at) + r.blit(ball, (int(self.bx - self.BALL_WIDTH / 2), + int(self.by - self.BALL_HEIGHT / 2))) + + # Check for a winner. + if self.bx < -50: + self.winner = "eileen" + + # Needed to ensure that event is called, noticing + # the winner. + renpy.timeout(0) + + elif self.bx > width + 50: + self.winner = "player" + renpy.timeout(0) + + # Ask that we be re-rendered ASAP, so we can show the next + # frame. + renpy.redraw(self, 0) + + # Return the Render object. + return r + + # Handles events. + def event(self, ev, x, y, st): + + import pygame + + # Mousebutton down == start the game by setting stuck to + # false. + if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1: + self.stuck = False + + # Ensure the pong screen updates. + renpy.restart_interaction() + + # Set the position of the player's paddle. + y = max(y, self.COURT_TOP) + y = min(y, self.COURT_BOTTOM) + self.playery = y + + # If we have a winner, return him or her. Otherwise, ignore + # the current event. + if self.winner: + return self.winner + else: + raise renpy.IgnoreEvent() + + screen pong(): + + default pong = PongDisplayable() + + add "bg pong field" + + add pong + + text _("Player"): + xpos 240 + xanchor 0.5 + ypos 25 + size 40 + + text _("Eileen"): + xpos (1280 - 240) + xanchor 0.5 + ypos 25 + size 40 + + if pong.stuck: + text _("Click to Begin"): + xalign 0.5 + ypos 50 + size 40 + + + +label demo_minigame: + + e "You may want to mix Ren'Py with other forms of gameplay. There are a couple of ways to do this." + + e "The first is with the screen system, which can be used to display data and create button and menu based interfaces." + + e "Screens will work for many simulation-style games and RPGs." + + e "When screens are not enough, you can write a creator-defined displayable to extend Ren'Py itself. A creator-defined displayable can process raw events and draw to the screen." id demo_minigame_a92baa6b + + e "That makes it possible to create all kinds of minigames. Would you like to play some pong?" + +example minigame hide: + label play_pong: + + window hide # Hide the window and quick menu while in pong + $ quick_menu = False + + call screen pong + + $ quick_menu = True + window show + + show eileen vhappy + + if _return == "eileen": + + e "I win!" + + else: + + e "You won! Congratulations." + +label pong_done: + + show eileen happy + + menu: + e "Would you like to play again?" + + "Sure.": + + jump play_pong + + "No thanks.": + + pass + + show example minigame large + + e "Here's the source code for the minigame. It's very complex, and assumes you understand Python well." + + e "I won't go over it in detail here. You can read more about it in the {a=https://www.renpy.org/doc/html/udd.html}Creator-Defined Displayable documentation{/a}." + + hide example + + e "Minigames can spice up your visual novel, but be careful – not every visual novel player wants to be good at arcade games." + + e "Part of the reason Ren'Py works well is that it's meant for certain types of games, like visual novels and life simulations." + + e "The further afield you get from those games, the more you'll find yourself fighting Ren'Py. At some point, it makes sense to consider other engines." + + show eileen vhappy + + e "And that's fine with us. We'll always be here for you when you're making visual novels." + + show eileen happy + + return diff --git a/testcases/originals/tutorial-8.2/indepth_style.rpy b/testcases/originals/tutorial-8.2/indepth_style.rpy new file mode 100644 index 00000000..fad167b5 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_style.rpy @@ -0,0 +1,1094 @@ + +transform examplepos: + xalign 0.5 + ypos 50 + + +screen style0(): + + frame: + at examplepos + + xpadding 20 + + ypadding 20 + + vbox: + + spacing 10 + + textbutton _("Button 1") action Return(True) + + textbutton _("Button 2"): + style "empty" + text_style "empty" + + xminimum 240 + ypadding 2 + + background RoundRect("#003c78") + hover_background RoundRect("#0050a0") + + text_color "#c8ffff" + text_xalign 0.5 + + action Return(True) + + +label new_gui: + + e "When you create a new project, Ren'Py will automatically create a GUI - a Graphical User Interface - for it." + + e "It defines the look of both in-game interface, like this text box, and out-of-game interface like the main and game menus." + + e "The default GUI is meant to be nice enough for a simple project. With a few small changes, it's what you're seeing in this game." + + e "The GUI is also meant to be easy for an intermediate creator to customize. Customizing the GUI consists of changing the image files in the gui directory, and changing variables in gui.rpy." + + e "At the same time, even when customized, the default GUI might be too recognizable for an extremely polished game. That's why we've made it easy to totally replace." + + e "We've put an extensive guide to customizing the GUI on the Ren'Py website. So if you want to learn more, visit the {a=https://www.renpy.org/doc/html/gui.html}GUI customization guide{/a}." + + return + +label styles: + + show eileen happy at left + + e "Ren'Py has a powerful style system that controls what displayables look like." + + e "While the default GUI uses variables to provide styles with sensible defaults, if you're replacing the GUI or creating your own screens, you'll need to learn about styles yourself." + +label styles_menu: + + $ reset_example() + + menu: + + e "What would you like to know about styles?" + + "Style basics.": + call style_basics from _call_style_basics + + "General style properties.": + call style_general from _call_style_general + + "Text style properties.": + call style_text from _call_style_text + + "Window and Button style properties.": + call style_button from _call_style_button + + "Bar style properties.": + call style_bar from _call_style_bar + + "Box, Grid, and Fixed style properties.": + call style_box from _call_style_box + + "The Displayable Inspector.": + call style_inspector from _call_style_inspector + + "That's all I want to know.": + return + + jump styles_menu + +label style_basics: + + e "Styles let a displayable look different from game to game, or even inside the same game." + + show screen style0 + with dissolve + + e "Both of these buttons use the same displayables. But since different styles have been applied, the buttons look different from each other." + + hide screen style0 + with dissolve + + e "Styles are a combination of information from four different places." + + example: + screen style1(): + text _("This text is colored green."): + style "my_text" + color "#c0ffc0" + + at examplepos + + show screen style1 + with dissolve + + e "The first place Ren'Py can get style information from is part of a screen. Each displayable created by a screen can take a style name and style properties." + + example: + screen textstyle(): + frame: + textbutton _("Danger"): + text_color "#c04040" + text_hover_color "#ff0000" + action Return(True) + + at examplepos + + + hide screen style1 + show screen textstyle + with dissolve + + e "When a screen displayable contains text, style properties prefixed with text_ apply to that text." + + + example: + image style2 = Text(_("This text is colored red."), color="#ffc0c0") + + + show style2 at examplepos + with dissolve + hide screen textstyle + with dissolve + + + e "The next is as part of a displayable created in an image statement. Style properties are just arguments to the displayable." + + hide style2 + with dissolve + + example: + define egreen = Character("Eileen", who_color="#c8ffc8", who_bold=True, what_color="#c8ffc8") + + + egreen "Style properties can also be given as arguments when defining a character." + + egreen "Arguments beginning with who_ are style properties applied to the character's name, while those beginning with what_ are applied to the character's dialogue." + + egreen "Style properties that don't have a prefix are also applied to the character's name." + + example: + style blue_text: + color "#c0c0ff" + + image style3 = Text(_("This text is colored blue."), style="blue_text") + + show style3 at examplepos + + e "Finally, there is the the style statement, which creates or changes a named style. By giving Text the style argument, we tell it to use the blue_text style." + + hide style3 + hide example + with dissolve + + e "A style property can inherit from a parent. If a style property is not given in a style, it comes from the parent of that style." + + e "By default the parent of the style has the same name, with the prefix up to the the first underscore removed. If the style does not have an underscore in its name, 'default' is used." + + e "For example, blue_text inherits from text, which in turn inherits from default. The default style defines all properties, so it doesn't inherit from anything." + + example: + style blue_text is text: + color "#c0c0ff" + + e "The parent can be explicitly changed by giving the style statement an 'is' clause. In this case, we're explictly setting the style to the parent of text." + + hide example + + e "Each displayable has a default style name. By default, it's usually the lower-case displayable name, like 'text' for Text, or 'button' for buttons." + + e "In a screen, a displayable can be given the style_prefix property to give a prefix for that displayable and its children." id style_basics_35db9a05 + + e "For example, a text displayable with a style_prefix of 'help' will be given the style 'help_text'." + + e "Lastly, when a displayable is a button, or inside a button, it can take style prefixes." + + e "The prefixes idle_, hover_, and insensitive_ are used when the button is unfocused, focused, and unfocusable." + + e "These can be preceded by selected_ to change how the button looks when it represents a selected value or screen." + + example: + style styled_button_text: + idle_color "#c0c0c0" + hover_color "#ffffff" + insensitive_color "#303030" + selected_idle_color "#e0e080" + selected_hover_color "#ffffc0" + + screen style4(): + + default result = 1 + + frame: + style_prefix "styled" + xpadding 20 + ypadding 20 + + at examplepos + + vbox: + textbutton "Button 1" action SetScreenVariable("result", 1) + textbutton "Button 2" action SetScreenVariable("result", 2) + textbutton "Button 3" action None + + show screen style4 + with dissolve + + e "This screen shows the style prefixes in action. You can click on a button to select it, or click outside to advance." + + hide screen style4 + with dissolve + + hide example + + e "Those are the basics of styles. If GUI customization isn't enough for you, styles let you customize just about everything in Ren'Py." + + return + + +screen general(style): + frame: + style style + text _("Orbiting Earth in the spaceship, I saw how beautiful our planet is.\n–Yuri Gagarin") + + +label style_general: + + e "The first group of style properties that we'll go over are the general style properties. These work with every displayable, or at least many different ones." + + example: + + style general is frame: + xalign 0.5 + yalign 0.2 + + show screen general("general") + with dissolve + + e "Every displayable takes the position properties, which control where it can be placed on screen. Since I've already mentioned them, I won't repeat them here." + + + example: + style minmax_general: + xmaximum 400 + yminimum 200 + + show screen general("minmax_general") + with dissolve + + e "The xmaximum and ymaximum properties set the maximum width and height of the displayable, respectively. This will cause Ren'Py to shrink things, if possible." + + e "Sometimes, the shrunken size will be smaller than the size given by xmaximum and ymaximum." + + e "Similarly, the xminimum and yminimum properties set the minimum width and height. If the displayable is smaller, Ren'Py will try to make it bigger." + + + example: + style xysize_general: + xsize 400 + ysize 200 + + show screen general("xysize_general") + + e "The xsize and ysize properties set the minimum and maximum size to the same value, fixing the size." + + e "These only works for displayables than can be resized. Some displayables, like images, can't be made bigger or smaller." + + example: + style area_general: + area (600, 20, 400, 200) + + show screen general("area_general") + + e "The area property takes a tuple - a parenthesis bounded list of four items. The first two give the position, and the second two the size." + + example: + style alt_general: + alt _("\"Orbiting Earth in the spaceship, I saw how beautiful our planet is.\" Said by Yuri Gagarin.") + + show screen general("alt_general") + + + e "Finally, the alt property changes the text used by self-voicing for the hearing impaired." + + hide screen general + hide example + with dissolve + + return + +screen text(style, vertical=False): + frame: + xalign 0.5 + ypos 50 + + if vertical: + left_padding 20 + right_padding 35 + bottom_padding 35 + + text _("Vertical") style style + else: + xsize 400 + text _("Far better it is to dare mighty things, to win glorious triumphs, even though checkered by failure, than to rank with those poor spirits who neither enjoy nor suffer much, because they live in the gray twilight that knows not victory nor defeat.\n\n–Theodore Roosevelt"): + style style + + +label style_text: + + e "The text style properties apply to text and input displayables." + + e "Text displayables can be created implicitly or explicitly. For example, a textbutton creates a text displayable with a style ending in button_text." + + e "These can also be set in gui.rpy by changing or defining variables with names like gui.button_text_size." + + example: + style bold_text: + bold True + + show screen text("bold_text") + + e "The bold style property makes the text bold. This can be done using an algorithm, rather than a different version of the font." + + example: + style color_text: + color "#c0ffc0" + + show screen text("color_text") + + e "The color property changes the color of the text. It takes hex color codes, just like everything else in Ren'Py." + + example: + style first_indent_text: + first_indent 40 + + show screen text("first_indent_text") + + e "The first_indent style property determines how far the first line is indented." + + example: + style font_text: + font "DejaVuSans-Bold.ttf" + + show screen text("font_text") + + e "The font style property changes the font the text uses. Ren'Py takes TrueType and OpenType fonts, and you'll have to include the font file as part of your visual novel." + + example: + style size_text: + size 14 + + show screen text("size_text") + + e "The size property changes the size of the text." + + + example: + style italic_text: + italic True + + show screen text("italic_text") + + e "The italic property makes the text italic. Again, this is better done with a font, but for short amounts of text Ren'Py can do it for you." + + + example: + style justify_text: + justify True + + show screen text("justify_text") + + e "The justify property makes the text justified, lining all but the last line up on the left and the right side." + + example: + style kerning_text: + kerning -0.5 + + show screen text("kerning_text") + + e "The kerning property kerns the text. When it's negative, characters are closer together. When positive, characters are farther apart." + + + example: + style leading_spacing_text: + line_leading 5 + line_spacing 7 + + show screen text("leading_spacing_text") + + e "The line_leading and line_spacing properties put spacing before each line, and between lines, respectively." + + + example: + style outlines_text: + outlines [ (1, "#408040", 0, 0) ] + + show screen text("outlines_text") + + e "The outlines property puts outlines around text. This takes a list of tuples, which is a bit complicated." + + e "But if you ignore the brackets and parenthesis, you have the width of the outline, the color, and then horizontal and vertical offsets." + + example: + style rest_indent_text: + rest_indent 40 + + show screen text("rest_indent_text") + + e "The rest_indent property controls the indentation of lines after the first one." + + + example: + style center_text: + textalign 0.5 + + show screen text("center_text") + + e "The textalign property controls the positioning of multiple lines of text inside the text displayable. For example, 0.5 means centered." id style_text_430c1959 + + e "It doesn't change the position of the text displayable itself. For that, you'll often want to set the textalign and xalign to the same value." id style_text_19aa0833 + + + example: + style right_text: + textalign 1.0 + yalign 1.0 + + show screen text("right_text") + + e "When both textalign and xalign are set to 1.0, the text is properly right-justified." id style_text_efc3c392 + + + example: + style underline_text: + underline True + + show screen text("underline_text") + + e "The underline property underlines the text." + + + hide screen text + hide example + with dissolve + + e "Those are the most common text style properties, but not the only ones. Here are a few more that you might need in special circumstances." + + example: + style antialias_text: + antialias False + + show screen text("antialias_text") + + e "By default, text in Ren'Py is antialiased, to smooth the edges. The antialias property can turn that off, and make the text a little more jagged." + + example: + style adjust_true_text: + adjust_spacing True + + show screen text("adjust_true_text") + + e "The adjust_spacing property is a very subtle one, that only matters when a player resizes the window. When True, characters will be shifted a bit so the Text has the same relative spacing." + + + example: + style adjust_true_text: + adjust_spacing False + + show screen text("adjust_false_text") + + e "When False, the text won't jump around as much. But it can be a little wider or narrower based on screen size." + + + example: + style layout_nobreak_text: + layout "nobreak" + + show screen text("layout_nobreak_text") + + e "The layout property has a few special values that control where lines are broken. The 'nobreak' value disables line breaks entirely, making the text wider." + + + example: + style layout_subtitle_text: + layout "subtitle" + xalign 0.5 + textalign 0.5 + + show screen text("layout_subtitle_text") + + e "When the layout property is set to 'subtitle', the line breaking algorithm is changed to try to make all lines even in length, as subtitles usually are." + + example: + style strikethrough_text: + strikethrough True + + show screen text("strikethrough_text") + + e "The strikethrough property draws a line through the text. It seems pretty unlikely you'd want to use this one." + + + example: + style vertical_text: + vertical True + size 18 + + show screen text("vertical_text", True) + + e "The vertical style property places text in a vertical layout. It's meant for Asian languages with special fonts." + + hide screen text + hide example + with dissolve + + e "And those are the text style properties. There might be a lot of them, but we want to give you a lot of control over how you present text to your players." + + + return + + + + +screen button(style): + + default selected = "top" + + frame: + xalign 0.5 + ypos 50 + background "#0004" + xsize 350 + + has vbox: + xalign 0.5 + + textbutton _("Top Choice"): + style style + action SetScreenVariable("selected", "top") + text_style "example_button_text" + + textbutton _("Bottom Choice"): + style style + action SetScreenVariable("selected", "bottom") + text_style "example_button_text" + + +example example_button_text: + style example_button_text: + xalign 0.5 + color "#404040" + hover_color "#4040c0" + +label style_button: + + e "Next up, we have the window and button style properties. These apply to windows like the text window at the bottom of this screen and frames like the ones we show examples in." + + e "These properties also apply to buttons, in-game and out-of-game. To Ren'Py, a button is a window you can click." + + example example_button large: + style example_button is default: + idle_background Frame("idle_background.png", 10, 10, tile=True) + hover_background Frame("hover_background.png", 10, 10, tile=True) + xalign 0.5 + + show screen button('example_button') + with dissolve + + e "I'll start off with this style, which everything will inherit from. To make our lives easier, it inherits from the default style, rather than the customized buttons in this game's GUI." id style_button_9b53ce93 + + e "The first style property is the background property. It adds a background to a button or window. Since this is a button, idle and hover variants choose different backgrounds when focused." id style_button_aece4a8c + + e "We also center the two buttons, using the xalign position property." + + show example example_button_text + + e "We've also customized the style of the button's text, using this style. It centers the text and makes it change color when hovered." + + example: + style oddly_padded_button is example_button: + left_padding 10 + right_padding 40 + top_padding 10 + bottom_padding 5 + + show screen button('oddly_padded_button') + + e "Without any padding around the text, the button looks odd. Ren'Py has padding properties that add space inside the button's background." + + example: + style padded_button is example_button: + xpadding 40 + ypadding 10 + + show screen button('padded_button') + + e "More commonly used are the xpadding and ypadding style properties, which add the same padding to the left and right, or the top and bottom, respectively." + + example: + style margin_button is padded_button: + ymargin 10 + + show screen button('margin_button') + + e "The margin style properties work the same way, except they add space outside the background. The full set exists: left_margin, right_margin, top_margin, bottom_margin, xmargin, and ymargin." + + + example: + style sized_button is margin_button: + size_group "example" + + show screen button('sized_button') + + e "The size_group style property takes a string. Ren'Py will make sure that all the windows or buttons with the same size_group string are the same size." + + + example: + style xfill_button is margin_button: + xfill True + + show screen button('xfill_button') + + e "Alternatively, the xfill and yfill style properties make a button take up all available space in the horizontal or vertical directions." + + + example: + style foreground_button is xfill_button: + foreground "check_foreground.png" + selected_foreground "check_selected_foreground.png" + + show screen button('foreground_button') + + e "The foreground property gives a displayable that is placed on top of the contents and background of the window or button." + + e "One way to use it is to provide extra decorations to a button that's serving as a checkbox. Another would be to use it with a Frame to provide a glossy shine that overlays the button's contents." + + example: + style beep_button is foreground_button: + hover_sound "pong_beep.opus" + activate_sound "pong_boop.opus" + + show screen button('beep_button') + + e "There are also a few style properties that only apply to buttons. The hover_sound and activate_sound properties play sound files when a button is focused and activated, respectively." + + + example: + style focus_mask_button is beep_button: + focus_mask True + + show screen button('focus_mask_button') + + e "Finally, the focus_mask property applies to partially transparent buttons. When it's set to True, only areas of the button that aren't transparent cause a button to focus." + + hide example + hide screen button + with dissolve + + return + +screen bar(style): + + default measure = 42.0 + + frame: + xalign 0.5 + ypos 50 + ypadding 20 + + has vbox: + spacing 20 + + bar: + value ScreenVariableValue("measure", range=100.0) + style style + xsize 400 + + + text "[measure:.0f]/100": + xalign 0.5 + + +screen vbar(style): + + default measure = 42.0 + + frame: + xalign 0.5 + ypos 20 + ypadding 20 + xsize 140 + + has vbox: + spacing 20 + xfill True + + bar: + value ScreenVariableValue("measure", range=100.0) + style style + ysize 250 + xalign 0.5 + + text "[measure:.0f]/100": + xalign 0.5 + textalign 0.5 + min_width 100 + + +image bar empty idle vertical = Transform("bar empty idle", rotate=90, rotate_pad=False) +image bar empty hover vertical = Transform("bar empty hover", rotate=90, rotate_pad=False) +image bar full idle vertical = Transform("bar full idle", rotate=90, rotate_pad=False) +image bar full hover vertical = Transform("bar full hover", rotate=90, rotate_pad=False) + +style empty_only is default: + left_bar Frame("bar empty idle.png", 4, 0) + hover_left_bar Frame("bar empty hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + +style full_only is default: + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar full idle.png", 4, 0) + hover_right_bar Frame("bar full hover.png", 4, 0) + ysize 30 + +label style_bar: + + show screen bar("empty_only") + with dissolve + + e "To demonstrate styles, let me first show two of the images we'll be using. This is the image we're using for parts of the bar that are empty." + + show screen bar("full_only") + + e "And here's what we use for parts of the bar that are full." + + example large: + style example_bar is default: + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + + show screen bar('example_bar') + + e "The left_bar and right_bar style properties, and their hover variants, give displayables for the left and right side of the bar. By default, the value is shown on the left." + + e "Also by default, both the left and right displayables are rendered at the full width of the bar, and then cropped to the appropriate size." + + e "We give the bar the ysize property to set how tall it is. We could also give it xsize to choose how wide, but here it's limited by the width of the frame it's in." + + example: + style invert_bar is default: + bar_invert True + left_bar Frame("bar empty idle.png", 4, 0) + hover_left_bar Frame("bar empty hover.png", 4, 0) + right_bar Frame("bar full idle.png", 4, 0) + hover_right_bar Frame("bar full hover.png", 4, 0) + ysize 30 + + show screen bar('invert_bar') + + e "When the bar_invert style property is True, the bar value is displayed on the right side of the bar. The left_bar and right_bar displayables might also need to be swapped." + + example: + style resizing_bar is default: + bar_resizing True + left_bar Frame("bar full idle.png", 4, 0) + hover_left_bar Frame("bar full hover.png", 4, 0) + right_bar Frame("bar empty idle.png", 4, 0) + hover_right_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + + + show screen bar('resizing_bar') + + e "The bar_resizing style property causes the bar images to be resized to represent the value, rather than being rendered at full size and cropped." + + example: + + style thumb_bar is default: + thumb "bar thumb idle.png" + hover_thumb "bar thumb hover.png" + base_bar Frame("bar empty idle.png", 4, 0) + hover_base_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + + show screen bar('thumb_bar') + + e "The thumb style property gives a thumb image, that's placed based on the bar's value. In the case of a scrollbar, it's resized if possible." id style_bar_7d361bac + + e "Here, we use it with the base_bar style property, which sets both bar images to the same displayable." + + example: + + style gutter_bar is default: + left_gutter 4 + right_gutter 4 + thumb "bar thumb idle.png" + hover_thumb "bar thumb hover.png" + base_bar Frame("bar empty idle.png", 4, 0) + hover_base_bar Frame("bar empty hover.png", 4, 0) + ysize 30 + + show screen bar('gutter_bar') + + e "The left_gutter and right_gutter properties set a gutter on the left or right size of the bar. The gutter is space the bar can't be dragged into, that can be used for borders." + + example: + style vertical_bar is default: + bar_vertical True + top_bar Frame("bar empty idle vertical", 4, 0) + hover_top_bar Frame("bar empty hover vertical", 4, 0) + bottom_bar Frame("bar full idle vertical", 4, 0) + hover_bottom_bar Frame("bar full hover vertical", 4, 0) + xsize 30 + + hide screen bar + show screen vbar('vertical_bar') + with dissolve + + e "The bar_vertical style property displays a vertically oriented bar. All of the other properties change names - left_bar becomes top_bar, while right_bar becomes bottom_bar." + + hide screen vbar + with dissolve + + e "Finally, there's one style we can't show here, and it's unscrollable. It controls what happens when a scrollbar can't be moved at all." + + e "By default, it's shown. But if unscrollable is 'insensitive', the bar becomes insensitive. If it's 'hide', the bar is hidden, but still takes up space." + + hide example + + e "That's it for the bar properties. By using them, a creator can customize bars, scrollbars, and sliders." + + return + +screen hbox(style, wide, highlight="#fff2"): + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + + frame: + style "empty" + background highlight + + hbox: + style style + text _("First Child") color "#ffe0e0" + text _("Second Child") color "#e0ffe0" + text _("Third Child") color "#e0e0ff" + + if wide: + text _("Fourth Child") color "#ffffe0" + text _("Fifth Child") color "#e0ffff" + text _("Sixth Child") color "#ffe0ff" + + +screen vbox(style, highlight="#fff2"): + + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + + frame: + style "empty" + background highlight + + vbox: + style style + text _("First Child") xalign 0.5 color "#ffe0e0" + text _("Second Child") xalign 0.5 color "#e0ffe0" + text _("Third Child") xalign 0.5 color "#e0e0ff" + + +screen grid(style): + + frame: + xalign 0.5 + ypos 50 + ypadding 20 + + grid 3 2: + style style + + text _("First Child") color "#ffe0e0" + text _("Second Child") color "#e0ffe0" + text _("Third Child") color "#e0e0ff" + + text _("Fourth Child") color "#ffffe0" + text _("Fifth Child") color "#e0ffff" + text _("Sixth Child") color "#ffe0ff" + + +screen fixed(style): + + frame: + xalign 0.5 + ypos 50 + ypadding 20 + xsize 500 + ysize 400 + + frame: + style "empty" + background "#fff2" + + fixed: + style style + + add Transform("logo base", zoom=.9): + xpos 10 + ypos 10 + + text "Ren'Py": + font "DejaVuSans.ttf" + xpos 150 + ypos 220 + size 80 + outlines [ (2, "#333", 0, 0) ] + + + +label style_box: + + show screen hbox('hbox', False, None) + with dissolve + + e "The hbox displayable is used to lay its children out horizontally. By default, there's no spacing between children, so they run together." + + hide screen hbox + show screen vbox('vbox', None) + with dissolve + + e "Similarly, the vbox displayable is used to lay its children out vertically. Both support style properties that control placement." + + show screen vbox('vbox') + with dissolve + + e "To make the size of the box displayable obvious, I'll add a highlight to the box itself, and not the frame containing it." + + example: + style fill_vbox: + xfill True + + show screen vbox('fill_vbox') + + e "Boxes support the xfill and yfill style properties. These properties make a box expand to fill the available space, rather than the space of the largest child." + + + example: + style spacing_vbox: + spacing 10 + xfill True + + show screen vbox('spacing_vbox') + + e "The spacing style property takes a value in pixels, and adds that much spacing between each child of the box." + + + example: + style first_spacing_vbox is vbox: + first_spacing 10 + xfill True + + show screen vbox('first_spacing_vbox') + + e "The first_spacing style property is similar, but it only adds space between the first and second children. This is useful when the first child is a title that needs different spacing." + + + example: + style reverse_vbox: + box_reverse True + xfill True + + show screen vbox('reverse_vbox') + + e "The box_reverse style property reverses the order of entries in the box." + + + + example: + style spacing_hbox: + spacing 20 + xfill True + + hide screen vbox + show screen hbox("spacing_hbox", False) + with dissolve + + e "We'll switch back to a horizontal box for our next example." + + example: + style wrap_hbox: + box_wrap True + spacing 5 + xfill True + + show screen hbox("wrap_hbox", True) + + e "The box_wrap style property fills the box with children until it's full, then starts again on the next line." + + hide screen hbox + show screen grid('spacing_grid') + with dissolve + + + example: + style spacing_grid: + xspacing 20 + yspacing 50 + + + e "Grids bring with them two more style properties. The xspacing and yspacing properties control spacing in the horizontal and vertical directions, respectively." + + hide example + hide screen grid + show screen fixed('fixed') + with dissolve + + e "Lastly, we have the fixed layout. The fixed layout usually expands to fill all space, and shows its children from back to front." + + e "But of course, we have some style properties that can change that." + + example: + style fit_fixed: + xfit True + yfit True + + show screen fixed('fit_fixed') + + e "When the xfit style property is True, the fixed lays out all its children as if it was full size, and then shrinks in width to fit them. The yfit style works the same way, but in height." + + example: + style reverse_fixed: + order_reverse True + + show screen fixed('reverse_fixed') + + e "The order_reverse style property changes the order in which the children are shown. Instead of back-to-front, they're displayed front-to-back." + + + hide screen fixed + hide example + with dissolve + + return + + +label style_inspector: + + e "Sometimes it's hard to figure out what style is being used for a particular displayable. The displayable inspector can help with that." + + e "To use it, place the mouse over a portion of the Ren'Py user interface, and hit shift+I. That's I for inspector." + + e "Ren'Py will pop up a list of displayables the mouse is over. Next to each is the name of the style that displayable uses." + + e "You can click on the name of the style to see where it gets its properties from." + + e "By default, the inspector only shows interface elements like screens, and not images. Type shift+alt+I if you'd like to see images as well." + + e "You can try the inspector right now, by hovering this text and hitting shift+I." + + return diff --git a/testcases/originals/tutorial-8.2/indepth_text.rpy b/testcases/originals/tutorial-8.2/indepth_text.rpy new file mode 100644 index 00000000..ec7b7336 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_text.rpy @@ -0,0 +1,212 @@ +# This file demonstrates some of the text-layout and handling +# capabilities of Ren'Py. + + +init 1: + define eslow = Character(kind=e, what_slow_cps=20) + +# init python: +# style.ruby_style = Style(style.default) +# style.ruby_style.yoffset = -20 +# style.ruby_style.size = 12 +# +# define eruby = Character( +# _("Eileen"), +# color="#c8ffc8", +# what_ruby_style=style.ruby_style, +# what_line_leading=10) + + +label a_label: + + e "You just clicked to jump to a label." + + jump after_a_label + +label text: + + e "Sometimes, when showing text, we'll want to change the way some of the text is displayed." + + example tags1 hide: + e "For example, we might want to have text that is {b}bold{/b}, {i}italic{/i}, {s}struckthrough{/s}, or {u}underlined{/u}." + + e "That's what text tags are for." + + show example tags1 + + e "Text tags are contained in braces, like the {{b} tag above. When a text tag takes a closing tag, the closing tag begins with a slash, like {{/b} does." + + e "We've already seen the b, i, s, and u tags, but there are lot more than those. I'll show you the rest of them." + + example: + + e "The a text tag can {a=https://www.renpy.org}link to a website{/a} or {a=jump:a_label}jump to a label{/a}." + +label after_a_label: + + example: + + e "The alpha text tag makes text {alpha=.5}translucent{/alpha}." + + example: + + e "The color text tag changes the {color=#0080c0}color{/color} of the text." + + example: + + e "The cps text tag {cps=25}makes text type itself out slowly{/cps}, even if slow text is off." + + e "The cps tag can also be relative to the default speed, {cps=*2}doubling{/cps} or {cps=*0.5}halving{/cps} it." + + + example: + + e "The font tag changes the font, for example to {font=DejaVuSans-Bold.ttf}DejaVuSans-Bold.ttf{/font}." + + e "Sometimes, changing to a bold font looks better than using the {{b} tag." + + + example: + + e "The k tag changes kerning. It can space the letters of a word {k=-.5}closer together{/k} or {k=.5}farther apart{/k}." + + + example: + + e "The size tag changes the size of text. It can make text {size=+10}bigger{/size} or {size=-10}smaller{/size}, or set it to a {size=30}fixed size{/size}." + + + example: + + e "The space tag {space=30} adds horizontal space in text.{vspace=30}The vspace tag adds vertical space between lines." + + hide example + + e "There are a few text tags that only makes sense in dialogue." + + example: + + e "The p tag breaks a paragraph,{p}and waits for the player to click." + + e "If it is given a number as an argument,{p=1.5}it waits that many seconds." + + example: + + e "The w tag also waits for a click,{w} except it doesn't break lines,{w=.5} the way p does." + + + example: + + eslow "The nw tag causes Ren'Py to continue past slow text,{nw}" + with flashbulb + extend " to the next statement." + + + example: + e "To break a line without pausing,\none can write \\n. \\' and \\\" include quotes in the text." + + + hide example + + e "The interpolation feature takes a variable name in square brackets, and inserts it into text." + + + example: + $ variable = _("{i}variable value{/i}") + + e "For example, this displays the [variable!t]." + + + example: + e "When the variable name is followed by !q, special characters are quoted. This displays the raw [variable!q!t], including the italics tags." + + example showtrans: + $ translatable = _("translatable text") + + e "When the variable name is followed by !t, it is translated to [variable!t]. It could be something else in a different language." + + example: + e "Finally, certain characters are special. [[, {{, and \\ need to be doubled if included in text. The %% character should be doubled if used in dialogue." + + hide example + pause .5 + + return + + + + +# label demo_text: +# +# e "Ren'Py gives you quite a bit of control over how text appears." +# +# e "Text tags let us control the appearance of text that is shown to the user." +# +# e "Text tags can make text {b}bold{/b}, {i}italic{/i}, {s}struckthrough{/s}, or {u}underlined{/u}." +# +# e "They can make the font size {size=+12}bigger{/size} or {size=-8}smaller{/size}." +# +# e "They let you pause{w} the display of the text, optionally with{p}line breaks." +# +# e "They let you include images inside text{image=exclamation.png} Neat{image=exclamation.png}" +# +# e "We can pause the text for a short time, and have it auto-advance.{w=1} Just like that." +# +# eslow "We can even have the text auto-advance,{nw}" +# +# with flashbulb +# extend " when we reach the end of a block of text, in slow text mode." +# +# e "They can change the {color=#f00}color{/color} {color=#ff0}of{/color} {color=#0f0}the{/color} {color=#0ff}text{/color}." +# +# # e "There are also bold, italic, strikethrough, and underline style properties, which can be styled onto any text." +# +# e "The kerning tag lets you adjust the spacing between characters.\n{k=.5}The spacing between characters can be increased.{/k}\n{k=-.5}The spacing between characters can be decreased.{/k}" +# +# eruby "You are able to write ruby text, which can help clarify how to pronounce words, like {rb}Ren'Py{/rb}{rt}ren-pie{/rt}." +# +# e "{a=define_hyperlink}Hyperlinks{/a} let buttons be defined using text tags." +# +# e "The space and vspace tags add {space=30} horizontal and {vspace=20}vertical space, respectively." +# +# e "You can define your own text tags, {=pink}that use a style you define.{/=pink}" +# +# e "If you find yourself using text tags on every line, you should probably look at style properties instead." +# +# e "Used with care, text tags can enhance {b}your{/b} game." +# +# e "{u}Used{/u} with {i}abandon,{/i} they {b}can{/b} make {b}your{/b} game {color=#333}hard{/color} {color=#888}to{/color} {color=#ccc}read{/color}." +# +# e "With great power comes great responsibility, after all." +# +# e "And we want to give you all the power you need." +# +# e "There are a couple of text adjustments that don't correspond to text tags." +# +# eoutline "The outlines setting lets you put outlines around the text." +# +# eoutline "You can have more than one outline, and each has its own color and offset." +# +# window hide +# +# esubtitle "Here, we have two outlines around the white text." +# +# esubtitle "The bottom one is a translucent black that's offset a little, while the top one is green." +# +# esubtitle "By hiding the window and adjusting the layout method, we are able to create reasonable subtitles." +# +# esubtitle "This might be an interesting look for a game." +# +# window show +# +# e "Well, that's it for fonts and text tags." +# +# +# return +# +# +# label define_hyperlink: +# +# definition "A hyperlink is a button that is defined inside text, using text tags. They're ideal for including definitions of words used in the script, but they shouldn't be used in place of menus." +# +# return diff --git a/testcases/originals/tutorial-8.2/indepth_transitions.rpy b/testcases/originals/tutorial-8.2/indepth_transitions.rpy new file mode 100644 index 00000000..261f8512 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_transitions.rpy @@ -0,0 +1,547 @@ +# This file demonstrates the built-in transitions which are defined in +# common/definitions.rpy, and also the new transitions given above. + +example slow_dissolve: + define slow_dissolve = Dissolve(1.0) + +example flashbulb: + define flashbulb = Fade(0.2, 0.0, 0.8, color='#fff') + +# Imagedissolve Transitions. + +example circleirisout: + define circleirisout = ImageDissolve("imagedissolve circleiris.png", 1.0, 8) + +example circleirisin: + define circleirisin = ImageDissolve("imagedissolve circleiris.png", 1.0, 8 , reverse=True) + +example circlewipe: + define circlewipe = ImageDissolve("imagedissolve circlewipe.png", 1.0, 8) + +example dream: + define dream = ImageDissolve("imagedissolve dream.png", 2.0, 64) + +example teleport: + define teleport = ImageDissolve("imagedissolve teleport.png", 1.0, 0) + +image bg circleiris = "imagedissolve circleiris" +image bg teleport = "imagedissolve teleport" + +example alphadissolve: + + image alpha_control: + "spotlight.png" + + anchor (.5, .5) + + parallel: + zoom 0 + linear .5 zoom .75 + pause 2 + linear 1.0 zoom 4.0 + + parallel: + xpos 0.0 ypos .6 + linear 1.5 xpos 1.0 + linear 1.0 xpos .5 ypos .2 + + pause .5 + repeat + + define alpha_example = AlphaDissolve("alpha_control", delay=3.5) + + +label demo_transitions: + + e "Ren'Py ships with a large number of built-in transitions, and also includes classes that let you define your own." + + menu demo_transitions_menu: + + e "What kind of transitions would you like demonstrated?" + + "Simple Transitions": + + call demo_simple_transitions from _call_demo_simple_transitions_1 + + "ImageDissolve Transitions": + + call demo_imagedissolve_transitions from _call_demo_imagedissolve_transitions_1 + + "MoveTransition Transitions": + + call demo_movetransition from _call_demo_movetransition_1 + + "CropMove Transitions": + + call demo_cropmove_transitions from _call_demo_cropmove_transitions_1 + + "PushMove Transitions": + + call demo_pushmove_transitions from _call_demo_pushmove_transitions_1 + + "AlphaDissolve Transitions": + + call demo_alphadissolve from _call_demo_alphadissolve + + "How about something else?": + + return + + jump demo_transitions_menu + + +label demo_simple_transitions: + + e "Okay, I can tell you about simple transitions. We call them simple because they don't take much in the way of configuration." + + e "But don't let that get you down, since they're the transitions you'll probably use the most in a game." + + example: + show bg whitehouse + with dissolve + + e "The 'dissolve' transition is probably the most useful, blending one scene into another." + + example slow_dissolve small: + show bg washington + with slow_dissolve + + e "The 'Dissolve' function lets you create your own dissolves, taking a different amount of time." + + + example: + show bg whitehouse + with fade + + e "The 'fade' transition fades to black, and then fades back in to the new scene." + + e "If you're going to stay at a black screen, you'll probably want to use 'dissolve' rather than 'fade'." + + example flashbulb small: + with flashbulb + + e "You can use 'Fade' to define your own fades. By changing the timing and the color faded to, you can use this for special effects, like flashbulbs." + + example: + show bg washington + with pixellate + + e "The 'pixellate' transition pixellates out the old scene, switches to the new scene, and then unpixellates that." + + e "It's probably not appropriate for most games, but we + think it's kind of neat." + + e "You can use 'Pixellate' to change the details of the pixellation." + + e "Motions can also be used as transitions." + + "..." + + "......" + + example: + play audio "punch.opus" + with vpunch + + e "Hey! Pay attention." + + e "I was about to demonstrate 'vpunch'... well, I guess I just did." + + example: + play audio "punch.opus" + with hpunch + + e "We can also shake the screen horizontally, with 'hpunch'. These were defined using the 'Move' function." + + e "There's also the 'move' transition, which is confusingly enough defined using the 'MoveTransition' function." + + example: + show eileen happy at right + with move + show eileen happy at center + with move + + e "The 'move' transition finds images that have changed placement, and slides them to their new place. It's an easy way to get motion in your game." + + hide example + + e "That's it for the simple transitions." + + return + + +label demo_imagedissolve_transitions: + + e "Perhaps the most flexible kind of transition is the ImageDissolve, which lets you use an image to control a dissolve." + + e "This lets us specify very complex transitions, fairly simply. Let's try some, and then I'll show you how they work." + + e "There are two ImageDissolve transitions built into Ren'Py." + + + example: + scene black + with blinds + + scene bg washington + show eileen happy + with blinds + + + e "The 'blinds' transition opens and closes what looks like vertical blinds." + + example: + scene black + with squares + + scene bg washington + show eileen happy + with squares + + e "The 'squares' transition uses these squares to show things." + + e "I'm not sure why anyone would want to use it, but it was used in some translated games, so we added it." + + hide example + + e "The most interesting transitions aren't in the standard library." + + e "These ones require an image the size of the screen, and so we couldn't include them as the size of the screen can change from game to game." + + example circleirisin small: + scene black + with circleirisin + + e "We can hide things with a 'circleirisin'..." + + example circleirisout small: + scene bg washington + with circleirisout + + e "... and show them again with a 'circleirisout'." + + example circlewipe small: + show bg whitehouse + with circlewipe + + e "The 'circlewipe' transitions changes screens using a circular wipe effect." + + example dream small: + scene bg washington + with dream + + e "The 'dream' transition does this weird wavy dissolve, and does it relatively slowly." + + example teleport small: + show eileen happy + with teleport + + e "The 'teleport' transition reveals the new scene one line at a time." + + show example circleirisout small + + scene bg circleiris + with dissolve + + e "This is the background picture used with the circleirisout transition." + + e "When we use an ImageDissolve, the white will dissolve in first, followed by progressively darker colors. Let's try it." + + show bg washington + with circleirisout + + show example circleirisout small + + e "If we give ImageDissolve the 'reverse' parameter, black areas will dissolve in first." + + show bg circleiris + with circleirisin + + e "This lets circleirisin and circleirisout use the same image." + + show example teleport small + + show bg teleport + with dissolve + + e "The teleport transition uses a different image, one that dissolves things in one line at a time." + + show bg washington + with teleport + + e "A dissolve only seems to affect parts of the scene that have changed..." + + show eileen happy + with teleport + + e "... which is how we apply the teleport effect to a single character." + + hide example + + return + +label demo_cropmove_transitions: + + e "The CropMove transition class provides a wide range of transition effects. It's not used very much in practice, though." + + show eileen happy at offscreenleft + with move + + e "I'll stand offscreen, so you can see some of its modes. I'll read out the mode name after each transition." + + example: + scene bg whitehouse + with wiperight + + e "We first have wiperight..." + + example: + scene bg washington + with wipeleft + + e "...followed by wipeleft... " + + example: + scene bg whitehouse + with wipeup + + e "...wipeup..." + + example: + scene bg washington + with wipedown + + e "...and wipedown." + + e "Next, the slides." + + example: + scene bg whitehouse + with slideright + + e "Slideright..." + + example: + scene bg washington + with slideleft + + e "...slideleft..." + + example: + scene bg whitehouse + with slideup + + e "...slideup..." + + example: + scene bg washington + with slidedown + + e "and slidedown." + + e "While the slide transitions slide in the new scene, the + slideaways slide out the old scene." + + example: + scene bg whitehouse + with slideawayright + + e "Slideawayright..." + + example: + scene bg washington + with slideawayleft + + e "...slideawayleft..." + + example: + scene bg whitehouse + with slideawayup + + e "...slideawayup..." + + example: + scene bg washington + with slideawaydown + + e "and slideawaydown." + + e "We also have a couple of transitions that use a + rectangular iris." + + example: + scene bg whitehouse + with irisout + + e "There's irisout..." + + example: + scene bg washington + show eileen happy + with irisin + + e "... and irisin." + + hide example + + e "It's enough to make you feel a bit dizzy." + + return + +label demo_pushmove_transitions: + + e "The PushMove transitions use the new scene to push the old one out. Let's take a look." + + example: + + show bg whitehouse + hide eileen + with pushright + + "There's pushright..." + + example: + + show bg washington + with pushleft + + "...pushleft..." + + + example: + + show bg whitehouse + with pushdown + + "...pushdown..." + + example: + + show bg washington + show eileen happy + with pushup + + "... and pushup. And that's it the for the PushMove-based transitions." + + hide example + pause .5 + + return + +label demo_movetransition: + + + e "The most common MoveTransition is move, which slides around images that have changed position on the screen." + + example move: + + show eileen happy at left + with move + + + e "Just like that." + + show example moveinout + + e "There are also the moveout and movein transitions." + + e "The moveout transitions (moveoutleft, moveoutright, moveouttop, and moveoutbottom) slide hidden images off the appropriate side of the screen." + + e "The movein transitions (moveinleft, moveinright, moveintop, and moveinbottom) slide in new images." + + e "Let's see them all in action." + + hide example + pause .5 + + example moveinout hide: + hide eileen happy + with moveoutleft + + show eileen happy + with moveinbottom + + hide eileen happy + with moveoutbottom + + show eileen happy + with moveinright + + hide eileen happy + with moveoutright + + show eileen happy: + yzoom -1 + xalign 0.5 + yalign 0.0 + + + with moveintop + + hide eileen happy + with moveouttop + + show eileen happy + with moveinleft + + e "That's it for the moveins and moveouts." + + e "Finally, there are the zoomin and zoomout transitions, which show and hide things using a zoom." + + example: + + hide eileen happy + with zoomout + + show eileen happy + with zoomin + + e "And that's all there is." + + hide example + pause .5 + + return + +label demo_alphadissolve: + + e "The AlphaDissolve transition lets you use one displayable to combine two others. Click, and I'll show you an example." + + scene black + with dissolve + + example alphadissolve large: + scene bg washington + show eileen happy at center + with alpha_example + + e "The AlphaDissolve displayable takes a control displayable, usually an ATL transform." + + scene + show alpha_control + + e "To be useful, the control displayable should be partially transparent." + + e "During an AlphaDissolve, the old screen is used to fill the transparent areas of the image, while the new screen fills the opaque areas." + + scene black + + e "For our spotlight example, the old screen is this all-black image." + + scene bg washington + show eileen happy at center + + e "The new screen is me just standing here." + + scene black + with dissolve + scene bg washington + show eileen happy at center + with alpha_example + + e "By combining them using AlphaDissolve, we can build a complicated effect out of simpler parts." + + hide example + pause .5 + + return diff --git a/testcases/originals/tutorial-8.2/indepth_translations.rpy b/testcases/originals/tutorial-8.2/indepth_translations.rpy new file mode 100644 index 00000000..a522fb20 --- /dev/null +++ b/testcases/originals/tutorial-8.2/indepth_translations.rpy @@ -0,0 +1,64 @@ +example translate_font: + translate japanese python: + gui.text_font = "SourceHanSansLite.ttf" + gui.name_text_font = "SourceHanSansLite.ttf" + gui.interface_text_font = "SourceHanSansLite.ttf" + + translate japanese style default: + language "japanese-strict" + +label translations: + + e "Ren'Py includes support for translating your game into languages other than the one it was originally written in." + + e "This includes the translation of every string in the game, including dialogue, menu choice, and interface strings, and of images and other assets." + + e "While Ren'Py can find dialogue and menu choice strings for you, you'll have to indicate which other strings need translation." + + show example characters + + e "For example, here is how we define a character and her name." + + show example characters showtrans + + e "To mark Lucy's name as translatable, we surround it by parentheses preceded by a single underscore." + + e "Notice how we don't translate the reddish color that we use for her name. That stays the same for all languages." + + hide example + + show launcher translate at launcher_place + with moveinleft + + e "Once that's done, you can generate the translation files. That's done by going to the launcher, and clicking translate." + + e "After you type in the name of the language you'll be translating to, choosing Generate Translations will scan your game and create translation files." + + e "The files will be generated in game/tl/language, where language is the name of the language you typed in." + + e "You'll need to edit those files to include translations for everything in your game." + + e "If you want to localize image files, you can also place them in game/tl/language." + + hide launcher + with moveoutleft + + show example translate_font large + + e "If the default fonts used by the game do not support the language you are translating to, you will have to change them." + + e "The translate python statement can be used to set the values of gui variables to change the font." + + e "The translate style statement sets style properties more directly." + + e "If you need to add new files, such as font files, you can place them into the game/tl/language directory where Ren'Py will find them." + + show example language_picker large + + e "Finally, you'll have to add support for picking the language of the game. That usually goes into the preferences screen, found in screens.rpy." + + e "Here's an excerpt of the preferences screen of this tutorial. The Language action tells Ren'Py to change the language. It takes a string giving a language name, or None." + + e "The None language is special, as it's the default language that the visual novel was written in. Since this tutorial was written in English, Language(None) selects English." + + return diff --git a/testcases/originals/tutorial-8.2/options.rpy b/testcases/originals/tutorial-8.2/options.rpy new file mode 100644 index 00000000..6355a2f4 --- /dev/null +++ b/testcases/originals/tutorial-8.2/options.rpy @@ -0,0 +1,226 @@ +## 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 Tutorial Game") + + +## 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 = False + + +## The version of the game. + +define config.version = renpy.version_string +define build.version = renpy.version_only + +## Text that is placed on the game's about screen. To insert a blank line +## between paragraphs, write \n\n. + +define gui.about = _("") + + +## 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 = "tutorial_7" + + +## 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 + + +## 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 = "tutorial-7" + + +## 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: + + ## Add SDK Fonts. + config.searchpath.append(config.renpy_base + "/sdk-fonts") + build.classify_renpy("sdk-fonts/**", "all") + build._sdk_fonts = True + + ## 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" + + +init python hide: + import datetime + + today = datetime.date.today() + if (today.month == 3) and (today.day == 19): + + # A cat, not a mouse. + config.mouse = { 'default' : [ + ("gui/mouse0.png", 0, 0), + ("gui/mouse1.png", 0, 0), + ("gui/mouse2.png", 0, 0), + ("gui/mouse1.png", 0, 0), + ] * 2 + [ + ("gui/mouse0.png", 0, 0), + ] * (10 * 20) +} + +define config.defer_tl_scripts = True diff --git a/testcases/originals/tutorial-8.2/screens.rpy b/testcases/originals/tutorial-8.2/screens.rpy new file mode 100644 index 00000000..6602a739 --- /dev/null +++ b/testcases/originals/tutorial-8.2/screens.rpy @@ -0,0 +1,1506 @@ +################################################################################ +## 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 + +example say_screen: + screen say(who, what): + window: + id "window" + + if who is not None: + text who id "who" + + text what id "what" + +screen say(who, what): + style_prefix "say" + + window: + id "window" + + if who is not None: + + window: + 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 + + +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 + + adjust_spacing False + + +## 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. +## +## http://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. +## +## http://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 + + +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.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.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.text_properties("navigation_button") + + +## Main Menu screen ############################################################ +## +## Used to display the main menu when Ren'Py starts. +## +## http://www.renpy.org/doc/html/screen_special.html#main-menu + +screen main_menu(): + + ## This ensures that any other menu screen is replaced. + tag menu + + style_prefix "main_menu" + + add gui.main_menu_background + + ## This empty frame darkens the main menu. + frame: + pass + + ## The use statement includes another screen inside this one. The actual + ## contents of the main menu are in the navigation screen. + use navigation + + text "[renpy.version_string] \"[renpy.version_name]\"" 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: + color "#fff" + yalign .995 + size 18 + kerning -1 + xoffset 290 + +style main_menu_version: + variant "small" + xoffset 365 + + +## 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): + + 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: + scrollbars "vertical" + mousewheel True + draggable True + pagekeys True + + + side_yfill True + + vbox: + transclude + + elif scroll == "vpgrid": + + vpgrid: + cols 1 + yinitial 1.0 + + 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: + textalign 0.5 + layout "subtitle" + hover_color gui.hover_color + +style page_button: + properties gui.button_properties("page_button") + +style page_button_text: + properties gui.text_properties("page_button") + +style slot_button: + properties gui.button_properties("slot_button") + +style slot_button_text: + properties gui.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 + + if renpy.mobile: + $ cols = 2 + else: + $ cols = 4 + + use game_menu(_("Preferences"), scroll="viewport"): + + vbox: + + hbox: + box_wrap True + + if renpy.variant("pc") or renpy.variant("web"): + + vbox: + spacing 12 + + vbox: + style_prefix "radio" + label _("Display") + textbutton _("Window") action Preference("display", "window") + textbutton _("Fullscreen") action Preference("display", "fullscreen") + + vbox: + + style_prefix "check" + label _("Examples") + textbutton _("Translations") action ToggleField(persistent, "show_translation_marker") + + + 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")) + + +#begin language_picker + ## Additional vboxes of type "radio_pref" or "check_pref" can be + ## added here, to add additional creator-defined preferences. + + vbox: + style_prefix "radio" + label _("Language") + + # Real languages should go alphabetical order by English name. + textbutton "English" text_font "DejaVuSans.ttf" action Language(None) + textbutton "Français" text_font "DejaVuSans.ttf" action Language("french") + textbutton "Русский" text_font "DejaVuSans.ttf" action Language("russian") + textbutton "Español" text_font "DejaVuSans.ttf" action Language("spanish") + textbutton "Українська" text_font "DejaVuSans.ttf" action Language("ukrainian") + + vbox: + style_prefix "radio" + label "" + + textbutton "한국어" text_font "SourceHanSansLite.ttf" action Language("korean") + textbutton "日本語" text_font "SourceHanSansLite.ttf" action Language("japanese") + textbutton "简体中文" text_font "SourceHanSansLite.ttf" action Language("schinese") + + # This should be last. + textbutton "Pig Latin" text_font "DejaVuSans.ttf" action Language("piglatin") + + +#end language_picker + + 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.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.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.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")): + + 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.") + +define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } + +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 + textalign 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 + textalign 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}.") + + hbox: + label "Shift+A" + text _("Opens the accessibility menu.") + + +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") + 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.text_properties("help_button") + +style help_label: + xsize 250 + right_padding 20 + +style help_label_text: + size gui.text_size + xalign 1.0 + textalign 1.0 + + + +################################################################################ +## Additional screens +################################################################################ + + +## Confirm screen ############################################################## +## +## The confirm screen is called when Ren'Py wants to ask the player a yes or no +## question. +## +## http://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: + textalign 0.5 + layout "subtitle" + +style confirm_button: + properties gui.button_properties("confirm_button") + +style confirm_button_text: + properties gui.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. +## +## http://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. + 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 = 6 + +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 + textalign 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 + textalign 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 + textalign 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.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 + + 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 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 slider_pref_vbox: + variant "small" + xsize None + +style slider_pref_slider: + variant "small" + xsize 600 diff --git a/testcases/originals/tutorial-8.2/script.rpy b/testcases/originals/tutorial-8.2/script.rpy new file mode 100644 index 00000000..40d82780 --- /dev/null +++ b/testcases/originals/tutorial-8.2/script.rpy @@ -0,0 +1,220 @@ +# Hi there! This is the Ren'Py tutorial game. It's actually a fairly bad +# example of Ren'Py programming style - the examples we present in the game +# itself are good, but to make them easy to present we wind up doing +# some non-standard high-level things. +# +# So feel free to poke around, but if you're really looking for an example +# of good Ren'Py programming style, consider checking out The Question +# instead. + +# Declare the characters. +define e = Character(_('Eileen'), color="#c8ffc8") + +init python: + + # A list of section and tutorial objects. + tutorials = [ ] + + class Section(object): + """ + Represents a section of the tutorial menu. + + `title` + The title of the section. This should be a translatable string. + """ + + def __init__(self, title): + self.kind = "section" + self.title = title + + tutorials.append(self) + + + class Tutorial(object): + """ + Represents a label that we can jump to. + """ + + def __init__(self, label, title, move=True): + self.kind = "tutorial" + self.label = label + self.title = title + + if move and (move != "after"): + self.move_before = True + else: + self.move_before = False + + if move and (move != "before"): + self.move_after = True + else: + self.move_after = False + + tutorials.append(self) + + + Section(_("Quickstart")) + + Tutorial("tutorial_playing", _("Player Experience")) + Tutorial("tutorial_create", _("Creating a New Game")) + Tutorial("tutorial_dialogue", _("Writing Dialogue")) + Tutorial("tutorial_images", _("Adding Images")) + Tutorial("tutorial_simple_positions", _("Positioning Images")) + Tutorial("tutorial_transitions", _("Transitions")) + Tutorial("tutorial_music", _("Music and Sound Effects")) + Tutorial("tutorial_menus", _("Choices and Python")) + Tutorial("tutorial_input", _("Input and Interpolation")) + Tutorial("tutorial_video", _("Video Playback")) + Tutorial("tutorial_nvlmode", _("NVL Mode"), move=None) + Tutorial("director", _("Tools and the Interactive Director")) + Tutorial("distribute", _("Building Distributions")) + + Section(_("In Depth")) + + Tutorial("text", _("Text Tags, Escapes, and Interpolation")) + Tutorial("demo_character", _("Character Objects")) + Tutorial("simple_displayables", _("Simple Displayables"), move=None) + Tutorial("demo_transitions", _("Transition Gallery")) + + # Positions and Transforms? + Tutorial("tutorial_positions", _("Position Properties")) + + # Advanced Transforms? + Tutorial("tutorial_atl", _("Transforms and Animation")) + Tutorial("transform_properties", _("Transform Properties")) + + Tutorial("new_gui", _("GUI Customization")) + Tutorial("styles", _("Styles and Style Properties"), move=None) + Tutorial("tutorial_screens", _("Screen Basics"), move=None) + Tutorial("screen_displayables", _("Screen Displayables"), move=None) + + Tutorial("demo_minigame", _("Minigames and CDDs")) + Tutorial("translations", _("Translations")) + +screen tutorials(adj): + + frame: + xsize 640 + xalign .5 + ysize 485 + ypos 30 + + has side "c r b" + + viewport: + yadjustment adj + mousewheel True + draggable True + + vbox: + for i in tutorials: + + if i.kind == "tutorial": + + textbutton i.title: + action Return(i) + left_padding 20 + xfill True + + else: + + null height 10 + text i.title alt "" + null height 5 + + bar adjustment adj style "vscrollbar" + + textbutton _("That's enough for now."): + xfill True + action Return(False) + top_margin 10 + + +# This is used to preserve the state of the scrollbar on the selection +# screen. +default tutorials_adjustment = ui.adjustment() + +# True if this is the first time through the tutorials. +default tutorials_first_time = True + +# The game starts here. +#begin start +label start: +#end start + + scene bg washington + show eileen vhappy + with dissolve + + # Start the background music playing. + play music "sunflower-slow-drag.ogg" + + window show + + e "Hi! My name is Eileen, and I'd like to welcome you to the Ren'Py tutorial." + + show eileen happy + + e "In this tutorial, we'll teach you the basics of Ren'Py, so you can make games of your own. We'll also demonstrate many features, so you can see what Ren'Py is capable of." + +label tutorials: + + show eileen happy at left + with move + + if tutorials_first_time: + $ e(_("What would you like to see?"), interact=False) + else: + $ e(_("Is there anything else you'd like to see?"), interact=False) + + $ tutorials_first_time = False + $ renpy.choice_for_skipping() + + call screen tutorials(adj=tutorials_adjustment) + + $ tutorial = _return + + if not tutorial: + jump end + + if tutorial.move_before: + show eileen happy at center + with move + + $ reset_example() + + call expression tutorial.label from _call_expression + + if tutorial.move_after: + hide example + show eileen happy at left + with move + + jump tutorials + +label end: + + show eileen happy at center + with move + + show _finale behind eileen + + + e "Thank you for viewing this tutorial." + + e "If you'd like to see a full Ren'Py game, select \"The Question\" in the launcher." + + e "You can download new versions of Ren'Py from {a=https://www.renpy.org/}https://www.renpy.org/{/a}. For help and discussion, check out the {a=https://lemmasoft.renai.us/forums/}Lemma Soft Forums{/a}." + + e "We'd like to thank Piroshki for contributing my sprites; Mugenjohncel for Lucy, the band, and drawn backgrounds; and Jake for the magic circle." + + e "The background music is \"Sunflower Slow Drag\", by Scott Joplin and Scott Hayden, performed by the United States Marine Band. The concert music is by Alessio." + + show eileen vhappy + + e "We look forward to seeing what you create with Ren'Py. Have fun!" + + window hide + + # Returning from the top level quits the game. + return diff --git a/testcases/originals/tutorial-8.2/testcases.rpy b/testcases/originals/tutorial-8.2/testcases.rpy new file mode 100644 index 00000000..01ae4411 --- /dev/null +++ b/testcases/originals/tutorial-8.2/testcases.rpy @@ -0,0 +1,299 @@ + +testcase player_experience: + scroll "Bar" until "Player Experience" + click until "Yes." + + # Dialogue after menu. + click + click + click + + # Rollback to the menu. + click button 4 + click button 4 + click button 4 + click button 4 + + # Roll forward. + click button 5 + click button 5 + + # Back again. + click button 4 + click button 4 + + # Roll forward. + type PAGEDOWN + type PAGEDOWN + + # Back again. + type PAGEUP + type PAGEUP + + "No." + + click until label tutorials + +testcase new_game: + scroll "Bar" until "Creating a New Game" + click until label tutorials + +testcase dialogue: + scroll "Bar" until "Writing Dialogue" + click until label tutorials + +testcase images: + scroll "Bar" until "Adding Images" + click until label tutorials + +testcase transitions: + scroll "Bar" until "Transitions" + click until label tutorials + +testcase music: + scroll "Bar" until "Music and Sound Effects" + click until label tutorials + +testcase choices: + scroll "Bar" until "Choices and Python" + click until "Yes, I do." + click until "Yes." + click until label tutorials + + +testcase input: + scroll "Bar" until "Input and Interpolation" + click until "Some games might prompt the player for input." + type "Tom" + type BACKSPACE + type "m" + type LEFT + type RIGHT + type "\n" + click until label tutorials + +testcase positioning_images: + scroll "Bar" until "Positioning Images" + click until label tutorials + +testcase video: + scroll "Bar" until "Video Playback" + click until label tutorials + +testcase nvl_mode: + scroll "Bar" until "NVL Mode" + click until "Yes." + click until label tutorials + +testcase tools: + scroll "Bar" until "Tools and the Interactive Director" + + # Not actually testing the various tools yet. + + click until label tutorials + +testcase building: + scroll "Bar" until "Building Distributions" + click until label tutorials + + +testcase text_tags: + scroll "Bar" until "Text Tags, Escapes, and Interpolation" + click until label tutorials + +testcase character_objects: + scroll "Bar" until "Character Objects" + click until label tutorials + +testcase simple_displayables: + scroll "Bar" until "Simple Displayables" + click until label tutorials + + + +testcase transition_gallery: + $ _test.transition_timeout = 60.0 + + scroll "Bar" until "Transition Gallery" + click until "Simple" + click until "ImageDissolve" + click until "MoveTransition" + click until "CropMove" + click until "PushMove" + click until "AlphaDissolve" + click until "something else" + click until label tutorials + + $ _test.transition_timeout = 0.05 + +testcase position_properties: + scroll "Bar" until "Position Properties" + click until "xpos .75 ypos .25" + click until label tutorials + +testcase transforms: + + scroll "Bar" until "Transforms and Animation" + click until label tutorials + + scroll "Bar" until "Transform Properties" + click until label tutorials + +testcase gui_customization: + + scroll "Bar" until "GUI Customization" + click until label tutorials + + +testcase styles: + + scroll "Bar" until "Styles and Style Properties" + click until "Style basics." + click until "General style properties." + click until "Text style properties." + click until "Window and Button style properties." + click until "Bar style properties." + click until "Box, Grid, and Fixed style properties." + click until "The Displayable Inspector." + click until "That's all I want to know." + click until label tutorials + + +testcase screens: + + scroll "Bar" until "Screen Basics" + click until "What screens can do." + click until "Yes." + click until "How to show screens." + click until "you'll have to click" + "Okay" + click until "Passing parameters to screens." + click until "the call screen statement" + "Okay" + click until "Screen properties." + click until "Close This Screen" + pause .5 + "Close This Screen" + click until "Special screen statements." + click until "Using other screens." + click until "That's it." + + scroll "Bar" until "Screen Displayables" + click until "Common properties" + click until "Adding images" + click until "Text" + click until "Buttons" + click until "Bars" + click until "Viewports" + click until "Imagemaps" + click until "Science" + click until "That's all" + + click until label tutorials + + +testcase translations: + + scroll "Bar" until "Translations" + click until label tutorials + + +testcase out_of_game: + "Back" + "Back" + + "Skip" + + "Back" + + $ _preferences.self_voicing = False + $ _preferences.afm_time = 1 + + + "Auto" + scroll "Bar" until "Player Experience" + "Auto" + "History" + + pause .5 + + "Save" + "Save Slot 1" + "Yes" + + "Load" + pause .5 + + "Load Slot 1" + "Yes" + + "Prefs" + pause .5 + + "About" + pause .5 + + "Help" + pause .5 + + "Main Menu" + "Yes" + + "Load" + pause .5 + + "Load Slot 1" + + click until "Yes." + click until label tutorials + + + +testcase template: + + scroll "Bar" until "-" + click until label tutorials + + +testcase default: + + $ _test.transition_timeout = 0.05 + + "Start" + click until label tutorials + + call player_experience + call new_game + call dialogue + call images + call positioning_images + call transitions + call music + call choices + call input + call video + call nvl_mode + call tools + call building + + call text_tags + call character_objects + call simple_displayables + call transition_gallery + call position_properties + call transforms + call gui_customization + call styles + call screens + + # Skip Minigames and CDDs. + + call translations + call out_of_game + + "That's enough for now." + click until "Quit" + pause .5 + + + diff --git a/testcases/originals/tutorial-8.2/tutorial_atl.rpy b/testcases/originals/tutorial-8.2/tutorial_atl.rpy new file mode 100644 index 00000000..91fa3b5d --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_atl.rpy @@ -0,0 +1,931 @@ +image bg band = Transform("concert1", zoom=.75) +image logo small = Transform("logo base", zoom=.66) + +image concert: + subpixel True + size (1280, 720) + xalign .5 + yalign .5 + + parallel: + "concert2" + pause 1.0 + + block: + "concert1" with Dissolve(.1) + pause .4 + "concert2" with Dissolve(.1) + pause .4 + "concert3" with Dissolve(.1) + pause .4 + "concert2" with Dissolve(.1) + pause .4 + repeat + + time 43.0 + + parallel: + + + # Lucy Strums. + crop (213, 778, 590, 332) + pause 1.0 + easeout .6 crop (286, 818, 469, 264) + + crop (87, 370, 590, 332) + easein .8 crop (14, 306, 791, 445) + + # Mary cymbals. + time 2.9 + crop (1035, 656, 417, 235) + time 3.4 + crop (564, 545, 552, 311) + time 3.9 + crop (1035, 656, 417, 235) + time 4.4 + crop (564, 545, 552, 311) + + # Zoom out. + time 5.0 + linear 4.0 crop (0, 482, 1738, 978) + easein 4.0 crop (267, 91, 2133, 1200) + easeout 4.0 crop (0, 91, 2133, 1200) + + + # Pan up Eileen + time 17.0 + crop (1047, 849, 1132, 637) + + linear 4.0 crop (868, 58, 1532, 862) + + time 22.25 + + # Mary's random crops. + block: + choice: + crop (741, 517, 725, 408) + choice: + crop (409, 594, 1157, 651) + choice: + crop (719, 526, 753, 424) + choice: + crop (692, 521, 449, 253) + choice: + crop (468, 903, 1038, 584) + + pass + + choice: + zoom 1.0 + choice: + zoom 1.3 + choice: + zoom 1.5 + + pause .43 + + repeat + + # Lucy and Mary + time 26.97 + zoom 1 + crop (0, 482, 1738, 978) + pause 1.0 + + # Mary + crop (0, 775, 984, 554) + easein 5.5 crop (0, 279, 984, 554) + + # Final shot. + easeout 4.0 crop (0, 91, 2133, 1200) + easein 4.0 crop (267, 91, 2133, 1200) + + + + +image pos: + "target1.png" + block: + rotate 0 + linear 2.0 rotate 360.0 + repeat + +image anchor: + "target2.png" + block: + rotate 360.0 + linear 2.0 rotate 0.0 + repeat + +example atl_right: + transform right: + xalign 1.0 + yalign 1.0 + +example atl_image: + image eileen animated: + "eileen vhappy" + pause .5 + "eileen happy" + pause .5 + repeat + +example atl_image1: + image eileen animated twice: + "eileen vhappy" + pause .5 + "eileen happy" + pause .5 + repeat 2 + +example atl_image2: + image eileen animated once: + "eileen vhappy" + pause .5 + "eileen happy" + +example atl_with: + image bg atl transitions: + "bg washington" + "bg whitehouse" with dissolve + pause 1.0 + "bg washington" with dissolve + pause 1.0 + repeat + + +example atl_transform: + transform topright: + xalign 1.0 yalign 0.0 + + +example atl_transform1: + transform move_jump: + xalign 1.0 yalign 0.0 + pause 1.0 + xalign 0.0 + pause 1.0 + repeat + +example atl_transform2: + transform move_slide: + xalign 1.0 yalign 0.0 + linear 3.0 xalign 0.0 + pause 1.0 + repeat + +transform reset: + xpos 0.5 + xanchor 0.5 + ypos 0.3 + yanchor 0.5 + + zoom 1.0 + xzoom 1.0 + yzoom 1.0 + + crop None + + xsize None + ysize None + fit None + + alpha 1.0 + + rotate None + rotate_pad True + nearest False + additive 0.0 + + xtile 1 + ytile 1 + + xpan None + ypan None + +label tutorial_positions: + + e "In this tutorial, I'll teach you how Ren'Py positions things on the screen. But before that, let's learn a little bit about how Python handles numbers." + + e "There are two main kinds of numbers in Python: integers and floating point numbers. An integer consists entirely of digits, while a floating point number has a decimal point." + + e "For example, 100 is an integer, while 0.5 is a floating point number, or float for short. In this system, there are two zeros: 0 is an integer, and 0.0 is a float." + + e "Ren'Py uses integers to represent absolute coordinates, and floats to represent fractions of an area with known size." + + e "When we're positioning something, the area is usually the entire screen." + + e "Let me get out of the way, and I'll show you where some positions are." + + hide eileen + with moveoutright + + show pos: + xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5 + subpixel True + + with dissolve + + show pos: + linear .5 xpos 0.0 ypos 0.0 + + e "The origin is the upper-left corner of the screen. That's where the x position (xpos) and the y position (ypos) are both zero." + + show pos: + ypos 0.0 + linear .5 xpos 0.5 + + e "When we increase xpos, we move to the right. So here's an xpos of .5, meaning half the width across the screen." + + show pos: + linear .5 xpos 1.0 + + e "Increasing xpos to 1.0 moves us to the right-hand border of the screen." + + show pos: + xpos 1280 + linear .5 xpos 640 + + e "We can also use an absolute xpos, which is given in an absolute number of pixels from the left side of the screen. For example, since this window is 1280 pixels across, using an xpos of 640 will return the target to the center of the top row." + + e "The y-axis position, or ypos works the same way. Right now, we have a ypos of 0.0." + + show pos: + xpos 640 + linear .5 ypos .5 + + e "Here's a ypos of 0.5." + + show pos: + linear .5 ypos 1.0 + + e "A ypos of 1.0 specifies a position at the bottom of the screen. If you look carefully, you can see the position indicator spinning below the text window." + + e "Like xpos, ypos can also be an integer. In this case, ypos would give the total number of pixels from the top of the screen." + + show pos: + linear .5 xpos .75 ypos .25 + + + menu: + + e "Can you guess where this position is, relative to the screen?" + + "xpos 1.0 ypos .5": + + e "Sorry, that's wrong. The xpos is .75, and the ypos is .25." + + e "In other words, it's 75%% of the way from the left side, and 25%% of the way from the top." + + "xpos .75 ypos .25": + + e "Good job! You got that position right." + + "xpos .25 ypos .33": + + e "Sorry, that's wrong. The xpos is .75, and the ypos is .25." + + e "In other words, it's 75%% of the way from the left side, and 25%% of the way from the top." + + hide pos + + show logo solid: + xpos 523 ypos 100 + + show anchor: + xanchor 0.5 yanchor 0.5 + xpos 523 ypos 100 + + with dissolve + + + e "The second position we care about is the anchor. The anchor is a spot on the thing being positioned." + + e "For example, here we have an xanchor of 0.0 and a yanchor of 0.0. It's in the upper-left corner of the logo image." + + show anchor: + linear .5 xpos 757 + + e "When we increase the xanchor to 1.0, the anchor moves to the right corner of the image." + + show anchor: + linear .5 ypos 460 + + e "Similarly, when both xanchor and yanchor are 1.0, the anchor is the bottom-right corner." + + show pos: + xanchor .5 yanchor .5 + xpos 957 ypos 460 + + + e "To place an image on the screen, we need both the position and the anchor." + + show logo solid: + linear .5 xpos 723 ypos 100 + + show anchor: + linear .5 xpos 957 ypos 460 + + e "We then line them up, so that both the position and anchor are at the same point on the screen." + + + show anchor: + linear .5 xpos 0 ypos 0 + show pos: + linear .5 xpos 0 ypos 0 + show logo solid: + linear .5 xpos 0 ypos 0 + + e "When we place both in the upper-left corner, the image moves to the upper-left corner of the screen." + + show anchor: + linear .5 xpos 0.5 ypos 0.5 + show pos: + linear .5 xpos 0.5 ypos 0.5 + show logo solid: + linear .5 xalign 0.5 yalign 0.5 + + e "With the right combination of position and anchor, any place on the screen can be specified, without even knowing the size of the image." + + show logo solid: + linear .5 yalign .3 + + with None + + hide anchor + hide pos + + with dissolve + + e "It's often useful to set xpos and xanchor to the same value. We call that xalign, and it gives a fractional position on the screen." + + show logo solid: + linear .5 xalign 0.0 + + e "For example, when we set xalign to 0.0, things are aligned to the left side of the screen." + + show logo solid: + linear .5 xalign 1.0 + + e "When we set it to 1.0, then we're aligned to the right side of the screen." + + show logo solid: + linear .5 xalign .5 + + e "And when we set it to 0.5, we're back to the center of the screen." + + e "Setting yalign is similar, except along the y-axis." + + e "Remember that xalign is just setting xpos and xanchor to the same value, and yalign is just setting ypos and yanchor to the same value." + + show logo solid: + linear .5 xcenter .75 + + e "The xcenter and ycenter properties position the center of the image. Here, with xcenter set to .75, the center of the image is three-quarters of the way to the right side of the screen." + + show logo solid: + linear 1.0 xcenter 1.0 + + e "The difference between xalign and xcenter is more obvious when xcenter is 1.0, and the image is halfway off the right side of the screen." + + show logo solid: + linear .5 xalign 0.5 yalign 0.5 + linear .5 xoffset 50 yoffset 20 + + pause .5 + + e "There are the xoffset and yoffset properties, which are applied after everything else, and offset things to the right or bottom, respectively." + + show logo solid: + linear .5 xoffset -50 yoffset -20 + + e "Of course, you can use negative numbers to offset things to the left and top." + + show logo solid: + linear .5 align (0.5, 0.5) offset (0, 0) + + e "Lastly, I'll mention that there are combined properties like align, pos, anchor, and center. Align takes a pair of numbers, and sets xalign to the first and yalign to the second. The others are similar." + + hide logo + with dissolve + show eileen happy + with moveinright + + e "Once you understand positions, you can use transformations to move things around the Ren'Py screen." + + return + + +label tutorial_atl: + + e "Ren'Py uses transforms to animate, manipulate, and place images. We've already seen the very simplest of transforms in use:" + + example simple_transform: + show eileen happy at right + + with move + + e "Transforms can be very simple affairs that place the image somewhere on the screen, like the right transform." + + hide example + + e "But transforms can also be far more complicated affairs, that introduce animation and effects into the mix. To demonstrate, let's have a Gratuitous Rock Concert!" + + stop music fadeout 1.0 + scene concert + with fade + + play music "renpyallstars.ogg" noloop + + e "But first, let's have... a Gratuitous Rock Concert!" + + play music "sunflower-slow-drag.ogg" fadeout 1.0 + + scene bg washington + show eileen happy + with dissolve + + e "That was a lot of work, but it was built out of small parts." + + e "Most transforms in Ren'Py are built using the Animation and Transform Language, or ATL for short." + + e "There are currently three places where ATL can be used in Ren'Py." + + show example atl_image + show eileen animated + + e "The first place ATL can be used is as part of an image statement. Instead of a displayable, an image may be defined as a block of ATL code." + + e "When used in this way, we have to be sure that ATL includes one or more displayables to actually show." + + show example atl_transform + show eileen happy at right + + e "The second way is through the use of the transform statement. This assigns the ATL block to a python variable, allowing it to be used in at clauses and inside other transforms." + + example: + + show logo base: + xalign .3 yalign .7 + linear 1.0 xalign .7 yalign .3 + linear 1.0 xalign .3 yalign .7 + repeat + + with dissolve + + e "Finally, an ATL block can be used as part of a show statement, instead of the at clause." id tutorial_atl_da7a7759 + + + example: + show logo base: + yoffset 10 + + e "When ATL is used as part of a show statement, values of properties exist even when the transform is changed. So even though your click stopped the motion, the image remains in the same place." id tutorial_atl_1dd345c6 + + hide logo + show eileen happy at center + with moveoutleft + + hide screen example + + e "The key to ATL is what we call composability. ATL is made up of relatively simple commands, which can be combined together to create complicated transforms." + + e "Before I explain how ATL works, let me explain what animation and transformation are." + + show eileen animated: + center + + e "Animation is when the displayable being shown changes. For example, right now I am changing my expression." + + show eileen happy + + show magic: + yalign .5 subpixel True + + parallel: + xalign .5 + linear 3.0 xalign .75 + linear 6.0 xalign .25 + linear 3.0 xalign .5 + repeat + + parallel: + alpha 1.0 zoom 1.0 + linear .75 alpha .5 zoom .9 + linear .75 alpha 1.0 zoom 1.0 + repeat + + parallel: + rotate 0 + linear 5 rotate 360 + repeat + + with dissolve + + e "Transformation involves moving or distorting an image. This includes placing it on the screen, zooming it in and out, rotating it, and changing its opacity." + + hide magic + with dissolve + + show example atl_image + show eileen animated + + e "To introduce ATL, let's start by looking at a simple animation. Here's one that consists of five lines of ATL code, contained within an image statement." id tutorial_atl_fbc9bf83 + + e "To change a displayable, simply mention it on a line of ATL. Here, we're switching back and forth between two images." + + e "Since we're defining an image, the first line of ATL must give a displayable. Otherwise, there would be nothing to show." + + e "The second and fourth lines are pause statements, which cause ATL to wait half a second each before continuing. That's how we give the delay between images." + + e "The final line is a repeat statement. This causes the current block of ATL to be restarted. You can only have one repeat statement per block." + + show example atl_image1 + show eileen animated twice + + e "If we were to write repeat 2 instead, the animation would loop twice, then stop." + + show example atl_image1 + show eileen animated once + + e "Omitting the repeat statement means that the animation stops once we reach the end of the block of ATL code." + + + show example atl_with + show bg atl transitions + + e "By default, displayables are replaced instantaneously. We can also use a with clause to give a transition between displayables." + + show bg washington + with dissolve + + hide screen example + + e "With animation done, we'll see how we can use ATL to transform images, starting with positioning an image on the screen." + + show logo base behind eileen + + show logo base at topright + with dissolve + + show example atl_transform + + e "The simplest thing we can do is to statically position an image. This is done by giving the names of the position properties, followed by the property values." id tutorial_atl_ddc55039 + + show example atl_transform1 + show logo base at move_jump + + e "With a few more statements, we can move things around on the screen." + + e "This example starts the image off at the top-right of the screen, and waits a second. It then moves it to the left side, waits another second, and repeats." + + e "The pause and repeat statements are the same statements we used in our animations. They work throughout ATL code." + + show example atl_transform2 + show logo base at move_slide + + e "Having the image jump around on the screen isn't all that useful. That's why ATL has the interpolation statement." + + e "The interpolation statement allows you to smoothly vary the value of a transform property, from an old to a new value." + + e "Here, we have an interpolation statement on the second ATL line. It starts off with the name of a time function, in this case linear." + + e "That's followed by an amount of time, in this case three seconds. It ends with a list of properties, each followed by its new value." + + e "The value of each property is interpolated from its value when the statement starts to the value at the end of the statement. This is done once per frame, allowing smooth animation." + + hide example + + show eileen happy at right + with move + + show logo base: + alignaround (.5, .3) + linear 2.0 xalign .5 yalign .3 clockwise circles 3 + + e "ATL supports more complicated move types, like circle and spline motion. But I won't be showing those here." + + hide logo with dissolve + + e "Apart from displayables, pause, interpolation, and repeat, there are a few other statements we can use as part of ATL." + + example large: + show eileen happy: + right + pause 1.25 + left + pause 1.25 + repeat + + with dissolve + + e "ATL transforms created using the statement become ATL statements themselves. Since the default positions are also transforms, this means that we can use left, right, and center inside of an ATL block." + + show eileen happy at center + show logo base behind eileen + with dissolve + + example: + show logo base: + xalign 0.0 yalign 0.0 + block: + linear 1.0 xalign 1.0 + linear 1.0 xalign 0.0 + repeat + time 11.5 + linear .5 xalign 1.0 + + e "Here, we have two new statements. The block statement allows you to include a block of ATL code. Since the repeat statement applies to blocks, this lets you repeat only part of an ATL transform." + + e "We also have the time statement, which runs after the given number of seconds have elapsed from the start of the block. It will run even if another statement is running, stopping the other statement." + + e "So this example bounces the image back and forth for eleven and a half seconds, and then moves it to the right side of the screen." + + + example: + show logo base: + parallel: + linear 1.0 xalign 0.0 + linear 1.0 xalign 1.0 + repeat + parallel: + linear 1.3 yalign 1.0 + linear 1.3 yalign 0.0 + repeat + + e "The parallel statement lets us run two blocks of ATL code at the same time." + + e "Here, the top block move the image in the horizontal direction, and the bottom block moves it in the vertical direction. Since they're moving at different speeds, it looks like the image is bouncing on the screen." + + show logo base: + yalign 0.0 + xalign 0.0 + + example: + show logo base: + choice: + linear 1.0 xalign 0.0 + choice: + linear 1.0 xalign 1.0 + repeat + + e "Finally, the choice statement makes Ren'Py randomly pick a block of ATL code. This allows you to add some variation as to what Ren'Py shows." + + hide logo base + with dissolve + hide example + + e "This tutorial game has only scratched the surface of what you can do with ATL. For example, we haven't even covered the on and event statements. For more information, you might want to check out {a=https://renpy.org/doc/html/atl.html}the ATL chapter in the reference manual{/a}." + + + return + + + +label transform_properties: + + e "Ren'Py has quite a few transform properties that can be used with ATL, the Transform displayable, and the add Screen Language statement." + e "Here, we'll show them off so you can see them in action and get used to what each does." + + + show eileen happy at right + with move + + + example: + show logo base: + xpos 0.5 + xanchor 0.5 + ypos 0.3 + yanchor 0.5 + + with dissolve + + e "First off, all of the position properties are also transform properties. These include the pos, anchor, align, center, and offset properties." + + + hide eileen + hide logo base + + show bg band: + xanchor 0 yanchor 0 + xpos 0 ypos -428 + + with dissolve + + example: + show bg band: + xanchor 0 yanchor 0 + xpos 0 ypos -428 + linear 3.0 xpos -220 ypos -60 + + e "The position properties can also be used to pan over a displayable larger than the screen, by giving xpos and ypos negative values." + + # Let's not demo this until it's not terrible. + if False: + + example: + show bg band: + subpixel False + linear 60.0 xpos 0 + + "The subpixel property controls how things are lined up with the screen. When False, images can be pixel-perfect, but there can be pixel jumping." + + example: + show bg band: + subpixel True + linear 60.0 xpos 0 + + "When it's set to True, movement is smoother at the cost of blurring images a little." + + + hide bg + show bg washington + + show eileen happy at right + + hide logo + + example: + show logo small: + anchor (0.5, 0.5) + around (640, 216) + angle 270 + radius 200 + + with dissolve + + e "Transforms also support polar coordinates. The around property sets the center of the coordinate system to coordinates given in pixels." + + example: + show logo small: + linear 1.0 angle 315 + linear 1.0 angle 270 + repeat + + e "The angle property gives the angle in degrees. Angles run clockwise, with the zero angle at the top of the screen." + + + example: + show logo small: + linear 1.0 radius 100 + linear 1.0 radius 200 + repeat + + e "The radius property gives the distance in pixels from the anchor of the displayable to the center of the coordinate system." + + + hide logo small + show logo base at reset + with dissolve + + example: + show logo base: + zoom 1.0 + linear 1.0 zoom 1.5 + linear 1.0 zoom 1.0 + repeat + + e "There are several ways to resize a displayable. The zoom property lets us scale a displayable by a factor, making it bigger and smaller." + + show logo base at reset + + example: + show logo base: + xzoom .75 yzoom 1.25 + linear 1.0 xzoom 1.25 yzoom .75 + linear 1.0 xzoom .75 yzoom 1.25 + repeat + + with dissolve + + e "The xzoom and yzoom properties allow the displayable to be scaled in the X and Y directions independently." + + show logo base at reset + + example: + show logo base: + linear 1.0 xzoom -1.0 yzoom 1.0 + + with dissolve + + e "By making xzoom or yzoom a negative number, we can flip the image horizontally or vertically." + + show logo base at reset + + example: + show logo base: + size (350, 540) + + with dissolve + + e "Instead of zooming by a scale factor, the size transform property can be used to scale a displayable to a size in pixels." + + show logo base at reset + + example: + show logo base: + alpha 1.0 + linear 1.0 alpha 0.0 + pause .5 + linear 1.0 alpha 1.0 + pause .5 + repeat + + with dissolve + + e "The alpha property is used to change the opacity of a displayable. This can make it appear and disappear." + + show logo base at reset + + example: + show logo base: + xanchor 0.5 yanchor 0.5 + rotate 0 + linear 4.0 rotate 360 + repeat + + with dissolve + + e "The rotate property rotates a displayable." + + example: + show logo base: + xalign 0.0 yalign 0.0 + rotate 0 + linear 4.0 rotate 360 + repeat + + with dissolve + + e "By default, when a displayable is rotated, Ren'Py will include extra space on all four sides, so the size doesn't change as it rotates. Here, you can see the extra space on the left and top, and it's also there on the right and bottom." + + example: + show logo base: + rotate_pad False + xalign 0.0 yalign 0.0 + rotate 0 + linear 4.0 rotate 360 + repeat + + with dissolve + + e "By setting rotate_pad to False, we can get rid of the space, at the cost of the size of the displayable changing as it rotates." + + show logo base at reset + + example: + show logo base: + xtile 3 + ytile 2 + + with dissolve + + e "The tile transform properties, xtile and ytile, repeat the displayable multiple times." + + show logo base at reset + + example: + show logo base: + crop (0, 0, 117, 360) + + with dissolve + + e "The crop property crops a rectangle out of a displayable, showing only part of it." + + hide logo base + with dissolve + + example: + show bg washington: + crop (0, 0, 800, 600) + size (1280, 720) + + linear 4.0 crop (451, 437, 409, 230) + + with dissolve + + e "When used together, crop and size can be used to focus in on specific parts of an image." + + hide bg + + example: + show bg panorama: + xpan 0 + linear 10.0 xpan 360 + repeat + + with dissolve + + e "The xpan and ypan properties can be used to pan over a displayable, given an angle in degrees, with 0 being the center." + + hide example + scene bg washington + show eileen happy + with dissolve + + e "Those are all the transform properties we have to work with. By putting them together in the right order, you can create complex things." + + show eileen happy + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_director.rpy b/testcases/originals/tutorial-8.2/tutorial_director.rpy new file mode 100644 index 00000000..ea9d6d89 --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_director.rpy @@ -0,0 +1,75 @@ +define director.show_tags = { "eileen", "lucy" } + +label director: + + e "There are a few tools you can access by pressing the right commands on the keyboard." + + e "Typing Shift+R turns on autoreload mode. When it's enabled, your game will automatically reload when you edit a script file." + + e "Shift+O brings you to the console, which lets you enter Ren'Py and Python commands to try them out." + + e "Shift+D pops up a developer menu with access to these and other functions." + + e "The most powerful tool is the interactive director that lets you add images, music, and voice lines to your game from inside Ren'Py." + + e "The idea is that you can use an editor to write the script and logic of your visual novel, and then interactively add images in the right places." + + if director_readonly: + + show eileen concerned + + e "It looks like Ren'Py is installed read-only on your system, so you won't be able to try out the interactive director now." + + e "You'll need to make your own project, and try it out there. But I can tell you how to use it." + + show eileen happy + + else: + + e "You can try the interactive director out right now, by using it to change this tutorial game." + + e "Be sure to click my dialogue at the bottom of the screen to advance the tutorial." + + e "If something goes wrong, don't worry. Quitting and restarting this tutorial will remove your changes and bring everything back to normal." + + + stop music fadeout 1.0 + scene + with dissolve + + $ _director_enable = not director_readonly + + e "To get started, let's go back to a blank slate, with no images on the screen." + + e "You can show the director at any time by pressing the 'D' key on your keyboard. Ren'Py will reload, and you'll come back here. Try it now." + + e "Let's add a background. Click the '+' to pick where to add it, then the 'scene' statement and 'washington' for the image. Finally, click 'Add' to add it." + + e "Next, add a sprite. Click '+', then 'show', 'eileen', 'happy', and 'Add'. Once you've added it, dissolve it in by clicking the second '+', then 'with', 'dissolve', and 'Add'." + + show eileen happy + + e "You can edit or remove statements with the pencil icon. You can move me to the right by editing the show statement, then clicking '(transform)', 'right', and 'Change'." + + e "Finally, you can use the play, queue, stop, and voice statements to manage audio. Try adding 'play', 'music', 'sunflower-slow-drag.ogg'." + + $ director.state.show_director = False + $ _director_enable = False + + if renpy.showing("lucy"): + + l "Finally, I get some more screen time!" + + queue music "sunflower-slow-drag.ogg" + + scene bg washington + show eileen happy + with dissolve + + e "The changes you make with the director are permanent. They're saved to the script, and you can rollback or repeat this section to see them." + + e "However, we reset this tutorial when the game restarts, so you can try again from a clean slate. That won't happen with your own visual novel." + + e "I hope these tools make developing your visual novel that much easier." + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_distribute.rpy b/testcases/originals/tutorial-8.2/tutorial_distribute.rpy new file mode 100644 index 00000000..dde1d9ad --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_distribute.rpy @@ -0,0 +1,35 @@ +label distribute: + + e "One thing Ren'Py makes easy is building distributions of your visual novel so you can give it to players." + + e "Before you build distributions, you should use the Lint command to check your game for problems." + + e "While not every potential problem lint reports is a real issue, they generally are, and you should try to understand what might be wrong." + + show launcher distribute at launcher_place + with moveinleft + + e "After lint has finished, you can choose Build Distributions to build the Windows, Linux, and Mac distributions of your game." + + e "This can be as simple as clicking the Build button, when you're not on a Mac." + + e "If you are on a Macintosh, you can have Ren'Py sign the Mac application, which makes it easier for players to run. To enable this, you need to set build.mac_identity in options.rpy." + + hide launcher distribute + with moveoutleft + + e "Ren'Py supports the mobile platforms, Android and iOS. We also support ChromeOS, through its ability to run Android apps." + + e "These mobile platforms can be a bit more complicated. While Android apps can be built everywhere, iOS requires a Mac." + + e "Mobile platforms might also require you to change your visual novel a little, due to the smaller limited devices. For example, buttons need to be made large enough for a person to touch." + + e "Rather than cover mobile here, I'll point you to the {a=https://www.renpy.org/doc/html/android.html}Android{/a} and {a=https://www.renpy.org/doc/html/ios.html}iOS{/a} documentation, where you can read more." + + e "Thanks to the distribution tools Ren'Py ships with, there are thousands of visual novels available." + + show eileen vhappy + + e "I hope that soon, yours will be one of them!" + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_nvlmode.rpy b/testcases/originals/tutorial-8.2/tutorial_nvlmode.rpy new file mode 100644 index 00000000..ee84614b --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_nvlmode.rpy @@ -0,0 +1,89 @@ +# Declare an nvl-version of eileen. + +example nvl1: + define nvle = Character(_("Eileen"), color="#c8ffc8", kind=nvl) + +define config.adv_nvl_transition = dissolve +define config.nvl_adv_transition = dissolve + +example nvl3a: + define menu = nvl_menu + +define menu = renpy.display_menu + +define config.lint_ignore_redefine += [ "store.menu" ] + +label tutorial_nvlmode: + + window hide + nvl clear + nvl show dissolve + + nvle "NVL-style games are games that cover the full screen with text, rather then placing it in a window at the bottom of the screen. Like this." + + show example nvl1 bottom + + nvle "To use NVL-mode, you need to define Characters with a kind=nvl." + + example large bottom: + + nvle "Then just use that character in a say statement." + + nvl clear + + nvle "You use 'nvl clear' to clear the screen when that becomes necessary." + + nvl hide dissolve + nvl show dissolve + + nvle "The 'nvl show' and 'nvl hide' statements use transitions to show and hide the NVL window." + + # Doing this during the game isn't recommended, it's better to do + # it in an init block. We have to do it here because we need to use + # both kinds of menus. + $ menu = nvl_menu + + show example nvl3a nvl3 large bottom + +example nvl3 hide: + + menu: + + nvle "NVL-mode also supports showing menus to the player, providing it's the last thing on the screen. Understand?" + + "Yes.": + + nvl clear + + nvle "Good!" + + "No.": + + nvl clear + + nvle "Well, hopefully the code below makes it a little more clear." + + +label after_nvl_menu: + + hide example + + nvle "Games can mix NVL-mode and the normal ADV-mode by having some characters that have kind=nvl, and some that do not." + + e "You can specify transitions that occur when going from NVL-mode to ADV-mode." + + nvle "As well as when going from ADV-mode to NVL-mode." + + nvle "Text tags like {{w}{w} work in NVL-mode." + + extend " As does the \"extend\" special character." + + nvle "And that's it for NVL-mode." + + $ menu = renpy.display_menu + + nvl hide dissolve + $ _last_say_who = None + window show dissolve + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_playing.rpy b/testcases/originals/tutorial-8.2/tutorial_playing.rpy new file mode 100644 index 00000000..491e560e --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_playing.rpy @@ -0,0 +1,108 @@ +# This file demonstrates the Ren'Py User Experience... the features you get +# for free by choosing to use Ren'Py as a visual novel engine. + + +transform popup_place: + xpos 0.1 xanchor 0.0 ypos 0.1 yanchor 0.0 + + +label tutorial_playing: + + e "As someone who has played more than a few visual novels, there are many features that I expect all games to have." + + e "Features like saving, loading, changing preferences, and so on." + + e "One of the nice things about Ren'Py is that the engine provides many of these features for you. You can spend your time creating your game, and let us provide these things." + + e "While you're in the game, you can access the game menu by right clicking or hitting the escape key." + + e "You can also access the game menu through some of the quick menu buttons at the bottom of this screen." + + show eileen happy at right + show popup save at popup_place + with moveinleft + + e "When you first enter the game menu, you'll see the save screen. Clicking on a numbered slot will save the game." + + e "Unlike other engines, Ren'Py doesn't limit the number of save slots that you can use. You can keep hitting next until you reach the page you want." + + e "Clicking on the title of the page allows you to start typing to change the page name." + + e "The load screen looks quite similar to the save screen, and lets you load a game from a save slot." + + e "It also lets you load one of the auto-saves that Ren'Py makes for you." + + show popup prefs at popup_place + with dissolve + + e "The game menu also has the preferences screen." + + e "This screen lets you decide how Ren'Py displays, pick what Ren'Py skips, control text speed and auto-click speed, and adjust sound, music, and voice volumes." + + e "The game menu also lets you end the game and return to the main menu, or quit Ren'Py entirely." + + show popup hrpprefs at popup_place + with dissolve + + e "While the default game menus look a bit generic, with a little work they can be customized or even entirely replaced, allowing you to create menus as unique as your game." + + hide popup + show eileen happy at center + with moveoutleft + + e "While inside the game, there are a few more things you can do." + + e "When I'm liking a visual novel, I want to see all the endings. Ren'Py's skip function lets me easily do this, by skipping text that I've already seen." + + e "I can skip a few lines by holding down Control, or I can toggle skip mode by clicking the skip button at the bottom of the screen." + + e "By default, we only skip read text, so this won't do anything the first time through the game." + + e "Clicking the auto button toggles auto-forward mode, which makes the game advance without you clicking." + + e "The Q.Save and Q.Load buttons provide a single-click way to make a save, and a fast way to load that save again." + + e "Pressing the 's' key saves a screenshot to disk, so I can upload pictures of the game to websites like {a=https://www.renpy.org}renpy.org{/a}." + + e "The history button displays a history of read text - but you can also use rollback, which is usually better." + + menu: + + e "Would you like to hear about rollback?" + + "Yes.": + + jump tutorial_rollback + + "No.": + + jump tutorial_rollback_done + + +label tutorial_rollback: + + e "You can invoke a rollback by clicking the 'Back' button, scrolling the mouse wheel up, or by pushing the page up key. That'll bring you back to the previous screen." + + e "While at a previous screen, you can roll forward by scrolling the mouse wheel down, or pushing the page down key." + + e "Rolling forward through a menu will make the same choice you did last time. But you don't have to do that - Ren'Py's rollback system allows you to make a different choice." + + e "You can try it by rolling back through the last menu, and saying 'No'." + + e "Click back a few times, press page up, or scroll up the mouse wheel." + + show eileen concerned + + e "Well, are you going to try it?" + + e "Your loss." + + e "Moving on." + + show eileen happy + +label tutorial_rollback_done: + + e "By allowing Ren'Py to take care of out-of-game issues like loading and saving, you can focus on making your game, while still giving players the experience they've come to expect when playing visual novels." + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_quickstart.rpy b/testcases/originals/tutorial-8.2/tutorial_quickstart.rpy new file mode 100644 index 00000000..059cb9f5 --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_quickstart.rpy @@ -0,0 +1,621 @@ +#begin characters +define l = Character(_("Lucy"), color="#ffcccc") +#end characters + +#begin slowdissolve +define slowdissolve = Dissolve(1.0) +#end slowdissolve + +default name = "Player" + +transform rightish: + xcenter .6 + ypos 50 + +transform launcher_place: + xpos 0.05 + xanchor 0 + ypos 0.1 + yanchor 0 + + + +label tutorial_create: + + show launcher step1 at launcher_place + with dissolve + + e "When you're ready to use Ren'Py to create your visual novel, the first step is to create a new project." + + e "You can create a new project by clicking 'Create New Project' on the front screen of the launcher." + + e "If this is your first time using Ren'Py, it'll ask you for the place you want to keep your projects. The best place is always somewhere that's frequently backed up." + + show launcher step2 + + e "After that, Ren'Py will ask for a name for your project. You'll have to stick to English letters and numbers, as zip files can't handle anything more than that." + + show launcher step3 + + e "The next thing Ren'Py will ask for is the resolution the visual novel will run at. This controls how large or small you'll have to make your game's artwork." + + show launcher step4 + + e "Finally, Ren'Py will ask you to select a color scheme. You can change this after the game has been created, so just pick a color that's pleasing." + + show launcher step5 + + e "Once that's done, Ren'Py will work for a bit and return you to the main menu with the new project selected. Now, when you click Launch, Ren'Py will start your new game." + + e "To get back here, you can choose 'Tutorial' to switch to this tutorial game." + + e "You'll also need to edit the games script to make changes. To do that, click 'script.rpy' on the front page of the launcher." + + e "If it's your first time doing so, Ren'Py will ask you to select a text editor. Atom might be a safe choice, but read the descriptions to be sure." + + e "After the text editor is downloaded, the script will open up and you can start to change what characters are saying." + + hide launcher + with dissolve + + return + + + + + +label tutorial_dialogue: + + e "Probably the most common thing a creator does with Ren'Py is to write dialogue for the player to read." + + e "But before I can show you how to write dialogue, let me show you how we present script examples." + + example large: + "Eileen" "Examples will show up in a window like the one above. You'll need to click outside of the example window in order to advance the tutorial." + + "Eileen" "When an example is bigger than the screen, you can scroll around in it using the mouse wheel or by simply dragging the mouse." + + "Eileen" "Script might seem scary at first, but if you look you'll see it's easy to match it up to what I'm saying." + + hide example + + e "Let's see the simplest possible Ren'Py game." + + scene black + with dissolve + + example dialogue1 hide: + + "Wow, It's really really dark in here." + + "Lucy" "Better watch out. You don't want to be eaten by a Grue." + + scene bg washington + show eileen happy + with dissolve + + show example start dialogue1 + + e "I'll show you the script of that example." + + e "This script demonstrates two kinds of Ren'Py statements, labels and say statements." + + e "The first line is a label statement. The label statement is used to give a name to a place in the program." + + e "In this case, we're naming a place \"start\". The start label is special, as it marks the place a game begins running." + + e "The next line is a simple say statement. It consists of a string beginning with a double-quote, and ending at the next double-quote." + + e "Special characters in strings can be escaped with a backslash. To include \" in a string, we have to write \\\"." + + + hide example + scene black + with dissolve + + "Wow, It's really really dark in here." + + scene bg washington + show eileen happy + with dissolve + + + show example start dialogue1 + + e "When Ren'Py sees a single string on a line by itself, it uses the narrator to say that string. So a single string can be used to express a character's thoughts." + + hide example + scene black + with dissolve + + "Lucy" "Better watch out. You don't want to be eaten by a Grue." + + scene bg washington + show eileen happy + with dissolve + + show example start dialogue1 + + e "When we have two strings separated by a space, the first is used as the character's name, and the second is what the character is saying." + + e "This two-argument form of the say statement is used for dialogue, where a character is speaking out loud." + + e "If you'd like, you can run this game yourself by erasing everything in your project's script.rpy file, and replacing it with the code in the box above." + + e "Be sure to preserve the spacing before lines. That's known as indentation, and it's used to help Ren'Py group lines of script into blocks." + + hide example + + e "Using a string for a character's name is inconvenient, for two reasons." + + e "The first is that's it's a bit verbose. While typing \"Lucy\" isn't so bad, imagine if you had to type \"Eileen Richardson\" thousands of times." + + e "The second is that it doesn't leave any place to put styling, which can change the look of a character." + + e "To solve these problems, Ren'Py lets you define Characters." + + show example characters + + e "Here's an example Character definition. It begins with the word \"define\". That tells Ren'Py that we are defining something." + + e "Define is followed by a short name for the character, like \"l\". We'll be able to use that short name when writing dialogue." + + e "This is followed by an equals sign, and the thing that we're defining. In this case, it's a Character." + + e "On the first line, the character's name is given to be \"Lucy\", and her name will be drawn a reddish color." + + e "These short names are case-sensitive. Capital L is a different name from lower-case l, so you'll need to be careful about that." + + hide example + + e "Now that we have a character defined, we can use it to say dialogue." + + scene black + with dissolve + + example dialogue2 hide: + + l "Why are you trying to put words into my mouth? And who are you calling \"it\"?" + + l "What's more, what are you going to do about the Grue problem? Are you just going to leave me here?" + + scene bg washington + show eileen happy + with dissolve + + show example characters start dialogue1 dialogue2 + + e "Here's the full game, including the two new lines of dialogue, both of which use the Character we defined to say dialogue." + + e "The one-argument form of the say statement is unchanged, but in the two-argument form, instead of the first string we can use a short name." + + e "When this say statement is run, Ren'Py will look up the short name, which is really a Python variable. It will then use the associated Character to show the dialogue." + + e "The Character object controls who is speaking, the color of their name, and many other properties of the dialogue." + + hide example + + e "Since the bulk of a visual novel is dialogue, we've tried to make it as easy to write as possible." + + e "Hopefully, by allowing the use of short names for characters, we've succeeded." + + return + +label tutorial_images: + + e "A visual novel isn't much without images. So let's add some images to our little game." + + e "Before we can show images, we must first choose image names, then place the image files into the images directory." + + e "An image name is something like 'bg cave' or 'lucy happy', with one or more parts separated by spaces." + + e "Each part should start with a lower-case letter, and then contain lower-case letters, numbers, and underscores." + + e "The first part of an image is called the tag. For 'bg cave' the tag is 'bg', while for 'lucy happy' the tag is 'lucy'." + + e "You can open the images directory by clicking the appropriate button in the Ren'Py launcher." + + e "The files in the images directory should have the same name as the image, followed by an extension like .jpg, .png, or .webp." + + e "Our example uses 'bg cave.jpg', 'lucy happy.png', and 'lucy mad.png'." + + hide screen example + + e "Let's see what those look like in the game." + + example images1 hide: + scene bg cave + show lucy happy + + l "Now that the lights are on, we don't have to worry about Grues anymore." + + show lucy mad at right + + l "But what's the deal with me being in a cave? Eileen gets to be out in the sun, and I'm stuck here!" + + scene bg washington + show eileen happy + with dissolve + + show example start images1 large + + e "Here's the script for that scene. Notice how it includes two new statements, the scene and show statement." + + e "The scene statement clears the screen, and then adds a background image." + + e "The show statement adds a new image on top of all the other images on the screen." + + e "If there is already an image with the same tag, the new image is used to replace the old one." + + e "Changes to the list of shown images take place instantly, so in the example, the user won't see the background by itself." + + e "The second show statement has an at clause, which gives a location on the screen. Common locations are left, right, and center, but you can define many more." + + example: + show logo base at rightish behind eileen + + e "In this example, we show an image named logo base, and we show it at a creator-defined position, rightish." + + e "We also specify that it should be shown behind another image, in this case eileen. That's me." + + example: + hide logo + + e "Finally, there's the hide statement, which hides the image with the given tag." + + e "Since the show statement replaces an image, and the scene statement clears the scene, it's pretty rare to hide an image." + + e "The main use is for when a character or prop leaves before the scene is over." + + hide example + + return + + +example slightleft: + transform slightleft: + xalign 0.25 + yalign 1.0 + +label tutorial_simple_positions: + + e "When the standard positions that come with Ren'Py aren't enough for you, you can create your own. Here, I'll show you the easy way to do it." + + example: + show eileen happy: + xalign 0.75 + yalign 1.0 + + with move + + e "The first way to do it is to show an image followed by a colon. Then indented on the next couple of lines are the xalign and yalign transform properties." + + e "Each of the transform properties is a name followed by a value. For xalign and yalign, the values are numbers." + + e "The xalign transform property is the important one, as it controls where the image is placed horizontally on the screen." + + + example: + show eileen happy: + xalign 0.0 + yalign 1.0 + + with move + + e "An xalign of 0.0 is the left side." + + + example: + show eileen happy: + xalign 0.5 + yalign 1.0 + + with move + + e "0.5 is the center." + + example: + show eileen happy: + xalign 1.0 + yalign 1.0 + + with move + + e "And 1.0 is the right. The decimal place is important and has to be there. Just 1 by itself won't work the same." + + example: + show eileen happy: + xalign 0.75 + yalign 1.0 + + with move + + e "Of course, you can pick any position in between." + + e "The yalign property is the same way, with 0.0 being the top of the screen and 1.0 being the bottom. Since most sprites stick to the bottom, it's almost always 1.0." + + hide example + show eileen happy at center + with move + + e "While being able to write positions like this is useful, having to repeatedly do so isn't. So Ren'Py lets you define a transform once, and reuse it." + + show example slightleft + + e "Usually transforms are defined at the top of a file, right after the characters. But it doesn't matter to Ren'Py where you define them." + + e "The transform is given a name, slightleft, and then the xalign and yalign properties." + + example: + show eileen vhappy at slightleft + + with move + + + e "Once a transform has been defined, you can use it in the at clause of the show statement." + + example: + show eileen happy + + e "Transforms are sticky. If you replace an image without using a transform, Ren'Py will keep the same transforms it had been using." + + hide example + + e "Of course, there's a lot more to transforms than this. If you want to learn more, you can read the sections on Position Properties, Transforms and Animation, and Transform Properties." + + e "But for many visual novels, xalign and yalign are the only properties that matter." + + return + + + + + + + + + + +label tutorial_transitions: + + e "It can be somewhat jarring for the game to jump from place to place." + + scene bg whitehouse + pause .5 + scene bg washington + show eileen happy + + e "To help take some of edge off a change in scene, Ren'Py supports the use of transitions. Let's try that scene change again, but this time we'll use transitions." + + example trans1 hide: + scene bg whitehouse + with Dissolve(.5) + + pause .5 + + scene bg washington + show eileen happy + with Dissolve(.5) + + show example trans1 large + + e "That's much smoother. Here's some example code showing how we include transitions in our game." + + e "It uses the with statement. The with statement causes the scene to transition from the last things shown to the things currently being shown." + + e "It takes a transition as an argument. In this case, we're using the Dissolve transition. This transition takes as an argument the amount of time the dissolve should take." + + e "In this case, each transition takes half a second." + + show example slowdissolve + + e "We can define a short name for a transition, using the define statement. Here, we're defining slowdissolve to be a dissolve that takes a whole second." + + hide example + + example trans2 hide: + + scene bg whitehouse + with slowdissolve + + scene bg washington + show eileen happy + with slowdissolve + + show example trans2 + + e "Once a transition has been given a short name, we can use it in our game." + + hide example + + e "Ren'Py defines some transitions for you, like dissolve, fade, and move. For more complex or customized transitions, you'll have to define your own." + + e "If you're interested, check out the Transitions Gallery section of this tutorial." + + return + +label tutorial_music: + + e "Another important part of a visual novel or simulation game is the soundtrack." + + e "Ren'Py breaks sound up into channels. The channel a sound is played on determines if the sound loops, and if it is saved and restored with the game." + + e "When a sound is played on the music channel, it is looped, and it is saved when the game is saved." + + e "When the channel named sound is used, the sound is played once and then stopped. It isn't saved." + + e "The sounds themselves are stored in audio files. Ren'Py supports the Opus, Ogg Vorbis, and mp3 formats." + + e "Let's check out some of the commands that can affect the music channel." + + example: + play music "sunflower-slow-drag.ogg" fadeout 1 + + e "The play music command replaces the currently playing music, and replaces it with the named filename." + + e "If you specify the currently-playing song, it will restart it." + + e "If the optional fadeout clause is given, it will fade out the currently playing music before starting the new music." + + example: + queue music "sunflower-slow-drag.ogg" + + e "The queue statement also adds music to the named channel, but it waits until the currently-playing song is finished before playing the new music." + + example: + stop music fadeout 1 + + e "The third statement is the stop statement. It stops the music playing on a channel. It too takes the fadeout clause." + + example: + play sound "tower_clock.ogg" + + e "Unlike the music channel, playing a sound on the sound channel causes it to play only once." + + example: + queue sound "tower_clock.ogg" + queue sound "tower_clock.ogg" + queue sound "tower_clock.ogg" + + e "You can queue up multiple sounds on the sound channel, but the sounds will only play one at a time." + + play music "sunflower-slow-drag.ogg" + + hide example + + e "Ren'Py has separate mixers for sound, music, and voices, so the player can adjust them as they like." + + return + +label tutorial_menus: + + e "Many visual novels require the player to make choices from in-game menus. These choices can add some challenge to the game, or adjust it to the player's preferences." + + e "Do you like to play visual novels with choices in them?" + + example menu1 hide: + menu: + + "Yes, I do.": + jump choice1_yes + + "No, I don't.": + jump choice1_no + + label choice1_yes: + + $ menu_flag = True + + e "While creating a multi-path visual novel can be a bit more work, it can yield a unique experience." + + jump choice1_done + + label choice1_no: + + $ menu_flag = False + + e "Games without menus are called kinetic novels, and there are dozens of them available to play." + + jump choice1_done + + label choice1_done: + + # ... the game continues here. + + + show example menu1 large + + e "Here, you can see the code for that menu. If you scroll down, you can see the code we run after the menu." + + e "Menus are introduced by the menu statement. The menu statement takes an indented block, in which there can be one line of dialogue and multiple choices." + + e "Each choice must end with a colon, as each choice has its own block of Ren'Py code, that is run when that choice is selected." + + e "Here, each block jumps to a label. While you could put small amounts of Ren'Py code inside a menu label, it's probably good practice to usually jump to a bigger block of code." + + e "Scrolling down past the menu, you can see the labels that the menu jumps to. There are three labels here, named choice1_yes, choice1_no, and choice1_done." + + e "When the first menu choice is picked, we jump to the choice1_yes, which runs two lines of script before jumping to choice1_done." + + e "Similarly, picking the second choice jumps us to choice1_no, which also runs two lines of script." + + e "The lines beginning with the dollar sign are lines of python code, which are used to set a flag based on the user's choice." + + e "The flag is named menu_flag, and it's set to True or False based on the user's choice. The if statement can be used to test a flag, so the game can remember the user's choices." + + example: + if menu_flag: + + e "For example, I remember that you plan to use menus in your game." + + else: + + e "For example, I remember that you're planning to make a kinetic novel, without menus." + + e "Here's an example that shows how we can test a flag, and do different things if it is true or not." + + +example: + menu: + e "Finally, this shows how you can show dialogue and menus at the same time. Understand?" + + "Yes.": + + e "Great." + + "No.": + + e "If you look at the example, before the first choice, there's an indented say statement." + +label menu3_done: + + hide example + + e "Although we won't demonstrate it here, Ren'Py supports making decisions based on a combinations of points, flags, and other factors." + + e "One of Ren'Py's big advantages is the flexibility using a scripting language like Python provides us. It lets us easily scale from kinetic novels to complex simulation games." + + return + +example guy: + define g = Character("[name]") + +label tutorial_input: + + e "Some games might prompt the player for input." + + example input hide: + python: + name = renpy.input(_("What's your name?")) + + name = name.strip() or __("Guy Shy") + + python: + if name.lower() == "shiro": + renpy.run(OpenURL("https://shiro-heartcat.tumblr.com/")) + + show example input + + e "That's done with Python, and especially the renpy.input function. The first line of this example prompts the player for some texts, and sticks it in the name variable." + + e "Often times, you'll want to clean the name up before you use it. The last line does that, by calling the strip method to remove whitespace, and replacing the name with a default if it's missing." + + example: + + e "To interpolate a variable, write it in square brackets. Isn't that right, [name]?" + + show example guy + + e "Variable names can also be shown in character names. To do that, just include the variable in square brackets in the character's name. Got it?" + + example: + g "I think I do." + + + example: + $ answer = 42 + $ flag = True + + e "Variable interpolation also works with other variables. Here, the answer is [answer] and the flag is [flag]." + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_screen_displayables.rpy b/testcases/originals/tutorial-8.2/tutorial_screen_displayables.rpy new file mode 100644 index 00000000..45d40ada --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_screen_displayables.rpy @@ -0,0 +1,897 @@ +label screen_displayables: + + e "There are quite a few screen displayables. Here, I'll tell you about some of the most important ones." + +label screen_displayables_menu: + + $ reset_example() + + menu: + + e "What would you like to know about?" + + "Common properties all displayables share.": + call screen_displayable_properties from _call_screen_displayable_properties + + "Adding images and other displayables.": + call add_displayable from _call_add_displayable + + "Text.": + call text_displayable from _call_text_displayable + + "Boxes and other layouts.": + call layout_displayables from _call_layout_displayables + + "Windows and frames.": + call window_displayables from _call_window_displayables + + "Buttons.": + call button_displayables from _call_button_displayables + + "Bars.": + call bar_displayables from _call_bar_displayables + + "Viewports.": + call viewport_displayables from _call_viewport_displayables + + "Imagemaps.": + call imagemap_displayables from _call_imagemap_displayables + + "That's all for now.": + return + + jump screen_displayables_menu + + + +label screen_displayable_properties: + + e "There are a few properties that every screen language displayable shares. Here, I'll demonstrate them for you." + + example large: + screen pos_example(): + frame: + xalign 0.5 ypos 50 + text _("This uses position properties.") + + e "First off, every screen language displayable supports the position properties. When the container a displayable is in supports it, you can use properties like align, anchor, pos, and so so on." + + example: + screen at_example(): + frame: + xalign 0.5 ypos 50 + text _("And the world turned upside down..."): + at rotated + + transform rotated: + rotate 180 rotate_pad False + + e "The at property applies a transform to the displayable, the same way the at clause in the show statement does." + + +# example large: +# screen event_example(): +# frame: +# at inout +# xalign 0.5 ypos 50 +# text _("This is an event.") at textform +# +# +# transform inout: +# on show: +# zoom 1.5 alpha 0.0 +# linear .5 zoom 1.0 alpha 1.0 +# +# on hide: +# linear .5 zoom 1.5 alpha 0.0 + +# +# example: +# screen default_example(): +# frame: +# xalign 0.5 ypos 50 +# +# vbox: +# +# textbutton "Choice 1" action Return(1) +# textbutton "Choice 2" action Return(1) default True +# textbutton "Choice 3" action Return(1) + + + hide screen at_example + with dissolve + + show example say_screen + + e "The id property is mostly used with the say screen, which is used to show dialogue. Outside of the say screen, it isn't used much." + + e "It tells Ren'Py which displayables are the background window, 'who' is speaking, and 'what' is being said. This used to apply per-Character styles, and help with auto-forward mode." + + example: + screen style_example(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("Flight pressure in tanks.") style "green_text" + text _("On internal power.") + text _("Launch enabled.") + text _("Liftoff!") + + style green_text: + color "#c8ffc8" + + e "The style property lets you specify the style of a single displayable." + + example: + screen style_prefix_example(): + frame: + xalign 0.5 ypos 50 + vbox: + vbox: + style_prefix "green" + text _("Flight pressure in tanks.") + text _("On internal power.") + + vbox: + style_prefix "yellow" + text _("Launch enabled.") + text _("Liftoff!") + + style yellow_text: + color "#ffffc8" + + + e "The style_prefix property sets the prefix of the style that's used for a displayable and its children." + + e "For example, when the style_prefix property is 'green', the vbox has the 'green_vbox' style, and the text in it has the 'green_text' style." + + hide example + + e "There are a few more properties than these, and you can find the rest in the documentation. But these are the ones you can expect to see in your game, in the default screens." + + return + +label add_displayable: + + e "Sometimes you'll have a displayable, like an image, that you want to add to a screen." + + example large: + + screen add_image_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" + + e "This can be done using the add statement, which adds an image or other displayable to the screen." + + e "There are a few ways to refer to the image. If it's in the images directory or defined with the image statement, you can just put the name inside a quoted string." + + example large: + + screen add_filename_example(): + frame: + xalign 0.5 ypos 50 + add "images/logo base.png" + + e "An image can also be referred to by its filename, relative to the game directory." id add_displayable_8ba81c26 + + example large: + + screen add_displayable_example(): + frame: + xalign 0.5 ypos 50 + add Solid("#0000ff", xsize=234, ysize=360) + + e "Other displayables can also be added using the add statement. Here, we add the Solid displayable, showing a solid block of color." + + + example large: + + screen add_transform_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" zoom 0.7 rotate 43.21 + + e "In addition to the displayable, the add statement can be given transform properties. These can place or otherwise transform the displayable being added." + + example: + + screen add_at_transform_example(): + frame: + xalign 0.5 ypos 50 + add "logo base" at unrotate + + transform unrotate: + zoom 0.7 rotate 43.21 + linear 1.0 rotate 0 + e "Of course, the add statement can also take the at property, letting you give it a more complex transform." + + hide example + + return + +label text_displayable: + + example large: + screen text_example(): + frame: + xalign 0.5 ypos 50 + text _("This is a text displayable."): + size 30 + + e "The screen language text statement adds a text displayable to the screen. It takes one argument, the text to be displayed." + + e "In addition to the common properties that all displayables take, text takes the text style properties. For example, size sets the size of the text." + + example large: + screen text_interpolation_example(): + $ answer = 42 + + frame: + xalign 0.5 ypos 50 + text _("The answer is [answer].") + + e "The text displayable can also interpolate values enclosed in square brackets." + + e "When text is displayed in a screen using the text statement, variables defined in the screen take precedence over those defined outside it." id text_displayable_32d76ccb + + e "Those variables may be parameters given to the screen, defined with the default or python statements, or set using the SetScreenVariable action." + + example large: + screen text_tax_example(): + frame: + xalign 0.5 ypos 50 + text _("Text tags {color=#c8ffc8}work{/color} in screens.") + + + e "There's not much more to say about text in screens, as it works the same way as all other text in Ren'Py." + + hide example + return + + +label layout_displayables: + + e "The layout displayables take other displayables and lay them out on the screen." + + example large: + screen hbox_example(): + frame: + xalign 0.5 ypos 50 + hbox: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + + e "For example, the hbox displayable takes its children and lays them out horizontally." + + example: + screen vbox_example(): + frame: + xalign 0.5 ypos 50 + vbox: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + + + e "The vbox displayable is similar, except it takes its children and arranges them vertically." + + e "Both of the boxes take the box style properties, the most useful of which is spacing, the amount of space to leave between children." + + example: + screen grid_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + text "1" + text "2" + text "3" + text "4" + text "5" + null + + e "The grid displayable displays its children in a grid of equally-sized cells. It takes two arguments, the number of columns and the number of rows." + + e "The grid has to be full, or Ren'Py will produce an error. Notice how in this example, the empty cell is filled with a null." + + e "Like the boxes, grid uses the spacing property to specify the space between cells." + + example: + screen grid_transpose_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + transpose True + text "1" + text "2" + text "3" + text "4" + text "5" + null + + e "Grid also takes the transpose property, to make it fill top-to-bottom before it fills left-to-right." + + + example: + screen grid_bigger_example(): + frame: + xalign 0.5 ypos 50 + grid 3 2: + spacing 10 + transpose True + text "1" + text "2" + text "3" + text "4" + text "5" + text _("Bigger") + + e "And just to demonstrate that all cells are equally-sized, here's what happens when once child is bigger than the others." + + + example: + screen fixed_example(): + frame: + xalign 0.5 ypos 50 + fixed: + xsize 400 ysize 300 + text "1" xpos 41 ypos 184 + text "2" xpos 135 ypos 177 + text "3" xpos 92 ypos 3 + text "4" xpos 359 ypos 184 + text "5" xpos 151 ypos 25 + + e "The fixed displayable displays the children using Ren'Py's normal placement algorithm. This lets you place displayables anywhere in the screen." + + e "By default, the layout expands to fill all the space available to it. To prevent that, we use the xsize and ysize properties to set its size in advance." + + example: + screen implicit_fixed_example(): + frame: + xalign 0.5 ypos 50 + xsize 440 ysize 316 + + text "1" xpos 41 ypos 184 + text "2" xpos 135 ypos 177 + text "3" xpos 92 ypos 3 + text "4" xpos 359 ypos 184 + text "5" xpos 151 ypos 25 + + e "When a non-layout displayable is given two or more children, it's not necessary to create a fixed. A fixed is automatically added, and the children are added to it." + + example large: + screen hbox_example(): + frame: + xalign 0.5 ypos 50 + + has hbox spacing 10 + + text "1" + text "2" + text "3" + text "4" + text "5" + + e "Finally, there's one convenience to save space. When many displayables are nested, adding a layout to each could cause crazy indent levels." + + e "The has statement creates a layout, and then adds all further children of its parent to that layout. It's just a convenience to make screens more readable." + + hide example + + return + + +label window_displayables: + + e "In the default GUI that Ren'Py creates for a game, most user interface elements expect some sort of background." + + example large: + screen noframe_example(): + vbox: + xalign 0.5 ypos 50 + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + + e "Without the background, text can be hard to read. While a frame isn't strictly required, many screens have one or more of them." + + example large: + screen frame_example(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + + + e "But when I add a background, it's much easier. That's why there are two displayables that are intended to give backgrounds to user interface elements." + + e "The two displayables are frame and window. Frame is the one we use above, and it's designed to provide a background for arbitrary parts of the user interface." + + show example say_screen + + e "On the other hand, the window displayable is very specific. It's used to provide the text window. If you're reading what I'm saying, you're looking at the text window right now." + + e "Both frames and windows can be given window style properties, allowing you to change things like the background, margins, and padding around the window." + + hide example + return + + +label button_displayables: + + e "One of the most flexible displayables is the button displayable, and its textbutton and imagebutton variants." + + example large: + screen button_example(): + frame: + xalign 0.5 ypos 50 + button: + action Notify(_("You clicked the button.")) + text _("Click me.") style "button_text" + + e "A button is a displayable that when selected runs an action. Buttons can be selected by clicking with the mouse, by touch, or with the keyboard and controller." + + e "Actions can do many things, like setting variables, showing screens, jumping to a label, or returning a value. There are many {a=https://www.renpy.org/doc/html/screen_actions.html}actions in the Ren'Py documentation{/a}, and you can also write your own." + + example large: + screen button_hover_example(): + frame: + xalign 0.5 ypos 50 + button: + action Notify(_("You clicked the button.")) + hovered Notify(_("You hovered the button.")) + unhovered Notify(_("You unhovered the button.")) + text _("Click me.") style "button_text" + + + e "It's also possible to run actions when a button gains and loses focus." + + example large: + screen button_heal_example(): + default health = 42 + + frame: + xalign 0.5 ypos 50 + button: + action SetScreenVariable("health", 100) + hbox: + spacing 10 + text _("Heal") style "button_text" yalign 0.5 + bar value AnimatedValue(health, 100, 1.0) yalign 0.5 xsize 200 + + e "A button takes another displayable as a child. Since that child can be a layout, it can take as many children as you want." id button_displayables_47af4bb9 + + example large: + screen textbutton_example(): + frame: + xalign 0.5 ypos 50 + textbutton _("This is a textbutton."): + action Notify(_("You clicked the button.")) + + + e "In many cases, buttons will be given text. To make that easier, there's the textbutton displayable that takes the text as an argument." + + e "Since the textbutton displayable manages the style of the button text for you, it's the kind of button that's used most often in the default GUI." + + + example large: + screen imagebutton_example(): + frame: + xalign 0.5 ypos 50 + imagebutton: + idle "logo bw" + hover "logo base" + + action Notify(_("You clicked the button.")) + + e "There's also the imagebutton, which takes displayables, one for each state the button can be in, and displays them as the button." + + e "An imagebutton gives you the most control over what a button looks like, but is harder to translate and won't look as good if the game window is resized." + + + example large: + screen button_inline_style_example(): + frame: + xalign 0.5 ypos 50 + textbutton _("Click me."): + idle_background Frame("button glossy idle", 12, 12) + hover_background Frame("button glossy hover", 12, 12) + xpadding 20 + ypadding 10 + xmargin 5 + ymargin 5 + + hover_sound "pong_beep.opus" + + text_idle_color "#c0c0c0" + text_hover_color "#ffffff" + + action Notify(_("You clicked the button.")) + + e "Buttons take Window style properties, that are used to specify the background, margins, and padding. They also take Button-specific properties, like a sound to play on hover." + + e "When used with a button, style properties can be given prefixes like idle and hover to make the property change with the button state." + + e "A text button also takes Text style properties, prefixed with text. These are applied to the text displayable it creates internally." + + example large: + screen button_style_example(): + frame: + xalign 0.5 ypos 50 + + has vbox + + textbutton _("Click me."): + style "custom_button" + action Notify(_("You clicked the button.")) + + textbutton _("Or me."): + style "custom_button" + action Notify(_("You clicked the other button.")) + + style custom_button: + idle_background Frame("button glossy idle", 12, 12) + hover_background Frame("button glossy hover", 12, 12) + xpadding 20 + ypadding 10 + xmargin 5 + ymargin 5 + size_group "custom_button" + + hover_sound "pong_beep.opus" + + style custom_button_text: + idle_color "#c0c0c0" + hover_color "#ffffff" + + e "Of course, it's prety rare we'd ever customize a button in a screen like that. Instead, we'd create custom styles and tell Ren'Py to use them." + + hide example + return + + return + + +label bar_displayables: + + example large: + screen bar_example(): + frame: + xalign 0.5 ypos 50 + xsize 500 + bar: + value StaticValue(66, 100) + + + e "The bar and vbar displayables are flexible displayables that show bars representing a value. The value can be static, animated, or adjustable by the player." + + e "The value property gives a BarValue, which is an object that determines the bar's value and range. Here, a StaticValue sets the range to 100 and the value to 66, making a bar that's two thirds full." + + e "A list of all the BarValues that can be used is found {a=https://www.renpy.org/doc/html/screen_actions.html#bar-values}in the Ren'Py documentation{/a}." + + e "In this example, we give the frame the xsize property. If we didn't do that, the bar would expand to fill all available horizontal space." + + example large: + screen bars_example(): + default n = 66 + + frame: + xalign 0.5 ypos 50 + xsize 500 + + vbox: + spacing 10 + + bar value AnimatedValue(n, 100, 0.5) style "bar" + bar value ScreenVariableValue("n", 100) style "slider" + bar value ScreenVariableValue("n", 100) style "scrollbar" + + e "There are a few different bar styles that are defined in the default GUI. The styles are selected by the style property, with the default selected by the value." + + e "The top style is the 'bar' style. It's used to display values that the player can't adjust, like a life or progress bar." + + e "The middle style is the 'slider' value. It's used for values the player is expected to adjust, like a volume preference." id bar_displayables_c2aa4725 + + e "Finally, the bottom style is the 'scrollbar' style, which is used for horizontal scrollbars. When used as a scrollbar, the thumb in the center changes size to reflect the visible area of a viewport." + + example large: + screen vbars_example(): + default n = 66 + + frame: + xalign 0.5 ypos 50 + ysize 300 + + hbox: + spacing 10 + + vbar value AnimatedValue(n, 100, 0.5) + vbar value ScreenVariableValue("n", 100) style "vslider" + vbar value ScreenVariableValue("n", 100) style "vscrollbar" + + e "The vbar displayable is similar to the bar displayable, except it uses vertical styles - 'vbar', 'vslider', and 'vscrollbar' - by default." + + + e "Bars take the Bar style properties, which can customize the look and feel greatly. Just look at the difference between the bar, slider, and scrollbar styles." + + hide example + return + + + +label imagemap_displayables: + + e "Imagemaps use two or more images to show buttons and bars. Let me start by showing you an example of an imagemap in action." + + window hide None + + example imagemap hide noshow: + screen imagemap_example(): + imagemap: + idle "imagemap ground" + hover "imagemap hover" + + hotspot (44, 238, 93, 93) action Jump("swimming") alt "Swimming" + hotspot (360, 62, 93, 93) action Jump("science") alt "Science" + hotspot (726, 106, 93, 93) action Jump("art") alt "Art" + hotspot (934, 461, 93, 93) action Jump("home") alt "Home" + + label imagemap_example: + + # Call the imagemap_example screen. + call screen imagemap_example + + label swimming: + + e "You chose swimming." + + e "Swimming seems like a lot of fun, but I didn't bring my bathing suit with me." + + jump imagemap_done + + label science: + + e "You chose science." + + e "I've heard that some schools have a competitive science team, but to me research is something that can't be rushed." + + jump imagemap_done + + label art: + e "You chose art." + + e "Really good background art is hard to make, which is why so many games use filtered photographs. Maybe you can change that." + + jump imagemap_done + + label home: + + e "You chose to go home." + + jump imagemap_done + + label imagemap_done: + + e "Anyway..." + + window show None + window auto + + e "To demonstrate how imagemaps are put together, I'll show you the five images that make up a smaller imagemap." + + show imagemap volume idle: + xalign 0.5 ypos 50 + with dissolve + + e "The idle image is used for the background of the imagemap, for hotspot buttons that aren't focused or selected, and for the empty part of an unfocused bar." + + show imagemap volume hover: + xalign 0.5 ypos 50 + with dissolve + + e "The hover image is used for hotspots that are focused but not selected, and for the empty part of a focused bar." + + e "Notice how both the bar and button are highlighted in this image. When we display them as part of a screen, only one of them will show up as focused." + + show imagemap volume selected_idle: + xalign 0.5 ypos 50 + with dissolve + + e "Selected images like this selected_idle image are used for parts of the bar that are filled, and for selected buttons, like the current screen and a checked checkbox." + + show imagemap volume selected_hover: + xalign 0.5 ypos 50 + with dissolve + + e "Here's the selected_hover image. The button here will never be shown, since it will never be marked as selected." + + show imagemap volume insensitive: + xalign 0.5 ypos 50 + with dissolve + + e "Finally, an insensitive image can be given, which is used when a hotspot can't be interacted with." + + hide imagemap + with dissolve + + e "Imagemaps aren't limited to just images. Any displayable can be used where an image is expected." + + example large: + screen volume_imagemap_example(): + imagemap: + xalign 0.5 ypos 50 + idle "imagemap volume idle" + hover "imagemap volume hover" + selected_idle "imagemap volume selected_idle" + selected_hover "imagemap volume selected_hover" + insensitive "imagemap volume insensitive" + + hotspot (237, 171, 126, 50) action Return(True) + hotbar (51, 96, 498, 52) value Preference("music volume") + + e "Here's an imagemap built using those five images. Now that it's an imagemap, you can interact with it if you want to." + + example large: + screen volume_imagemap_auto_example(): + imagemap: + xalign 0.5 ypos 50 + auto "imagemap volume %s" + + hotspot (237, 171, 126, 50) action Return(True) + hotbar (51, 96, 498, 52) value Preference("music volume") + + + e "To make this a little more concise, we can replace the five images with the auto property, which replaces '%%s' with 'idle', 'hover', 'selected_idle', 'selected_hover', or 'insensitive' as appropriate." + + e "Feel free to omit the selected and insensitive images if your game doesn't need them. Ren'Py will use the idle or hover images to replace them." + + e "The hotspot and hotbar statements describe areas of the imagemap that should act as buttons or bars, respectively." + + e "Both take the coordinates of the area, in (x, y, width, height) format." + + e "A hotspot takes an action that is run when the hotspot is activated. It can also take actions that are run when it's hovered and unhovered, just like a button can." + + e "A hotbar takes a BarValue object that describes how full the bar is, and the range of values the bar should display, just like a bar and vbar does." + + + hide screen volume_imagemap_auto_example + show example imagemap + with dissolve + + e "A useful pattern is to define a screen with an imagemap that has hotspots that jump to labels, and call that using the call screen statement." + + e "That's what we did in the school example I showed before. Here's the script for it. It's long, but the imagemap itself is fairly simple." + + hide example + + e "Imagemaps have pluses and minuses. On one hand, they are easy for a designer to create, and can look very good. At the same time, they can be hard to translate, and text baked into images may be blurry when the window is scaled." + + e "It's up to you and your team to decide if imagemaps are right for your project." + + return + + +label viewport_displayables: + + e "Sometimes, you'll want to display something bigger than the screen. That's what the viewport displayable is for." + + example large: + screen viewport_screen(): + + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + + draggable True + mousewheel True + arrowkeys True + + add "bg band" + + with dissolve + + e "Here's an example of a simple viewport, used to display a single image that's far bigger than the screen. Since the viewport will expand to the size of the screen, we use the xysize property to make it smaller." + + e "By default the viewport can't be moved, so we give the draggable, mousewheel, and arrowkeys properties to allow it to be moved in multiple ways." + + example large: + screen edgescroll_viewport_screen(): + + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + + edgescroll (150, 500) + mousewheel True + arrowkeys True + + add "bg band" + + + e "When I give the viewport the edgescroll property, the viewport automatically scrolls when the mouse is near its edges. The two numbers are the size of the edges, and the speed in pixels per second." + + example large: + screen scrollbar_viewport_screen(): + + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + + scrollbars "both" + spacing 5 + + draggable True + mousewheel True + arrowkeys True + + add "bg band" + + with dissolve + + e "Giving the viewport the scrollbars property surrounds it with scrollbars. The scrollbars property can take 'both', 'horizontal', and 'vertical' as values." + + e "The spacing property controls the space between the viewport and its scrollbars, in pixels." + + + example large: + screen initial_viewport_screen(): + + viewport: + xalign 0.5 ypos 50 xysize (700, 300) + + xinitial 0.5 + yinitial 1.0 + + scrollbars "both" + spacing 5 + + draggable True + mousewheel True + arrowkeys True + + add "bg band" + + with dissolve + + e "The xinitial and yinitial properties set the initial amount of scrolling, as a fraction of the amount that can be scrolled." + + example large: + screen vpgrid_screen(): + + vpgrid: + cols 6 + rows 4 + + xalign 0.5 ypos 50 xysize (700, 300) + + child_size (1000, None) + + scrollbars "both" + side_spacing 5 + + draggable True + mousewheel True + arrowkeys True + + for i in range(6 * 4): + add "logo base" + + + with dissolve + + e "Finally, there's the vpgrid displayable. It combines a viewport and a grid into a single displayable, except it's more efficient than either, since it doesn't have to draw every child." + + e "It takes the cols and rows properties, which give the number of rows and columns of children. If one is omitted, Ren'Py figures it out from the other and the number of children." + + + hide example + + return diff --git a/testcases/originals/tutorial-8.2/tutorial_screens.rpy b/testcases/originals/tutorial-8.2/tutorial_screens.rpy new file mode 100644 index 00000000..f4196b3e --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_screens.rpy @@ -0,0 +1,662 @@ +################################################################################ +# Stats screen. +# +# This displays RPG-like statistics. +################################################################################ + +default player_hp = 15 +default player_hp_max = 42 +default eileen_hp = 100 +default eileen_hp_max = 100 + +default player_lv = 4 +default eileen_lv = 99 + +# This screen displays a single stat. +screen single_stat(name, hp, hp_max, lv, xalign): + + frame: + xalign xalign + + vbox: + spacing 5 + + hbox: + text "[name!t]" min_width 220 + text _(" Lv. [lv]") + + hbox: + text _("HP"): + min_width 40 + yalign 0.5 + + bar: + value AnimatedValue(hp, hp_max, 1.0) + xmaximum 180 + ysize 26 + + + text " [hp]/[hp_max]": + yalign 0.5 + + +# This screen uses single_stat to display two stats at once. +screen stats(): + use single_stat(_("Player"), player_hp, player_hp_max, player_lv, 0.0) + use single_stat(_("Eileen"), eileen_hp, eileen_hp_max, eileen_lv, 1.0) + + +################################################################################ +# Day picker +# +# This code displays a day picker, including statistics and a way of choosing +# an action for each period of the day. +################################################################################ + + +# This constant defines the periods in the day. +define day_periods = [ _('Morning'), _('Afternoon'), _('Evening') ] + +# This constant defines what to do in each period. +define day_choices = [ _('Study'), _('Exercise'), _('Eat'), _('Drink'), _('Be Merry') ] + +# This is a dictionary mapping a period to a +default day_plan = { + 'Morning' : 'Eat', + 'Afternoon' : 'Drink', + 'Evening' : 'Be Merry' + } + +# These variables display statistics to the player. +default stat_strength = 10 +default stat_intelligence = 25 +default stat_moxie = 100 +default stat_chutzpah = 75 + +# These styles are used to style the various stats. +style stat_text is default: + min_width 200 + textalign 1.0 + yalign 0.5 + +style stat_hbox is hbox: + spacing 10 + +style stat_vbox: + spacing 5 + + +# This is the day planner screen. It displays the + +screen day_planner(): + + # This vbox organizes everything. + vbox: + + spacing 5 + + # A frame containing all the stats. + frame: + style_prefix "stat" + xpadding 150 + xfill True + + vbox: + + hbox: + text _("Strength") + bar value StaticValue(stat_strength, 100) + + hbox: + text _("Intelligence") + bar value StaticValue(stat_strength, 100) + + hbox: + text _("Moxie") + bar value StaticValue(stat_strength, 100) + + hbox: + text _("Chutzpah") + bar value StaticValue(stat_strength, 100) + + + # A grid of three frames, one for each of the periods in the day. + grid 3 1: + + spacing 5 + xfill True + + for p in day_periods: + + frame: + xfill True + + vbox: + label p + + null height 5 + + for i in day_choices: + textbutton i action SetDict(day_plan, p, i) + + # This is a grid containing two empty space and the done button, + # so everything lines up. + grid 3 1: + xfill True + spacing 5 + + null + + frame: + textbutton _("Done"): + action Return(True) + xfill True + + text_xalign 0.5 + + null + + + + + +label tutorial_screens: + + e "Screens are the most powerful part of Ren'Py. Screens let you customize the out-of-game interface, and create new in-game interface components." + +label screens_menu: + + $ reset_example() + + menu: + + e "What would you like to know about screens?" + + "What screens can do.": + call screens_demo from _call_screens_demo + + "How to show screens.": + call screens_showing from _call_screens_showing + + "Passing parameters to screens.": + call screens_parameters from _call_screens_parameters + + "Screen properties.": + call screens_properties from _call_screens_properties + + "Special screen statements.": + call screens_control from _call_screens_control + + "Using other screens.": + call screen_use from _call_screen_use + + "That's it.": + return + + jump screens_menu + + +label screens_demo: + + e "Screens are how we create the user interface in Ren'Py. With the exception of images and transitions, everything you see comes from a screen." + + e "When I'm speaking to you, I'm using the 'say' screen. It's responsible for taking dialogue and presenting it to the player." + + menu: + + e "And when the menu statement displays an in-game choice, the 'choice' screen is used. Got it?" + + "Yes.": + pass + + "I do.": + pass + + e "Text input uses the 'input' screen, NVL mode uses the 'nvl' screen, and so on." + + e "More than one screen can be displayed at once. For example, the buttons at the bottom - Back, History, Skip, and so on - are all displayed by a quick_menu screen that's shown all of the time." + + e "There are a lot of special screens, like 'main_menu', 'load', 'save', and 'preferences'. Rather than list them all here, I'll {a=https://www.renpy.org/doc/html/screen_special.html}send you to the documentation{/a}." + + e "In a newly created project, all these screens live in screens.rpy. You can edit that file in order to change them." + + e "You aren't limited to these screens either. In Ren'Py, you can make your own screens, and use them for your game's interface." + + $ player_hp = 15 + + show screen stats + with dissolve + + e "For example, in an RPG like visual novel, a screen can display the player's statistics." + + $ player_hp = 42 + + e "Which reminds me, I should probably heal you." + + hide screen stats + show screen day_planner + + with dissolve + + e "Complex screens can be the basis of whole game mechanics. A stats screen like this can be the basis of dating and life-sims." + + hide screen day_planner + with dissolve + + e "While screens might be complex, they're really just the result of a lot of simple parts working together to make something larger than all of them." + + return + + + + +label screens_showing: + + example large noshow: + screen simple_screen(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("This is a screen.") + textbutton _("Okay"): + action Return(True) + + + e "Here's an example of a very simple screen. The screen statement is used to tell Ren'Py this is a screen, and its name is simple_screen." id screens_showing_1b51e9a4 + + e "Inside the screen statement, lines introduces displayables such as frame, vbox, text, and textbutton; or properties like action, xalign, and ypos." + + show screen simple_screen + with dissolve + + e "I'll work from the inside out to describe the statements. But first, I'll show the screen so you can see it in action." + + e "The text statement is used to display the text provided." + + e "The textbutton statement introduces a button that can be clicked. When the button is clicked, the provided action is run." + + e "Both are inside a vbox, which means vertical box, statement - that places the text on top of the button." + + e "And that is inside a frame that provides the background and borders. The frame has an at property that takes a transform giving its position." + + hide screen simple_screen + hide example + with dissolve + + e "There are a trio of statements that are used to display screens." + + example: + show screen simple_screen + + e "The first is the show screen statement, which displays a screen and lets Ren'Py keep going." + + e "The screen will stay shown until it is hidden." + + hide screen simple_screen + + e "Hiding a screen is done with the hide screen statement." + + show example call_screen + + e "The call screen statement stops Ren'Py from executing script until the screen either returns a value, or jumps the script somewhere else." + + e "Since we can't display dialogue at the same time, you'll have to click 'Okay' to continue." + + hide example + + example call_screen hide: + call screen simple_screen + + e "When a call screen statement ends, the screen is automatically hidden." + + e "Generally, you use show screen to show overlays that are up all the time, and call screen to show screens the player interacts with for a little while." + + return + + +label screens_parameters: + + example large noshow: + screen parameter_screen(message, okay=Return(True), cancel=Return(False)): + frame: + xalign 0.5 ypos 50 + vbox: + text "[message!t]" + textbutton _("Okay"): + action okay + textbutton _("Cancel"): + action cancel + + example hide show_parameter_screen: + show screen parameter_screen(_("Hello, world."), cancel=Notify(_("You can't cancel this."))) + + with dissolve + + e "Here's an example of a screen that takes three parameters. The message parameter is a message to show, while the okay and cancel actions are run when the appropriate button is chosen." + + e "While the message parameter always has to be supplied, the okay and cancel parameters have default values that are used if no argument is given." + + e "Each parameter is a variable that is defined inside the screen. Inside the screen, these variables take priority over those used in the rest of Ren'Py." + + show example show_parameter_screen + + e "When a screen is shown, arguments can be supplied for each of the parameters. Arguments can be given by position or by name." + + example: + show screen parameter_screen(_("Shiro was here.")) + + with dissolve + + e "Parameters let us change what a screen displays, simply by re-showing it with different arguments." + + hide screen parameter_screen + with dissolve + + show example call_parameter_screen + + e "The call screen statement can also take arguments, much like show screen does." + + hide example + + example hide call_parameter_screen: + call screen parameter_screen(_("Click either button to continue.")) + + return + + +label screens_properties: + + e "There are a few properties that can be applied to a screen itself." + + example large: + screen modal_example(): + modal True + + frame: + xalign 0.5 ypos 50 + textbutton _("Close This Screen"): + action Hide("modal_example") + + e "When the modal property is true, you can't interact with things beneath the screen. You'll have to click 'Close This Screen' before you can continue." + + example: + screen a_tag_screen(): + tag tag_screen + + frame: + xalign 0.33 ypos 50 + text _("A Tag Screen") + + screen b_tag_screen(): + tag tag_screen + + frame: + xalign 0.66 ypos 50 + text _("B Tag Screen") + + + e "When a screen has the tag property, it's treated like the tag part of an image name. Here, I'm showing a_tag_screen." + + show screen b_tag_screen + + e "When I show b_tag_screen, it replaces a_tag_screen." + + e "This is useful in the game and main menus, where you want the load screen to replace the preferences screen. By default, all those screens have tag menu." + + show eileen concerned + + e "For some reason, tag takes a name, and not an expression. It's too late to change it." + + show eileen happy + with None + + hide screen b_tag_screen + + example noshow: + screen zorder_100_screen(): + zorder 100 + frame: + xalign 0.5 xoffset 50 ypos 70 + text "Zorder 100" + + screen zorder_0_screen(): + frame: + xalign 0.5 ypos 50 + text "Zorder 0" + + show screen zorder_100_screen + show screen zorder_0_screen + + with dissolve + + e "The zorder property controls the order in which screens overlap each other. The larger the zorder number, the closer the screen is to the player." + + e "By default, a screen has a zorder of 0. When two screens have the same zorder number, the screen that is shown second is closer to the player." + + + + hide screen zorder_100_screen + hide screen zorder_0_screen + + example nohide: + + screen variant_screen(): + variant "small" + frame: + xalign 0.5 ypos 50 + text _("You're on a small device.") + + screen variant_screen(): + frame: + xalign 0.5 ypos 50 + text _("You're not on a small device.") + + e "The variant property selects a screen based on the properties of the device it's running on." + + e "In this example, the first screen will be used for small devices like telephones, and the other screen will be used for tablets and computers." + + + example: + + screen style_prefix_screen(): + style_prefix "red" + + frame: + xalign 0.5 ypos 50 + text _("This text is red.") + + style red_frame: + background "#440000d9" + + style red_text: + color "#ffc0c0" + + + e "Finally, the style_prefix property specifies a prefix that's applied to the styles in the screen." + + e "When the 'red' prefix is given, the frame gets the 'red_frame' style, and the text gets the 'red_text' style." + + e "This can save a lot of typing when styling screens with many displayables in them." + + hide example + + return + +label warp_screen_displayables: + $ renpy.pop_call() + jump screen_displayables + +label screens_control: + + e "The screen language has a few statements that do things other than show displayables. If you haven't seen the section on {a=jump:warp_screen_displayables}Screen Displayables{/a} yet, you might want to check it out, then come back here." + + example large: + screen single_python_screen(): + + $ message = _("Hello, World.") + + frame: + xalign 0.5 ypos 50 + vbox: + text "[message!t]" + + e "The python statement works just about the same way it does in the script. A single line of Python is introduced with a dollar sign. This line is run each time the screen updates." + + example large: + screen block_python_screen(): + + python: + message1 = _("Hello, World.") + message2 = _("It's good to meet you.") + + frame: + xalign 0.5 ypos 50 + vbox: + text "[message1!t]" + text "[message2!t]" + + e "Similarly, the python statement introduces an indented block of python statements. But there is one big difference in Python in screens and Python in scripts." + + e "The Python you use in screens isn't allowed to have side effects. That means that it can't do things like change the value of a variable." + + e "The reason for this is that Ren'Py will run a screen, and the Python in it, during screen prediction." + + example large: + + screen default_screen(): + + default n = 0 + + frame: + xalign 0.5 ypos 50 + vbox: + text "n = [n]" + textbutton _("Increase") action SetScreenVariable("n", n + 1) + + e "The default statement lets you set the value of a screen variable the first time the screen runs. This value can be changed with the SetScreenVariable and ToggleScreenVariable actions." + + e "The default statement differs from the Python statement in that it is only run once. Python runs each time the screen updates, and hence the variable would never change value." + + + example large: + + screen if_screen(): + + default n = 0 + + frame: + xalign 0.5 ypos 50 + vbox: + if n > 2: + text "n = [n]" color "#cfc" + else: + text "n = [n]" color "#fcc" + + textbutton _("Increase") action SetScreenVariable("n", n + 1) + + e "The if statement works like it does in script, running one block if the condition is true and another if the condition is false." + + example large: + + screen for_screen(): + + $ landings = [ _("Earth"), _("Moon"), _("Mars") ] + + frame: + xalign 0.5 ypos 50 + + vbox: + for i in landings: + textbutton "[i!t]" action Return(i) + + e "The for statement takes a list of values, and iterates through them, running the block inside the for loop with the variable bound to each list item." + + example large: + + screen on_key_screen(): + + frame: + xalign 0.5 ypos 50 + + text _("Now press 'a'.") + + on "show" action Notify(_("The screen was just shown.")) + + key "a" action Notify(_("You pressed the 'a' key.")) + + + e "The on and key statements probably only make sense at the top level of the screen." + + e "The on statement makes the screen run an action when an event occurs. The 'show' event happens when the screen is first shown, and the 'hide' event happens when it is hidden." + + e "The key event runs an event when a key is pressed." + + hide example + + return + +label screen_use: + + e "The screen language use statement lets you include a screen inside another. This can be useful to prevent duplication inside screens." + + example large: + + screen duplicate_stats(): + frame: + xalign 0.5 ypos 50 + vbox: + text _("Health") xalign 0.5 + bar value StaticValue(90, 100) xalign 0.5 xsize 250 + + null height 15 + + text _("Magic") xalign 0.5 + bar value StaticValue(42, 100) xalign 0.5 xsize 250 + + e "Take for example this screen, which shows two stat entries. There's already a lot of duplication there, and if we had more stats, there would be more." + + example large: + + screen using_stats(): + frame: + xalign 0.5 ypos 50 + vbox: + use stat(_("Health"), 90) + null height 15 + use stat(_("Magic"), 42) + + screen stat(name, amount): + + text name xalign 0.5 + bar value StaticValue(amount, 100) xalign 0.5 xsize 250 + + e "Here, we moved the statements that show the text and bar into a second screen, and the use statement includes that screen in the first one." + + e "The name and amount of the stat are passed in as arguments to the screen, just as is done in the call screen statement." + + e "By doing it this way, we control the amount of duplication, and can change the stat in one place." + + example large: + + screen transclusion_example(): + + use boilerplate(): + text _("There's not much left to see.") + + screen boilerplate(): + frame: + xalign 0.5 ypos 50 + + vbox: + transclude + + e "The transclude statement goes one step further, by letting the use statement take a block of screen language statements." + + e "When the included screen reaches the transclude statement it is replaced with the block from the use statement." + + e "The boilerplate screen is included in the first one, and the text from the first screen is transcluded into the boilerplate screen." + + e "Use and transclude are complex, but very powerful. If you think about it, 'use boilerplate' is only one step removed from writing your own Screen Language statement." + + hide example + return diff --git a/testcases/originals/tutorial-8.2/tutorial_video.rpy b/testcases/originals/tutorial-8.2/tutorial_video.rpy new file mode 100644 index 00000000..9861fae7 --- /dev/null +++ b/testcases/originals/tutorial-8.2/tutorial_video.rpy @@ -0,0 +1,45 @@ +# This file contains demonstrations of Ren'Py's multimedia +# support. Right now, this is just showing off sound and music, but +# Ren'Py does support movies, and we'll add them sometime later. + +example movie_image: + image launch = Movie(play="oa4_launch.webm", pos=(10, 10), anchor=(0, 0)) + +label tutorial_video: + + e "Ren'Py supports playing movies. There are two ways of doing this." + + e "The first way allows you to show a movie as an image, along with every other image that's displayed on the screen." + + show example movie_image + + e "To do this, we first have to define an image to be a Movie displayable. Movie displayables take a movie to play, and can be given position properties." + + stop music fadeout .25 + show example movie_play + pause .25 + + example movie_play hide: + show launch behind eileen + + e "Then, we can show the movie displayable, which starts the movie playing." + + example: + hide launch + + e "When we no longer want to play the movie, we can hide it." + + show example movie_cutscene + + e "The other way to show a movie is with the renpy.movie_cutscene python function. This shows the movie fullscreen, either until it ends or until the user clicks." + + hide screen example + + example movie_cutscene hide: + $ renpy.movie_cutscene("oa4_launch.webm") + + e "A Movie displayable can also take a mask with an alpha channel, which lets you make movie sprites. But that's more complicated, so I'll stop here for now." + + play music "sunflower-slow-drag.ogg" + + return diff --git a/testcases/strip_testcases.py b/testcases/strip_testcases.py deleted file mode 100644 index 1ee48a7b..00000000 --- a/testcases/strip_testcases.py +++ /dev/null @@ -1,39 +0,0 @@ -# Takes the testcases in the originals folder, and strips any comments. - -from pathlib import Path - - -def normalize(infile: Path, outfile: Path): - with infile.open("r", encoding="utf-8-sig") as fin: - with outfile.open("w", encoding="utf-8", newline="\n") as fout: - for line in fin: - if line.lstrip().startswith("#"): - fout.write("\n") - elif not line.strip(): - fout.write("\n") - else: - fout.write(line.rstrip()) - fout.write("\n") - - fout.write("# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc\n") - -def main(): - # Originals are stored as originals/*/*.orig.rpyc - # strip comments and store the result as stripped/*/*.rpyc - - orig_folder = Path(__file__).parent / "originals" - stripped_folder = Path(__file__).parent / "stripped" - stripped_folder.mkdir(exist_ok=True) - - for folder in orig_folder.iterdir(): - # ensure the destination directories exist - destfolder = stripped_folder / folder.name - destfolder.mkdir(exist_ok=True) - - for file in folder.iterdir(): - if file.name.endswith(".rpy"): - destfile = destfolder / file.name - normalize(file, destfile) - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/testcases/validate_expected.py b/testcases/validate_expected.py new file mode 100644 index 00000000..0c6ecb3e --- /dev/null +++ b/testcases/validate_expected.py @@ -0,0 +1,67 @@ +# Takes the testcases in the originals folder, and strips any comments. + +from pathlib import Path +import argparse +import shutil +import subprocess + +ROOT = Path(__file__).parent +ORIGINAL = ROOT / "originals" # original .rpy files +EXPECTED = ROOT / "expected" # expected result from decompiling .rpyc files +COMPILED = ROOT / "compiled" # .rpyc files from compiling original + +def normalize(source: Path, dest: Path): + with source.open("r", encoding="utf-8-sig") as fin: + with dest.open("w", encoding="utf-8", newline="\n") as fout: + for line in fin: + # strip out empty lines or comments + l = line.strip() + if not l or l.startswith("#"): + continue + + # strip any comments in general (yes this ignores that they might be inside a string) + if "#" in line: + line, _ = line.split("#", 1) + + # strip any trailing whitespace + fout.write(line.rstrip() + "\n") + +def copy_rpy(source: Path, dest: Path): + if source.name.endswith(".rpy"): + shutil.copyfile(source, dest) + +def process_recursively(source_dir, dest_dir, function): + # Recursively traverses source_dir and ensures dest_dir has the same folder structure. + # Then, calls `function(source_file, dest_file) for every file in source_dir. + dest_dir.mkdir(exist_ok=True) + for source_item in source_dir.iterdir(): + dest_item = dest_dir / source_item.name + + if source_item.is_dir(): + process_recursively(source_item, dest_item, function) + + elif source_item.is_file(): + function(source_item, dest_item) + +def main(): + parser = argparse.ArgumentParser(description="Testcase utilities. Compares `expected` with `originals`") + + parser.add_argument('-u', '--update', dest='update', action='store_true', + help="update the contents of 'expected' with .rpy files found in 'compiled' before running") + args = parser.parse_args() + + + if args.update: + process_recursively(COMPILED, EXPECTED, copy_rpy) + + + temp_original = ROOT / "temp-originals" + temp_expected = ROOT / "temp-expected" + + process_recursively(ORIGINAL, temp_original, normalize) + process_recursively(EXPECTED, temp_expected, normalize) + + subprocess.run(["diff", "-ur", temp_original, temp_expected]) + +if __name__ == '__main__': + main() \ No newline at end of file