commit d0a3798dd2b8feea8ae4bd2603819be2fbf0bd89 Author: Jacoder23 Date: Fri Apr 5 13:40:52 2024 +0800 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1312a47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Exclude rpyc files, except the ones in old-game +*.rpyc +!old-game/** + +*.rpyc~ +*.rpymc +*.rpy~ +log.txt +*.save +*.psd +.vscode/ +game/saves/persistent +*.bak +cache/ + +game/cache/bytecode.rpyb +game/cache/pyanalysis.rpyb +game/cache/screens.rpyb +game/saves/navigation.json +traceback.txt +errors.txt +game/saves/navigation.json +game/script_version.txt +game/saves/sync/** diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec4dd43 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# MESSAGE TO ANY DEVELOPERS # +# This isn't under any open source license but I've left the source accessible (or at least more accessible than just leaving the .rpyc) if you wanna read it # +# I should put this under a license... maybe I'll do it after the jam has ended # +# be cool, go to school # \ No newline at end of file diff --git a/game/00auto-highlight.rpy b/game/00auto-highlight.rpy new file mode 100644 index 0000000..fac0e37 --- /dev/null +++ b/game/00auto-highlight.rpy @@ -0,0 +1,196 @@ +""" + Auto Highlight Ren'Py Module + 2021 Daniel Westfall + + http://twitter.com/sodara9 + I'd appreciate being given credit if you do end up using it! :D Would really + make my day to know I helped some people out! + http://opensource.org/licenses/mit-license.php + Github: https://github.com/SoDaRa/Auto-Highlight + itch.io: https://wattson.itch.io/renpy-auto-highlight +""" +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +""" Setup (IMPORTANT) """ +## To get this working you'll need to do two additional things along with having this file in your project. + +# - First, you'll need to setup your character definitions to support it. +# Example: +# define eil = Character("Eileen", callback = name_callback, cb_name = "eileen") +# - cb_name provides the 'name' parameter to the function 'name_callback' +# - Remember that Ren'py supports say_with_arguments. +# So you can assign one for a particular line by doing: +# eil "I think someone else should be focused" (cb_name = "pileen") +# - Finally, if you wish for the special narrator to make all sprites unfocused or something similar, +# you can copy this. +# define narrator = Character(callback = name_callback, cb_name = None) + +# - Second, you'll need to apply the sprite_highlight transform to all images you want this +# applied to. For people using layeredimages, this is very easy. As an example: +# layeredimage eileen: +# at sprite_highlight('eileen') +# ... +# - However, if you're using individual sprites, you'll have to be sure this is applied to every one. +# image eileen happy = At('eileen_happy', sprite_highlight('eileen')) +# image eileen sad = At('eileen_sad', sprite_highlight('eileen')) +# Or, if you'd prefer an ATL example +# image eileen happy: +# 'eileen_happy' +# function SpriteFocus('eileen') + +""" General Note """ +# - This file has to be compiled before any scripts that define images that use this. +# As such, this file is named 00auto-highlight.rpy to help with that. +# - Be sure that all images that you want to share the same sprite highlight name +# are using the same image tag. + +""" Variables """ +# - sprite_focus - (Dictionary) It is used to help inform who should be animated +# and occasionaly holds timing data +# - Has entries added to it in the SpriteFocus __call__ function. +# - I chose to use a define because it's status should not affect the story and +# it can be cleared safely when the player closes the game. Then, when someone boots +# up again, it will only have entries added to it as needed. +# - If you wish for it's status to be kept between play sessions, then change the 'define' to 'default' +define sprite_focus = {} + +# - speaking_char - (Varient) Is manipulated by the character callback to help us know +# who the current speaking character is. +# - Keeps track of which character is currently speaking. Is updated in name_callback +# and checked in SpriteFocus __call__ to determine if sprite's character is speaking +# or not. +default speaking_char = None + +""" Transforms """ +# - This is the actual transform that will help apply the changes to your sprites. +# - SpriteFocus is used as a callable class here. The function statement doesn't +# pass additional parameters to our function, so I use a callable class here to +# give the function statement something it can call like a function, while still +# providing a way to pass through the transform parameter. +transform sprite_highlight(sprite_name): + function SpriteFocus(sprite_name) + # I don't recommend adding ATL down here since the above statement won't return None. +init -10 python: + import math + + # name: Name of the character talking at present. Usually a string. + # Used by SpriteFocus's __call__ function to determine which sprites to put in talking and non-talking states + def name_callback(event, interact=True, name=None, **kwargs): + global speaking_char + if event == "begin": + speaking_char = name + + # Used to help make sprite_tf more reusable while still using the function statement in the ATL + class SpriteFocus(object): + # char_name - Used to check who we are manipulating. This is used as a + # key into sprite_focus and should be equal to it's equivalent string + # that is written to speaking_char in the character callback. + def __init__(self, char_name): + self.char_name = char_name + + ## Main function ## + # trans - Renpy transform object that we'll manipulate + # start_time - (float) Starting time of the current transform + # anim_time - (float) Animation time of the current transform. May be >= st. + def __call__(self, trans, start_time, anim_time): + # The ease function we use to make the animation move a bit more naturally. + def get_ease(t): + return .5 - math.cos(math.pi * t) / 2.0 + #### Setup #### + global sprite_focus, speaking_char # Get the global variables we defined earlier + char_name = self.char_name # Just to save having self.char_name everywhere + # Add an entry for our char_name if it's not in the dictionary yet + if char_name not in sprite_focus: + sprite_focus[char_name] = False + anim_length = 0.2 # How long (in seconds) the animation will last + bright_change = 0.08 # How much the brightness changes + sat_change = 0.2 # How much the saturation changes + zoom_change = 0.0025 # How much the zoom changes + # - y_change is mostly here because the Minotaur Hotel sprites were made to be kept level with + # the bottom of the screen. The zoom change causes them to rise slightly + # above it. So I apply a small yoffset to keep them in place. + # - If you have full sprites, this can be omitted. + # If you do, remember to remove the cooresponding lines in the Transform Manipulation near the bottom + y_change = 1 # How much y_offset to apply. + + # is_talking - (Boolean) Determines if we're the talking char or not. + # True means we are talking. False means we aren't. + is_talking = speaking_char == char_name + # - If you would like to add support for multiple characters to be highlighted + # then you may want to pass a list of names to speaking_char. And then have something like: + # if isinstance(speaking_char, list): + # is_talking = char_name in speaking_char + # - Or if you want some special name like "all" to mean every sprite should be focused: + # if speaking_char == 'all': + # is_talking = True + + #### Check & Update Status #### + # - If our key in the sprite_focus dictionary is a number AND anim_time is less than that number + # then we want to update our talking status in sprite_focus to be a boolean. + # - This is to prevent any issues that arrise from anim_time being less than a value we put into sprite_focus. + # - IMPORTANT: Anytime our value in sprite_focus is set to a boolean will + # represent us being either talking (boolean True) or not talking (boolean False). + # It being set to a number will represent animating from one to another. + if isinstance(sprite_focus[char_name], (int, float)) and anim_time < sprite_focus[char_name]: + sprite_focus[char_name] = is_talking + # If our value in the sprite_focus is not equivalent to our talking status AND is a boolean + if sprite_focus[char_name] != is_talking and isinstance(sprite_focus[char_name], bool): + # Since our talking status has flipped, log the time so we can use it as a timer in the next section + sprite_focus[char_name] = anim_time + # Unless we're in rollback or are skipping. In which case, we'll want to just snap to the new status + if renpy.is_skipping() or renpy.in_rollback(): + sprite_focus[char_name] = is_talking + + #### Determine Time and Position in Animation #### + # - Figure out the current time of the animation + # - This will still work, even if our entry in sprite_focus is currently a boolean. + # However, it will never be used in such a scenario due to the next if statement. + # - Also where that anim_time value we stored in sprite_focus is used + curr_time = max(anim_time - sprite_focus[char_name],0) # Prevent going below zero + # - The following variable is the actual value we'll use to animate on. + # - By default, it's set to 1.0. Which cooresponds to the animation being completed. + # It should always remain within the range 0 to 1. + curr_ease = 1.0 + # If curr_time is still less than the animation length AND we aren't a boolean in sprite_focus + if curr_time < anim_length and not isinstance(sprite_focus[char_name], bool): + curr_ease = get_ease(curr_time/anim_length) # Get our actual animation position + else: + sprite_focus[char_name] = is_talking # If done with time, register talking status + + #### Transform Manipulation #### + # - This bit is what actually applies the changes to the sprite we're manipulating + # - If you want a different effect for the talking and non-talking versions, you'll mostly + # be doing stuff in here. The actual values you want will depend on the properties you want + # to change. But will boil down to having the curr_ease * some_amount_of_change. + # - Both transformations should also smoothly flow into each other. + # For example, if the talking non-talking version has the sprite moved down 10 pixels, + # the talking version should start from 10 pixels down and rise up. + if is_talking: # Apply the talking transformation + trans.matrixcolor = SaturationMatrix((1.0-sat_change) + curr_ease * sat_change) * BrightnessMatrix(-bright_change + curr_ease * bright_change) + trans.zoom = min(curr_ease * zoom_change + (1.0-zoom_change), 1.0) + trans.yoffset = y_change - curr_ease * y_change # Delete here if you removed y_change earlier + else: # Apply the not-talking transformation + trans.matrixcolor = SaturationMatrix(1.0 - curr_ease * sat_change) * BrightnessMatrix(curr_ease * -bright_change) + trans.zoom = max(1.0 - curr_ease * zoom_change, (1.0-zoom_change)) + trans.yoffset = y_change * curr_ease # Delete here if you removed y_change earlier + # Finally, we don't really want to ever stop running this. + # So we just ask to be continuously redrawn ASAP forever. + # Returning > 0 will cause it to redraw slower. And returning None will cause it to stop running + return 0 diff --git a/game/gui/bar/bottom.png b/game/gui/bar/bottom.png new file mode 100644 index 0000000..74483a9 Binary files /dev/null and b/game/gui/bar/bottom.png differ diff --git a/game/gui/bar/left.png b/game/gui/bar/left.png new file mode 100644 index 0000000..f6ad898 Binary files /dev/null and b/game/gui/bar/left.png differ diff --git a/game/gui/bar/right.png b/game/gui/bar/right.png new file mode 100644 index 0000000..c191f89 Binary files /dev/null and b/game/gui/bar/right.png differ diff --git a/game/gui/bar/top.png b/game/gui/bar/top.png new file mode 100644 index 0000000..dfe4a51 Binary files /dev/null and b/game/gui/bar/top.png differ diff --git a/game/gui/bubble.png b/game/gui/bubble.png new file mode 100644 index 0000000..3b23ff3 Binary files /dev/null and b/game/gui/bubble.png differ diff --git a/game/gui/button/check_foreground.png b/game/gui/button/check_foreground.png new file mode 100644 index 0000000..ef5cedd Binary files /dev/null and b/game/gui/button/check_foreground.png differ diff --git a/game/gui/button/check_selected_foreground.png b/game/gui/button/check_selected_foreground.png new file mode 100644 index 0000000..5b5b8ce Binary files /dev/null and b/game/gui/button/check_selected_foreground.png differ diff --git a/game/gui/button/choice_hover_background.png b/game/gui/button/choice_hover_background.png new file mode 100644 index 0000000..2c76ede Binary files /dev/null and b/game/gui/button/choice_hover_background.png differ diff --git a/game/gui/button/choice_idle_background.png b/game/gui/button/choice_idle_background.png new file mode 100644 index 0000000..1ab6ca6 Binary files /dev/null and b/game/gui/button/choice_idle_background.png differ diff --git a/game/gui/button/radio_foreground.png b/game/gui/button/radio_foreground.png new file mode 100644 index 0000000..a0b8b46 Binary files /dev/null and b/game/gui/button/radio_foreground.png differ diff --git a/game/gui/button/radio_selected_foreground.png b/game/gui/button/radio_selected_foreground.png new file mode 100644 index 0000000..be9db43 Binary files /dev/null and b/game/gui/button/radio_selected_foreground.png differ diff --git a/game/gui/button/slot_hover_background.png b/game/gui/button/slot_hover_background.png new file mode 100644 index 0000000..0a4800b Binary files /dev/null and b/game/gui/button/slot_hover_background.png differ diff --git a/game/gui/button/slot_idle_background.png b/game/gui/button/slot_idle_background.png new file mode 100644 index 0000000..74c321b Binary files /dev/null and b/game/gui/button/slot_idle_background.png differ diff --git a/game/gui/frame.png b/game/gui/frame.png new file mode 100644 index 0000000..cb62b42 Binary files /dev/null and b/game/gui/frame.png differ diff --git a/game/gui/namebox.png b/game/gui/namebox.png new file mode 100644 index 0000000..c24135c Binary files /dev/null and b/game/gui/namebox.png differ diff --git a/game/gui/notify.png b/game/gui/notify.png new file mode 100644 index 0000000..780c311 Binary files /dev/null and b/game/gui/notify.png differ diff --git a/game/gui/nvl.png b/game/gui/nvl.png new file mode 100644 index 0000000..7bbc928 Binary files /dev/null and b/game/gui/nvl.png differ diff --git a/game/gui/scrollbar/horizontal_hover_bar.png b/game/gui/scrollbar/horizontal_hover_bar.png new file mode 100644 index 0000000..b4d1d7c Binary files /dev/null and b/game/gui/scrollbar/horizontal_hover_bar.png differ diff --git a/game/gui/scrollbar/horizontal_hover_thumb.png b/game/gui/scrollbar/horizontal_hover_thumb.png new file mode 100644 index 0000000..1e1fc7c Binary files /dev/null and b/game/gui/scrollbar/horizontal_hover_thumb.png differ diff --git a/game/gui/scrollbar/horizontal_idle_bar.png b/game/gui/scrollbar/horizontal_idle_bar.png new file mode 100644 index 0000000..682c6a0 Binary files /dev/null and b/game/gui/scrollbar/horizontal_idle_bar.png differ diff --git a/game/gui/scrollbar/horizontal_idle_thumb.png b/game/gui/scrollbar/horizontal_idle_thumb.png new file mode 100644 index 0000000..028d049 Binary files /dev/null and b/game/gui/scrollbar/horizontal_idle_thumb.png differ diff --git a/game/gui/scrollbar/vertical_hover_bar.png b/game/gui/scrollbar/vertical_hover_bar.png new file mode 100644 index 0000000..2c91c98 Binary files /dev/null and b/game/gui/scrollbar/vertical_hover_bar.png differ diff --git a/game/gui/scrollbar/vertical_hover_thumb.png b/game/gui/scrollbar/vertical_hover_thumb.png new file mode 100644 index 0000000..386f8d9 Binary files /dev/null and b/game/gui/scrollbar/vertical_hover_thumb.png differ diff --git a/game/gui/scrollbar/vertical_idle_bar.png b/game/gui/scrollbar/vertical_idle_bar.png new file mode 100644 index 0000000..af08003 Binary files /dev/null and b/game/gui/scrollbar/vertical_idle_bar.png differ diff --git a/game/gui/scrollbar/vertical_idle_thumb.png b/game/gui/scrollbar/vertical_idle_thumb.png new file mode 100644 index 0000000..cc7e626 Binary files /dev/null and b/game/gui/scrollbar/vertical_idle_thumb.png differ diff --git a/game/gui/skip.png b/game/gui/skip.png new file mode 100644 index 0000000..51235e8 Binary files /dev/null and b/game/gui/skip.png differ diff --git a/game/gui/slider/horizontal_hover_bar.png b/game/gui/slider/horizontal_hover_bar.png new file mode 100644 index 0000000..cc21cc0 Binary files /dev/null and b/game/gui/slider/horizontal_hover_bar.png differ diff --git a/game/gui/slider/horizontal_hover_thumb.png b/game/gui/slider/horizontal_hover_thumb.png new file mode 100644 index 0000000..29e0b68 Binary files /dev/null and b/game/gui/slider/horizontal_hover_thumb.png differ diff --git a/game/gui/slider/horizontal_idle_bar.png b/game/gui/slider/horizontal_idle_bar.png new file mode 100644 index 0000000..f6ad898 Binary files /dev/null and b/game/gui/slider/horizontal_idle_bar.png differ diff --git a/game/gui/slider/horizontal_idle_thumb.png b/game/gui/slider/horizontal_idle_thumb.png new file mode 100644 index 0000000..ffdf062 Binary files /dev/null and b/game/gui/slider/horizontal_idle_thumb.png differ diff --git a/game/gui/slider/vertical_hover_bar.png b/game/gui/slider/vertical_hover_bar.png new file mode 100644 index 0000000..9f38626 Binary files /dev/null and b/game/gui/slider/vertical_hover_bar.png differ diff --git a/game/gui/slider/vertical_hover_thumb.png b/game/gui/slider/vertical_hover_thumb.png new file mode 100644 index 0000000..9d15c2d Binary files /dev/null and b/game/gui/slider/vertical_hover_thumb.png differ diff --git a/game/gui/slider/vertical_idle_bar.png b/game/gui/slider/vertical_idle_bar.png new file mode 100644 index 0000000..74483a9 Binary files /dev/null and b/game/gui/slider/vertical_idle_bar.png differ diff --git a/game/gui/slider/vertical_idle_thumb.png b/game/gui/slider/vertical_idle_thumb.png new file mode 100644 index 0000000..6f46137 Binary files /dev/null and b/game/gui/slider/vertical_idle_thumb.png differ diff --git a/game/gui/textbox.png b/game/gui/textbox.png new file mode 100644 index 0000000..fef1bc1 Binary files /dev/null and b/game/gui/textbox.png differ diff --git a/game/gui/thoughtbubble.png b/game/gui/thoughtbubble.png new file mode 100644 index 0000000..8d5d44b Binary files /dev/null and b/game/gui/thoughtbubble.png differ diff --git a/game/gui/window_icon.png b/game/gui/window_icon.png new file mode 100644 index 0000000..dda29dd Binary files /dev/null and b/game/gui/window_icon.png differ diff --git a/game/images/reimu happy.png b/game/images/reimu happy.png new file mode 100644 index 0000000..93a9473 Binary files /dev/null and b/game/images/reimu happy.png differ diff --git a/game/images/reimu.png b/game/images/reimu.png new file mode 100644 index 0000000..da04602 Binary files /dev/null and b/game/images/reimu.png differ diff --git a/game/images/yuuka happy.png b/game/images/yuuka happy.png new file mode 100644 index 0000000..2b389de Binary files /dev/null and b/game/images/yuuka happy.png differ diff --git a/game/images/yuuka.png b/game/images/yuuka.png new file mode 100644 index 0000000..0256612 Binary files /dev/null and b/game/images/yuuka.png differ diff --git a/game/kinetic_text_tags.rpy b/game/kinetic_text_tags.rpy new file mode 100644 index 0000000..6c35933 --- /dev/null +++ b/game/kinetic_text_tags.rpy @@ -0,0 +1,841 @@ +""" + Kinetic Text Tags Ren'Py Module + 2021 Daniel Westfall + + http://twitter.com/sodara9 + I'd appreciate being given credit if you do end up using it! :D Would really + make my day to know I helped some people out! + Really hope this can help the community create some really neat ways to spice + up their dialogue! + http://opensource.org/licenses/mit-license.php + Github: https://github.com/SoDaRa/Kinetic-Text-Tags + itch.io: https://wattson.itch.io/kinetic-text-tags + Forum Post: https://lemmasoft.renai.us/forums/viewtopic.php?f=51&t=60527&sid=75b4eb1aa5212a33cbfe9b0354e5376b +""" +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +### UPDATE ### +# With the new ATL text tag, a handful of effects I've made have become redundant. +# Namely the bounce (bt), fadein (fi) and rotation (rotat) effects. +# However, I'll leave them in here for posterity and in case someone would like +# to reuse some of the code for whatever purpose. +# Plus the bounce and fadein may be faster to type for some. And I'd probably +# break some code if I did. Though feel free to remove them if you find them +# to be clutter. + +##### Our preference to disable the chaos text ##### +default preferences.chaos_on = False # You can change this to be gui.chaos_text or persistent.chaos_text if you'd prefer. + +init python: + import random + import math + + # This will maintain what styles we want to apply and help us apply them + class DispTextStyle(): + # Notes: + # - "" denotes a style tag. Since it's usually {=user_style} and we partition + # it over the '=', it ends up being an empty string + # - If you want to add your own tags to the list, I recommend adding them + # before the "" + # - Self-closing tags should not be added here and should be handled + # in the text tag function. + custom_tags = ["omega", "bt", "fi", "sc", "rotat", "chaos", "move"] + accepted_tags = ["", "b", "s", "u", "i", "color", "alpha", "font", "size", "outlinecolor", "plain", 'cps'] + custom_cancel_tags = ["/" + tag for tag in custom_tags] + cancel_tags = ["/" + tag for tag in accepted_tags] + def __init__(self): + self.tags = {} + + # For setting style properties. Returns false if it accepted none of the tags + def add_tags(self, char): + tag, _, value = char.partition("=") # Separate the tag and its info + # Add tag to dictionary if we accept it + if tag in self.accepted_tags or tag in self.custom_tags: + if value == "": + self.tags[tag] = True + else: + self.tags[tag] = value + return True + # Remove mark tag as cleared if should no longer apply it + if tag in self.cancel_tags or tag in self.custom_cancel_tags: + tag = tag.replace("/", "") + self.tags.pop(tag) + return True + return False # If we got any other tag, tell the function to let it pass + + # Applies all style properties to the string + def apply_style(self, char): + new_string = "" + # Go through and apply all the tags + new_string += self.start_tags() + # Add the character in the middle + new_string += char + # Now close all the tags we opened + new_string += self.end_tags() + return new_string + + # Spits out start tags. Primarily used for SwapText + def start_tags(self): + new_string = "" + # Go through the custom tags + for tag in self.custom_tags: + if tag in self.tags: + if self.tags[tag] == True: + new_string += "{" + tag + "}" + else: + new_string += "{" + tag + "=" +self.tags[tag] + "}" + # Go through the standard tags + for tag in self.accepted_tags: + if tag in self.tags: + if self.tags[tag] == True: + new_string += "{" + tag + "}" + else: + new_string += "{" + tag + "=" +self.tags[tag] + "}" + return new_string + + # Spits out ending tags. Primarily used for SwapText + def end_tags(self): + new_string = "" + # The only tags we are required to end are any custom text tags. + # And should also end them in the reverse order they were applied. + reversed_cancels = [tag for tag in self.custom_cancel_tags] + reversed_cancels.reverse() + for tag in reversed_cancels: + temp = tag.replace("/", "") + if temp in self.tags: + new_string += "{" + tag + "}" + return new_string + + + ### TEXT WRAPPER CLASSES ### + # Basic text displacement demonstration + class BounceText(renpy.Displayable): + def __init__(self, child, char_offset, amp=20, period=4.0, speed = 1.0, **kwargs): + + # Pass additional properties on to the renpy.Displayable + # constructor. + super(BounceText, self).__init__(**kwargs) # REMEMBER TO RENAME HERE TO YOUR CLASS + + # For all of my classes, I assume I am being passed a displayable + # of class Text. If you might not, I recommend going with the default of + # self.child = renpy.displayable(child) + self.child = child + self.amp = amp # The amplitude of the sine wave + self.char_offset = char_offset # The offset into the sine wave + self.period = period # Affects the distance between peaks in the wave. + self.speed = speed # Affects how fast our wave moves as a function of time. + + def render(self, width, height, st, at): + # Where the current offset is calculated + # (self.char_offset * -.1) makes it look like the left side is leading + # We use st to allow this to change over time + curr_height = math.sin(self.period*((st * self.speed)+(float(self.char_offset) * -.1))) * float(self.amp) + + #### A Transform can be used for several effects #### + # t = Transform(child=self.child, alpha = curr_height) + + # Create a render from the child. + # Replace self.child with t to include an alpha or zoom transform + child_render = renpy.render(self.child, width, height, st, at) + + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + + # This will position our child's render. Replacing our need for an offset Transform + render.subpixel_blit(child_render, (0, curr_height)) + + renpy.redraw(self, 0) # This lets it know to redraw this indefinitely + return render + + def event(self, ev, x, y, st): + return self.child.event(ev, x, y, st) + + def visit(self): + return [ self.child ] + + # Simple fade in. Helps show some ideas for timing + # May want to modify to allow it to skip to the end if the user clicks. + # Otherwise plays for the full time given. + class FadeInText(renpy.Displayable): + def __init__(self, child, char_num, fade_time, slide_distance=100, **kwargs): + super(FadeInText, self).__init__(**kwargs) + + # The child. + self.child = child + self.fade_time = fade_time + self.display_time = .01 + self.slide_distance = slide_distance + # This is to get seconds per character on screen for later + # Allowing this effect to scale with the player's desired text speed + cps = 0.0 + if preferences.text_cps is not 0: # Avoid division by 0.0 + cps = (1.0 / preferences.text_cps) + self.time_offset = char_num * cps # How long to wait before doing things + + def render(self, width, height, st, at): + curr_alpha = 0.0 + xoff = 5.0 + if st > self.time_offset: + adjust_st = st - self.time_offset # Adjust for time delay + curr_alpha = adjust_st/self.fade_time + xoff = max(self.slide_distance - ((adjust_st/self.fade_time) * self.slide_distance), 0) + # Example of using transform to adjust alpha + t = Transform(child=self.child, alpha = curr_alpha) + child_render = renpy.render(t, width, height, st, at) + + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + render.subpixel_blit(child_render, (xoff, 0)) + # Stop redrawing when the animation is finished. + if st <= self.fade_time + self.time_offset: + renpy.redraw(self, 0) + return render + + def visit(self): + return [ self.child ] + + # Simple random motion effect + class ScareText(renpy.Displayable): + def __init__(self, child, square=2, **kwargs): + super(ScareText, self).__init__(**kwargs) + + self.child = child + + self.square = square # The size of the square it will wobble within. + # Include more variables if you'd like to have more control over the positioning. + + def render(self, width, height, st, at): + # Randomly move the offset of the text's render. + xoff = (random.random()-.5) * float(self.square) + yoff = (random.random()-.5) * float(self.square) + + child_render = renpy.render(self.child, width, height, st, at) + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + + render.subpixel_blit(child_render, (xoff, yoff)) + renpy.redraw(self, 0) + return render + + def visit(self): + return [ self.child ] + + # Demonstration of changing text styles on the fly + # Could also predefine some styles and swap between those as well! + # Also for this effect in particular, I ---HIGHLY--- advise building in some way to disable it + # as it can be pretty harsh on the eyes. + # An example of how you can make this a preference option is included below. + class ChaosText(renpy.Displayable): + # Some may want to have this list be more of a global variable than baked into the class. + font_list = ["FOT-PopJoyStd-B.otf", "GrenzeGotisch-VariableFont_wght.ttf", "Pacifico-Regular.ttf", "RobotoSlab-ExtraBold.ttf",\ + "RobotoSlab-Medium.ttf", "SyneTactile-Regular.ttf", "TurretRoad-Bold.ttf", "TurretRoad-ExtraBold.ttf", "TurretRoad-ExtraLight.ttf", \ + "TurretRoad-Light.ttf", "TurretRoad-Medium.ttf", "TurretRoad-Regular.ttf"] + #Just a list so we can pull any hex value randomly + color_choice = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"] + def __init__(self, orig_text, **kwargs): + + super(ChaosText, self).__init__(**kwargs) #REMEMBER TO RENAME HERE TO YOUR CLASS + + # Create our child + self.child = renpy.text.text.Text(orig_text) + self.orig_text = orig_text + self.last_style = None # This will be used for renders if the user wants to stop chaos text + + def render(self, width, height, st, at): + if not preferences.chaos_on: # This preference is defined near the top of this file. And can be set in the preferences screen (see line 783-787 in screens.rpy) + if self.last_style is not None: # If this is our first render, then should do that first + # Rest of this is just a repeat of what's below. + self.child.set_text(self.last_style.apply_style(self.orig_text)) + child_render = renpy.render(self.child, width, height, st, at) + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + render.subpixel_blit(child_render, (0, 0)) + return render + + # We'll create a new text style for this render + new_style = DispTextStyle() + new_color = "" + # Create a random color using hex values + for i in range(0,6): + new_color += renpy.random.choice(self.color_choice) + new_color = "#" + new_color + new_style.add_tags("color=" + str(new_color)) + # Random size + rand_size = renpy.random.randint(0,50) + new_style.add_tags("size="+str(rand_size)) + # Random font + rand_font = renpy.random.choice(self.font_list) + new_style.add_tags("font="+rand_font) + #Apply our style to our Text child + self.child.set_text(new_style.apply_style(self.orig_text)) + # Create a render from the child. + child_render = renpy.render(self.child, width, height, st, at) + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + render.subpixel_blit(child_render, (0, 0)) + renpy.redraw(self,0) + + self.last_style = new_style # Save the current style for if the user wishes to turn off the Chaos tag + return render + + def visit(self): + return [ self.child ] + + # Demonstration of using a Transform on the text and applying rotation + class RotateText(renpy.Displayable): + def __init__(self, child, speed=300, **kwargs): + super(RotateText, self).__init__(**kwargs) + + self.child = child + + self.speed = speed # The speed of our rotation + + def render(self, width, height, st, at): + + theta = math.radians(st * float(self.speed)) + t = Transform(child=self.child, rotate=st*float(self.speed)) + child_render = renpy.render(t, width, height/2, st, at) + + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height/2) + + # Problem with using a Transform though is that each character will be padded + # Because the rotation may make it wider or taller depending on the character and angle. + # How best to tackle this though may vary depending on how you'd like to implement it. + render.blit(child_render, (0,0)) + renpy.redraw(self, 0) + return render + + def visit(self): + return [ self.child ] + + # The following is an alternative version of rotate that allows for rotation in the x and y axis + # Functionally equivalent to using a Transform and flipping it using ATL xzoom and yzoom constrained between 0 and 1 + # Using a Transform might be better in some cases, but I'll leave this here for anyone who'd prefer to work with angles + # for this kind of effect. + # Other matrix functions of note include + # renpy.display.matrix.perspective(w,h,n,p,f) + # renpy.display.matrix.screen_projection(w,h) < Renpy space to OpenGL viewport + # renpy.display.matrix.texture_projection(w,h) < Renpy space to OpenGL render-to-texture + # You can look up more about them in the renpy\display\matrix_functions.pyx file + # Credit to the FancyText module creator yukinogatari for the idea. + # FancyText module can be found at https://lemmasoft.renai.us/forums/viewtopic.php?f=51&t=59587 + """ + class RotateText(renpy.Displayable): + def __init__(self, child, speed=100, **kwargs): + super(RotateText, self).__init__(**kwargs) + + self.child = child + self.speed = speed # The speed of our rotation + + def render(self, width, height, st, at): + angle = st * self.speed + # Which parameter you put the 'angle' into will affect which axis the render rotates on. + # Try moving it around and seeing what happens. + rotation_m = renpy.display.matrix.rotate(angle,0,0) + + child_render = renpy.render(self.child, width, height, st, at) + c_width, c_height = child_render.get_size() + # This applies the rotation to our child's render. + child_render.reverse = rotation_m + + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + + # Math nerds might realize I'm not offsetting the transform. + # While renpy.display.matrix.offset(x,y,z) is a thing, it won't change much + # The real place to apply the offset is in your final blit. Which is what we'll calculate here + + # Rotations on x axis + theta2 = math.radians(st * float(self.speed) + 180) + c = math.cos(theta2) + 1.0 + xoff = 0 + yoff = c * self.height + if yoff > self.height: + yoff = self.height + + render.subpixel_blit(child_render, (xoff,yoff)) + renpy.redraw(self, 0) + return render + + def visit(self): + return [ self.child ] + """ + + # Simple text swap effect + # It can be prone to having letters out of place when part of a larger string + # I recommended you pass it the entire line to avoid this issue. + # Can also just define every line it'll need in advance and just tell it which + # ones to swap to to be extra sneaky. Then the text won't be in your script at all! + class SwapText(renpy.Displayable): + def __init__(self, start_tags, text1, text2, end_tags, swap_time, **kwargs): + super(SwapText, self).__init__(**kwargs) + #Style tags we'll need as well as the text + self.start_tags = start_tags + self.text1 = text1 + self.text2 = text2 + self.end_tags = end_tags + # How long between swapping text + self.s_time = swap_time + # An internal timer to keep track of when to swap + self.timer = 0.0 + # Determines if we swap to text1 or text2 next + self.swap_to_1 = False + self.child = Text(start_tags + text1 + end_tags) + self.st = 0.0 + + + def render(self, width, height, st, at): + delta = st - self.st # How long since last update + self.timer += delta + if self.timer > self.s_time: + # If time to swap, determine which one to swap to. + if self.swap_to_1: + self.child.set_text(self.start_tags + self.text1 + self.end_tags) + self.swap_to_1 = False + self.timer = 0.0 + else: + self.child.set_text(self.start_tags + self.text2 + self.end_tags) + self.swap_to_1 = True + self.timer = 0.0 + + child_render = renpy.render(self.child, width, height, st, at) + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + render.subpixel_blit(child_render, (0,0)) + renpy.redraw(self, 0) + + self.st = st # So we can check how long since last update + return render + + def visit(self): + return [ self.child ] + + # An example of text that moves and reacts to the mouse. + # Sidenote: The position the mouse is distorted if the screen is resized. + # I did try to find a way to counteract this, but didn't have much luck. + # Seems to only happen on the x component though. No clue why. + # If anyone can pinpoint the issue, please let me know and I'll be happy to fix it. + class MoveText(renpy.Displayable): + def __init__(self, child, **kwargs): + super(MoveText, self).__init__(**kwargs) + self.affect_distance = 150 + self.child = child + self.mouse_pos = (1000,1000) + self.pos = (0,0) + + def render(self, width, height, st, at): + child_render = renpy.render(self.child, width, height, st, at) + self.width, self.height = child_render.get_size() + render = renpy.Render(self.width, self.height) + # x and y we get in the event function are relative to the top left corner of the displayable initially. + # So we'll want to update it to reflect the actual position of our text + trans_x = self.mouse_pos[0] - self.pos[0] - (self.width / 2) + trans_y = self.mouse_pos[1] - self.pos[1] - (self.height / 2) + + vl = math.hypot(trans_x,trans_y) + xpos, ypos = self.pos + # Can skip calculation if vector length is further than our specified effect distance + if vl < self.affect_distance: + distance = 3.0 * (self.affect_distance-vl) / self.affect_distance + xpos -= distance * trans_x / vl + ypos -= distance * trans_y / vl + self.pos = (xpos, ypos) # Preserve the new pos + # Use our child's position as determined by the event function + render.subpixel_blit(child_render, (xpos, ypos)) + renpy.redraw(self, 0) + return render + + def event(self, ev, x, y, st): + self.mouse_pos = (x,y) + # Pass the event to our child. + return self.child.event(ev, x, y, st) + + def visit(self): + return [ self.child ] + + + ### CUSTOM TAG FUNCTIONS ### + # Letters move in a sine wave. + # Arguments are separated by dashes. + # Arguments: + # 'a': (int) The amplitude (height) of the text's sine wave motion. How high and low it'll go from it's default position in pixels. + # 'p': (float) The period of the wave. Distance between peaks in the wave. + # 's': (float) The speed of the wave. How fast it moves with time. + # Example: {bt=[height]}Text{/bt} + # Example: {bt=h5-p2.0-s0.5}Text{/bt} + # If a lone number is given, it is treated as the amplitude only to ensure backwards compatibility + # Example: {bt=10}Text{/bt} + def bounce_tag(tag, argument, contents): + new_list = [ ] # The list we will be appending our displayables into + amp, period, speed = 20, 4.0, 1.0 + if argument == "": # If the argument received is blank, insert a default value + amp = 20 + else: + argument = argument.split('-') + if len(argument) == 1 and argument[0][0].isdigit(): # Default behavior to ensure backward compatibility + amp = int(argument[0]) + else: + for arg in argument: + if arg[0] == 'a': + amp = int(arg[1:]) + elif arg[0] == 'p': + period = float(arg[1:]) + elif arg[0] == 's': + speed = float(arg[1:]) + + char_offset = 0 # Since we want our text to move in a wave, + # we want to let each character know where it is in the wave. + # So they move in harmony. Otherwise they rise and fall all together. + my_style = DispTextStyle() # This will keep track of what tags and styling to add to each letter + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: # Extract every character from the string + char_text = Text(my_style.apply_style(char)) # Create a Text displayable with our styles applied + char_disp = BounceText(char_text, char_offset, amp=amp, period=period, speed=speed) # Put the Text into the Wrapper + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) # Add it back in as a displayable + char_offset += 1 + elif kind == renpy.TEXT_TAG: + if text.find("image") != -1: + tag, _, value = text.partition("=") + my_img = renpy.displayable(value) + img_disp = BounceText(my_img, char_offset, amp=amp, period=period, speed=speed) + new_list.append((renpy.TEXT_DISPLAYABLE, img_disp)) + char_offset += 1 + elif not my_style.add_tags(text): + new_list.append((kind, text)) + # I honestly never got around to testing this. Not often the text + # already has a displayable in it. Let me know if it breaks though. + elif kind == renpy.TEXT_DISPLAYABLE: + char_disp = BounceText(text, char_offset, amp=amp, period=period, speed=speed) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + char_offset += 1 + else: # Don't touch any other type of content + new_list.append((kind,text)) + + return new_list + + # Letters will start off to the right & invisible. And will then move left while increasing their opacity. Good for meditation and calm text. + # offset: (int) Offset within the line. Needed to help time start of fade-in with other slow text characters. + # time: (float) How long in seconds the animation lasts. + # distance: (int) How many pixels the fade in occurs across + # Example: {fi=[offset]-[time]-[distance]}Text{/fi} + def fade_in_tag(tag, argument, contents): + new_list = [ ] + my_index, fade_time, slide_distance = 0, 5.0, 100 + if argument != "": + argument = argument.split('-') + if len(argument) > 0: + my_index = int(argument[0]) + if len(argument) > 1: + fade_time = float(argument[1]) + if len(argument) > 2: + slide_distance = int(argument[2]) + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + if char == ' ': + new_list.append((renpy.TEXT_TEXT, ' ')) # Skips blank space since looks weird counting it + continue + char_text = Text(my_style.apply_style(char)) + char_disp = FadeInText(char_text, my_index, fade_time, slide_distance) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + my_index += 1 + elif kind == renpy.TEXT_TAG: + if text.find("image") != -1: + tag, _, value = text.partition("=") + my_img = renpy.displayable(value) + img_disp = FadeInText(my_img, my_index, fade_time, slide_distance) + new_list.append((renpy.TEXT_DISPLAYABLE, img_disp)) + my_index += 1 + elif not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + return new_list + + # Letters change position every frame randomly. Good for very angry or quivering dialogue. + # range: (int) Letters are confined to a square around their default location. Range determines length of the sides of that square. + # Higher values will make it very chaotic while smaller values will make it quite minimal. + # Example: {sc=[range]}Text{/sc} + def scare_tag(tag, argument, contents): + new_list = [ ] + if argument == "": + argument = 5 + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + char_text = Text(my_style.apply_style(char)) + char_disp = ScareText(char_text, argument) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if text.find("image") != -1: + tag, _, value = text.partition("=") + my_img = renpy.displayable(value) + img_disp = ScareText(my_img, argument) + new_list.append((renpy.TEXT_DISPLAYABLE, img_disp)) + elif not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + + return new_list + + # Letters change their font, color and size every frame. + # Example: {chaos}Text{/chaos} + # Honestly more a demonstration of what can be done than useful in it's own right. + # If you create tags this chaotic, please include a way to turn it off for people with epilepsy. + def chaos_tag(tag, argument, contents): + new_list = [ ] + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + char_disp = ChaosText(my_style.apply_style(char)) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + + return new_list + + # Letters rotate in place. Good for stylized intros or UI + # Speed: (int) How fast the rotation will be. + # Example: {rotat=[speed]}Text{/rotat} + def rotate_tag(tag, argument, contents): + new_list = [ ] + # Argument here will reprsent the desired speed of the rotation. + if argument == "": + argument = 400 + else: + argument = int(argument) + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + char_text = Text(my_style.apply_style(char)) + char_disp = RotateText(char_text, argument) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if text.find("image") != -1: + tag, _, value = text.partition("=") + my_img = renpy.displayable(value) + img_disp = RotateText(my_img, argument) + new_list.append((renpy.TEXT_DISPLAYABLE, img_disp)) + elif not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + + return new_list + + # Causes letters to change between two strings every couple of seconds. + # text1: (String) First set of characters to display. Should be equal to the length of the characters we're replacing + # text2: (String) Second set of characters to display. Should be equal to the length of text1 + # swap_time: (int) Length of time between character swap + # Arguments are separated by '@'. Length of strings should not exceed length of text they are replacing. + # Example: {swap=Text@Four@0.5}Text{} + # This is a pretty static way of doing it mostly made to demonstrate the concept. + # Included for others to build upon for their needs. + def swap_tag(tag, argument, contents): + new_list = [ ] + if argument == "": + return contents + text1, _, argument = argument.partition("@") + text2, _, argument = argument.partition("@") + if len(text1) != len(text2): + new_list.append((renpy.TEXT_TEXT, "ERROR!")) + swap_time = float(argument) + + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + # This one replaces the whole text rather than extracting over letters + # That way it can take up this whole block with its own Text displayable + char_disp = SwapText(my_style.start_tags(), text1, text2, my_style.end_tags(), swap_time) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + return new_list + + # Makes it so the text within moves away from the mouse. More example of what can be done than useful + # Example: {move}Text{/move} + def move_tag(tag, argument, contents): + new_list = [ ] + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + char_text = Text(my_style.apply_style(char)) + char_disp = MoveText(char_text) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if text.find("image") != -1: + tag, _, value = text.partition("=") + my_img = renpy.displayable(value) + img_disp = MoveText(my_img) + new_list.append((renpy.TEXT_DISPLAYABLE, img_disp)) + elif not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + return new_list + + # Some text effects won't allow for a paragraph break if applied to a whole line + # Which can cause your text to just continue straight off the screen. + # To amend this, you can insert the {para} tag. + # This will let the Text displayable holding us know when to wrap. + # Can also use \n in most cases. But leaving this for people who may already be using it + # or for cases where \n doesn't work. + def paragraph_tag(tag, argument): + return [(renpy.TEXT_PARAGRAPH, "")] + + # This tag is made to automatically wrap several Classes inside one another + # This is to reduce strain on the render pipeline and memory from nested classes + # Notes: + # GradientText and GlitchText are omitted because they were made after the 1.0 release. + # SwapText and MoveText are omitted for possible issues. + # SwapText because is not included in this due to it replacing whole sections rather than + # individual letters. Would be better to embed an Omega inside a SwapText. + # MoveText because of potential issues of having things like BounceText affect + # affecting the position of the letter visually. + # Would be better to have an event call attached to one of those so it can account + # for the transformations of other tags + # Argument Notes (all tag args accept same arguments as original tag): + # BT: BounceText + # SC: ScareText + # FI: FadeInText + # ROT: RotateText + # CH: ChaosText + # All tag arguments are seperated by @. + # Example: {omega=BT=[bt_arg]@SC=[sc_arg]@FI=[fi_arg1]-[fi_arg2]@ROT=[rot_arg]@CH}Text{/omega} + def omega_tag(tag, argument, contents): + new_list = [ ] + if argument == "": # This tag must have arguments + return contents + # Variable for each of our tags. None if it takes one argument. + # Boolean if 0 or many arguments. + bt_tag = None + sc_tag = None + fi_tag = False + rot_tag = None + chao_tag = False + fi_arg_1 = None + fi_arg_2 = None + + args = [ ] + arg_count = argument.count('@') # Count how many partitions we will need to make + for x in range(arg_count): # Extract all the tags and arguments with them + new_arg, _, argument = argument.partition('@') + args.append(new_arg) + args.append(argument) + # Determine what tags we'll need to apply and the arguments associated with them + for arg in args: + tag, _, value = arg.partition('=') + if tag == "BT": + if value is not "": + bt_tag = value + else: + bt_tag = 10 + elif tag == "SC": + if value is not "": + bt_tag = value + else: + bt_tag = 5 + # Multiargument tag example. Be sure to use different partitions for these + elif tag == "FI": + fi_tag = True + str1, _, str2 = value.partition('-') + fi_arg_1 = int(str1) + fi_arg_2 = float(str2) + elif tag == "ROT": + rot_tag = value + elif tag == "CH": + chao_tag = True + + my_style = DispTextStyle() + my_index = 0 # Some Classes will need an index + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + # Apply base Wrappers to letter + if chao_tag: + char_disp = ChaosText(my_style.apply_style(char)) + else: + char_disp = Text(my_style.apply_style(char)) + # Apply further Wraps + # Be sure to consider if the order will be important to you + if bt_tag is not None: + char_disp = BounceText(char_disp, my_index, bt_tag) + if sc_tag is not None: + char_disp = ScareText(char_disp, sc_tag) + if fi_tag: + char_disp = FadeInText(char_disp, my_index + fi_arg_1, fi_arg_2) + if rot_tag is not None: + char_disp = RotateText(char_disp, rot_tag) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + + return new_list + + """ + # Template tag function to copy off of. + def TEMPLATE_tag(tag, argument, contents): + new_list = [ ] + if argument == "": + argument = 5 + my_style = DispTextStyle() + for kind,text in contents: + if kind == renpy.TEXT_TEXT: + for char in text: + char_text = Text(my_style.apply_style(char)) + char_disp = TEMPLATEText(char_text, argument) + new_list.append((renpy.TEXT_DISPLAYABLE, char_disp)) + elif kind == renpy.TEXT_TAG: + if not my_style.add_tags(text): + new_list.append((kind, text)) + else: + new_list.append((kind,text)) + return new_list + """ + + # Define our new text tags + config.custom_text_tags["bt"] = bounce_tag + config.custom_text_tags["fi"] = fade_in_tag + config.custom_text_tags["sc"] = scare_tag + config.custom_text_tags["rotat"] = rotate_tag + config.custom_text_tags["chaos"] = chaos_tag + config.custom_text_tags["swap"] = swap_tag + config.custom_text_tags["move"] = move_tag + config.custom_text_tags["omega"] = omega_tag + config.self_closing_custom_text_tags["para"] = paragraph_tag + # Template tag function + #config.custom_text_tags[""] = _tag diff --git a/game/optional files/adjust_attributes.rpy b/game/optional files/adjust_attributes.rpy new file mode 100644 index 0000000..6662caa --- /dev/null +++ b/game/optional files/adjust_attributes.rpy @@ -0,0 +1,54 @@ +## Adjust Attributes ########################################################### +## +## This is a special configuration value which can be used to easily create +## shorthand for layered images. This code is adapted slightly from Ren'Py Tom's +## article on the topic: +## https://patreon.renpy.org/dev-2021-04.html#adjust-attribute-example +## +## You can learn more about config.adjust_attributes here: +## https://www.renpy.org/doc/html/config.html#var-config.adjust_attributes +## +## As per usual, if you do not need it, you may freely remove this file. +## + +init -100 python: + + class Aliases(object): + """ + Expands attributes into other attributes. + """ + + def __init__(self, **aliases): + + # A map from an attribute name to a tuple of + # attributes it expands to. + self.aliases = { } + + for k, v in aliases.items(): + self.aliases[k] = tuple(v.split()) + + def __call__(self, name): + + # The image tag + rv = [ name[0] ] + + # The remaining attributes + for i in name[1:]: + ## Also remove the provided attributes, if negated + if i.startswith("-"): + prefix = "-" + i = i[1:] + else: + prefix = "" + + for attr in self.aliases.get(i, ( i, )): + rv.append(prefix + attr) + + # Turn the results back into a tuple + return tuple(rv) + +## A possible use case: +# define config.adjust_attributes['eileen'] = Aliases( +# happy="eyes_happy mouth_happy", +# concerned="eyes_concerned mouth_concerned", +# ) \ No newline at end of file diff --git a/game/optional files/afm_indicator.rpy b/game/optional files/afm_indicator.rpy new file mode 100644 index 0000000..f3c4f10 --- /dev/null +++ b/game/optional files/afm_indicator.rpy @@ -0,0 +1,68 @@ + +## Auto indicator screen ####################################################### +## +## This screen is used to indicate that auto-forward mode is in progress. +## Created by me, Feniks. You may remove this whole file if you don't need +## an auto-forward indicator screen. +## + +init python: + def auto_indicator(): + """ + A function which, when called, determines if the Auto indicator + should be shown on-screen or not. + """ + + # Auto mode is on + if preferences.afm_enable and not renpy.get_screen('auto_indicator'): + renpy.show_screen('auto_indicator') + # Auto mode is off + elif not preferences.afm_enable and renpy.get_screen('auto_indicator'): + renpy.hide_screen('auto_indicator') + + return + + # This adds the auto indicator to a list of overlay functions + # so that it can automatically show the Auto indicator. + if auto_indicator not in config.overlay_functions: + config.overlay_functions.append(auto_indicator) + +screen auto_indicator(): + + zorder 100 + style_prefix "auto" + + frame: + has hbox + + text _("Auto-Forward") + + text "▸" at auto_blink(1.0) style "skip_triangle" + +## This transform is used to blink the arrows one after another. +transform auto_blink(cycle): + alpha 0.0 + linear 0.5 alpha 1.0 + pause 0.2 + linear 0.5 alpha 0.0 + pause (cycle - .4) + repeat + +style auto_hbox: + spacing 9 + +style auto_frame: + is empty + ypos 15 + background Frame("#0008", 24, 8, 75, 8, tile=False) + padding (24, 8, 75, 8) + +style auto_text: + size 24 + +style auto_triangle: + is auto_text + ## We have to use a font that has the BLACK RIGHT-POINTING SMALL TRIANGLE + ## glyph in it. + font "DejaVuSans.ttf" + diff --git a/game/optional files/confirm_action.rpy b/game/optional files/confirm_action.rpy new file mode 100644 index 0000000..64ff822 --- /dev/null +++ b/game/optional files/confirm_action.rpy @@ -0,0 +1,98 @@ +## Custom Confirm Action ####################################################### +## +## This file contains an action similar to the Confirm screen action which +## it can be used for information and confirmation prompts. +## It can be removed if unneeded. In order to work, it requires that +## `no_action=None` on the confirm screen so that a second action is optional +## (this is the case by default for this template). +## You may remove this file without consequence. +## See the original Confirm action here: +## https://www.renpy.org/doc/html/screen_actions.html#Confirm +## +## It has three main use cases: +## 1) CConfirm("You haven't unlocked this image yet.") +## This shows a prompt to the user with the provided text and a +## "Confirm" button to dismiss the prompt. There is no "Cancel" button. +## 2) CConfirm("Purchase flower? ($10)", SetVariable('money', money-10)) +## This shows a prompt with Confirm and Cancel buttons. The Confirm +## button dismisses the prompt and executes the action or list of +## actions provided after the prompt, and Cancel hides the prompt without +## executing any other actions. +## 3) CConfirm("Go to the next chapter?", yes=Jump("chapter2"), no=MainMenu()) +## This shows a prompt with Confirm and Cancel buttons. Clicking either +## button will dismiss the prompt in addition to performing the provided +## yes/no action. +## + +init python: + + class CConfirm(Show): + """ + A class which makes it easy to show simple confirmation prompts + to the player. + + It also sets the default value of confirm_selected to True rather + than False. + """ + def __init__(self, prompt, yes=None, no=None, confirm_selected=True, + *args, **kwargs): + + if config.confirm_screen and renpy.has_screen('confirm'): + screen = "confirm" + elif renpy.has_screen("yesno_prompt"): + screen = "yesno_prompt" + else: + screen = None + + # Just a prompt; this only gets a Confirm button which + # dismisses the prompt. + if yes is None: + yes = Hide(screen, config.exit_yesno_transition) + no = None + else: + # All provided actions should hide the confirm screen + # after they are clicked + if isinstance(yes, list): + yes.insert(0, Hide(screen, config.exit_yesno_transition)) + elif yes != Hide(screen, config.exit_yesno_transition): + yes = [Hide(screen, config.exit_yesno_transition), yes] + + # Has both buttons, but "Cancel" should just hide the prompt + if no is None: + no = Hide(screen, config.exit_yesno_transition) + elif no is not None: + if isinstance(no, list): + no.insert(0, Hide(screen, config.exit_yesno_transition)) + elif no != Hide(screen, config.exit_yesno_transition): + no = [Hide(screen, config.exit_yesno_transition), no] + + self.prompt = prompt + self.yes = yes + self.no = no + self.confirm_selected = confirm_selected + self.screen = screen + + super(CConfirm, self).__init__(screen, config.exit_yesno_transition, + *args, message=self.prompt, yes_action=self.yes, + no_action=self.no, **kwargs) + + def get_sensitive(self): + if self.yes is None: + return False + + return renpy.is_sensitive(self.yes) + + def get_selected(self): + return renpy.is_selected(self.yes) + + def get_tooltip(self): + return renpy.display.behavior.get_tooltip(self.yes) + + def __call__(self): + + if self.screen is None: + return Confirm(self.prompt, self.yes, self.no, self.confirm_selected)() + elif self.confirm_selected or not self.get_selected(): + return super(CConfirm, self).__call__() + else: + return renpy.run(self.yes) \ No newline at end of file diff --git a/game/optional files/gallery.rpy b/game/optional files/gallery.rpy new file mode 100644 index 0000000..1671418 --- /dev/null +++ b/game/optional files/gallery.rpy @@ -0,0 +1,76 @@ +## Gallery ##################################################################### +## +## A basic setup for a gallery screen, using Ren'Py's built-in Gallery +## system. More information here: +## https://www.renpy.org/doc/html/rooms.html#image-gallery +## + +init python: + + ## First, some constants to speed up declarations + + ## The size of gallery buttons/thumbnails + gallery_thumb_size = (400, 225) + ## For convenience's sake: list off all the gallery image + ## names we're going to use in this gallery + gallery_buttons = [ + 'xia_cg_1', 'ashwin_cg_1', 'zoran_cg_1' + ] + + ## Set up the gallery + g = Gallery() + g.locked_button = Transform("#333", xysize=gallery_thumb_size) + + ## And declare the various gallery images + ## This file doesn't assume the presence of any GUI files, so I'm + ## just using basic squares, declared as images below, but you will + ## replace these with actual images. + ## These use the names declared in the gallery_buttons list + g.button("xia_cg_1") + g.unlock_image("cg xia1") + + g.button("ashwin_cg_1") + g.unlock_image("cg ashwin1") + + g.button("zoran_cg_1") + g.unlock_image("cg zoran1") + +## Declarations for the images used in the gallery. May or may not +## be needed if you're using Ren'Py's automatic image names. +image cg xia1 = Transform("#bd580a", xysize=(config.screen_width, config.screen_height)) +image cg ashwin1 = Transform("#127151", xysize=(config.screen_width, config.screen_height)) +image cg zoran1 = Transform("#8157b9", xysize=(config.screen_width, config.screen_height)) + +## This is just the button name + _thumb to make it easier to iterate +image xia_cg_1_thumb = Transform("#bd580a", xysize=gallery_thumb_size) +image ashwin_cg_1_thumb = Transform("#127151", xysize=gallery_thumb_size) +image zoran_cg_1_thumb = Transform("#8157b9", xysize=gallery_thumb_size) + +screen gallery(): + + tag menu + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + use game_menu(_("Gallery")) + + + fixed: + style_prefix 'gal' + ## Organize the gallery images into a grid + grid 2 2: + for btn in gallery_buttons: + add g.make_button(btn, "{}_thumb".format(btn)) + ## If you're not using the loop, this will look instead like: + # add g.make_button("button_name", "button_thumbnail.png") + +style gal_fixed: + yfill True + xsize config.screen_width-420 + align (1.0, 0.5) + +style gal_grid: + align (0.5, 0.5) + xsize config.screen_width-420 + ysize config.screen_height-200 + spacing 50 \ No newline at end of file diff --git a/game/optional files/mobile_input.rpy b/game/optional files/mobile_input.rpy new file mode 100644 index 0000000..d1064fc --- /dev/null +++ b/game/optional files/mobile_input.rpy @@ -0,0 +1,62 @@ +## Mobile Input ################################################################ +## +## This is a custom InputValue which does not begin as selected/ready for +## input and requires an action to be enabled. Pressing the Enter button +## will disable the input again. +## +## This makes it a good choice for most custom input screens, particularly +## on mobile devices where the keyboard can take up most of the screen space. +## +## As per usual, this file may be removed without consequence. +## +## Read more about InputValue here: +## https://www.renpy.org/doc/html/screen_python.html#inputvalue +## +init python: + + class EnterInputValue(FieldInputValue): + """ + Subclass of InputValue which allows the Enter key to dismiss + the input button. Does not begin as selected (so, on mobile the + keyboard won't immediately appear). + """ + + def __init__(self, object, field, default=False): + self.object = object + self.field = field + + self.default = default + + def enter(self): + """Disable this input when the user presses Enter.""" + renpy.run(self.Disable()) + raise renpy.IgnoreEvent() + +default demo_name = "Feniks" + +## An example screen using EnterInputValue +screen name_input_screen(): + + ## The "object" here is `store` since it's a regular store variable. + ## If the variable was persistent.name, it would use `persistent` instead. + default name_input = EnterInputValue(store, 'demo_name') + + add "#601249bb" + + vbox: + align (0.5, 0.5) + spacing 25 + button: + background "#000b" + hover_background "#0003" + # Ensure you can type without needing to hover this button + key_events True + # Enable the input + action name_input.Toggle() + has hbox + spacing 25 + text "Name:" + # The actual input, which uses the EnterInputValue earlier + input value name_input + + textbutton "Done" action Return() xalign 0.5 \ No newline at end of file diff --git a/game/optional files/special_labels.rpy b/game/optional files/special_labels.rpy new file mode 100644 index 0000000..bd76b2e --- /dev/null +++ b/game/optional files/special_labels.rpy @@ -0,0 +1,21 @@ +## Special Labels ############################################################## +## +## These are special labels that Ren'Py automatically recognizes if they +## are included with the game. Read more here: +## https://www.renpy.org/doc/html/label.html#special-labels +## + +## Splash Screen ############################################################### +## +## Put the splash screen code here. It runs when the game is launched. +## +label splashscreen(): + return + +## After Load ################################################################## +## +## Adjust any variables etc in the after_load label +## Also consider: define config.after_load_callbacks = [ ... ] +## +label after_load(): + return diff --git a/game/options.rpy b/game/options.rpy new file mode 100644 index 0000000..68e301f --- /dev/null +++ b/game/options.rpy @@ -0,0 +1,243 @@ +## 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. + +## TODO: Change these top three values (config.name, build.name, +## and config.save_directory) to something unique for your project! + +## 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 = _("Jam13Yuuka") + +## 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 = "Jam13Yuuka" + +## 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. + +## Note: a typical save_directory value looks like "FreshProject-1671818013" +define config.save_directory = "Jam13Yuuka-1256128491" + + +## The version of the game. + +define config.version = "1.0" + +## Text that is placed on the game's about screen. Place the text between the +## triple-quotes, and leave a blank line between paragraphs. + +define gui.about = _p("""A game created in 3 days for Touhou Fan Game Jam 13. + +By Jacoder23, Shyraku, Nanossis, and hermit_irl.""") + +# TODO: REPLACE THE ABOUT WITH NEW CREDITS WHEN POSSIBLE + +## Sounds and music ############################################################ + +## These three variables control, among other things, which mixers are shown +## to the player by default. Setting one of these to False will hide the +## appropriate mixer. + +define config.has_sound = True +define config.has_music = True +define config.has_voice = True + + +## To allow the user to play a test sound on the sound or voice channel, +## uncomment a line below and use it to set a sample sound to play. + +# define config.sample_sound = "sample-sound.ogg" +# define config.sample_voice = "sample-voice.ogg" + + +## Uncomment the following line to set an audio file that will be played while +## the player is at the main menu. This file will continue playing into the +## game, until it is stopped or another file is played. + +# define config.main_menu_music = "main-menu-theme.ogg" + + +## Transitions ################################################################# +## +## These variables set transitions that are used when certain events occur. +## Each variable should be set to a transition, or None to indicate that no +## transition should be used. + +## Entering or exiting the game menu. + +define config.enter_transition = dissolve +define config.exit_transition = dissolve + + +## Between screens of the game menu. + +define config.intra_transition = dissolve + + +## A transition that is used after a game has been loaded. + +define config.after_load_transition = None + + +## Used when entering the main menu after the game has ended. + +define config.end_game_transition = None + +## 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 = 40 + + +## The default auto-forward delay. Larger numbers lead to longer waits, with 0 +## to 30 being the valid range. + +default preferences.afm_time = 15 + +## Icon ######################################################################## +## +## The icon displayed on the taskbar or dock. + +define config.window_icon = "gui/window_icon.png" + +## Custom Options ############################################################## +## +## Config variables that I like to have set up. + +## Convenience for not crashing on grids without enough items +## https://www.renpy.org/doc/html/config.html#var-config.allow_underfull_grids +## In modern Ren'Py, this is already the default. +define config.allow_underfull_grids = True + +## Default volume % for the various volume sliders +## https://www.renpy.org/doc/html/preferences.html#audio-channel-defaults +define config.default_music_volume = 0.5 +define config.default_sfx_volume = 0.5 +define config.default_voice_volume = 0.5 + +## Optional; this reverts the behaviour of the volume sliders back to +## pre-8.1, so muting the game shows the volume sliders all at 0 +# define config.preserve_volume_when_muted = False + +## The number of auto save slots Ren'Py will save to before it +## starts overwriting the first one +define config.autosave_slots = 6 +## Same thing, but for quick save +define config.quicksave_slots = 6 + +## Build configuration ######################################################### +## +## This section controls how Ren'Py turns your project into distribution files. + +init python: + + ## The following functions take file patterns. File patterns are case- + ## insensitive, and matched against the path relative to the base directory, + ## with and without a leading /. If multiple patterns match, the first is + ## used. + ## + ## In a pattern: + ## + ## / is the directory separator. + ## + ## * matches all characters, except the directory separator. + ## + ## ** matches all characters, including the directory separator. + ## + ## For example, "*.txt" matches txt files in the base directory, "game/ + ## **.ogg" matches ogg files in the game directory or any of its + ## subdirectories, and "**.psd" matches psd files anywhere in the project. + + ## Classify files as None to exclude them from the built distributions. + + build.classify('**~', None) + build.classify('**.bak', None) + build.classify('**/.**', None) + build.classify('**/#**', None) + build.classify('**/thumbs.db', None) + build.classify('**.psd', None) + build.classify('game/cache/**', None) + ## NOTE: This excludes markdown and txt files. If you use these formats + ## for README or instructions, you may want to remove these lines. + build.classify('**.txt', None) + build.classify('**.md', None) + build.classify('**.rpy', None) + + ## To archive files, classify them as 'archive'. + + build.classify("game/**.rpy", "archive") + build.classify("game/**.rpym", "archive") + + build.classify("game/**.webp", "archive") + build.classify("game/**.webm", "archive") + build.classify("game/**.mp4", "archive") + build.classify("game/**.png", "archive") + build.classify("game/**.jpg", "archive") + build.classify("game/**.ttf", "archive") + build.classify("game/**.otf", "archive") + build.classify("game/**.mp3", "archive") + build.classify("game/**.wav", "archive") + build.classify("game/**.ogg", "archive") + build.classify("game/**.opus", "archive") + build.classify("game/**.rpyc", "archive") + build.classify("game/**.rpymc", "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 perform in-app purchases. It can be +## found in the Google Play developer console, under "Monetize" > "Monetization +## Setup" > "Licensing". + +# define build.google_play_key = "..." + + +## The username and project name associated with an itch.io project, separated +## by a slash. + +# define build.itch_project = "renpytom/test-project" diff --git a/game/screens/choice_screen.rpy b/game/screens/choice_screen.rpy new file mode 100644 index 0000000..aee41e4 --- /dev/null +++ b/game/screens/choice_screen.rpy @@ -0,0 +1,36 @@ + +## Choice screen ############################################################### +## +## This screen is used to display the in-game choices presented by the menu +## statement. The one parameter, items, is a list of objects, each with caption +## and action fields. +## +## https://www.renpy.org/doc/html/screen_special.html#choice + +screen choice(items): + style_prefix "choice" + + vbox: + for i in items: + textbutton i.caption action i.action + + +style choice_vbox: + xalign 0.5 + ypos 405 + yanchor 0.5 + spacing 33 + +style choice_button: + is default # This means it doesn't use the usual button styling + xysize (926, None) + background Frame("gui/button/choice_[prefix_]background.png", + 150, 25, 150, 25, tile=False) + padding (12, 12) + +style choice_button_text: + is default # This means it doesn't use the usual button text styling + xalign 0.5 yalign 0.5 + idle_color "#ccc" + hover_color "#fff" + insensitive_color "#444" diff --git a/game/screens/dialogue_screens.rpy b/game/screens/dialogue_screens.rpy new file mode 100644 index 0000000..7496f39 --- /dev/null +++ b/game/screens/dialogue_screens.rpy @@ -0,0 +1,284 @@ + +## Say screen ################################################################## +## +## The say screen is used to display dialogue to the player. It takes two +## parameters, who and what, which are the name of the speaking character and +## the text to be displayed, respectively. (The who parameter can be None if no +## name is given.) +## +## This screen must create a text displayable with id "what", as Ren'Py uses +## this to manage text display. It can also create displayables with id "who" +## and id "window" to apply style properties. +## +## https://www.renpy.org/doc/html/screen_special.html#say + +screen say(who, what): + style_prefix "say" + + window: + id "window" + + if who is not None: + + window: + id "namebox" + style "namebox" + text who id "who" + + text what id "what" + + ## If there's a side image, display it in front of the text. + add SideImage() xalign 0.0 yalign 1.0 + + +## Make the namebox available for styling through the Character object. +init python: + config.character_id_prefixes.append('namebox') + +# Style for the dialogue window +style window: + xalign 0.5 + yalign 1.0 + xysize (1231, 277) + padding (40, 10, 40, 40) + background Image("gui/textbox.png", xalign=0.5, yalign=1.0) + +# Style for the dialogue +style say_dialogue: + adjust_spacing False + ypos 60 + +# The style for dialogue said by the narrator +style say_thought: + is say_dialogue + +# Style for the box containing the speaker's name +style namebox: + xpos 20 + xysize (None, None) + background Frame("gui/namebox.png", 5, 5, 5, 5, tile=False, xalign=0.0) + padding (5, 5, 5, 5) + +# Style for the text with the speaker's name +style say_label: + color '#f93c3e' + xalign 0.0 + yalign 0.5 + size gui.name_text_size + font gui.name_text_font + + +## 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" + + 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 _("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_hbox: + xalign 0.5 + yalign 1.0 yoffset -8 + spacing 8 + +style quick_button: + background None + padding (15, 6, 15, 0) + +style quick_button_text: + size 21 + selected_color '#f93c3e' + idle_color "#aaa" + +## NVL screen ################################################################## +## +## This screen is used for NVL-mode dialogue and menus. +## +## https://www.renpy.org/doc/html/screen_special.html#nvl + + +screen nvl(dialogue, items=None): + + window: + style "nvl_window" + + has vbox + spacing 15 + + 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 True + + 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 + +# The style for the NVL "textbox" +style nvl_window: + is default + xfill True yfill True + background "gui/nvl.png" + padding (0, 15, 0, 30) + +# The style for the text of the speaker's name +style nvl_label: + is say_label + xpos 645 xanchor 1.0 + ypos 0 yanchor 0.0 + xsize 225 + min_width 225 + textalign 1.0 + +# The style for dialogue in NVL +style nvl_dialogue: + is say_dialogue + xpos 675 + ypos 12 + xsize 885 + min_width 885 + +# The style for dialogue said by the narrator in NVL +style nvl_thought: + is nvl_dialogue + +style nvl_button: + xpos 675 + xanchor 0.0 + + +## Bubble screen ############################################################### +## +## The bubble screen is used to display dialogue to the player when using speech +## bubbles. The bubble screen takes the same parameters as the say screen, must +## create a displayable with the id of "what", and can create displayables with +## the "namebox", "who", and "window" ids. +## +## https://www.renpy.org/doc/html/bubble.html#bubble-screen + +screen bubble(who, what): + style_prefix "bubble" + + window: + id "window" + + if who is not None: + + window: + id "namebox" + style "bubble_namebox" + + text who: + id "who" + + text what: + id "what" + +style bubble_window: + is empty + xpadding 30 + top_padding 5 + bottom_padding 5 + +style bubble_namebox: + is empty + xalign 0.5 + +style bubble_who: + is default + xalign 0.5 + textalign 0.5 + color "#000" + +style bubble_what: + is default + align (0.5, 0.5) + text_align 0.5 + layout "subtitle" + color "#000" + +define bubble.frame = Frame("gui/bubble.png", 55, 55, 55, 95) +define bubble.thoughtframe = Frame("gui/thoughtbubble.png", 55, 55, 55, 55) + +define bubble.properties = { + "bottom_left" : { + "window_background" : Transform(bubble.frame, xzoom=1, yzoom=1), + "window_bottom_padding" : 27, + }, + + "bottom_right" : { + "window_background" : Transform(bubble.frame, xzoom=-1, yzoom=1), + "window_bottom_padding" : 27, + }, + + "top_left" : { + "window_background" : Transform(bubble.frame, xzoom=1, yzoom=-1), + "window_top_padding" : 27, + }, + + "top_right" : { + "window_background" : Transform(bubble.frame, xzoom=-1, yzoom=-1), + "window_top_padding" : 27, + }, + + "thought" : { + "window_background" : bubble.thoughtframe, + } +} + +define bubble.expand_area = { + "bottom_left" : (0, 0, 0, 22), + "bottom_right" : (0, 0, 0, 22), + "top_left" : (0, 22, 0, 0), + "top_right" : (0, 22, 0, 0), + "thought" : (0, 0, 0, 0), +} diff --git a/game/screens/game_menu.rpy b/game/screens/game_menu.rpy new file mode 100644 index 0000000..111586d --- /dev/null +++ b/game/screens/game_menu.rpy @@ -0,0 +1,87 @@ +## 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 title and navigation. +## +## This screen no longer includes a background, and it no longer transcludes +## its contents. It is intended to be easily removable from any given menu +## screen and thus you are required to do some of the heavy lifting for +## setting up containers for the contents of your menu screens. +## + +screen game_menu(title): + + style_prefix "game_menu" + + vbox: + xpos 60 yalign 0.5 + spacing 6 + + 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) + + textbutton _("Return"): + style "return_button" + action Return() + + ## Remove this line if you don't want to show the screen + ## title text as a label (for example, if it's baked into + ## the background image.) + label title + + if main_menu: + key "game_menu" action ShowMenu("main_menu") + +style return_button: + xpos 60 + yalign 1.0 + yoffset -45 + +style game_menu_viewport: + xsize config.screen_width-420 + ysize config.screen_height-200 + align (0.5, 0.5) + +style game_menu_side: + yfill True + align (1.0, 0.5) + +style game_menu_vscrollbar: + unscrollable "hide" + +style game_menu_label: + padding (10, 10) +style game_menu_label_text: + size 45 diff --git a/game/screens/history_screen.rpy b/game/screens/history_screen.rpy new file mode 100644 index 0000000..259c197 --- /dev/null +++ b/game/screens/history_screen.rpy @@ -0,0 +1,87 @@ + +## 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 + +define config.history_length = 250 + +screen history(): + + tag menu + + ## Avoid predicting this screen, as it can be very large. + predict False + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + use game_menu(_("History")) + + viewport: + style_prefix 'game_menu' + mousewheel True draggable True pagekeys True + scrollbars "vertical" yinitial 1.0 + + has vbox + + style_prefix "history" + + for h in _history_list: + + frame: + has hbox + 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"] + xsize 200 # this number and the null width + # number should be the same + else: + null width 200 + + $ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags) + text what: + substitute False + + if not _history_list: + label _("The dialogue history is empty.") + + +## This determines what tags are allowed to be displayed on the history screen. + +define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } + + +style history_frame: + xsize 1400 + ysize None + background None + +style history_hbox: + spacing 20 + +style history_vbox: + spacing 20 + +style history_name: + xalign 1.0 + +style history_name_text: + textalign 1.0 + align (1.0, 0.0) + color '#f93c3e' + +style history_text: + textalign 0.0 + +style history_label: + xfill True + +style history_label_text: + xalign 0.5 diff --git a/game/screens/input.rpy b/game/screens/input.rpy new file mode 100644 index 0000000..9f912df --- /dev/null +++ b/game/screens/input.rpy @@ -0,0 +1,30 @@ + +## Input screen ################################################################ +## +## This screen is used to display renpy.input. The prompt parameter is used to +## pass a text prompt in. +## +## This screen must create an input displayable with id "input" to accept the +## various input parameters. +## +## https://www.renpy.org/doc/html/screen_special.html#input + +screen input(prompt): + style_prefix "input" + + window: + # This makes the background the same as the ADV dialogue box + + vbox: + xanchor 0.0 ypos 20 spacing 10 + text prompt style "input_prompt" + input id "input" + +style input_prompt: + xalign 0.0 + +style input: + xalign 0.0 + xmaximum 1116 + + diff --git a/game/screens/main_menu.rpy b/game/screens/main_menu.rpy new file mode 100644 index 0000000..b3dfbcb --- /dev/null +++ b/game/screens/main_menu.rpy @@ -0,0 +1,44 @@ + +## Main Menu screen ############################################################ +## +## Used to display the main menu when Ren'Py starts. +## +## https://www.renpy.org/doc/html/screen_special.html#main-menu + +## Replace this with your background image, if you like +image main_menu_background = HBox( + Solid("#292835", xsize=350), + Solid("#21212d") +) + +screen main_menu(): + + ## This ensures that any other menu screen is replaced. + tag menu + + add "main_menu_background" + + vbox: + xpos 60 + yalign 0.5 + spacing 6 + + textbutton _("Start") action Start() + + textbutton _("Load") action ShowMenu("load") + + textbutton _("Preferences") action ShowMenu("preferences") + + 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) + diff --git a/game/screens/other_screens.rpy b/game/screens/other_screens.rpy new file mode 100644 index 0000000..dba3b51 --- /dev/null +++ b/game/screens/other_screens.rpy @@ -0,0 +1,200 @@ + +## 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. + +## Text that is placed on the game's about screen. Place the text between the +## triple-quotes, and leave a blank line between paragraphs. + +define gui.about = _p(""" +EasyRenPyGui is made by {a=https://github.com/shawna-p}Feniks{/a} {a=https://feniksdev.com/}@feniksdev.com{/a} +""") + + +screen about(): + + tag menu + + add "#21212db2" # The background; can be whatever + + use game_menu(_("About")) + + viewport: + style_prefix 'game_menu' + mousewheel True draggable True pagekeys True + scrollbars "vertical" + + has vbox + style_prefix "about" + + 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_text: + size 36 + + +## 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" + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + use game_menu(_("Help")) + + viewport: + style_prefix 'game_menu' + mousewheel True draggable True pagekeys True + scrollbars "vertical" + + has vbox + style_prefix "help" + spacing 23 + + 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\nClick Rollback Side") + text _("Rolls back to earlier dialogue.") + + hbox: + label _("Mouse Wheel Down") + text _("Rolls forward to later dialogue.") + + +screen gamepad_help(): + + hbox: + label _("Right Trigger\nA/Bottom Button") + text _("Advances dialogue and activates the interface.") + + hbox: + label _("Left Trigger\nLeft Shoulder") + text _("Rolls back to earlier dialogue.") + + hbox: + label _("Right Shoulder") + text _("Rolls forward to later dialogue.") + + + hbox: + label _("D-Pad, Sticks") + text _("Navigate the interface.") + + hbox: + label _("Start, Guide, B/Right Button") + text _("Accesses the game menu.") + + hbox: + label _("Y/Top Button") + text _("Hides the user interface.") + + textbutton _("Calibrate") action GamepadCalibrate() + + +style help_button: + xmargin 12 + +style help_label: + xsize 375 + right_padding 30 + +style help_label_text: + xalign 1.0 + textalign 1.0 diff --git a/game/screens/popup_screens.rpy b/game/screens/popup_screens.rpy new file mode 100644 index 0000000..ac2206b --- /dev/null +++ b/game/screens/popup_screens.rpy @@ -0,0 +1,157 @@ + +## Confirm screen ############################################################## +## +## The confirm screen is called when Ren'Py wants to ask the player a yes or no +## question. +## +## https://www.renpy.org/doc/html/screen_special.html#confirm + +screen confirm(message, yes_action, no_action=None): + + ## Ensure other screens do not get input while this screen is displayed. + modal True + + zorder 200 + + style_prefix "confirm" + + add "#0008" # You can replace this with your own overlay image + + frame: + has vbox + + label _(message) style "confirm_prompt" + + hbox: + + textbutton _("Confirm") action yes_action + # Modified so you can just have a confirmation prompt + if no_action is not None: + textbutton _("Cancel") action no_action + + ## Right-click and escape answer "no". + if no_action is not None: + key "game_menu" action no_action + else: + key "game_menu" action yes_action + +style confirm_frame: + background Frame("gui/frame.png", 60, 60, 60, 60, tile=False) + padding (60, 60, 60, 60) + xalign 0.5 + yalign 0.5 + +style confirm_vbox: + align (0.5, 0.5) + spacing 45 + +style confirm_prompt: + xalign 0.5 + +style confirm_prompt_text: + textalign 0.5 + align (0.5, 0.5) + layout "subtitle" + +style confirm_hbox: + xalign 0.5 + spacing 150 + +style confirm_button: + xalign 0.5 + +style confirm_button_text: + textalign 0.5 + + +## 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: + has hbox + + 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_hbox: + spacing 9 + +style skip_frame: + is empty + ypos 15 + background Frame("gui/skip.png", 24, 8, 75, 8, tile=False) + padding (24, 8, 75, 8) + +style skip_text: + size 24 + +style skip_triangle: + is skip_text + ## 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 + ypos 68 + + background Frame("gui/notify.png", 24, 8, 60, 8, tile=False) + padding (24, 8, 60, 8) + +style notify_text: + size 24 + + + diff --git a/game/screens/preferences.rpy b/game/screens/preferences.rpy new file mode 100644 index 0000000..06e3c3d --- /dev/null +++ b/game/screens/preferences.rpy @@ -0,0 +1,151 @@ + +## 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 + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + use game_menu(_("Preferences")) + + viewport: + style_prefix 'game_menu' + mousewheel True draggable True pagekeys True + scrollbars "vertical" + has vbox + + hbox: + box_wrap True + + if renpy.variant("pc") or renpy.variant("web"): + # Only need fullscreen/windowed on desktop and web builds + + vbox: + style_prefix "radio" + label _("Display") + textbutton _("Window"): + # Ensures this button is selected when + # not in fullscreen. + selected not preferences.fullscreen + 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. + + null height 60 + + 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 15 + textbutton _("Mute All"): + style_prefix "check" + action Preference("all mute", "toggle") + +### PREF +style pref_label: + top_margin 15 + bottom_margin 3 + +style pref_label_text: + yalign 1.0 + +style pref_vbox: + xsize 338 + +## RADIO +style radio_label: + is pref_label + +style radio_label_text: + is pref_label_text + +style radio_vbox: + is pref_vbox + spacing 0 + +style radio_button: + foreground "gui/button/radio_[prefix_]foreground.png" + padding (35, 6, 6, 6) + +## CHECK +style check_label: + is pref_label +style check_label_text: + is pref_label_text + +style check_vbox: + is pref_vbox + spacing 0 + +style check_button: + foreground "gui/button/check_[prefix_]foreground.png" + padding (35, 6, 6, 6) + +## SLIDER +style slider_label: + is pref_label +style slider_label_text: + is pref_label_text + +style slider_slider: + xsize 525 + +style slider_button: + yalign 0.5 + left_margin 15 + +style slider_vbox: + is pref_vbox + xsize 675 + diff --git a/game/screens/save_load.rpy b/game/screens/save_load.rpy new file mode 100644 index 0000000..3bfba87 --- /dev/null +++ b/game/screens/save_load.rpy @@ -0,0 +1,158 @@ +## 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 + + +## The width and height of thumbnails used by the save slots. +define config.thumbnail_width = 384 +define config.thumbnail_height = 216 + + +screen save(): + + tag menu + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + use file_slots(_("Save")) + + +screen load(): + + tag menu + + add HBox(Transform("#292835", xsize=350), "#21212db2") # The background; can be whatever + + 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: + xsize 1500 xalign 1.0 + ## 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 it. + ## This can be pretty easily removed if you want. + ## Don't forget to also remove the `default` at the top if so. + button: + style "page_label" + key_events True + action page_name_value.Toggle() + + input: + style "page_label_text" + value page_name_value + + ## The grid of file slots. + grid 3 2: + style_prefix "slot" + + for i in range(3*2): + $ slot = i + 1 + + button: + action FileAction(slot) + has vbox + + add FileScreenshot(slot) xalign 0.5 + + ## https://www.fabriziomusacchio.com/blog/2021-08-15-strftime_Cheat_Sheet/ + 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" + + # This means the player can hover this save + # slot and hit delete to delete it + key "save_delete" action FileDelete(slot) + + ## Buttons to access other pages. + vbox: + style_prefix "page" + hbox: + 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() + + if config.has_sync: + if CurrentScreenName() == "save": + textbutton _("Upload Sync"): + action UploadSync() + else: + textbutton _("Download Sync"): + action DownloadSync() + + +style page_label: + xpadding 75 + ypadding 5 + xalign 0.5 + +style page_label_text: + textalign 0.5 + layout "subtitle" + hover_color '#ff8335' + +style slot_grid: + xalign 0.5 + yalign 0.5 + spacing 15 + +style slot_time_text: + size 25 + xalign 0.5 + +style slot_vbox: + spacing 12 + +style slot_button: + xysize (414, 309) + padding (15, 15, 15, 15) + background "gui/button/slot_[prefix_]background.png" + +style slot_button_text: + size 21 + xalign 0.5 + idle_color '#aaaaaa' + hover_color '#ff8335' + selected_idle_color '#ffffff' + +style page_hbox: + xalign 0.5 + spacing 5 + +style page_vbox: + xalign 0.5 + yalign 1.0 + spacing 5 + +style page_button: + padding (15, 6, 15, 6) + xalign 0.5 + diff --git a/game/script.rpy b/game/script.rpy new file mode 100644 index 0000000..b302904 --- /dev/null +++ b/game/script.rpy @@ -0,0 +1,54 @@ +define yuuka = Character("Yuuka", callback = name_callback, cb_name = "yuuka") +define reimu = Character("Reimu", callback = name_callback, cb_name = "reimu") +define marisa = Character("Marisa", callback = name_callback, cb_name = "marisa") +define alice = Character("Alice", callback = name_callback, cb_name = "alice") +define yumemi = Character("Yumemi", callback = name_callback, cb_name = "yumemi") +define narrator = Character(callback = name_callback, cb_name = None) + +image yuuka: + "yuuka.png" + function SpriteFocus('yuuka') + +image yuuka happy: + "yuuka happy.png" + function SpriteFocus('yuuka') + +image reimu: + "reimu.png" + function SpriteFocus('reimu') + +image reimu happy: + "reimu happy.png" + function SpriteFocus('reimu') + +label start: + + scene bg room + + show yuuka happy: + xalign 0.2 + yalign 0.99 + + show reimu happy: + xalign 0.8 + yalign 0.99 + + yuuka "Good evening." + + yuuka "You all may know me as the substitute this past week for Professor Okazaki." + + yuuka "I've been teaching Botany, a topic on which I am incredibly overqualified for, to idiots younger than you but still very much like you." + + yuuka "But enough of the customary ribbing and teasing, I am here because Professor Okazaki is returning today." + + yuuka "As is stated in the faculty handbook she wrote and doodled over, because clearly not even she could have handwriting THAT horrendous," + + yuuka "I must give a presentation to report to the faculty and administration exactly what I did as subsitute for Professor Okazaki." + + yuuka "This is that presentation." + + yuuka "You might also be wondering why I've invited select students to attend this presentation." + + yuuka "I will be getting to that, please do not leave your seats through out this presentation. I will be brief." + + return \ No newline at end of file diff --git a/game/styles.rpy b/game/styles.rpy new file mode 100644 index 0000000..f981818 --- /dev/null +++ b/game/styles.rpy @@ -0,0 +1,131 @@ +################################################################################ +## Initialization +################################################################################ + +## The init offset statement causes the initialization statements in this file +## to run before init statements in any other file. +init offset = -2 + +## Calling gui.init resets the styles to sensible default values, and sets the +## width and height of the game. +init python: + gui.init(1920, 1080) + +define config.check_conflicting_properties = True + +################################################################################ +## GUI Configuration Variables +################################################################################ +## Some choice gui values have been left in, to make them +## easier to adjust for accessibility purposes e.g. to allow +## players to change the default text font or size by rebuilding the gui. +## You may add more back if you need to adjust them, or find-and-replace +## any instances where they are used directly with their value. + +# The text font for dialogue and choice menus +define gui.text_font = gui.preference("font", "DejaVuSans.ttf") +# The text font for buttons +define gui.interface_text_font = gui.preference("interface_font", "DejaVuSans.ttf") +# The default size of in-game text +define gui.text_size = gui.preference("size", 33) +# The font for character names +define gui.name_text_font = gui.preference("name_font", "DejaVuSans.ttf") +# The size for character names +define gui.name_text_size = gui.preference("name_size", 45) + +## 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" + + +################################################################################ +## Style Initialization +################################################################################ + +init offset = -1 + +################################################################################ +## Styles +################################################################################ + +style default: + font gui.text_font + size gui.text_size + language gui.language + +style input: + adjust_spacing False + +style hyperlink_text: + hover_underline True + color "#f93c3e" + +style gui_text: + color '#ffffff' + size gui.text_size + font gui.interface_text_font + +style button: + xysize (None, None) + padding (0, 0) + +style button_text: + is gui_text + yalign 0.5 + xalign 0.0 + ## The color used for a text button when it is neither selected nor hovered. + idle_color '#888888' + ## The color that is used for buttons and bars that are hovered. + hover_color '#ff8335' + ## 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. + selected_color '#ffffff' + ## The color used for a text button when it cannot be selected. + insensitive_color '#8888887f' + +style label_text: + is gui_text + size 36 + color '#f93c3e' + + +style bar: + ysize 38 + left_bar Frame("gui/bar/left.png", 6, 6, 6, 6, tile=False) + right_bar Frame("gui/bar/right.png", 6, 6, 6, 6, tile=False) + +style vbar: + xsize 38 + top_bar Frame("gui/bar/top.png", 6, 6, 6, 6, tile=False) + bottom_bar Frame("gui/bar/bottom.png", 6, 6, 6, 6, tile=False) + +style scrollbar: + ysize 18 + base_bar Frame("gui/scrollbar/horizontal_[prefix_]bar.png", 6, 6, 6, 6, tile=False) + thumb Frame("gui/scrollbar/horizontal_[prefix_]thumb.png", 6, 6, 6, 6, tile=False) + unscrollable 'hide' + +style vscrollbar: + xsize 18 + base_bar Frame("gui/scrollbar/vertical_[prefix_]bar.png", 6, 6, 6, 6, tile=False) + thumb Frame("gui/scrollbar/vertical_[prefix_]thumb.png", 6, 6, 6, 6, tile=False) + unscrollable 'hide' + +style slider: + ysize 38 + base_bar Frame("gui/slider/horizontal_[prefix_]bar.png", 6, 6, 6, 6, tile=False) + thumb "gui/slider/horizontal_[prefix_]thumb.png" + +style vslider: + xsize 38 + base_bar Frame("gui/slider/vertical_[prefix_]bar.png", 6, 6, 6, 6, tile=False) + thumb "gui/slider/vertical_[prefix_]thumb.png" + + +style frame: + padding (6, 6, 6, 6) + background Frame("gui/frame.png", 6, 6, 6, 6, tile=False) diff --git a/game/tl/None/common.rpym b/game/tl/None/common.rpym new file mode 100644 index 0000000..be9d264 --- /dev/null +++ b/game/tl/None/common.rpym @@ -0,0 +1,1503 @@ + +translate None strings: + + # renpy/common/00accessibility.rpy:28 + old "Self-voicing disabled." + new "Self-voicing disabled." + + # renpy/common/00accessibility.rpy:29 + old "Clipboard voicing enabled. " + new "Clipboard voicing enabled. " + + # renpy/common/00accessibility.rpy:30 + old "Self-voicing enabled. " + new "Self-voicing enabled. " + + # renpy/common/00accessibility.rpy:32 + old "bar" + new "bar" + + # renpy/common/00accessibility.rpy:33 + old "selected" + new "selected" + + # renpy/common/00accessibility.rpy:34 + old "viewport" + new "viewport" + + # renpy/common/00accessibility.rpy:35 + old "horizontal scroll" + new "horizontal scroll" + + # renpy/common/00accessibility.rpy:36 + old "vertical scroll" + new "vertical scroll" + + # renpy/common/00accessibility.rpy:37 + old "activate" + new "activate" + + # renpy/common/00accessibility.rpy:38 + old "deactivate" + new "deactivate" + + # renpy/common/00accessibility.rpy:39 + old "increase" + new "increase" + + # renpy/common/00accessibility.rpy:40 + old "decrease" + new "decrease" + + # renpy/common/00accessibility.rpy:120 + old "Accessibility Menu. Use up and down arrows to navigate, and enter to activate buttons and bars." + new "Accessibility Menu. Use up and down arrows to navigate, and enter to activate buttons and bars." + + # renpy/common/00accessibility.rpy:139 + old "Font Override" + new "Font Override" + + # renpy/common/00accessibility.rpy:143 + old "Default" + new "Default" + + # renpy/common/00accessibility.rpy:147 + old "DejaVu Sans" + new "DejaVu Sans" + + # renpy/common/00accessibility.rpy:151 + old "Opendyslexic" + new "Opendyslexic" + + # renpy/common/00accessibility.rpy:157 + old "Text Size Scaling" + new "Text Size Scaling" + + # renpy/common/00accessibility.rpy:163 + old "Reset" + new "Reset" + + # renpy/common/00accessibility.rpy:169 + old "Line Spacing Scaling" + new "Line Spacing Scaling" + + # renpy/common/00accessibility.rpy:181 + old "High Contrast Text" + new "High Contrast Text" + + # renpy/common/00accessibility.rpy:183 + old "Enable" + new "Enable" + + # renpy/common/00accessibility.rpy:187 + old "Disable" + new "Disable" + + # renpy/common/00accessibility.rpy:194 + old "Self-Voicing" + new "Self-Voicing" + + # renpy/common/00accessibility.rpy:198 + old "Off" + new "Off" + + # renpy/common/00accessibility.rpy:202 + old "Text-to-speech" + new "Text-to-speech" + + # renpy/common/00accessibility.rpy:206 + old "Clipboard" + new "Clipboard" + + # renpy/common/00accessibility.rpy:210 + old "Debug" + new "Debug" + + # renpy/common/00accessibility.rpy:216 + old "Voice Volume" + new "Voice Volume" + + # renpy/common/00accessibility.rpy:224 + old "Self-Voicing Volume Drop" + new "Self-Voicing Volume Drop" + + # renpy/common/00accessibility.rpy:235 + old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was." + new "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was." + + # renpy/common/00accessibility.rpy:240 + old "Return" + new "Return" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Monday" + new "{#weekday}Monday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Tuesday" + new "{#weekday}Tuesday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Wednesday" + new "{#weekday}Wednesday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Thursday" + new "{#weekday}Thursday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Friday" + new "{#weekday}Friday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Saturday" + new "{#weekday}Saturday" + + # renpy/common/00action_file.rpy:26 + old "{#weekday}Sunday" + new "{#weekday}Sunday" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Mon" + new "{#weekday_short}Mon" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Tue" + new "{#weekday_short}Tue" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Wed" + new "{#weekday_short}Wed" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Thu" + new "{#weekday_short}Thu" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Fri" + new "{#weekday_short}Fri" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Sat" + new "{#weekday_short}Sat" + + # renpy/common/00action_file.rpy:37 + old "{#weekday_short}Sun" + new "{#weekday_short}Sun" + + # renpy/common/00action_file.rpy:47 + old "{#month}January" + new "{#month}January" + + # renpy/common/00action_file.rpy:47 + old "{#month}February" + new "{#month}February" + + # renpy/common/00action_file.rpy:47 + old "{#month}March" + new "{#month}March" + + # renpy/common/00action_file.rpy:47 + old "{#month}April" + new "{#month}April" + + # renpy/common/00action_file.rpy:47 + old "{#month}May" + new "{#month}May" + + # renpy/common/00action_file.rpy:47 + old "{#month}June" + new "{#month}June" + + # renpy/common/00action_file.rpy:47 + old "{#month}July" + new "{#month}July" + + # renpy/common/00action_file.rpy:47 + old "{#month}August" + new "{#month}August" + + # renpy/common/00action_file.rpy:47 + old "{#month}September" + new "{#month}September" + + # renpy/common/00action_file.rpy:47 + old "{#month}October" + new "{#month}October" + + # renpy/common/00action_file.rpy:47 + old "{#month}November" + new "{#month}November" + + # renpy/common/00action_file.rpy:47 + old "{#month}December" + new "{#month}December" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Jan" + new "{#month_short}Jan" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Feb" + new "{#month_short}Feb" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Mar" + new "{#month_short}Mar" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Apr" + new "{#month_short}Apr" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}May" + new "{#month_short}May" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Jun" + new "{#month_short}Jun" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Jul" + new "{#month_short}Jul" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Aug" + new "{#month_short}Aug" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Sep" + new "{#month_short}Sep" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Oct" + new "{#month_short}Oct" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Nov" + new "{#month_short}Nov" + + # renpy/common/00action_file.rpy:63 + old "{#month_short}Dec" + new "{#month_short}Dec" + + # renpy/common/00action_file.rpy:258 + old "%b %d, %H:%M" + new "%b %d, %H:%M" + + # renpy/common/00action_file.rpy:395 + old "Save slot %s: [text]" + new "Save slot %s: [text]" + + # renpy/common/00action_file.rpy:480 + old "Load slot %s: [text]" + new "Load slot %s: [text]" + + # renpy/common/00action_file.rpy:533 + old "Delete slot [text]" + new "Delete slot [text]" + + # renpy/common/00action_file.rpy:612 + old "File page auto" + new "File page auto" + + # renpy/common/00action_file.rpy:614 + old "File page quick" + new "File page quick" + + # renpy/common/00action_file.rpy:616 + old "File page [text]" + new "File page [text]" + + # renpy/common/00action_file.rpy:674 + old "Page {}" + new "Page {}" + + # renpy/common/00action_file.rpy:674 + old "Automatic saves" + new "Automatic saves" + + # renpy/common/00action_file.rpy:674 + old "Quick saves" + new "Quick saves" + + # renpy/common/00action_file.rpy:815 + old "Next file page." + new "Next file page." + + # renpy/common/00action_file.rpy:887 + old "Previous file page." + new "Previous file page." + + # renpy/common/00action_file.rpy:948 + old "Quick save complete." + new "Quick save complete." + + # renpy/common/00action_file.rpy:963 + old "Quick save." + new "Quick save." + + # renpy/common/00action_file.rpy:982 + old "Quick load." + new "Quick load." + + # renpy/common/00action_other.rpy:383 + old "Language [text]" + new "Language [text]" + + # renpy/common/00action_other.rpy:746 + old "Open [text] directory." + new "Open [text] directory." + + # renpy/common/00director.rpy:712 + old "The interactive director is not enabled here." + new "The interactive director is not enabled here." + + # renpy/common/00director.rpy:1511 + old "⬆" + new "⬆" + + # renpy/common/00director.rpy:1517 + old "⬇" + new "⬇" + + # renpy/common/00director.rpy:1581 + old "Done" + new "Done" + + # renpy/common/00director.rpy:1591 + old "(statement)" + new "(statement)" + + # renpy/common/00director.rpy:1592 + old "(tag)" + new "(tag)" + + # renpy/common/00director.rpy:1593 + old "(attributes)" + new "(attributes)" + + # renpy/common/00director.rpy:1594 + old "(transform)" + new "(transform)" + + # renpy/common/00director.rpy:1619 + old "(transition)" + new "(transition)" + + # renpy/common/00director.rpy:1631 + old "(channel)" + new "(channel)" + + # renpy/common/00director.rpy:1632 + old "(filename)" + new "(filename)" + + # renpy/common/00director.rpy:1661 + old "Change" + new "Change" + + # renpy/common/00director.rpy:1663 + old "Add" + new "Add" + + # renpy/common/00director.rpy:1666 + old "Cancel" + new "Cancel" + + # renpy/common/00director.rpy:1669 + old "Remove" + new "Remove" + + # renpy/common/00director.rpy:1704 + old "Statement:" + new "Statement:" + + # renpy/common/00director.rpy:1725 + old "Tag:" + new "Tag:" + + # renpy/common/00director.rpy:1741 + old "Attributes:" + new "Attributes:" + + # renpy/common/00director.rpy:1752 + old "Click to toggle attribute, right click to toggle negative attribute." + new "Click to toggle attribute, right click to toggle negative attribute." + + # renpy/common/00director.rpy:1764 + old "Transforms:" + new "Transforms:" + + # renpy/common/00director.rpy:1775 + old "Click to set transform, right click to add to transform list." + new "Click to set transform, right click to add to transform list." + + # renpy/common/00director.rpy:1776 + old "Customize director.transforms to add more transforms." + new "Customize director.transforms to add more transforms." + + # renpy/common/00director.rpy:1788 + old "Behind:" + new "Behind:" + + # renpy/common/00director.rpy:1799 + old "Click to set, right click to add to behind list." + new "Click to set, right click to add to behind list." + + # renpy/common/00director.rpy:1811 + old "Transition:" + new "Transition:" + + # renpy/common/00director.rpy:1821 + old "Click to set." + new "Click to set." + + # renpy/common/00director.rpy:1822 + old "Customize director.transitions to add more transitions." + new "Customize director.transitions to add more transitions." + + # renpy/common/00director.rpy:1834 + old "Channel:" + new "Channel:" + + # renpy/common/00director.rpy:1845 + old "Customize director.audio_channels to add more channels." + new "Customize director.audio_channels to add more channels." + + # renpy/common/00director.rpy:1857 + old "Audio Filename:" + new "Audio Filename:" + + # renpy/common/00gui.rpy:448 + old "Are you sure?" + new "Are you sure?" + + # renpy/common/00gui.rpy:449 + old "Are you sure you want to delete this save?" + new "Are you sure you want to delete this save?" + + # renpy/common/00gui.rpy:450 + old "Are you sure you want to overwrite your save?" + new "Are you sure you want to overwrite your save?" + + # renpy/common/00gui.rpy:451 + old "Loading will lose unsaved progress.\nAre you sure you want to do this?" + new "Loading will lose unsaved progress.\nAre you sure you want to do this?" + + # renpy/common/00gui.rpy:452 + old "Are you sure you want to quit?" + new "Are you sure you want to quit?" + + # renpy/common/00gui.rpy:453 + old "Are you sure you want to return to the main menu?\nThis will lose unsaved progress." + new "Are you sure you want to return to the main menu?\nThis will lose unsaved progress." + + # renpy/common/00gui.rpy:454 + old "Are you sure you want to continue where you left off?" + new "Are you sure you want to continue where you left off?" + + # renpy/common/00gui.rpy:455 + old "Are you sure you want to end the replay?" + new "Are you sure you want to end the replay?" + + # renpy/common/00gui.rpy:456 + old "Are you sure you want to begin skipping?" + new "Are you sure you want to begin skipping?" + + # renpy/common/00gui.rpy:457 + old "Are you sure you want to skip to the next choice?" + new "Are you sure you want to skip to the next choice?" + + # renpy/common/00gui.rpy:458 + old "Are you sure you want to skip unseen dialogue to the next choice?" + new "Are you sure you want to skip unseen dialogue to the next choice?" + + # renpy/common/00gui.rpy:459 + old "This save was created on a different device. Maliciously constructed save files can harm your computer. Do you trust this save's creator and everyone who could have changed the file?" + new "This save was created on a different device. Maliciously constructed save files can harm your computer. Do you trust this save's creator and everyone who could have changed the file?" + + # renpy/common/00gui.rpy:460 + old "Do you trust the device the save was created on? You should only choose yes if you are the device's sole user." + new "Do you trust the device the save was created on? You should only choose yes if you are the device's sole user." + + # renpy/common/00keymap.rpy:323 + old "Failed to save screenshot as %s." + new "Failed to save screenshot as %s." + + # renpy/common/00keymap.rpy:335 + old "Saved screenshot as %s." + new "Saved screenshot as %s." + + # renpy/common/00library.rpy:248 + old "Skip Mode" + new "Skip Mode" + + # renpy/common/00library.rpy:317 + old "This program contains free software under a number of licenses, including the MIT License and GNU Lesser General Public License. A complete list of software, including links to full source code, can be found {a=https://www.renpy.org/l/license}here{/a}." + new "This program contains free software under a number of licenses, including the MIT License and GNU Lesser General Public License. A complete list of software, including links to full source code, can be found {a=https://www.renpy.org/l/license}here{/a}." + + # renpy/common/00preferences.rpy:289 + old "display" + new "display" + + # renpy/common/00preferences.rpy:309 + old "transitions" + new "transitions" + + # renpy/common/00preferences.rpy:318 + old "skip transitions" + new "skip transitions" + + # renpy/common/00preferences.rpy:320 + old "video sprites" + new "video sprites" + + # renpy/common/00preferences.rpy:329 + old "show empty window" + new "show empty window" + + # renpy/common/00preferences.rpy:338 + old "text speed" + new "text speed" + + # renpy/common/00preferences.rpy:346 + old "joystick" + new "joystick" + + # renpy/common/00preferences.rpy:346 + old "joystick..." + new "joystick..." + + # renpy/common/00preferences.rpy:353 + old "skip" + new "skip" + + # renpy/common/00preferences.rpy:356 + old "skip unseen [text]" + new "skip unseen [text]" + + # renpy/common/00preferences.rpy:361 + old "skip unseen text" + new "skip unseen text" + + # renpy/common/00preferences.rpy:363 + old "begin skipping" + new "begin skipping" + + # renpy/common/00preferences.rpy:367 + old "after choices" + new "after choices" + + # renpy/common/00preferences.rpy:374 + old "skip after choices" + new "skip after choices" + + # renpy/common/00preferences.rpy:376 + old "auto-forward time" + new "auto-forward time" + + # renpy/common/00preferences.rpy:390 + old "auto-forward" + new "auto-forward" + + # renpy/common/00preferences.rpy:397 + old "Auto forward" + new "Auto forward" + + # renpy/common/00preferences.rpy:400 + old "auto-forward after click" + new "auto-forward after click" + + # renpy/common/00preferences.rpy:409 + old "automatic move" + new "automatic move" + + # renpy/common/00preferences.rpy:418 + old "wait for voice" + new "wait for voice" + + # renpy/common/00preferences.rpy:427 + old "voice sustain" + new "voice sustain" + + # renpy/common/00preferences.rpy:436 + old "self voicing" + new "self voicing" + + # renpy/common/00preferences.rpy:439 + old "self voicing enable" + new "self voicing enable" + + # renpy/common/00preferences.rpy:441 + old "self voicing disable" + new "self voicing disable" + + # renpy/common/00preferences.rpy:445 + old "self voicing volume drop" + new "self voicing volume drop" + + # renpy/common/00preferences.rpy:453 + old "clipboard voicing" + new "clipboard voicing" + + # renpy/common/00preferences.rpy:456 + old "clipboard voicing enable" + new "clipboard voicing enable" + + # renpy/common/00preferences.rpy:458 + old "clipboard voicing disable" + new "clipboard voicing disable" + + # renpy/common/00preferences.rpy:462 + old "debug voicing" + new "debug voicing" + + # renpy/common/00preferences.rpy:465 + old "debug voicing enable" + new "debug voicing enable" + + # renpy/common/00preferences.rpy:467 + old "debug voicing disable" + new "debug voicing disable" + + # renpy/common/00preferences.rpy:471 + old "emphasize audio" + new "emphasize audio" + + # renpy/common/00preferences.rpy:480 + old "rollback side" + new "rollback side" + + # renpy/common/00preferences.rpy:490 + old "gl powersave" + new "gl powersave" + + # renpy/common/00preferences.rpy:496 + old "gl framerate" + new "gl framerate" + + # renpy/common/00preferences.rpy:499 + old "gl tearing" + new "gl tearing" + + # renpy/common/00preferences.rpy:502 + old "font transform" + new "font transform" + + # renpy/common/00preferences.rpy:505 + old "font size" + new "font size" + + # renpy/common/00preferences.rpy:513 + old "font line spacing" + new "font line spacing" + + # renpy/common/00preferences.rpy:521 + old "system cursor" + new "system cursor" + + # renpy/common/00preferences.rpy:530 + old "renderer menu" + new "renderer menu" + + # renpy/common/00preferences.rpy:533 + old "accessibility menu" + new "accessibility menu" + + # renpy/common/00preferences.rpy:536 + old "high contrast text" + new "high contrast text" + + # renpy/common/00preferences.rpy:545 + old "audio when minimized" + new "audio when minimized" + + # renpy/common/00preferences.rpy:554 + old "audio when unfocused" + new "audio when unfocused" + + # renpy/common/00preferences.rpy:563 + old "web cache preload" + new "web cache preload" + + # renpy/common/00preferences.rpy:578 + old "voice after game menu" + new "voice after game menu" + + # renpy/common/00preferences.rpy:587 + old "restore window position" + new "restore window position" + + # renpy/common/00preferences.rpy:596 + old "reset" + new "reset" + + # renpy/common/00preferences.rpy:609 + old "main volume" + new "main volume" + + # renpy/common/00preferences.rpy:610 + old "music volume" + new "music volume" + + # renpy/common/00preferences.rpy:611 + old "sound volume" + new "sound volume" + + # renpy/common/00preferences.rpy:612 + old "voice volume" + new "voice volume" + + # renpy/common/00preferences.rpy:613 + old "mute main" + new "mute main" + + # renpy/common/00preferences.rpy:614 + old "mute music" + new "mute music" + + # renpy/common/00preferences.rpy:615 + old "mute sound" + new "mute sound" + + # renpy/common/00preferences.rpy:616 + old "mute voice" + new "mute voice" + + # renpy/common/00preferences.rpy:617 + old "mute all" + new "mute all" + + # renpy/common/00preferences.rpy:699 + old "Clipboard voicing enabled. Press 'shift+C' to disable." + new "Clipboard voicing enabled. Press 'shift+C' to disable." + + # renpy/common/00preferences.rpy:701 + old "Self-voicing would say \"[renpy.display.tts.last]\". Press 'alt+shift+V' to disable." + new "Self-voicing would say \"[renpy.display.tts.last]\". Press 'alt+shift+V' to disable." + + # renpy/common/00preferences.rpy:703 + old "Self-voicing enabled. Press 'v' to disable." + new "Self-voicing enabled. Press 'v' to disable." + + # renpy/common/00speechbubble.rpy:392 + old "Speech Bubble Editor" + new "Speech Bubble Editor" + + # renpy/common/00speechbubble.rpy:397 + old "(hide)" + new "(hide)" + + # renpy/common/00speechbubble.rpy:408 + old "(clear retained bubbles)" + new "(clear retained bubbles)" + + # renpy/common/00sync.rpy:70 + old "Sync downloaded." + new "Sync downloaded." + + # renpy/common/00sync.rpy:190 + old "Could not connect to the Ren'Py Sync server." + new "Could not connect to the Ren'Py Sync server." + + # renpy/common/00sync.rpy:192 + old "The Ren'Py Sync server timed out." + new "The Ren'Py Sync server timed out." + + # renpy/common/00sync.rpy:194 + old "An unknown error occurred while connecting to the Ren'Py Sync server." + new "An unknown error occurred while connecting to the Ren'Py Sync server." + + # renpy/common/00sync.rpy:267 + old "The Ren'Py Sync server does not have a copy of this sync. The sync ID may be invalid, or it may have timed out." + new "The Ren'Py Sync server does not have a copy of this sync. The sync ID may be invalid, or it may have timed out." + + # renpy/common/00sync.rpy:412 + old "Please enter the sync ID you generated.\nNever enter a sync ID you didn't create yourself." + new "Please enter the sync ID you generated.\nNever enter a sync ID you didn't create yourself." + + # renpy/common/00sync.rpy:431 + old "The sync ID is not in the correct format." + new "The sync ID is not in the correct format." + + # renpy/common/00sync.rpy:451 + old "The sync could not be decrypted." + new "The sync could not be decrypted." + + # renpy/common/00sync.rpy:474 + old "The sync belongs to a different game." + new "The sync belongs to a different game." + + # renpy/common/00sync.rpy:479 + old "The sync contains a file with an invalid name." + new "The sync contains a file with an invalid name." + + # renpy/common/00sync.rpy:538 + old "This will upload your saves to the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}.\nDo you want to continue?" + new "This will upload your saves to the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}.\nDo you want to continue?" + + # renpy/common/00sync.rpy:546 + old "Yes" + new "Yes" + + # renpy/common/00sync.rpy:547 + old "No" + new "No" + + # renpy/common/00sync.rpy:569 + old "Enter Sync ID" + new "Enter Sync ID" + + # renpy/common/00sync.rpy:580 + old "This will contact the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}." + new "This will contact the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}." + + # renpy/common/00sync.rpy:609 + old "Sync Success" + new "Sync Success" + + # renpy/common/00sync.rpy:612 + old "The Sync ID is:" + new "The Sync ID is:" + + # renpy/common/00sync.rpy:618 + old "You can use this ID to download your save on another device.\nThis sync will expire in an hour.\nRen'Py Sync is supported by {a=https://www.renpy.org/sponsors.html}Ren'Py's Sponsors{/a}." + new "You can use this ID to download your save on another device.\nThis sync will expire in an hour.\nRen'Py Sync is supported by {a=https://www.renpy.org/sponsors.html}Ren'Py's Sponsors{/a}." + + # renpy/common/00sync.rpy:622 + old "Continue" + new "Continue" + + # renpy/common/00sync.rpy:646 + old "Sync Error" + new "Sync Error" + + # renpy/common/00iap.rpy:231 + old "Contacting App Store\nPlease Wait..." + new "Contacting App Store\nPlease Wait..." + + # renpy/common/00updater.rpy:504 + old "No update methods found." + new "No update methods found." + + # renpy/common/00updater.rpy:551 + old "Could not download file list: " + new "Could not download file list: " + + # renpy/common/00updater.rpy:554 + old "File list digest does not match." + new "File list digest does not match." + + # renpy/common/00updater.rpy:762 + old "An error is being simulated." + new "An error is being simulated." + + # renpy/common/00updater.rpy:950 + old "Either this project does not support updating, or the update status file was deleted." + new "Either this project does not support updating, or the update status file was deleted." + + # renpy/common/00updater.rpy:964 + old "This account does not have permission to perform an update." + new "This account does not have permission to perform an update." + + # renpy/common/00updater.rpy:967 + old "This account does not have permission to write the update log." + new "This account does not have permission to write the update log." + + # renpy/common/00updater.rpy:1047 + old "Could not verify update signature." + new "Could not verify update signature." + + # renpy/common/00updater.rpy:1366 + old "The update file was not downloaded." + new "The update file was not downloaded." + + # renpy/common/00updater.rpy:1384 + old "The update file does not have the correct digest - it may have been corrupted." + new "The update file does not have the correct digest - it may have been corrupted." + + # renpy/common/00updater.rpy:1534 + old "While unpacking {}, unknown type {}." + new "While unpacking {}, unknown type {}." + + # renpy/common/00updater.rpy:2014 + old "Updater" + new "Updater" + + # renpy/common/00updater.rpy:2021 + old "An error has occured:" + new "An error has occured:" + + # renpy/common/00updater.rpy:2023 + old "Checking for updates." + new "Checking for updates." + + # renpy/common/00updater.rpy:2025 + old "This program is up to date." + new "This program is up to date." + + # renpy/common/00updater.rpy:2027 + old "[u.version] is available. Do you want to install it?" + new "[u.version] is available. Do you want to install it?" + + # renpy/common/00updater.rpy:2029 + old "Preparing to download the updates." + new "Preparing to download the updates." + + # renpy/common/00updater.rpy:2031 + old "Downloading the updates." + new "Downloading the updates." + + # renpy/common/00updater.rpy:2033 + old "Unpacking the updates." + new "Unpacking the updates." + + # renpy/common/00updater.rpy:2035 + old "Finishing up." + new "Finishing up." + + # renpy/common/00updater.rpy:2037 + old "The updates have been installed. The program will restart." + new "The updates have been installed. The program will restart." + + # renpy/common/00updater.rpy:2039 + old "The updates have been installed." + new "The updates have been installed." + + # renpy/common/00updater.rpy:2041 + old "The updates were cancelled." + new "The updates were cancelled." + + # renpy/common/00updater.rpy:2056 + old "Proceed" + new "Proceed" + + # renpy/common/00updater.rpy:2071 + old "Preparing to download the game data." + new "Preparing to download the game data." + + # renpy/common/00updater.rpy:2073 + old "Downloading the game data." + new "Downloading the game data." + + # renpy/common/00updater.rpy:2075 + old "The game data has been downloaded." + new "The game data has been downloaded." + + # renpy/common/00updater.rpy:2077 + old "An error occured when trying to download game data:" + new "An error occured when trying to download game data:" + + # renpy/common/00updater.rpy:2082 + old "This game cannot be run until the game data has been downloaded." + new "This game cannot be run until the game data has been downloaded." + + # renpy/common/00updater.rpy:2089 + old "Retry" + new "Retry" + + # renpy/common/00compat.rpy:421 + old "Fullscreen" + new "Fullscreen" + + # renpy/common/00gallery.rpy:627 + old "Image [index] of [count] locked." + new "Image [index] of [count] locked." + + # renpy/common/00gallery.rpy:647 + old "prev" + new "prev" + + # renpy/common/00gallery.rpy:648 + old "next" + new "next" + + # renpy/common/00gallery.rpy:649 + old "slideshow" + new "slideshow" + + # renpy/common/00gallery.rpy:650 + old "return" + new "return" + + # renpy/common/00gltest.rpy:89 + old "Renderer" + new "Renderer" + + # renpy/common/00gltest.rpy:93 + old "Automatically Choose" + new "Automatically Choose" + + # renpy/common/00gltest.rpy:100 + old "Force GL Renderer" + new "Force GL Renderer" + + # renpy/common/00gltest.rpy:105 + old "Force ANGLE Renderer" + new "Force ANGLE Renderer" + + # renpy/common/00gltest.rpy:110 + old "Force GLES Renderer" + new "Force GLES Renderer" + + # renpy/common/00gltest.rpy:116 + old "Force GL2 Renderer" + new "Force GL2 Renderer" + + # renpy/common/00gltest.rpy:121 + old "Force ANGLE2 Renderer" + new "Force ANGLE2 Renderer" + + # renpy/common/00gltest.rpy:126 + old "Force GLES2 Renderer" + new "Force GLES2 Renderer" + + # renpy/common/00gltest.rpy:132 + old "Gamepad" + new "Gamepad" + + # renpy/common/00gltest.rpy:136 + old "Enable (No Blocklist)" + new "Enable (No Blocklist)" + + # renpy/common/00gltest.rpy:150 + old "Calibrate" + new "Calibrate" + + # renpy/common/00gltest.rpy:159 + old "Powersave" + new "Powersave" + + # renpy/common/00gltest.rpy:173 + old "Framerate" + new "Framerate" + + # renpy/common/00gltest.rpy:177 + old "Screen" + new "Screen" + + # renpy/common/00gltest.rpy:181 + old "60" + new "60" + + # renpy/common/00gltest.rpy:185 + old "30" + new "30" + + # renpy/common/00gltest.rpy:191 + old "Tearing" + new "Tearing" + + # renpy/common/00gltest.rpy:207 + old "Changes will take effect the next time this program is run." + new "Changes will take effect the next time this program is run." + + # renpy/common/00gltest.rpy:214 + old "Quit" + new "Quit" + + # renpy/common/00gltest.rpy:242 + old "Performance Warning" + new "Performance Warning" + + # renpy/common/00gltest.rpy:247 + old "This computer is using software rendering." + new "This computer is using software rendering." + + # renpy/common/00gltest.rpy:249 + old "This game requires use of GL2 that can't be initialised." + new "This game requires use of GL2 that can't be initialised." + + # renpy/common/00gltest.rpy:251 + old "This computer has a problem displaying graphics: [problem]." + new "This computer has a problem displaying graphics: [problem]." + + # renpy/common/00gltest.rpy:255 + old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display." + new "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display." + + # renpy/common/00gltest.rpy:259 + old "The {a=edit:1:log.txt}log.txt{/a} file may contain information to help you determine what is wrong with your computer." + new "The {a=edit:1:log.txt}log.txt{/a} file may contain information to help you determine what is wrong with your computer." + + # renpy/common/00gltest.rpy:264 + old "More details on how to fix this can be found in the {a=[url]}documentation{/a}." + new "More details on how to fix this can be found in the {a=[url]}documentation{/a}." + + # renpy/common/00gltest.rpy:269 + old "Continue, Show this warning again" + new "Continue, Show this warning again" + + # renpy/common/00gltest.rpy:273 + old "Continue, Don't show warning again" + new "Continue, Don't show warning again" + + # renpy/common/00gltest.rpy:281 + old "Change render options" + new "Change render options" + + # renpy/common/00gamepad.rpy:32 + old "Select Gamepad to Calibrate" + new "Select Gamepad to Calibrate" + + # renpy/common/00gamepad.rpy:35 + old "No Gamepads Available" + new "No Gamepads Available" + + # renpy/common/00gamepad.rpy:54 + old "Calibrating [name] ([i]/[total])" + new "Calibrating [name] ([i]/[total])" + + # renpy/common/00gamepad.rpy:58 + old "Press or move the '[control!s]' [kind]." + new "Press or move the '[control!s]' [kind]." + + # renpy/common/00gamepad.rpy:68 + old "Skip (A)" + new "Skip (A)" + + # renpy/common/00gamepad.rpy:71 + old "Back (B)" + new "Back (B)" + + # renpy/common/_errorhandling.rpym:662 + old "Open" + new "Open" + + # renpy/common/_errorhandling.rpym:664 + old "Opens the traceback.txt file in a text editor." + new "Opens the traceback.txt file in a text editor." + + # renpy/common/_errorhandling.rpym:666 + old "Copy BBCode" + new "Copy BBCode" + + # renpy/common/_errorhandling.rpym:668 + old "Copies the traceback.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/." + new "Copies the traceback.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/." + + # renpy/common/_errorhandling.rpym:670 + old "Copy Markdown" + new "Copy Markdown" + + # renpy/common/_errorhandling.rpym:672 + old "Copies the traceback.txt file to the clipboard as Markdown for Discord." + new "Copies the traceback.txt file to the clipboard as Markdown for Discord." + + # renpy/common/_errorhandling.rpym:703 + old "An exception has occurred." + new "An exception has occurred." + + # renpy/common/_errorhandling.rpym:726 + old "Rollback" + new "Rollback" + + # renpy/common/_errorhandling.rpym:728 + old "Attempts a roll back to a prior time, allowing you to save or choose a different choice." + new "Attempts a roll back to a prior time, allowing you to save or choose a different choice." + + # renpy/common/_errorhandling.rpym:731 + old "Ignore" + new "Ignore" + + # renpy/common/_errorhandling.rpym:735 + old "Ignores the exception, allowing you to continue." + new "Ignores the exception, allowing you to continue." + + # renpy/common/_errorhandling.rpym:737 + old "Ignores the exception, allowing you to continue. This often leads to additional errors." + new "Ignores the exception, allowing you to continue. This often leads to additional errors." + + # renpy/common/_errorhandling.rpym:741 + old "Reload" + new "Reload" + + # renpy/common/_errorhandling.rpym:743 + old "Reloads the game from disk, saving and restoring game state if possible." + new "Reloads the game from disk, saving and restoring game state if possible." + + # renpy/common/_errorhandling.rpym:746 + old "Console" + new "Console" + + # renpy/common/_errorhandling.rpym:748 + old "Opens a console to allow debugging the problem." + new "Opens a console to allow debugging the problem." + + # renpy/common/_errorhandling.rpym:761 + old "Quits the game." + new "Quits the game." + + # renpy/common/_errorhandling.rpym:782 + old "Parsing the script failed." + new "Parsing the script failed." + + # renpy/common/_developer/developer.rpym:38 + old "Developer Menu" + new "Developer Menu" + + # renpy/common/_developer/developer.rpym:43 + old "Interactive Director (D)" + new "Interactive Director (D)" + + # renpy/common/_developer/developer.rpym:45 + old "Reload Game (Shift+R)" + new "Reload Game (Shift+R)" + + # renpy/common/_developer/developer.rpym:47 + old "Console (Shift+O)" + new "Console (Shift+O)" + + # renpy/common/_developer/developer.rpym:49 + old "Variable Viewer" + new "Variable Viewer" + + # renpy/common/_developer/developer.rpym:51 + old "Persistent Viewer" + new "Persistent Viewer" + + # renpy/common/_developer/developer.rpym:53 + old "Image Location Picker" + new "Image Location Picker" + + # renpy/common/_developer/developer.rpym:55 + old "Filename List" + new "Filename List" + + # renpy/common/_developer/developer.rpym:59 + old "Show Image Load Log (F4)" + new "Show Image Load Log (F4)" + + # renpy/common/_developer/developer.rpym:62 + old "Hide Image Load Log (F4)" + new "Hide Image Load Log (F4)" + + # renpy/common/_developer/developer.rpym:65 + old "Image Attributes" + new "Image Attributes" + + # renpy/common/_developer/developer.rpym:69 + old "Show Translation Identifiers" + new "Show Translation Identifiers" + + # renpy/common/_developer/developer.rpym:72 + old "Hide Translation Identifiers" + new "Hide Translation Identifiers" + + # renpy/common/_developer/developer.rpym:77 + old "Speech Bubble Editor (Shift+B)" + new "Speech Bubble Editor (Shift+B)" + + # renpy/common/_developer/developer.rpym:81 + old "Show Filename and Line" + new "Show Filename and Line" + + # renpy/common/_developer/developer.rpym:84 + old "Hide Filename and Line" + new "Hide Filename and Line" + + # renpy/common/_developer/developer.rpym:127 + old "Layer [l]:" + new "Layer [l]:" + + # renpy/common/_developer/developer.rpym:131 + old " [name] [attributes] (hidden)" + new " [name] [attributes] (hidden)" + + # renpy/common/_developer/developer.rpym:135 + old " [name] [attributes]" + new " [name] [attributes]" + + # renpy/common/_developer/developer.rpym:187 + old "Nothing to inspect." + new "Nothing to inspect." + + # renpy/common/_developer/developer.rpym:198 + old "Hide deleted" + new "Hide deleted" + + # renpy/common/_developer/developer.rpym:198 + old "Show deleted" + new "Show deleted" + + # renpy/common/_developer/developer.rpym:349 + old "Rectangle copied to clipboard." + new "Rectangle copied to clipboard." + + # renpy/common/_developer/developer.rpym:352 + old "Position copied to clipboard." + new "Position copied to clipboard." + + # renpy/common/_developer/developer.rpym:364 + old "Rectangle: %r" + new "Rectangle: %r" + + # renpy/common/_developer/developer.rpym:367 + old "Mouse position: %r" + new "Mouse position: %r" + + # renpy/common/_developer/developer.rpym:372 + old "Right-click or escape to quit." + new "Right-click or escape to quit." + + # renpy/common/_developer/developer.rpym:420 + old "Type to filter: " + new "Type to filter: " + + # renpy/common/_developer/developer.rpym:538 + old "Textures: [tex_count] ([tex_size_mb:.1f] MB)" + new "Textures: [tex_count] ([tex_size_mb:.1f] MB)" + + # renpy/common/_developer/developer.rpym:542 + old "Image cache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)" + new "Image cache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)" + + # renpy/common/_developer/developer.rpym:552 + old "✔ " + new "✔ " + + # renpy/common/_developer/developer.rpym:555 + old "✘ " + new "✘ " + + # renpy/common/_developer/developer.rpym:560 + old "\n{color=#cfc}✔ predicted image (good){/color}\n{color=#fcc}✘ unpredicted image (bad){/color}\n{color=#fff}Drag to move.{/color}" + new "\n{color=#cfc}✔ predicted image (good){/color}\n{color=#fcc}✘ unpredicted image (bad){/color}\n{color=#fff}Drag to move.{/color}" + + # renpy/common/_developer/developer.rpym:606 + old "\n{color=#fff}Copied to clipboard.{/color}" + new "\n{color=#fff}Copied to clipboard.{/color}" + + # renpy/common/_developer/developer.rpym:612 + old "\n{color=#fff}Click to copy.\nDrag to move.{/color}" + new "\n{color=#fff}Click to copy.\nDrag to move.{/color}" + + # renpy/common/_developer/developer.rpym:657 + old "Click to open in editor." + new "Click to open in editor." + + # renpy/common/_developer/inspector.rpym:38 + old "Displayable Inspector" + new "Displayable Inspector" + + # renpy/common/_developer/inspector.rpym:61 + old "Size" + new "Size" + + # renpy/common/_developer/inspector.rpym:65 + old "Style" + new "Style" + + # renpy/common/_developer/inspector.rpym:71 + old "Location" + new "Location" + + # renpy/common/_developer/inspector.rpym:122 + old "Inspecting Styles of [displayable_name!q]" + new "Inspecting Styles of [displayable_name!q]" + + # renpy/common/_developer/inspector.rpym:139 + old "displayable:" + new "displayable:" + + # renpy/common/_developer/inspector.rpym:145 + old " (no properties affect the displayable)" + new " (no properties affect the displayable)" + + # renpy/common/_developer/inspector.rpym:147 + old " (default properties omitted)" + new " (default properties omitted)" + + # renpy/common/_developer/inspector.rpym:185 + old "" + new "" + + # renpy/common/00console.rpy:537 + old "Press to exit console. Type help for help.\n" + new "Press to exit console. Type help for help.\n" + + # renpy/common/00console.rpy:541 + old "Ren'Py script enabled." + new "Ren'Py script enabled." + + # renpy/common/00console.rpy:543 + old "Ren'Py script disabled." + new "Ren'Py script disabled." + + # renpy/common/00console.rpy:793 + old "help: show this help\n help : show signature and documentation of " + new "help: show this help\n help : show signature and documentation of " + + # renpy/common/00console.rpy:817 + old "Help may display undocumented functions. Please check that the function or\nclass you want to use is documented.\n\n" + new "Help may display undocumented functions. Please check that the function or\nclass you want to use is documented.\n\n" + + # renpy/common/00console.rpy:826 + old "commands:\n" + new "commands:\n" + + # renpy/common/00console.rpy:836 + old " : run the statement\n" + new " : run the statement\n" + + # renpy/common/00console.rpy:838 + old " : run the expression or statement" + new " : run the expression or statement" + + # renpy/common/00console.rpy:846 + old "clear: clear the console history" + new "clear: clear the console history" + + # renpy/common/00console.rpy:850 + old "exit: exit the console" + new "exit: exit the console" + + # renpy/common/00console.rpy:858 + old "stack: print the return stack" + new "stack: print the return stack" + + # renpy/common/00console.rpy:880 + old "load : loads the game from slot" + new "load : loads the game from slot" + + # renpy/common/00console.rpy:893 + old "save : saves the game in slot" + new "save : saves the game in slot" + + # renpy/common/00console.rpy:904 + old "reload: reloads the game, refreshing the scripts" + new "reload: reloads the game, refreshing the scripts" + + # renpy/common/00console.rpy:912 + old "watch : watch a python expression\n watch short: makes the representation of traced expressions short (default)\n watch long: makes the representation of traced expressions as is" + new "watch : watch a python expression\n watch short: makes the representation of traced expressions short (default)\n watch long: makes the representation of traced expressions as is" + + # renpy/common/00console.rpy:949 + old "unwatch : stop watching an expression" + new "unwatch : stop watching an expression" + + # renpy/common/00console.rpy:995 + old "unwatchall: stop watching all expressions" + new "unwatchall: stop watching all expressions" + + # renpy/common/00console.rpy:1016 + old "jump