Initial commit

This commit is contained in:
Jacoder23 2024-04-05 13:40:52 +08:00
commit d0a3798dd2
67 changed files with 4612 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

24
.gitignore vendored Normal file
View File

@ -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/**

4
README.md Normal file
View File

@ -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 #

196
game/00auto-highlight.rpy Normal file
View File

@ -0,0 +1,196 @@
"""
Auto Highlight Ren'Py Module
2021 Daniel Westfall <SoDaRa2595@gmail.com>
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

BIN
game/gui/bar/bottom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
game/gui/bar/left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
game/gui/bar/right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
game/gui/bar/top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
game/gui/bubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
game/gui/frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
game/gui/namebox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

BIN
game/gui/notify.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
game/gui/nvl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
game/gui/skip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
game/gui/textbox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
game/gui/thoughtbubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
game/gui/window_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
game/images/reimu happy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 KiB

BIN
game/images/reimu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

BIN
game/images/yuuka happy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

BIN
game/images/yuuka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

841
game/kinetic_text_tags.rpy Normal file
View File

@ -0,0 +1,841 @@
"""
Kinetic Text Tags Ren'Py Module
2021 Daniel Westfall <SoDaRa2595@gmail.com>
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

View File

@ -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",
# )

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

243
game/options.rpy Normal file
View File

@ -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\<config.save_directory>
##
## Macintosh: $HOME/Library/RenPy/<config.save_directory>
##
## Linux: $HOME/.renpy/<config.save_directory>
##
## 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"

View File

@ -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"

View File

@ -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),
}

View File

@ -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

View File

@ -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

30
game/screens/input.rpy Normal file
View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

158
game/screens/save_load.rpy Normal file
View File

@ -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

54
game/script.rpy Normal file
View File

@ -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

131
game/styles.rpy Normal file
View File

@ -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)

1503
game/tl/None/common.rpym Normal file

File diff suppressed because it is too large Load Diff

1
project.json Normal file
View File

@ -0,0 +1 @@
{ "type" : "template" }