diff --git a/Additional Mod Features/Better BSODs/extras/bsod.rpy b/Additional Mod Features/Better BSODs/extras/bsod.rpy index ba5f8425..72873b82 100644 --- a/Additional Mod Features/Better BSODs/extras/bsod.rpy +++ b/Additional Mod Features/Better BSODs/extras/bsod.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # bsod.rpy # This file contains the screen code to display a fake Blue Screen of death. diff --git a/Additional Mod Features/Gallery/extras/gallery.rpy b/Additional Mod Features/Gallery/extras/gallery.rpy index 8413a47d..46fe08f1 100644 --- a/Additional Mod Features/Gallery/extras/gallery.rpy +++ b/Additional Mod Features/Gallery/extras/gallery.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the screen code for the Gallery Menu. # The code is designed to work with Ren'Py 8 and uses the `_ren.py` approach for Python code. diff --git a/Additional Mod Features/Gallery/extras/py/gallery_ren.py b/Additional Mod Features/Gallery/extras/py/gallery_ren.py index c11f94de..f3cf0a25 100644 --- a/Additional Mod Features/Gallery/extras/py/gallery_ren.py +++ b/Additional Mod Features/Gallery/extras/py/gallery_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Python code for the Gallery Menu. # The code is designed to work with Ren'Py 8 and uses the `_ren.py` approach for Python code. diff --git a/Documentation/New Poemgame Guide.pdf b/Documentation/New Poemgame Guide.pdf deleted file mode 100644 index 9ecc4128..00000000 Binary files a/Documentation/New Poemgame Guide.pdf and /dev/null differ diff --git a/README.md b/README.md index 70352bc6..be5c3aeb 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,6 @@ If you prefer a different approach, you may use one of these alternatives: - 📱 [Android Mod Guide](./Documentation/Android%20Mod%20Guide.pdf) - Complete guide for Android porting - 🎮 [Discord RPC Guide](./Documentation/Discord%20RPC%20Guide.pdf) - Set up Discord Rich Presence -- 📝 [New Poem Game Guide](./Documentation/New%20Poemgame%20Guide.pdf) - In-depth poem game documentation ### Community & Support @@ -218,5 +217,5 @@ Thanks to the following people for their contributions to the DDLC Mod Template: ---
- Copyright © 2019-2025 Azariel "Bronya Rand" Del Carmen (bronya_rand). All rights reserved. Doki Doki Literature Club, the Doki Doki Literature Club code, is the property of Team Salvato. Copyright © 2017 Team Salvato. All rights reserved. + Copyright © 2019-2026 Azariel "Bronya Rand" Del Carmen (bronya_rand). All rights reserved. Doki Doki Literature Club, the Doki Doki Literature Club code, is the property of Team Salvato. Copyright © 2017 Team Salvato. All rights reserved.
diff --git a/game/act_two/poems_special.rpy b/game/act_two/poems_special.rpy index dd57840e..9a013dfc 100644 --- a/game/act_two/poems_special.rpy +++ b/game/act_two/poems_special.rpy @@ -33,6 +33,8 @@ label poem_special(poem=1): $ quick_menu = False play sound page_turn + $ show_poem_content_warning(poem) + if poem == 7: show poem_special7a as ps with Dissolve(1.0) else: diff --git a/game/core/content_warnings.rpy b/game/core/content_warnings.rpy new file mode 100644 index 00000000..4b6876c6 --- /dev/null +++ b/game/core/content_warnings.rpy @@ -0,0 +1,80 @@ +# This file contains the content warning popup screen used in the mobile version of DDLC. + +# The logic for displaying the content warning is handled in `content_warning_ren.py` in the `py` directory. +# To show a content warning, call the `show_content_warning(warning_text)` function. + +default persistent.enable_content_warnings = False +default cw_prev_volume = preferences.get_mixer('music') + +image cw_darken: + Solid("#000") + alpha 0.8 + +image cw_warning_icon = "gui/cwicon.png" + +screen content_warning_popup(warning_text): + modal True + zorder 200 + style_prefix "cw_popup" + + default cw_screen_shown = False + + frame at cw_fade: + background "cw_darken" + + frame at cw_popup_fade: + padding (50, 30) + xalign 0.5 + yalign 0.5 + + has vbox + xminimum 500 + ymaximum 1.0 + spacing 20 + + add "cw_warning_icon" xalign 0.5 zoom 0.3 + + text "[warning_text!t]" + + textbutton _("OK"): + sensitive cw_screen_shown + action [Function(cw_restore_volume), Hide()] + + timer 3.0 action SetLocalVariable("cw_screen_shown", True) + +style cw_popup_text is default: + xalign 0.5 + outlines [] + color "#000" + size get_variable_size(24, 32, 1.0) + textalign 0.5 + +style cw_popup_frame: + background Frame(Transform("gui/cw_frame.png", zoom=1.0), 30, 30) + +style cw_popup_button is confirm_button: + xalign 0.5 +style cw_popup_button_text is confirm_button_text + +transform cw_fade: + on show: + alpha 0.0 + linear 0.5 alpha 1.0 + on hide: + alpha 1.0 + linear 0.5 alpha 0.0 + +transform cw_popup_fade: + subpixel True + on show: + alpha 0.0 + yoffset 40 + parallel: + easein_quad 0.4 alpha 1.0 + parallel: + easein_quad 1.2 yoffset 0 + on hide: + parallel: + easeout_quad 0.4 alpha 0.0 + parallel: + easeout_quad 1.2 yoffset 40 \ No newline at end of file diff --git a/game/core/py/content_warning_ren.py b/game/core/py/content_warning_ren.py new file mode 100644 index 00000000..01f9f846 --- /dev/null +++ b/game/core/py/content_warning_ren.py @@ -0,0 +1,62 @@ +# This file contains the Python code for the Content Warning system from the mobile version of DDLC. + +# The logic for displaying the content warning is handled here, +# while the display code is in `content_warnings.rpy` in the `wip` directory. + +## These import is not used when the game is running, but exists so IDEs reports +## one warning than multiple. +from game.definitions.py.core_ren import pause, persistent, store +import renpy # type: ignore + +persistent.enable_content_warnings = False + +"""renpy +init python: +""" + + +def show_content_warning(warning_text: str): + """ + Shows a content warning popup with the given warning text. + + :param warning_text: The content warning text to display. + :type warning_text: str + """ + if not persistent.enable_content_warnings: + return + + store.cw_prev_volume = renpy.store.preferences.get_mixer("music") + if store.cw_prev_volume > 0: + renpy.audio.music.set_volume(store.cw_prev_volume / 2) + + renpy.show_screen("content_warning_popup", warning_text=warning_text) + store.mc.add_history(None, "", (_("Content Warning: ") + warning_text)) + + pause(3.0) + + +def cw_restore_volume(): + """ + Restores the volume levels after the content warning screen is dismissed. + """ + renpy.music.set_volume(store.cw_prev_volume, 2) + +def show_poem_content_warning(special_poem: int): + """ + Shows a content warning for specific special poems. + Displays a content warning for certain special poems based on their number + or None if no warning is needed. + + :param special_poem: The special poem number. + :type special_poem: int + """ + poem_warnings = { + 1: "Crude sketch of a stick figure in the likeness of a character hanging from a noose.", + 4: "First-person written account glorifying self-harm via cutting, and considering potential suicide. Visual depiction of blood smeared on paper.", + 9: "First-person written references to implied neglect and potential parental abuse, including withholding food.", + 10: "Brief, first person written account of implied mental health struggles and potential future self harm.", + 11: "First-person written account of implied drowning and accompanying panic, physical struggle, and claustrophobia." + } + + if special_poem in poem_warnings.keys(): + show_content_warning(poem_warnings[special_poem]) diff --git a/game/definitions/cgs.rpy b/game/definitions/cgs.rpy index 332370c0..28a0d846 100644 --- a/game/definitions/cgs.rpy +++ b/game/definitions/cgs.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file defines all the character graphics (CGs) in DDLC such as Yuri's # Chocolate CG and Natsuki's Manga CG. diff --git a/game/definitions/definitions.rpy b/game/definitions/definitions.rpy index 111c9ace..2fa0bf8e 100644 --- a/game/definitions/definitions.rpy +++ b/game/definitions/definitions.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file defines important stuff for DDLC and your mod! # This variable declares whether to enable Developer Tools from Ren'Py. @@ -1394,6 +1394,14 @@ default chapter = 0 default currentpos = 0 default faint_effect = None +# Variables brough from DDLC Mobile +default hide_quick_skip_and_auto = False + +default persistent.reduce_motion = False +default persistent.use_alt_poem_font = False +default persistent.reduce_transparency = False +default persistent.high_contrast = False + # Default Name Variables # To define a default name make a character name variable like in this example: # default e_name = "Eileen" diff --git a/game/definitions/effects.rpy b/game/definitions/effects.rpy index 7c6242c3..f56d1409 100644 --- a/game/definitions/effects.rpy +++ b/game/definitions/effects.rpy @@ -1,26 +1,12 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file defines all the effects in DDLC used in Act 2 and beyond. +define effects = object() init python: - # This function returns the size of a 16:9 screenshot surface. - def screenshot_srf_size(): - width, height = renpy.get_physical_size() - if float(width) / float(height) > 16.0/9.0: - width = height * 16 / 9 - else: - height = width * 9 / 16 - return (width, height) - # This screenshot is used to screenshot the game which is used for different # effects in-game. def screenshot_srf(): - if renpy.version_tuple > (7, 3, 5, 606): - srf = renpy.display.draw.screenshot(None) - else: - srf = renpy.display.draw.screenshot(None, False) - - # The screenshot's size must match the window. - srf = renpy.display.scale.smoothscale(srf, screenshot_srf_size()) + srf = renpy.display.draw.screenshot(None) return srf # This function inverts the image in-game for the Invert Class. @@ -35,16 +21,21 @@ init python: class Invert(renpy.Displayable): def __init__(self, delay=0.0, screenshot_delay=0.0): super(Invert, self).__init__() - self.width, self.height = screenshot_srf_size() - self.srf = invert() + self.width, self.height = renpy.get_physical_size() + self.height = self.width * 9 / 16 + effects.invert_srf = invert() self.delay = delay def render(self, width, height, st, at): render = renpy.Render(self.width, self.height) if st >= self.delay: - render.blit(self.srf, (0, 0)) + render.blit(effects.invert_srf, (0, 0)) return render + def hide_windows_enabled(enabled=True): + global _windows_hidden + _windows_hidden = not enabled + ## Invert(length, delay) # This screen is called using the state `show screen invert(0.15, 0.3)` to invert the screen. # Syntax @@ -55,8 +46,11 @@ screen invert(length, delay=0.0): timer delay action PauseAudio("music") timer delay action Play("sound", "sfx/glitch1.ogg") timer length + delay action Hide("invert") + on "show" action Function(hide_windows_enabled, enabled=False) on "hide" action PauseAudio("music", False) on "hide" action Stop("sound") + on "hide" action Function(hide_windows_enabled, enabled=True) + init python: # This class defines the code for the tear piece effect in 'screen tear'. @@ -81,26 +75,39 @@ init python: class Tear(renpy.Displayable): def __init__(self, number, offtimeMult, ontimeMult, offsetMin, offsetMax, srf=None): super(Tear, self).__init__() - self.width, self.height = screenshot_srf_size() + self.width, self.height = renpy.get_physical_size() + screenshot_offset_x = 0 + screenshot_offset_y = 0 + if float(self.width) / float(self.height) > 16.0/9.0: + screenshot_offset_x = int((self.width - int(self.height * 16 / 9)) / 2) + self.width = int(self.height * 16 / 9) + else: + screenshot_offset_y = int((self.height - int(self.width * 9 / 16)) / 2) + self.height = int(self.width * 9 / 16) self.number = number + if not srf: effects_vars.tear_srf = screenshot_srf() + else: effects_vars.tear_srf = srf + + if (screenshot_offset_x != 0 or screenshot_offset_y != 0) and effects_vars.tear_srf.get_size() == renpy.get_physical_size(): + effects_vars.tear_srf = screenshot_srf().subsurface(screenshot_offset_x, screenshot_offset_y, self.width, self.height) if not srf: self.srf = screenshot_srf() else: self.srf = srf self.pieces = [] tearpoints = [0, self.height] for i in range(number): - tearpoints.append(random.randint(10, int(self.height) - 10)) + tearpoints.append(random.randint(10, int(self.height - 10))) tearpoints.sort() for i in range(number+1): self.pieces.append(TearPiece(tearpoints[i], tearpoints[i+1], offtimeMult, ontimeMult, offsetMin, offsetMax)) def render(self, width, height, st, at): render = renpy.Render(self.width, self.height) - render.blit(self.srf, (0,0)) + render.blit(effects_vars.tear_srf, (0,0)) for piece in self.pieces: piece.update(st) - subsrf = self.srf.subsurface((0, max(0, piece.startY - 1), self.width, max(0, piece.endY - piece.startY))) + subsrf = effects_vars.tear_srf.subsurface((0, max(0, piece.startY - 1), self.width, max(0, piece.endY - piece.startY))) render.blit(subsrf, (piece.offset, piece.startY)) renpy.redraw(self, 0) return render @@ -117,6 +124,8 @@ init python: screen tear(number=10, offtimeMult=1, ontimeMult=1, offsetMin=0, offsetMax=50, srf=None): zorder 150 add Tear(number, offtimeMult, ontimeMult, offsetMin, offsetMax, srf) size (1280,720) + on "show" action Function(hide_windows_enabled, enabled=False) + on "hide" action Function(hide_windows_enabled, enabled=True) # RectStatic # These images transforms show glitched rectangles in-game during Act 3 when Monika @@ -171,7 +180,7 @@ init python: ## ParticleBurst # This class declares the code used for the ParticleBurst effect. class ParticleBurst(object): - def __init__(self, theDisplayable, explodeTime=0, numParticles=20, particleTime = 0.500, particleXSpeed = 3, particleYSpeed = 5): + def __init__(self, theDisplayable, explodeTime=0, numParticles=20, particleTime = 0.500, particleTimeOffset = 0.0, particleXSpeed = 3, particleYSpeed = 5): self.sm = SpriteManager(update=self.update) self.stars = [ ] @@ -179,6 +188,7 @@ init python: self.explodeTime = explodeTime self.numParticles = numParticles self.particleTime = particleTime + self.particleTimeOffset = particleTimeOffset self.particleXSpeed = particleXSpeed self.particleYSpeed = particleYSpeed self.gravity = 240 @@ -200,10 +210,11 @@ init python: def update(self, st): sindex=0 + st_offset = st - self.particleTimeOffset for s, ySpeed, xSpeed, particleTime in self.stars: - if (st < particleTime): - s.x = xSpeed * 120 * (st + .20) - s.y = (ySpeed * 120 * (st + .20) + (self.gravity * st * st)) + if ((st_offset) < particleTime): + s.x = xSpeed * 120 * (st_offset + .20) + s.y = (ySpeed * 120 * (st_offset + .20) + (self.gravity * st_offset * st_offset)) else: s.destroy() self.stars.pop(sindex) @@ -288,49 +299,6 @@ init python: pindex += 1 return 0 -# This image transform adds a blood drop that gets longer and -# thinner over time. -image blood_particle_drip: - "gui/blood_drop.png" - yzoom 0 yanchor 0.2 subpixel True - linear 10 yzoom 8 - -# This image transform adds a blood drop that gets thinner -# randomly by time. -image blood_particle: - subpixel True - "gui/blood_drop.png" - zoom 0.75 - alpha 0.75 - choice: - linear 0.25 zoom 0 - choice: - linear 0.35 zoom 0 - choice: - linear 0.35 zoom 0 - choice: - linear 0.55 zoom 0 - -# This image transform adds a blood drop that squirts and -# drops for three minutes. -image blood: - size (1, 1) - truecenter - Blood("blood_particle").sm - -# This image transform adds a blood drop that doesn't squirts, -# and increases the chance of dropping. -image blood_eye: - size (1, 1) - truecenter - Blood("blood_particle", dripChance=0.5, numSquirts=0).sm - -# This image transform adds a blood drop that has a very low -# chance to drop. -image blood_eye2: - size (1, 1) - truecenter - Blood("blood_particle", dripChance=0.005, numSquirts=0, burstSize=0).sm init python: ## AnimatedMask @@ -358,7 +326,6 @@ init python: mr = renpy.render(self.mask, width, height, st, at) mb = renpy.Render(width, height) - if self.moving: mb.place(self.mask, ((-st * 50) % (width * 2)) - (width * 2), 0) mb.place(self.maskb, -width / 2, 0) @@ -366,8 +333,6 @@ init python: mb.place(self.mask, 0, 0) mb.place(self.maskb, 0, 0) - - cw, ch = cr.get_size() mw, mh = mr.get_size() @@ -381,49 +346,72 @@ init python: nr = renpy.render(self.null, width, height, st, at) rv = renpy.Render(w, h) + rv.mesh = True + rv.add_shader("renpy.imagedissolve") - complete = self.oc + math.pow(math.sin(st * self.speed / 8), 64 * self.frequency) * self.amount - - rv.operation = renpy.display.render.IMAGEDISSOLVE - rv.operation_alpha = 1.0 - rv.operation_complete = complete - rv.operation_parameter = self.op + mult = self.op if (self.op > 0) else 1 + progress = self.oc + math.pow(math.sin(st * self.speed / 8), 64 * self.frequency) * self.amount - if renpy.display.render.models: - - target = rv.get_size() - - op = self.op - - # Prevent a DBZ if the user gives us a 0 ramp. - if op < 1: - op = 1 - - # Compute the offset to apply to the alpha. - start = -1.0 - end = op / 256.0 - offset = start + (end - start) * complete - - rv.mesh = True - - rv.add_shader("renpy.imagedissolve",) - rv.add_uniform("u_renpy_dissolve_offset", offset) - rv.add_uniform("u_renpy_dissolve_multiplier", 256.0 / op) - rv.add_property("mipmap", renpy.config.mipmap_dissolves if (self.style.mipmap is None) else self.style.mipmap) + offset = ((mult / 256.0) + 1) * progress - 1.0 - rv.blit(mb, (0, 0), focus=False, main=False) + rv.add_uniform("u_renpy_dissolve_offset", offset) + rv.add_uniform("u_renpy_dissolve_multiplier", 256.0 / mult) + + rv.blit(mb, (0, 0), focus=False, main=False) rv.blit(nr, (0, 0), focus=False, main=False) rv.blit(cr, (0, 0)) renpy.redraw(self, 0) return rv - # This function makes a image be transparent for a bit then - # fade in and out in Act 3. def monika_alpha(trans, st, at): trans.alpha = math.pow(math.sin(st / 8), 64) * 1.4 return 0 +# This image transform adds a blood drop that gets longer and +# thinner over time. +image blood_particle_drip: + "gui/blood_drop.png" + yzoom 0 yanchor 0.2 subpixel True + linear 10 yzoom 8 + +# This image transform adds a blood drop that gets thinner +# randomly by time. +image blood_particle: + subpixel True + "gui/blood_drop.png" + zoom 0.75 + alpha 0.75 + choice: + linear 0.25 zoom 0 + choice: + linear 0.35 zoom 0 + choice: + linear 0.35 zoom 0 + choice: + linear 0.55 zoom 0 + +# This image transform adds a blood drop that squirts and +# drops for three minutes. +image blood: + size (1, 1) + truecenter + Blood("blood_particle").sm + +# This image transform adds a blood drop that doesn't squirts, +# and increases the chance of dropping. +image blood_eye: + size (1, 1) + truecenter + Blood("blood_particle", dripChance=0.5, numSquirts=0).sm + +# This image transform adds a blood drop that has a very low +# chance to drop. +image blood_eye2: + size (1, 1) + truecenter + Blood("blood_particle", dripChance=0.005, numSquirts=0, burstSize=0).sm + ## The Old Blue Screen of Death # These images tricks the player to think their PC has crashed. # This feature has been depreciated in favor for Better BSODs diff --git a/game/definitions/py/0core_ren.py b/game/definitions/py/0core_ren.py index b62c3e07..3c2be0dc 100644 --- a/game/definitions/py/0core_ren.py +++ b/game/definitions/py/0core_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file handles locking the game to ensure that only one instance is running at a time. # This is basically a revamped version of `singleton.py` to allow enabling/disabling singleton behavior. diff --git a/game/definitions/py/core_ren.py b/game/definitions/py/core_ren.py index 5c02e315..5dce8651 100644 --- a/game/definitions/py/core_ren.py +++ b/game/definitions/py/core_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the major Python code for DDLC and the Mod Template + Features. # Altering this file may break the game or mod functionality. @@ -8,6 +8,7 @@ import subprocess import sys import platform +import math import renpy # type: ignore """renpy @@ -31,6 +32,7 @@ ## DDLC Functions + def _get_android_data_directory() -> str | None: """ Returns the Android data directory path. @@ -40,14 +42,16 @@ def _get_android_data_directory() -> str | None: """ if not renpy.android: return None - + import jnius # type: ignore + activity = jnius.autoclass("org.renpy.android.PythonSDLActivity") current_activity = jnius.cast("android.app.Activity", activity.mActivity) data_directory = current_activity.getFilesDir().getAbsolutePath() return data_directory + def get_characters_folder(): """ Returns the path to the characters folder. @@ -61,7 +65,9 @@ def get_characters_folder(): if android_public_directory: characters_folder = os.path.join(android_public_directory, "characters") else: - characters_folder = os.path.join(renpy.config.basedir, "characters").replace("\\", "/") + characters_folder = os.path.join(renpy.config.basedir, "characters").replace( + "\\", "/" + ) return characters_folder @@ -184,6 +190,7 @@ def pause(time=None): :param time: The time to pause in seconds. If None, pauses indefinitely. """ global _windows_hidden + skipping_state = renpy.config.allow_skipping if not time: _windows_hidden = True @@ -194,8 +201,10 @@ def pause(time=None): if time <= 0: return _windows_hidden = True + renpy.config.allow_skipping = False renpy.pause(time) _windows_hidden = False + renpy.config.allow_skipping = skipping_state ## OS Functions @@ -208,9 +217,9 @@ def get_process_list(): :return: A list of process names. :rtype: set[str] """ - if renpy.android: + if renpy.mobile: return set() # Process listing is not supported on Android - + process_list: set[str] = set() if renpy.windows: try: @@ -298,9 +307,9 @@ def get_user_account_name(): :return: The username of the current user or None if it cannot be determined. :rtype: str | None """ - if renpy.android: - return None # User account retrieval is not supported on Android - + if renpy.mobile: + return None # User account retrieval is not supported on Android + # Reject if streaming to protect privacy if is_user_streaming(): return None @@ -404,6 +413,15 @@ def recolorize( ### Dynamic Super Positioning +def get_resolution_scale() -> float: + """ + Returns the scale factor based on the current screen width compared to the original game's width (1280). + + :return: The scale factor. + :rtype: float + """ + return renpy.config.screen_width / 1280.0 + def dsp(original_position_value: int | float) -> int: """ Dynamically adjusts the position value of an element based on the @@ -411,11 +429,8 @@ def dsp(original_position_value: int | float) -> int: This assumes that the original position value is set for a 1280x720 resolution. """ - valueIsInt = isinstance(original_position_value, int) - scale_position_by = renpy.config.screen_width / 1280.0 - if valueIsInt: - return int(original_position_value * scale_position_by) - return original_position_value * scale_position_by + scale_position_by = get_resolution_scale() + return int(original_position_value * scale_position_by) ### Dynamic Super Resolution @@ -432,6 +447,133 @@ def dsr(image_path: str): ) +### DDLC Mobile Scaling Functions +def get_variable_size(min: int, max: int, mod: float | None = None) -> int: + """ + Returns a size value that scales between min and max based on the mod value. + + :param min: The minimum size value. + :param max: The maximum size value. + :param mod: The modifier value to determine the scaling factor. If None, uses the current text scale. + :type min: int + :type max: int + :type mod: float | None + + :return: The calculated size value. + :rtype: int + """ + scale = mod + if scale is None: + scale = renpy.store.gui.text_scale + return round(min * (1 - scale) + max * scale) + + +def get_variable_size_f(min: int, max: int, mod: float | None = None) -> float: + """ + Returns a size value that scales between min and max based on the mod value. + + :param min: The minimum size value. + :param max: The maximum size value. + :param mod: The modifier value to determine the scaling factor. If None, uses the current text scale. + :type min: int + :type max: int + :type mod: float | None + + :return: The calculated size value. + :rtype: float + """ + scale = mod + if scale is None: + scale = renpy.store.gui.text_scale + return min * (1 - scale) + max * scale + + +def get_relative_size( + size: int, ref_min: int, ref_max: int, mod: float | None = None +) -> int: + """ + Returns a size value that is relative to a reference size range based on the mod value. + :param size: The base size value. + :param ref_min: The minimum reference size value. + :param ref_max: The maximum reference size value. + :param mod: The modifier value to determine the scaling factor. If None, uses the current text scale. + :type size: int + :type ref_min: int + :type ref_max: int + :type mod: float | None + + :return: The calculated relative size value. + :rtype: int + """ + scale = mod + if scale is None: + scale = renpy.store.gui.text_scale + return round(size * (get_variable_size(ref_min, ref_max, scale) / ref_min)) + + +def get_relative_size_f( + size: int, ref_min: int, ref_max: int, mod: float | None = None +) -> float: + """ + Returns a size value that is relative to a reference size range based on the mod value. + :param size: The base size value. + :param ref_min: The minimum reference size value. + :param ref_max: The maximum reference size value. + :param mod: The modifier value to determine the scaling factor. If None, uses the current text scale. + :type size: int + :type ref_min: int + :type ref_max: int + :type mod: float | None + + :return: The calculated relative size value. + :rtype: float + """ + scale = mod + if scale is None: + scale = renpy.store.gui.text_scale + return size * (get_variable_size_f(ref_min, ref_max, scale) / ref_min) + + +def get_scaled_outlines(outlines, ref_min: int, ref_max: int, mod: float | None = None): + """ + Returns a list of outlines with sizes scaled relative to a reference size range based on the mod value. + + :param outlines: + :param ref_min: The minimum reference size value. + :param ref_max: The maximum reference size value. + :param mod: The modifier value to determine the scaling factor. If None, uses the current text scale. + :type ref_min: int + :type ref_max: int + :type mod: float | None + + :return: A list of tuples representing the scaled outlines. + :rtype: list[tuple[int, str, int, int]] + """ + scale = mod + if scale is None: + scale = renpy.store.gui.text_scale + + if isinstance(outlines[0], int): + return ( + math.ceil(get_relative_size_f(outlines[0], ref_min, ref_max, scale)), + outlines[1], + outlines[2], + outlines[3], + ) + else: + scaled_outlines = [] + for outline in outlines: + scaled_outlines.append( + ( + math.ceil(get_relative_size_f(outline[0], ref_min, ref_max, scale)), + outline[1], + outline[2], + outline[3], + ) + ) + return scaled_outlines + + ## Initialize Core Code # Setup mapping for the game menu and hide windows. @@ -447,6 +589,11 @@ def dsr(image_path: str): # Initialize gesture mapping for Android devices. if renpy.android: renpy.config.keymap["rollback"] = [] - renpy.config.keymap["history"] = [ 'K_PAGEUP', 'repeat_K_PAGEUP', 'K_AC_BACK', 'mousedown_4' ] + renpy.config.keymap["history"] = [ + "K_PAGEUP", + "repeat_K_PAGEUP", + "K_AC_BACK", + "mousedown_4", + ] renpy.pure(dsp) diff --git a/game/definitions/py/splash_ren.py b/game/definitions/py/splash_ren.py index 97bfcee8..b9979f76 100644 --- a/game/definitions/py/splash_ren.py +++ b/game/definitions/py/splash_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file checks that 'audio.rpa', 'fonts.rpa' and 'images.rpa' are in the # game folder and if the project is in a cloud folder (OneDrive). # Note: For building a mod for PC/Android, you must keep the DDLC RPAs diff --git a/game/definitions/splash.rpy b/game/definitions/splash.rpy index e6ea7a84..6eed77c7 100644 --- a/game/definitions/splash.rpy +++ b/game/definitions/splash.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This is where the splashscreen, disclaimer and menu code reside in. # This image text shows the splash message when the game loads. @@ -119,6 +119,10 @@ image menu_nav: #recolorize("gui/overlay/main_menu.png", "#ffbde1") menu_nav_move +image game_nav: + "gui/overlay/main_menu.png" + #recolorize("gui/overlay/main_menu.png", "#ffbde1") + ## Main Menu Effects # These transforms and image transform store the effects that appear in the # main menu on startup. diff --git a/game/definitions/transforms.rpy b/game/definitions/transforms.rpy index e64ee934..cf6a08c4 100644 --- a/game/definitions/transforms.rpy +++ b/game/definitions/transforms.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file defines the placements and animations in DDLC. # This transform sizes the character properly at the given X position. @@ -320,6 +320,11 @@ transform cgfade: alpha 1.0 linear 0.5 alpha 0.0 +# From Mobile Version of DDLC. Keeps the base CG of Natsuki's closet scene static. +transform n_cg2_static: + xoffset 0 + alpha 1.0 + # This transform causes Natsuki to wiggle on screen when she panics in her closet CG. transform n_cg2_wiggle: subpixel True @@ -332,6 +337,8 @@ transform n_cg2_wiggle: easeout 0.15 xoffset 0 easein 0.15 xoffset -5 ease 0.15 xoffset 0 + on replaced: + xoffset 0 # This transform loop repeats the wiggle effect each second. transform n_cg2_wiggle_loop: @@ -553,3 +560,6 @@ init python: transform malpha(a=1.00): i11 alpha a + +transform loc_text_fit: + fit "scale-down" \ No newline at end of file diff --git a/game/gui.rpy b/game/gui.rpy index d96d7536..43a49965 100644 --- a/game/gui.rpy +++ b/game/gui.rpy @@ -10,6 +10,9 @@ init -2 python: # This sets the resolution of DDLC to 1280x720p gui.init(1280, 720) +default preferences.text_scale = (1.0 if renpy.variant("small") else 0.0) +define gui.text_scale = gui.preference("text_scale", (1.0 if renpy.variant("small") else 0.0)) + ## GUI Sounds # These variables set the sound effects for the GUI elements in the game. define -2 gui.hover_sound = "gui/sfx/hover.ogg" # Hover Sound Effect @@ -38,6 +41,10 @@ define -2 gui.selected_color = '#bb5588' # This color is used for a text button when it cannot be selected. define -2 gui.insensitive_color = '#aaaaaa7f' +# These colors are used for outlining text in High Contrast mode. +define gui.hc_label_outline_color = "#b59" +define gui.hc_label_outline_selected_color = "#ffbde1" + # These colors are used for bars that are not filled in completely. They are not # used directly, but are used when re-generating bar image files. define -2 gui.muted_color = '#6666a3' @@ -51,54 +58,60 @@ define -2 gui.interface_text_color = '#ffffff' # These variables set the font and its' size for DDLC's text in-game. # This font is used for in-game text. -define -2 gui.default_font = "gui/font/Aller_Rg.ttf" +define gui.text_font = "gui/font/Aller_Rg.ttf" # This font is used for character names. -define -2 gui.name_font = "gui/font/RifficFree-Bold.ttf" +define gui.name_text_font = "gui/font/RifficFree-Bold.ttf" # This font is used for out-of-game text. -define -2 gui.interface_font = "gui/font/Aller_Rg.ttf" +define gui.interface_text_font = "gui/font/Aller_Rg.ttf" # The text size of normal dialogue text. -define -2 gui.text_size = 24 +define gui.text_size = 24 +define gui.max_text_size = 36 # This determines the text size of character names. -define -2 gui.name_text_size = 24 +define gui.name_text_size = 24 +define gui.max_name_text_size = 36 # This determines the text size of the game's user interface. -define -2 gui.interface_text_size = 24 +define gui.interface_text_size = 24 +define gui.max_interface_text_size = 36 # This determines the text size of the game's label in the user interface. -define -2 gui.label_text_size = 28 +define gui.label_text_size = 28 +define gui.max_label_text_size = 36 # This determines the text size of the notification screen. -define -2 gui.notify_text_size = 16 +define gui.notify_text_size = 16 +define gui.max_notify_text_size = 25 # This determines the text size of the game's title on the bottom-right. -define -2 gui.title_text_size = 38 +define gui.title_text_size = 38 ## Main Menu and Game Menu # These variables set what is shown in the game menu. # This sets the background for the main menu -define -2 gui.main_menu_background = "menu_bg" +define gui.main_menu_background = "menu_bg" # This sets background for the pause/game menu -define -2 gui.game_menu_background = "game_menu_bg" +define gui.game_menu_background = "game_menu_bg" ## Dialogue # These variables set the dialogue box positions and placement in-game. # This controls the height of the textbox containing dialogue. -define -2 gui.textbox_height = 182 +define gui.textbox_height = 147 +define gui.max_textbox_height = 211 # This controls the placement of the textbox vertically on the screen. # 0.0 is the top, 0.5 is the center, and 1.0 is the bottom. -define -2 gui.textbox_yalign = 0.99 +define gui.textbox_yalign = 0.99 # This controls the placement of the speaking character's name. -define gui.name_xpos = 350 -define gui.name_ypos = -3 +define gui.name_xpos = 0.14 +define gui.name_ypos = -0.26 # This controls the horizontal alignment of the character's name. define gui.name_xalign = 0.5 @@ -107,6 +120,8 @@ define gui.name_xalign = 0.5 # characters' name. define gui.namebox_width = 168 define gui.namebox_height = 39 +define gui.max_namebox_width = 224 +define gui.max_namebox_height = 52 # This controls the borders of the box containing the characters' name in # left, top, right, and bottom order. @@ -116,14 +131,18 @@ define gui.namebox_borders = Borders(5, 5, 5, 2) define gui.namebox_tile = False # This controls the placement of dialogue relative to the textbox. -define gui.text_xpos = 268 -define gui.text_ypos = 62 +define gui.dialogue_xpos = 40 +define gui.dialogue_ypos = 23 # This controls the maximum width of dialogue text. -define gui.text_width = 744 +define gui.dialogue_width = 764 +define gui.max_dialogue_width = 1146 # This controls the horizontal alignment of the dialogue text. -define gui.text_xalign = 0.0 +define gui.dialogue_text_xalign = 0.0 + +define gui.ctc_xalign = get_variable_size_f(0.81, 0.96, gui.text_scale) +define gui.ctc_zoom = ((1.25 if renpy.mobile else 1.0) + get_variable_size_f(0, 0.5, gui.text_scale)) ## Buttons # These variables set the buttons in-game. @@ -143,10 +162,11 @@ define gui.button_borders = Borders(4, 4, 4, 4) define gui.button_tile = False # This controls the font that the button will use. -define gui.button_text_font = gui.interface_font +define gui.button_text_font = gui.interface_text_font # This controls the font size of the text used by the button. define gui.button_text_size = gui.interface_text_size +define gui.max_button_text_size = gui.max_interface_text_size # This controls the color of button text in various states. define gui.button_text_idle_color = gui.idle_color @@ -159,8 +179,14 @@ define gui.button_text_xalign = 0.0 # This controls the borders on each side of the # check/radio buttons in left, top, right, bottom order. -define gui.radio_button_borders = Borders(28, 4, 4, 4) -define gui.check_button_borders = Borders(28, 4, 4, 4) +define gui.radio_button_borders = Borders((34 + get_variable_size(0, 16, gui.text_scale)), 4, 4, 4) +define gui.check_button_borders = Borders((34 + get_variable_size(0, 16, gui.text_scale)), 4, 4, 4) + +define gui.radio_button_text_yalign = (0.5 if renpy.mobile else 0.0) +define gui.radio_button_text_xoffset = (5 if renpy.mobile else 0) + +define gui.check_button_text_yoffset = get_variable_size(-8, -12, gui.text_scale) +define gui.check_button_text_xoffset = (5 if renpy.mobile else 0) # This controls the horizontal alignment of the confirm button. define gui.confirm_button_text_xalign = 0.5 @@ -171,9 +197,11 @@ define gui.page_button_borders = Borders(10, 4, 10, 4) ## Quick Buttons # These variables set the buttons in the quick menu and it's text. +define gui.quick_menu_spacing = 0 +define gui.max_quick_menu_spacing = 30 define gui.quick_button_text_size = 14 - +define gui.max_quick_button_text_size = 20 define gui.quick_button_text_idle_color = "#522" define gui.quick_button_text_hover_color = "#fcc" define gui.quick_button_text_selected_color = gui.accent_color @@ -183,15 +211,17 @@ define gui.quick_button_text_insensitive_color = "#a66" # These variables set the buttons of the choice (menu) buttons. define gui.choice_button_width = 420 +define gui.max_choice_button_width = 600 define gui.choice_button_height = None - define gui.choice_button_tile = False define gui.choice_button_borders = Borders(100, 5, 100, 5) -define gui.choice_button_text_font = gui.default_font +define gui.choice_button_text_font = gui.text_font define gui.choice_button_text_size = gui.text_size define gui.choice_button_text_xalign = 0.5 +define gui.max_choice_button_text_size = gui.max_text_size +define gui.choice_button_text_xalign = 0.5 define gui.choice_button_text_idle_color = "#000" define gui.choice_button_text_hover_color = "#fa9" @@ -222,8 +252,11 @@ define gui.file_slot_rows = 2 # These variables control the positioning and spacing of various user interface # elements. -define gui.navigation_xpos = 80 +define gui.navigation_xpos = 50 +define gui.max_navigation_xpos = 80 + define gui.skip_ypos = 10 + define gui.notify_ypos = 45 # This controls the spacing between each menu/choice option in the choice screen. @@ -231,6 +264,7 @@ define gui.choice_spacing = 22 # This controls the spacing between each navigation option in the navigation screen. define gui.navigation_spacing = 6 +define gui.max_navigation_spacing = 36 # This controls the spacing between each preference and preference button option # in the preference screen. @@ -239,9 +273,11 @@ define gui.pref_button_spacing = 0 # This controls the spacing between each page option in the page screen. define gui.page_spacing = 0 +define gui.slot_spacing = 10 # This controls the spacing between each save/load slot option in the save/load screen. -define gui.slot_spacing = 10 +define gui.pref_button_spacing = 10 +define gui.max_pref_button_spacing = 20 ## Frames # These variables control the border of frames in-game such as the confirm prompt. @@ -307,13 +343,13 @@ define gui.history_height = None define gui.history_name_xpos = 150 define gui.history_name_ypos = 0 define gui.history_name_width = 150 -define gui.history_name_xalign = 1.0 +define gui.history_name_xalign = 0.0 # This controls the position, width, and alignment of the characters' dialogue in # the history menu. -define gui.history_text_xpos = 170 -define gui.history_text_ypos = 5 -define gui.history_text_width = 740 +define gui.history_text_xpos = 20 +define gui.history_text_ypos = get_variable_size(40, 57) +define gui.history_text_width = 800 define gui.history_text_xalign = 0.0 ## NVL @@ -354,67 +390,17 @@ define gui.nvl_thought_xalign = 0.0 define gui.nvl_button_xpos = 450 define gui.nvl_button_xalign = 0.0 +define gui.riffic_font = "gui/font/RifficFree-Bold.ttf" +define gui.halogen_font = "gui/font/Halogen.ttf" + ## Mobile Phones & Tablets # These variables control how DDLC is displayed on a mobile platform. init python: - # This increases the size of the quick buttons to make them easier to touch - # on tablets and phones. - if renpy.variant("touch"): - - gui.quick_button_borders = Borders(20, 14, 20, 0) - - # This changes the size and spacing of various GUI elements to ensure they - # are easily visible on smaller devices. - if renpy.variant("small"): - - ## Font Size - gui.text_size = 24 - gui.name_text_size = 24 - gui.notify_text_size = 24 - gui.interface_text_size = 26 - gui.button_text_size = 26 - gui.label_text_size = 28 - - ## Dialogue Box/Name Box Positions, Heights and Alignments. - gui.textbox_height = 182 - gui.name_xpos = 350 - gui.text_xpos = 268 - gui.text_ypos = 62 - gui.text_width = 744 - gui.text_xalign = 0.0 - - ## Choice Button Width - gui.choice_button_width = 420 - - ## Spacing - gui.navigation_spacing = 6 - gui.pref_button_spacing = 10 - - ## History - gui.history_height = None - gui.history_text_width = 740 - - ## Save/Load File Slots - gui.file_slot_cols = 3 + @gui.variant + def small(): + gui.file_slot_cols = 2 gui.file_slot_rows = 2 - - ## NVL - gui.nvl_height = 115 - - gui.nvl_name_width = 150 - gui.nvl_name_xpos = 430 - - gui.nvl_text_width = 590 - gui.nvl_text_xpos = 450 - gui.nvl_text_ypos = 8 - - gui.nvl_thought_width = 780 - gui.nvl_thought_xpos = 240 - - gui.nvl_button_width = 1240 - gui.nvl_button_xpos = 450 - - ## Quick Menu - gui.quick_button_text_size = 14 + + gui.quick_button_borders = Borders(60, 14, 60, 0) diff --git a/game/gui/check_foreground.png b/game/gui/check_foreground.png new file mode 100644 index 00000000..9830f3d6 Binary files /dev/null and b/game/gui/check_foreground.png differ diff --git a/game/gui/check_foreground_hc.png b/game/gui/check_foreground_hc.png new file mode 100644 index 00000000..eb14d05c Binary files /dev/null and b/game/gui/check_foreground_hc.png differ diff --git a/game/gui/check_selected_foreground.png b/game/gui/check_selected_foreground.png new file mode 100644 index 00000000..c6b31c87 Binary files /dev/null and b/game/gui/check_selected_foreground.png differ diff --git a/game/gui/check_selected_foreground_hc.png b/game/gui/check_selected_foreground_hc.png new file mode 100644 index 00000000..93e8cc55 Binary files /dev/null and b/game/gui/check_selected_foreground_hc.png differ diff --git a/game/gui/cw_frame.png b/game/gui/cw_frame.png new file mode 100644 index 00000000..960747fb Binary files /dev/null and b/game/gui/cw_frame.png differ diff --git a/game/gui/cwicon.png b/game/gui/cwicon.png new file mode 100644 index 00000000..0e9e0bb5 Binary files /dev/null and b/game/gui/cwicon.png differ diff --git a/game/gui/namebox-deselected.png b/game/gui/namebox-deselected.png new file mode 100644 index 00000000..8e4ce4d9 Binary files /dev/null and b/game/gui/namebox-deselected.png differ diff --git a/game/gui/namebox-selected.png b/game/gui/namebox-selected.png new file mode 100644 index 00000000..66adc801 Binary files /dev/null and b/game/gui/namebox-selected.png differ diff --git a/game/gui/pref_accessibility_icon.png b/game/gui/pref_accessibility_icon.png new file mode 100644 index 00000000..d0ee74d6 Binary files /dev/null and b/game/gui/pref_accessibility_icon.png differ diff --git a/game/gui/pref_accessibility_icon_selected.png b/game/gui/pref_accessibility_icon_selected.png new file mode 100644 index 00000000..292a487f Binary files /dev/null and b/game/gui/pref_accessibility_icon_selected.png differ diff --git a/game/gui/pref_bronya_icon.png b/game/gui/pref_bronya_icon.png new file mode 100644 index 00000000..c5184be4 Binary files /dev/null and b/game/gui/pref_bronya_icon.png differ diff --git a/game/gui/pref_bronya_icon_selected.png b/game/gui/pref_bronya_icon_selected.png new file mode 100644 index 00000000..510c2e9e Binary files /dev/null and b/game/gui/pref_bronya_icon_selected.png differ diff --git a/game/gui/pref_display_icon.png b/game/gui/pref_display_icon.png new file mode 100644 index 00000000..02abbd9e Binary files /dev/null and b/game/gui/pref_display_icon.png differ diff --git a/game/gui/pref_display_icon_mobile.png b/game/gui/pref_display_icon_mobile.png new file mode 100644 index 00000000..868a1998 Binary files /dev/null and b/game/gui/pref_display_icon_mobile.png differ diff --git a/game/gui/pref_display_icon_mobile_selected.png b/game/gui/pref_display_icon_mobile_selected.png new file mode 100644 index 00000000..e33b897b Binary files /dev/null and b/game/gui/pref_display_icon_mobile_selected.png differ diff --git a/game/gui/pref_display_icon_selected.png b/game/gui/pref_display_icon_selected.png new file mode 100644 index 00000000..82d25f56 Binary files /dev/null and b/game/gui/pref_display_icon_selected.png differ diff --git a/game/gui/pref_language_icon.png b/game/gui/pref_language_icon.png new file mode 100644 index 00000000..71af8a05 Binary files /dev/null and b/game/gui/pref_language_icon.png differ diff --git a/game/gui/pref_language_icon_selected.png b/game/gui/pref_language_icon_selected.png new file mode 100644 index 00000000..310d4591 Binary files /dev/null and b/game/gui/pref_language_icon_selected.png differ diff --git a/game/gui/scrollbar/horizontal_poem_bar.png b/game/gui/scrollbar/horizontal_poem_bar.png new file mode 100644 index 00000000..4e09093c Binary files /dev/null and b/game/gui/scrollbar/horizontal_poem_bar.png differ diff --git a/game/gui/scrollbar/horizontal_poem_bar_short.png b/game/gui/scrollbar/horizontal_poem_bar_short.png new file mode 100644 index 00000000..e3677946 Binary files /dev/null and b/game/gui/scrollbar/horizontal_poem_bar_short.png differ diff --git a/game/gui/scrollbar/horizontal_poem_bar_short_hc.png b/game/gui/scrollbar/horizontal_poem_bar_short_hc.png new file mode 100644 index 00000000..173b4c7f Binary files /dev/null and b/game/gui/scrollbar/horizontal_poem_bar_short_hc.png differ diff --git a/game/gui/scrollbar/horizontal_poem_thumb.png b/game/gui/scrollbar/horizontal_poem_thumb.png new file mode 100644 index 00000000..e8a43faf Binary files /dev/null and b/game/gui/scrollbar/horizontal_poem_thumb.png differ diff --git a/game/gui/scrollbar/vertical_poem_bar_hc.png b/game/gui/scrollbar/vertical_poem_bar_hc.png new file mode 100644 index 00000000..f4f3adcb Binary files /dev/null and b/game/gui/scrollbar/vertical_poem_bar_hc.png differ diff --git a/game/gui/scrollbar/vertical_poem_thumb.png b/game/gui/scrollbar/vertical_poem_thumb.png new file mode 100644 index 00000000..4bcf541e Binary files /dev/null and b/game/gui/scrollbar/vertical_poem_thumb.png differ diff --git a/game/gui/slider/horizontal_hover_thumb.png b/game/gui/slider/horizontal_hover_thumb.png new file mode 100644 index 00000000..d6eea72f Binary files /dev/null and b/game/gui/slider/horizontal_hover_thumb.png differ diff --git a/game/gui/slider/horizontal_hover_thumb_hc.png b/game/gui/slider/horizontal_hover_thumb_hc.png new file mode 100644 index 00000000..f45e4522 Binary files /dev/null and b/game/gui/slider/horizontal_hover_thumb_hc.png differ diff --git a/game/gui/slider_autopace_icon_hc_max.png b/game/gui/slider_autopace_icon_hc_max.png new file mode 100644 index 00000000..d84342ff Binary files /dev/null and b/game/gui/slider_autopace_icon_hc_max.png differ diff --git a/game/gui/slider_autopace_icon_hc_min.png b/game/gui/slider_autopace_icon_hc_min.png new file mode 100644 index 00000000..b7039f77 Binary files /dev/null and b/game/gui/slider_autopace_icon_hc_min.png differ diff --git a/game/gui/slider_autopace_icon_max.png b/game/gui/slider_autopace_icon_max.png new file mode 100644 index 00000000..f3d8aac5 Binary files /dev/null and b/game/gui/slider_autopace_icon_max.png differ diff --git a/game/gui/slider_autopace_icon_min.png b/game/gui/slider_autopace_icon_min.png new file mode 100644 index 00000000..74a9c150 Binary files /dev/null and b/game/gui/slider_autopace_icon_min.png differ diff --git a/game/gui/slider_textsize_icon_hc_max.png b/game/gui/slider_textsize_icon_hc_max.png new file mode 100644 index 00000000..15e380cf Binary files /dev/null and b/game/gui/slider_textsize_icon_hc_max.png differ diff --git a/game/gui/slider_textsize_icon_hc_min.png b/game/gui/slider_textsize_icon_hc_min.png new file mode 100644 index 00000000..b4c3d09c Binary files /dev/null and b/game/gui/slider_textsize_icon_hc_min.png differ diff --git a/game/gui/slider_textsize_icon_max.png b/game/gui/slider_textsize_icon_max.png new file mode 100644 index 00000000..5c098a07 Binary files /dev/null and b/game/gui/slider_textsize_icon_max.png differ diff --git a/game/gui/slider_textsize_icon_min.png b/game/gui/slider_textsize_icon_min.png new file mode 100644 index 00000000..c8395bdd Binary files /dev/null and b/game/gui/slider_textsize_icon_min.png differ diff --git a/game/gui/slider_volume_icon_hc_max.png b/game/gui/slider_volume_icon_hc_max.png new file mode 100644 index 00000000..868a1998 Binary files /dev/null and b/game/gui/slider_volume_icon_hc_max.png differ diff --git a/game/gui/slider_volume_icon_hc_min.png b/game/gui/slider_volume_icon_hc_min.png new file mode 100644 index 00000000..7ae1c037 Binary files /dev/null and b/game/gui/slider_volume_icon_hc_min.png differ diff --git a/game/gui/slider_volume_icon_hc_mute.png b/game/gui/slider_volume_icon_hc_mute.png new file mode 100644 index 00000000..9c1f388c Binary files /dev/null and b/game/gui/slider_volume_icon_hc_mute.png differ diff --git a/game/gui/slider_volume_icon_max.png b/game/gui/slider_volume_icon_max.png new file mode 100644 index 00000000..e33b897b Binary files /dev/null and b/game/gui/slider_volume_icon_max.png differ diff --git a/game/gui/slider_volume_icon_min.png b/game/gui/slider_volume_icon_min.png new file mode 100644 index 00000000..37e554d2 Binary files /dev/null and b/game/gui/slider_volume_icon_min.png differ diff --git a/game/gui/slider_volume_icon_mute.png b/game/gui/slider_volume_icon_mute.png new file mode 100644 index 00000000..f6e872b8 Binary files /dev/null and b/game/gui/slider_volume_icon_mute.png differ diff --git a/game/gui/textbox_alphamask.png b/game/gui/textbox_alphamask.png new file mode 100644 index 00000000..a09622a4 Binary files /dev/null and b/game/gui/textbox_alphamask.png differ diff --git a/game/gui/textbox_alphamask_opaque.png b/game/gui/textbox_alphamask_opaque.png new file mode 100644 index 00000000..7d5ade50 Binary files /dev/null and b/game/gui/textbox_alphamask_opaque.png differ diff --git a/game/gui/textbox_border.png b/game/gui/textbox_border.png new file mode 100644 index 00000000..5605e40c Binary files /dev/null and b/game/gui/textbox_border.png differ diff --git a/game/gui/textbox_highlight.png b/game/gui/textbox_highlight.png new file mode 100644 index 00000000..b92642e3 Binary files /dev/null and b/game/gui/textbox_highlight.png differ diff --git a/game/gui/textbox_tile.png b/game/gui/textbox_tile.png new file mode 100644 index 00000000..452cb597 Binary files /dev/null and b/game/gui/textbox_tile.png differ diff --git a/game/gui/viewport-vertical-fade.png b/game/gui/viewport-vertical-fade.png new file mode 100644 index 00000000..61aad170 Binary files /dev/null and b/game/gui/viewport-vertical-fade.png differ diff --git a/game/poem_game/py/poemgame_chibi_ren.py b/game/poem_game/py/poemgame_chibi_ren.py index 6743b322..a1d18cc5 100644 --- a/game/poem_game/py/poemgame_chibi_ren.py +++ b/game/poem_game/py/poemgame_chibi_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the transform code for the Chibi animations in the DDLC poem game. # The code is designed to work with Ren'Py 8 and uses the `_ren.py` approach for Python code. diff --git a/game/poem_game/py/poemgame_ren.py b/game/poem_game/py/poemgame_ren.py index 33c03b8d..93af3173 100644 --- a/game/poem_game/py/poemgame_ren.py +++ b/game/poem_game/py/poemgame_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Python code for DDLC's poem game. # The code logic has been rewritten to use the Ren'Py `_ren.py` approach for Python code. diff --git a/game/poem_game/py/poemwords_ren.py b/game/poem_game/py/poemwords_ren.py index d9e4f4a0..10c0e756 100644 --- a/game/poem_game/py/poemwords_ren.py +++ b/game/poem_game/py/poemwords_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Python code of assigning words to characters in the poem game of DDLC. # This file replaces the original `poemwords.txt` file and defines the words used in the poem game diff --git a/game/poem_game/script-poemgame.rpy b/game/poem_game/script-poemgame.rpy index 4ae47afa..e3d801f4 100644 --- a/game/poem_game/script-poemgame.rpy +++ b/game/poem_game/script-poemgame.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Ren'Py code for the Poem Game in DDLC. # For the Python code, see `poemgame_ren.py` in the `py` directory. diff --git a/game/poem_responses/poems.rpy b/game/poem_responses/poems.rpy index a6166f72..c334bd02 100644 --- a/game/poem_responses/poems.rpy +++ b/game/poem_responses/poems.rpy @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Ren'Py code for displaying poems in DDLC. # For the Python code, see `poems_ren.py` in the `py` directory. diff --git a/game/poem_responses/py/poemresponses_ren.py b/game/poem_responses/py/poemresponses_ren.py index d357c3a3..661892e3 100644 --- a/game/poem_responses/py/poemresponses_ren.py +++ b/game/poem_responses/py/poemresponses_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains code that manages parts of the poem response minigame. ## Not included in the game, but used for IDEs to avoid multiple warnings. diff --git a/game/poem_responses/py/poems_ren.py b/game/poem_responses/py/poems_ren.py index 45fe15ad..5d96d3e5 100644 --- a/game/poem_responses/py/poems_ren.py +++ b/game/poem_responses/py/poems_ren.py @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Azariel Del Carmen (bronya_rand). All rights reserved. +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file contains the Python code for displaying poems in DDLC. # The logic for displaying poems has been changed drastically compared to the original diff --git a/game/py/screens_ren.py b/game/py/screens_ren.py new file mode 100644 index 00000000..4b38225a --- /dev/null +++ b/game/py/screens_ren.py @@ -0,0 +1,28 @@ +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. +# This file contains utility functions for screen adjustments in the Mod Template (some used for DDLC Mobile features). + +"""renpy +init python: +""" + +TABS = 4 # Default number of tabs in preferences. + +# (230 - 300) / (4 - 3) from DDLC Mobile to Mod Template defaults +# If adding more tabs, adjust accordingly. +WIDTH_LINEAR_SLOPE = -70 + + +def get_pref_tab_button_width(): + """ + Docstring for get_button_width + + :param tabs: Description + :type tabs: int + """ + # y - 300 = WIDTH_LINEAR_SLOPE(x - 3) using DDLC Mobile original + # width of 300 at 3 tabs (or y = WIDTH_LINEAR_SLOPE * x + 510) + width = WIDTH_LINEAR_SLOPE * TABS + 510 + + # Ensure the width is within reasonable bounds + min_width = 150 + return max(width, min_width) diff --git a/game/screens.rpy b/game/screens.rpy index 15c326a3..370d8cb7 100644 --- a/game/screens.rpy +++ b/game/screens.rpy @@ -1,5 +1,4 @@ -## screens.rpy - +# Copyright 2019-2026 Azariel Del Carmen (bronya_rand). All rights reserved. # This file declares all the screens and styles in DDLC. ## Initialization @@ -7,8 +6,8 @@ init offset = -1 -# Thanks RenpyTom! Borrowed from the Ren'Py Launcher init python: + # Thanks RenpyTom! Borrowed from the Ren'Py Launcher def scan_translations(): languages = renpy.known_languages() @@ -25,6 +24,43 @@ init python: return (rv[:bound], rv[bound:2*bound]) + def textbox_frame(high_contrast=False, opaque=False, dots=True, glare=True, button_glare=False): + alphamask = ("textbox_alphamask_opaque" if opaque else "textbox_alphamask") + bg_glare = Crop((0,0,1.0,1.0), "textbox_highlight") if glare else Solid("#00000000") + if button_glare and glare: + bg_glare = Crop((0,0,1.0,1.0), Frame("gui/quick_button_highlight.png")) + bg = Solid("#ffbde1") + if high_contrast: + bg = Solid("#39051d") + elif dots: + bg = Image("gui/textbox_tile.png", oversample=3) + + r = Fixed( + Transform(AlphaMask(Fixed(Frame(bg, tile=True), bg_glare, fit_first=True), alphamask), xalign=0.5, yalign=0.5, zoom=1.002), + Frame(Image("gui/textbox_border.png", oversample=3), 42, 42)) + + return r + +image textbox_border: + Frame(Image("gui/textbox_border.png", oversample=3), 42, 42) +image textbox_highlight: + Image("gui/textbox_highlight.png", oversample=3, xalign=0.5, yalign=1.0) +image textbox_alphamask: + Frame(Image("gui/textbox_alphamask.png", oversample=3), 42, 42) +image textbox_alphamask_opaque: + Frame(Image("gui/textbox_alphamask_opaque.png", oversample=3), 42, 42) +image textbox_buttonglare: + Fixed(Frame(Image("gui/textbox_tile.png", oversample=3), tile=True), Crop((0,0,1.0,1.0), Frame("gui/quick_button_highlight.png")), fit_first=True) + +image textbox_background = textbox_frame() +image textbox_background_opaque = textbox_frame(opaque=True) +image textbox_background_hc = textbox_frame(high_contrast=True, glare=False) +image textbox_background_hc_opaque = textbox_frame(high_contrast=True, opaque=True, glare=False) +image rounded_frame_background = textbox_frame(dots=False) +image rounded_frame_background_opaque = textbox_frame(opaque=True, dots=False) +image rounded_frame_background_hc = textbox_frame(high_contrast=True, glare=False) +image rounded_frame_background_hc_opaque = textbox_frame(high_contrast=True, opaque=True, glare=False) + default translations = scan_translations() # Enables the ability to add more settings in the game such as Uncensored Mode. @@ -45,10 +81,10 @@ define -2 text_outline_color = "#b59" ################################################################################ style default: - font gui.default_font - size gui.text_size + font gui.text_font + size get_variable_size(gui.text_size, gui.max_text_size, gui.text_scale) color gui.text_color - outlines [(2, "#000000aa", 0, 0)] + outlines get_scaled_outlines([(2, "#000000aa", 0, 0)], gui.text_size, gui.max_text_size, gui.text_scale) line_overlap_split 1 line_spacing 1 @@ -58,64 +94,73 @@ style default_monika is normal: style edited is default: font "gui/font/VerilySerifMono.otf" kerning 8 - outlines [(10, "#000", 0, 0)] - xpos gui.text_xpos - xanchor gui.text_xalign - xsize gui.text_width - ypos gui.text_ypos - text_align gui.text_xalign - layout ("subtitle" if gui.text_xalign else "tex") + outlines get_scaled_outlines([(10, "#000", 0, 0)], gui.text_size, gui.max_text_size, gui.text_scale) + xpos gui.dialogue_xpos + xanchor gui.dialogue_text_xalign + xsize get_variable_size(gui.dialogue_width, gui.max_dialogue_width, gui.text_scale) + ypos gui.dialogue_ypos + text_align gui.dialogue_text_xalign + layout ("subtitle" if gui.dialogue_text_xalign else "tex") style normal is default: - xpos gui.text_xpos - xanchor gui.text_xalign - xsize gui.text_width - ypos gui.text_ypos + xpos gui.dialogue_xpos + xanchor gui.dialogue_text_xalign + xsize get_variable_size(gui.dialogue_width, gui.max_dialogue_width, gui.text_scale) + ypos gui.dialogue_ypos - text_align gui.text_xalign - layout ("subtitle" if gui.text_xalign else "tex") + text_align gui.dialogue_text_xalign + layout ("subtitle" if gui.dialogue_text_xalign else "tex") style input: color gui.accent_color +style hyperlink_text: + color gui.accent_color + hover_color gui.hover_color + hover_underline True + style splash_text: - size 24 + size get_variable_size(24, 36, gui.text_scale) color "#000" - font gui.default_font + font gui.text_font text_align 0.5 outlines [] +style poemgame_button is button: + xsize 226 + ysize 63 + style poemgame_text: yalign 0.5 - font "gui/font/Halogen.ttf" - size 30 + font gui.halogen_font + size get_variable_size(30, 36, gui.text_scale) color "#000" outlines [] hover_xoffset -3 - hover_outlines [(3, "#fef", 0, 0), (2, "#fcf", 0, 0), (1, "#faf", 0, 0)] + hover_outlines get_scaled_outlines([(3, "#fef", 0, 0), (2, "#fcf", 0, 0), (1, "#faf", 0, 0)], 30, 36, gui.text_scale) style gui_text: - font gui.interface_font + font gui.interface_text_font color gui.interface_text_color - size gui.interface_text_size - + size get_variable_size(gui.interface_text_size, gui.max_interface_text_size, gui.text_scale) style button: properties gui.button_properties("button") style button_text is gui_text: properties gui.button_text_properties("button") + size get_variable_size(gui.button_text_size, gui.max_button_text_size, gui.text_scale) yalign 0.5 style label_text is gui_text: color gui.accent_color - size gui.label_text_size + size get_variable_size(gui.label_text_size, gui.max_label_text_size, gui.text_scale) style prompt_text is gui_text: color gui.text_color - size gui.interface_text_size + size get_variable_size(gui.interface_text_size, gui.max_interface_text_size, gui.text_scale) style vbar: xsize gui.bar_size @@ -134,23 +179,30 @@ style scrollbar: unscrollable "hide" bar_invert True + style vscrollbar: xsize 18 base_bar Frame("gui/scrollbar/vertical_poem_bar.png", tile=False) - thumb Frame("gui/scrollbar/vertical_poem_thumb.png", left=6, top=6, tile=True) + thumb Frame("gui/slider/horizontal_hover_thumb.png", left=6, top=6, tile=True) unscrollable "hide" bar_invert True + thumb_offset (8, 8) + +style vscrollbar_hc is vscrollbar: + base_bar Frame("gui/scrollbar/vertical_poem_bar_hc.png", tile=False) + thumb Frame("gui/slider/horizontal_hover_thumb_hc.png", left=6, top=6, tile=True) style slider: - ysize 18 + ysize (34 if renpy.mobile else 18) base_bar Frame("gui/scrollbar/horizontal_poem_bar.png", tile=False) - thumb "gui/slider/horizontal_hover_thumb.png" + thumb Transform("gui/slider/horizontal_hover_thumb.png", zoom=(1.0 if renpy.mobile else 0.7), anchor=(0.5, 0.5)) + thumb_offset 8 style vslider: xsize gui.slider_size base_bar Frame("gui/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile) thumb "gui/slider/vertical_[prefix_]thumb.png" - + thumb_offset (8, 8) style frame: padding gui.frame_borders.padding @@ -208,11 +260,11 @@ style namebox_label is say_label style window: xalign 0.5 - xfill True + xsize get_variable_size(gui.dialogue_width, gui.max_dialogue_width, gui.text_scale) + (gui.dialogue_xpos * 2) yalign gui.textbox_yalign - ysize gui.textbox_height + ysize get_variable_size(gui.textbox_height, gui.max_textbox_height, gui.text_scale) - background Transform("gui/textbox.png", xalign=0.5, yalign=1.0) + background "textbox_background" style window_monika is window: background Transform("gui/textbox_monika.png", xalign=0.5, yalign=1.0) @@ -229,30 +281,63 @@ style namebox: style say_label: color gui.accent_color - font gui.name_font - size gui.name_text_size + font gui.name_text_font + size get_variable_size(gui.name_text_size, gui.max_name_text_size, gui.text_scale) xalign gui.name_xalign yalign 0.5 - outlines [(3, text_outline_color, 0, 0), (1, text_outline_color, 1, 1)] - #outlines [(3, "#b59", 0, 0), (1, "#b59", 1, 1)] + outlines get_scaled_outlines([(3, text_outline_color, 0, 0), (1, text_outline_color, 1, 1)], gui.name_text_size, gui.max_name_text_size, gui.text_scale) style say_dialogue: - xpos gui.text_xpos - xanchor gui.text_xalign - xsize gui.text_width - ypos gui.text_ypos - - text_align gui.text_xalign - layout ("subtitle" if gui.text_xalign else "tex") - -image ctc: - xalign 0.81 yalign 0.98 xoffset -5 alpha 0.0 subpixel True - "gui/ctc.png" + xpos gui.dialogue_xpos + xanchor gui.dialogue_text_xalign + xfill True + ypos gui.dialogue_ypos + xsize get_variable_size(gui.dialogue_width, gui.max_dialogue_width, gui.text_scale) + text_align gui.dialogue_text_xalign + layout ("subtitle" if gui.dialogue_text_xalign else "tex") + +image ctc_image: + subpixel True + alpha 0.0 + xoffset -5 + Transform("gui/ctc.png", zoom=get_resolution_scale()) block: easeout 0.75 alpha 1.0 xoffset 0 easein 0.75 alpha 0.5 xoffset -5 repeat +image auto_ctc_base = Transform("gui/auto_ctc_base.png", zoom=get_resolution_scale()) +image auto_ctc_fill = Transform("gui/auto_ctc_fill.png", zoom=get_resolution_scale()) + +transform auto_ctc_pos: + zoom 0.7 + yalign 0.98 + xoffset -5 + alpha 0.0 + easeout 0.1 alpha 0.8 + on hide: + easeout 0.1 alpha 0.0 + +screen ctc: + fixed at auto_ctc_pos: + style "ctc_fixed" + + if _preferences.afm_enable == True: + add "auto_ctc_base" at auto_ctc_pos: + fit "cover" + align (0.5, 0.5) + # add "auto_ctc_fill" at radial_alpha(wait_timer=get_automode_time()), auto_ctc_pos: + # fit "cover" + # align (0.5, 0.5) + else: + add "ctc_image" at auto_ctc_pos: + fit "scale-up" + align (0.5, 0.5) + +style ctc_fixed: + xalign gui.ctc_xalign + xysize (round(50 * gui.ctc_zoom), round(50 * gui.ctc_zoom)) + ## Input screen ################################################################ ## ## This screen is used to display renpy.input. The prompt parameter is used to @@ -264,7 +349,7 @@ image ctc: ## http://www.renpy.org/doc/html/screen_special.html#input image input_caret: - Solid("#b59") + Solid(text_outline_color) size (2,25) subpixel True block: linear 0.35 alpha 0 @@ -275,26 +360,25 @@ screen input(prompt): style_prefix "input" window: + has vbox + xpos gui.dialogue_xpos + xanchor 0.5 + ypos gui.dialogue_ypos - vbox: - xpos gui.text_xpos - xanchor 0.5 - ypos gui.text_ypos - - text prompt style "input_prompt" - input id "input" + text prompt style "input_prompt" + input id "input" style input_prompt is default style input_prompt: - xmaximum gui.text_width - xalign gui.text_xalign - text_align gui.text_xalign + xmaximum gui.dialogue_width + xalign gui.dialogue_text_xalign + text_align gui.dialogue_text_xalign style input: caret "input_caret" - xmaximum gui.text_width + xmaximum gui.dialogue_width xalign 0.5 text_align 0.5 @@ -372,13 +456,13 @@ style choice_vbox: style choice_button is default: properties gui.button_properties("choice_button") - hover_sound gui.hover_sound + xsize get_variable_size(gui.choice_button_width, gui.max_choice_button_width, gui.text_scale) + hover_sound (gui.hover_sound if not renpy.mobile else None) activate_sound gui.activate_sound - idle_background Frame("gui/button/choice_idle_background.png", gui.choice_button_borders) - hover_background Frame("gui/button/choice_hover_background.png", gui.choice_button_borders) style choice_button_text is default: properties gui.button_text_properties("choice_button") + size get_variable_size(gui.choice_button_text_size, gui.max_choice_button_text_size, gui.text_scale) outlines [] @@ -445,8 +529,12 @@ style quick_button: style quick_button_text: properties gui.button_text_properties("quick_button") + size get_variable_size(gui.quick_button_text_size, gui.max_quick_button_text_size, gui.text_scale) outlines [] +style quick_hbox: + spacing get_variable_size(gui.quick_menu_spacing, gui.max_quick_menu_spacing, gui.text_scale) + ################################################################################ # Main and Game Menu Screens @@ -458,8 +546,11 @@ style quick_button_text: ## to other menus, and to start the game. init python: - def FinishEnterName(launchGame=True): - if not player: return + def FinishEnterName(start_game=True): + if not player: + if renpy.mobile: + renpy.hide_screen("name_input") + return persistent.playername = player renpy.save_persistent() renpy.hide_screen("name_input") @@ -467,38 +558,44 @@ init python: renpy.jump_out_of_context("start") screen navigation(): + if main_menu: + add "menu_nav" + else: + add "game_nav" vbox: style_prefix "navigation" - xpos gui.navigation_xpos - yalign 0.8 - - spacing gui.navigation_spacing + if renpy.mobile: + xpos gui.navigation_xpos + spacing gui.max_navigation_spacing + else: + xpos get_variable_size(gui.max_navigation_xpos, gui.navigation_xpos, gui.text_scale) + spacing get_variable_size(gui.navigation_spacing, gui.max_navigation_spacing, gui.text_scale) + + yanchor 1.0 + yalign 0.85 + xmaximum 200 if not persistent.autoload or not main_menu: if main_menu: if persistent.playthrough == 1: - textbutton _("ŔŗñĮ¼»ŧþŀÂŻŕěōì«") action If(persistent.playername, true=Start(), false=Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName))) + textbutton _("ŔŗñĮ¼»ŧþŀÂŻŕěōì«") action If(persistent.playername, true=Start(), false=Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName))) at loc_text_fit else: - textbutton _("New Game") action If(persistent.playername, true=Start(), false=Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName))) + textbutton _("New Game") action If(persistent.playername, true=Start(), false=Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName))) at loc_text_fit else: - textbutton _("History") action [ShowMenu("history"), SensitiveIf(renpy.get_screen("history") == None)] + textbutton _("History") action [ShowMenu("history"), SensitiveIf(renpy.get_screen("history") == None)] at loc_text_fit - textbutton _("Save Game") action [ShowMenu("save"), SensitiveIf(renpy.get_screen("save") == None)] + textbutton _("Save Game") action [ShowMenu("save"), SensitiveIf(renpy.get_screen("save") == None)] at loc_text_fit - textbutton _("Load Game") action [ShowMenu("load"), SensitiveIf(renpy.get_screen("load") == None)] + textbutton _("Load Game") action [ShowMenu("load"), SensitiveIf(renpy.get_screen("load") == None)] at loc_text_fit if enable_extras_menu: - textbutton _("Extras") action [ShowMenu("extras"), SensitiveIf(renpy.get_screen("extras") == None)] - - if _in_replay: - - textbutton _("End Replay") action EndReplay(confirm=True) + textbutton _("Extras") action [ShowMenu("extras"), SensitiveIf(renpy.get_screen("extras") == None)] at loc_text_fit elif not main_menu: if persistent.playthrough != 3: @@ -511,6 +608,9 @@ screen navigation(): if not enable_extras_menu: textbutton _("Credits") action ShowMenu("about") + if _in_replay: + textbutton _("End Replay") action EndReplay(confirm=True) at loc_text_fit + if renpy.variant("pc"): ## Help isn't necessary or relevant to mobile devices. @@ -523,22 +623,24 @@ screen navigation(): style navigation_button is gui_button -style navigation_button_text is gui_button_text +style navigation_button_text is gui_button_text: + size (gui.max_button_text_size if renpy.mobile else get_variable_size(gui.button_text_size, gui.max_button_text_size, gui.text_scale)) style navigation_button: size_group "navigation" properties gui.button_properties("navigation_button") - hover_sound gui.hover_sound + ysize (54 if renpy.mobile else gui.button_height) + hover_sound (gui.hover_sound if not renpy.mobile else None) activate_sound gui.activate_sound style navigation_button_text: properties gui.button_text_properties("navigation_button") - font "gui/font/RifficFree-Bold.ttf" + layout "nobreak" + font gui.riffic_font color "#fff" - outlines [(4, text_outline_color, 0, 0), (2, text_outline_color, 2, 2)] - #outlines [(4, "#b59", 0, 0), (2, "#b59", 2, 2)] - hover_outlines [(4, "#fac", 0, 0), (2, "#fac", 2, 2)] - insensitive_outlines [(4, "#fce", 0, 0), (2, "#fce", 2, 2)] + outlines get_scaled_outlines([(4, text_outline_color, 0, 0), (2, text_outline_color, 2, 2)], (gui.max_button_text_size if renpy.mobile else gui.button_text_size), gui.max_button_text_size, gui.text_scale) + hover_outlines get_scaled_outlines([(4, "#fac", 0, 0), (2, "#fac", 2, 2)], (gui.max_button_text_size if renpy.mobile else gui.button_text_size), gui.max_button_text_size, gui.text_scale) + insensitive_outlines get_scaled_outlines([(4, "#fce", 0, 0), (2, "#fce", 2, 2)], (gui.max_button_text_size if renpy.mobile else gui.button_text_size), gui.max_button_text_size, gui.text_scale) ## Main Menu screen ############################################################ @@ -568,6 +670,13 @@ screen main_menu(): ## contents of the main menu are in the navigation screen. use navigation + if gui.show_name: + vbox: + text "[config.name!t]": + style "main_menu_title" + text "[config.version!t]": + style "main_menu_version" + if not persistent.ghost_menu: add "menu_particles" add "menu_particles" @@ -576,6 +685,9 @@ screen main_menu(): if persistent.ghost_menu: add "menu_art_s_ghost" add "menu_art_m_ghost" + + if renpy.mobile: + timer 4.0 action Show("fullscreen_return_button") else: if persistent.playthrough == 1 or persistent.playthrough == 2: add "menu_art_s_glitch" @@ -586,17 +698,13 @@ screen main_menu(): add "menu_art_m" add "menu_fade" - if gui.show_name: - - vbox: - text "[config.name!t]": - style "main_menu_title" - - text "[config.version]": - style "main_menu_version" - key "K_ESCAPE" action Quit(confirm=False) +screen fullscreen_return_button: + button: + xysize (1.0, 1.0) + action Jump("quit") + style main_menu_frame is empty style main_menu_vbox is vbox style main_menu_text is gui_text @@ -610,7 +718,7 @@ style main_menu_frame: xsize 310 yfill True - background "menu_nav" + background None style main_menu_vbox: xalign 1.0 @@ -655,60 +763,67 @@ screen game_menu(title, scroll=None): style_prefix "game_menu" + use navigation + frame: style "game_menu_outer_frame" - hbox: - - # Reserve space for the navigation section. - frame: - style "game_menu_navigation_frame" + has hbox - frame: - style "game_menu_content_frame" + frame: + style "game_menu_navigation_frame" - if scroll == "viewport": + frame: + style "game_menu_content_frame" - viewport: - scrollbars "vertical" - mousewheel True - draggable True - yinitial 1.0 + # if renpy.get_screen("history"): + # background Frame("history_background") + # padding (10, 10) + # xsize 1.0 - side_yfill True + if scroll == "viewport": - vbox: - transclude + viewport: + at vp_vert_scroll_mask + scrollbars "vertical" + vscrollbar_xoffset -30 + mousewheel True + draggable True - elif scroll == "vpgrid": + side_yfill True - vpgrid: - cols 1 - yinitial 1.0 + has vbox + transclude - scrollbars "vertical" - mousewheel True - draggable True + elif scroll == "vpgrid": - side_yfill True + vpgrid: + cols 1 - transclude + scrollbars "vertical" + mousewheel True + draggable True - else: + side_yfill True transclude - use navigation + else: + + transclude - if not main_menu and persistent.playthrough == 2 and not persistent.menu_bg_m and renpy.random.randint(0, 49) == 0: + if not main_menu and persistent.playthrough == 2 and not persistent.menu_bg_m and renpy.random.randint(0, 49) == 0 and persistent.content_warnings_enabled == False: on "show" action Show("game_menu_m") textbutton _("Return"): style "return_button" - + if renpy.mobile: + xpos gui.navigation_xpos + else: + xpos get_variable_size(gui.max_navigation_xpos, gui.navigation_xpos, gui.text_scale) action Return() - label title + label title xoffset -16 if main_menu: key "game_menu" action ShowMenu("main_menu") @@ -729,10 +844,9 @@ style return_button_text is navigation_button_text style game_menu_outer_frame: bottom_padding 30 - top_padding 120 + top_padding 30 - background "gui/overlay/game_menu.png" - # background recolorize("gui/overlay/game_menu.png") + background None style game_menu_navigation_frame: xsize 280 @@ -757,15 +871,14 @@ style game_menu_label: ysize 120 style game_menu_label_text: - font "gui/font/RifficFree-Bold.ttf" + font gui.riffic_font size gui.title_text_size color "#fff" outlines [(6, text_outline_color, 0, 0), (3, text_outline_color, 2, 2)] - #outlines [(6, "#b59", 0, 0), (3, "#b59", 2, 2)] yalign 0.5 style return_button: - xpos gui.navigation_xpos + xpos get_variable_size(gui.max_navigation_xpos, gui.navigation_xpos, gui.text_scale) yalign 1.0 yoffset -30 @@ -978,14 +1091,15 @@ style page_button_text: style slot_button: properties gui.button_properties("slot_button") - idle_background Frame("gui/button/slot_idle_background.png", gui.choice_button_borders) - hover_background Frame("gui/button/slot_hover_background.png", gui.choice_button_borders) style slot_button_text: properties gui.button_text_properties("slot_button") color "#666" outlines [] +style mobile_slot_time_text is slot_button_text: + size 24 + screen viewframe_options(title): style_prefix "viewframe" @@ -1109,195 +1223,236 @@ style viewframe_text is confirm_prompt_text: # textbutton _("Reset") action [Hide("display_options"), Function(renpy.reset_physical_size)] # textbutton _("Set") action [Hide("display_options"), Function(set_physical_resolution, scale)] -screen ddlc_preferences(): - hbox: - box_wrap True +image slider_volume_icon_min: + ("gui/slider_volume_icon_min.png" if not persistent.high_contrast else "gui/slider_volume_icon_hc_min.png") +image slider_volume_icon_max: + ("gui/slider_volume_icon_max.png" if not persistent.high_contrast else "gui/slider_volume_icon_hc_max.png") - if renpy.variant("pc"): +image slider_autopace_icon_min: + ("gui/slider_autopace_icon_min.png" if not persistent.high_contrast else "gui/slider_autopace_icon_hc_min.png") +image slider_autopace_icon_max: + ("gui/slider_autopace_icon_max.png" if not persistent.high_contrast else "gui/slider_autopace_icon_hc_max.png") - vbox: - style_prefix "radio" - label _("Display") - textbutton _("Windowed") action Preference("display", "window") - textbutton _("Fullscreen") action Preference("display", "fullscreen") - # textbutton _("More") action Show("display_options") +image slider_textsize_icon_min: + ("gui/slider_textsize_icon_min.png" if not persistent.high_contrast else "gui/slider_textsize_icon_hc_min.png") +image slider_textsize_icon_max: + ("gui/slider_textsize_icon_max.png" if not persistent.high_contrast else "gui/slider_textsize_icon_hc_max.png") + +screen display_preferences(music_volume, sound_volume, voice_volume): + vbox: + fixed: + ysize 200 + + if not renpy.mobile: + vbox: + style_prefix "radio" + xalign 0.0 + label _("Display") + textbutton _("Windowed") action Preference("display", "window") + textbutton _("Fullscreen") action Preference("display", "fullscreen") + # textbutton _("More") action Show("display_options") - if config.developer: vbox: - style_prefix "radio" - label _("Rollback Side") - textbutton _("Disable") action Preference("rollback side", "disable") - textbutton _("Left") action Preference("rollback side", "left") - textbutton _("Right") action Preference("rollback side", "right") + style_prefix "slider" + xalign (1.0 if not renpy.mobile else 0.0) + ysize 1.0 - 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")) - - null height (4 * gui.pref_spacing) + if config.has_music: + label _("Music Volume") - hbox: - style_prefix "slider" - box_wrap True + side "c l r": + bar: + value ScreenVariableValue("music_volume", range=1.0, offset=0, step=0.1, force_step=True, style="slider") + changed preferences.set_mixer("music", music_volume) + add "slider_volume_icon_min" yalign 0.5 zoom 0.5 + add "slider_volume_icon_max" yalign 0.5 zoom 0.5 - vbox: - - hbox: - label _("Text Speed") + if config.has_sound: + label _("Sound Volume") + + side "c l r": + bar: + value ScreenVariableValue("sound_volume", range=1.0, offset=0, step=0.1, force_step=True, style="slider") + changed preferences.set_mixer("sfx", sound_volume) + add "slider_volume_icon_min" yalign 0.5 zoom 0.5 + add "slider_volume_icon_max" yalign 0.5 zoom 0.5 + + if config.sample_sound: + textbutton _("Test") action Play("sound", config.sample_sound) - null width 5 + if config.has_voice: + label _("Voice Volume") - text str(preferences.text_cps) style "value_text" + side "c l r": + bar: + value ScreenVariableValue("voice_volume", range=1.0, offset=0, step=0.1, force_step=True, style="slider") + changed preferences.set_mixer("voice", voice_volume) + add "slider_volume_icon_min" yalign 0.5 zoom 0.5 + add "slider_volume_icon_max" yalign 0.5 zoom 0.5 - #bar value Preference("text speed") - bar value FieldValue(_preferences, "text_cps", range=180, max_is_zero=False, style="slider", offset=20) + if config.sample_voice: + textbutton _("Test") action Play("voice", config.sample_voice) - hbox: - label _("Auto-Forward Time") - - null width 5 - - text str(round(preferences.afm_time)) style "value_text" + if config.has_music or config.has_sound or config.has_voice: + null height gui.pref_spacing - bar value Preference("auto-forward time") + textbutton _("Mute All"): + action Preference("all mute", "toggle") + style "mute_all_button" - vbox: - - if config.has_music: - hbox: - label _("Music Volume") - - null width 5 - - text str(round(preferences.get_mixer("music") * 100)) style "value_text" +screen language_preferences(): + vbox: + hbox: + style_prefix "slider" + box_wrap False - hbox: - bar value Preference("music volume") + vbox: + label _("Text Speed") - if config.has_sound: + side "c l r": + bar value FieldValue(_preferences, "text_cps", style="slider", range=180, step=30, force_step=True, max_is_zero=False, offset=20): + alt "Text Speed" + add "slider_autopace_icon_min" yalign 0.5 zoom 0.5 + add "slider_autopace_icon_max" yalign 0.5 zoom 0.5 - hbox: - label _("Sound Volume") - - null width 5 + label _("Auto-Forward Speed") + + side "c l r": + bar value FieldValue(_preferences, "afm_time", style="slider", range=18, step=3, force_step=True, offset=1): + bar_invert True + add "slider_autopace_icon_min" yalign 0.5 zoom 0.5 + add "slider_autopace_icon_max" yalign 0.5 zoom 0.5 - text str(round(preferences.get_mixer("sfx") * 100)) style "value_text" + label _("Text Size") - hbox: - bar value Preference("sound volume") + side "c l r": + bar value FieldValue(_preferences, "text_scale", style="slider", range=0.5, offset=0.5, step=0.25, force_step=True): + released gui.SetPreference("text_scale", preferences.text_scale) + alt "Text Size" + add "slider_textsize_icon_min" yalign 0.5 zoom 0.5 + add "slider_textsize_icon_max" yalign 0.5 zoom 0.5 - if config.sample_sound: - textbutton _("Test") action Play("sound", config.sample_sound) + vbox: + xsize 1.0 + if renpy.mobile: + style_prefix "radio" + label _("Allow Skipping") + textbutton _("Previously Read Text Only") action Preference("skip", "seen") + textbutton _("All Text") action Preference("skip", "all") + else: + style_prefix "check" + label _("Skip") + textbutton _("Unseen Text") action Preference("skip", "toggle") + textbutton _("After Choices") action Preference("after choices", "toggle") + + # null height 40 + + # if persistent.high_contrast == True: + # add "gui/long_divider_dark_hc.png": + # xzoom 0.65 + # yzoom 0.80 + # xoffset 30 + # else: + # add "gui/long_divider_dark.png": + # xzoom 0.65 + # yzoom 0.80 + # xoffset 30 + + # null height 14 - if config.has_voice: - hbox: - label _("Voice Volume") - - null width 5 - - text str(round(preferences.get_mixer("voice") * 100)) style "value_text" + # label _("Language") - hbox: - bar value Preference("voice volume") + # hbox: + # xsize 1.0 - if config.sample_voice: - textbutton _("Test") action Play("voice", config.sample_voice) + ## TODO: Language selection dropdown/menu + +screen accessibility_preferences(): + viewport id "settings_accessibility_viewport": + # at vp_vert_scroll_mask + draggable True + mousewheel True + ysize 1.0 + has vbox + null height 20 + + vbox: + style_prefix "check" + xsize 1.0 + + textbutton _("Content Warnings") action ToggleField(persistent, "show_content_warnings", True, False) alt _("Enable Content Warnings") + text _("Enables content warnings that will appear before scenes with disturbing subject matter.") style "pref_hint_text" + textbutton _("High Contrast Textboxes") action [ToggleField(persistent, "high_contrast", True, False), Function(gui.rebuild)] alt _("High Contrast Textboxes") + text _("Replaces the dialogue box with a darker variant, making dialogue easier to read.") style "pref_hint_text" + textbutton _("Reduce Textbox Transparency") action [ToggleField(persistent, "reduce_transparency", True, False), Function(gui.rebuild)] alt _("Reduce Textbox Transparency") + text _("Makes the dialogue box opaque, making dialogue easier to read.") style "pref_hint_text" + textbutton _("Alternate Poem Font") action ToggleField(persistent, "use_alt_poem_font", True, False) alt _("Use Alternate Poem Font") + text _("Switches handwritten fonts in characters' poems with an easier-to-read font.") style "pref_hint_text" - if config.has_music or config.has_sound or config.has_voice: - null height gui.pref_spacing + null height 20 - textbutton _("Mute All"): - action Preference("all mute", "toggle") - style "mute_all_button" + vbar value YScrollValue("settings_accessibility_viewport") xpos 1.0 xoffset -10 style ("vscrollbar" if not persistent.high_contrast else "vscrollbar_hc") screen template_preferences(): - hbox: - box_wrap True + vbox: + hbox: + box_wrap False - if extra_settings: vbox: - style_prefix "check" - label _("Game Modes") - textbutton _("Uncensored Mode") action If(persistent.uncensored_mode, - ToggleField(persistent, "uncensored_mode"), - Show("confirm", message="Are you sure you want to turn on Uncensored Mode?\nDoing so will enable more adult/sensitive\ncontent in your playthrough.\n\nThis setting will be dependent on the modder if\nthey programmed these checks in their story.", - yes_action=[Hide("confirm"), ToggleField(persistent, "uncensored_mode")], - no_action=Hide("confirm") - )) - - vbox: - style_prefix "name" - label _("Player Name") + style_prefix "name" + label _("Player Name") - null height 3 + null height 3 - if player == "": - text _("No Name Set") xalign 0.5 - else: - text "[player]" xalign 0.5 + if player == "": + text _("No Name Set") xalign 0.5 + else: + text "[player]" xalign 0.5 - textbutton _("Change Name") action Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName, launchGame=False)): - text_style "navigation_button_text" + textbutton _("Change Name") action Show(screen="name_input", message="Please enter your name", ok_action=Function(FinishEnterName, launchGame=False)): + text_style "navigation_button_text" - python: - has_discord_module = True - try: - RPC - except NameError: - has_discord_module = False - - if not renpy.android and has_discord_module: - vbox: - style_prefix "name" - label _("Discord RPC") - - python: - connect_status = _("Disconnected") - if not persistent.enable_discord: - connect_status = _("Disabled") - if RPC.rpc_connected: - connect_status = _("Connected") - - null height 3 - - text "[connect_status]" xalign 0.5 - - python: - enable_text = _("Enable") - if persistent.enable_discord: - enable_text = _("Disable") + python: + has_discord_module = True + try: + RPC + except NameError: + has_discord_module = False + + if not renpy.mobile and has_discord_module: + vbox: + style_prefix "name" + label _("Discord RPC") + + python: + connect_status = _("Disconnected") + if not persistent.enable_discord: + connect_status = _("Disabled") + if RPC.rpc_connected: + connect_status = _("Connected") + + null height 3 - textbutton enable_text action [ToggleField(persistent, "enable_discord"), - If(persistent.enable_discord, Function(RPC.disconnect), Function(RPC.connect))]: - text_style "navigation_button_text" - if persistent.enable_discord and not RPC.rpc_connected: - textbutton _("Reconnect") action Function(RPC.connect): - text_style "navigation_button_text" + text "[connect_status]" xalign 0.5 - null height (4 * gui.pref_spacing) + python: + enable_text = _("Enable") + if persistent.enable_discord: + enable_text = _("Disable") - hbox: - box_wrap True + textbutton enable_text action [ToggleField(persistent, "enable_discord"), + If(persistent.enable_discord, Function(RPC.disconnect), Function(RPC.connect))]: + text_style "navigation_button_text" + if persistent.enable_discord and not RPC.rpc_connected: + textbutton _("Reconnect") action Function(RPC.connect): + text_style "navigation_button_text" - if enable_languages and translations: - vbox: - style_prefix "radio" - label _("Language") - hbox: - viewport: - mousewheel True - scrollbars "vertical" - ysize 120 - has vbox + # null height 80 - for tran in translations: - vbox: - for tlid, tlname in tran: - textbutton tlname: - action Language(tlid) +style name_label is pref_label +style name_label_text is pref_label_text +style name_text is radio_button_text: + color ("#000" if not persistent.high_contrast else "#ffdfee") ## Preferences screen ########################################################## ## @@ -1306,40 +1461,89 @@ screen template_preferences(): ## ## https://www.renpy.org/doc/html/screen_special.html#preferences -screen preferences(): +init python: + def vp_vert_scroll_mask(d): + return AlphaMask(d, Frame("gui/viewport-vertical-fade.png", 0, 13)) +image pref_background: + textbox_frame(high_contrast=persistent.high_contrast, opaque=persistent.reduce_transparency, dots=False, glare=False) + +screen preferences(): tag menu + + ## These default variables keep track of the current tab and volume levels. + default music_volume = preferences.get_mixer("music") + default sound_volume = preferences.get_mixer("sfx") + default voice_volume = preferences.get_mixer("voice") + default current_tab = "display" + on "show" action [SetScreenVariable("music_volume", preferences.get_mixer("music")), SetScreenVariable("sound_volume", preferences.get_mixer("sfx")), SetScreenVariable("voice_volume", preferences.get_mixer("voice"))] if renpy.mobile: $ cols = 2 else: $ cols = 4 - default ddlc_settings = True - - use game_menu(_("Settings"), scroll="viewport"): - - vbox: - xoffset 50 - + use game_menu(_("Settings")): + side "t c": hbox: - style_prefix "navigation" - xoffset 150 - spacing 5 - textbutton _("DDLC Settings") action [SetScreenVariable("ddlc_settings", True), SensitiveIf(not ddlc_settings)] - textbutton _("Template Settings") action [SetScreenVariable("ddlc_settings", False), SensitiveIf(ddlc_settings)] + xalign 0.5 + button: + style ("pref_active_tab_button" if current_tab == "display" else "pref_tab_button") + action SetScreenVariable("current_tab", "display") + left_padding 10 + has side 'l c' + + if renpy.mobile: + add ("gui/pref_display_icon_mobile_selected.png" if current_tab == "display" else "gui/pref_display_icon_mobile.png"): + yalign 0.75 + zoom 0.5 + else: + add ("gui/pref_display_icon_selected.png" if current_tab == "display" else "gui/pref_display_icon.png"): + yalign 0.75 + zoom 0.5 + label (_("Audio") if renpy.mobile else _("Display & Sound")) yalign 0.0 text_size (24 if renpy.mobile else 18) at loc_text_fit style "pref_tab_label" + button: + style ("pref_active_tab_button" if current_tab == "language" else "pref_tab_button") + action SetScreenVariable("current_tab", "language") + has side 'l c' + add ("gui/pref_language_icon_selected.png" if current_tab == "language" else "gui/pref_language_icon.png"): + yalign 0.75 + zoom 0.5 + label _("Language & Text") yalign 0.0 text_size (24 if renpy.mobile else 18) at loc_text_fit style "pref_tab_label" + button: + style ("pref_active_tab_button" if current_tab == "accessibility" else "pref_tab_button") + action SetScreenVariable("current_tab", "accessibility") + has side 'l c' + add ("gui/pref_accessibility_icon_selected.png" if current_tab == "accessibility" else "gui/pref_accessibility_icon.png"): + yalign 0.75 + zoom 0.5 + label _("Accessibility") yalign 0.0 text_size (24 if renpy.mobile else 18) at loc_text_fit style "pref_tab_label" + button: + style ("pref_active_tab_button" if current_tab == "bronya" else "pref_tab_button") + action SetScreenVariable("current_tab", "bronya") + has side 'l c' + add ("gui/pref_bronya_icon_selected.png" if current_tab == "bronya" else "gui/pref_bronya_icon.png"): + yalign 0.75 + zoom 0.5 + label _("Mod Template") yalign 0.0 text_size (24 if renpy.mobile else 18) at loc_text_fit style "pref_tab_label" - null height 10 - - if ddlc_settings: - use ddlc_preferences - else: - use template_preferences + frame: + padding (30, 30) + background "pref_background" + + showif current_tab == "display": + use display_preferences(music_volume, sound_volume, voice_volume) + elif current_tab == "language": + use language_preferences + elif current_tab == "accessibility": + use accessibility_preferences + elif current_tab == "bronya": + use template_preferences text "v[config.version]": - xalign 1.0 yalign 1.0 - xoffset -10 yoffset -10 - style "main_menu_version" + xalign 1.0 yalign 1.0 + xoffset -10 yoffset -10 + style "main_menu_version" style pref_label is gui_label style pref_label_text is gui_label_text @@ -1372,41 +1576,77 @@ style pref_label: bottom_margin 2 style pref_label_text: - font "gui/font/RifficFree-Bold.ttf" - size 24 + font gui.riffic_font + size get_variable_size(24, 28, gui.text_scale) color "#fff" - outlines [(3, "#b59", 0, 0), (1, "#b59", 1, 1)] + outlines (get_scaled_outlines([(3, text_outline_color, 0, 0), (1, text_outline_color, 1, 1)], 24, 28, gui.text_scale) if not persistent.high_contrast else get_scaled_outlines([(2, gui.hc_label_outline_color, 0, 0), (1, gui.hc_label_outline_color, 1, 1)], 24, 28, gui.text_scale)) yalign 1.0 +style pref_tab_button: + xysize (get_pref_tab_button_width(), 60) + yalign 1.0 + background Frame("gui/namebox-deselected.png", 3, 3) + hover_sound (gui.hover_sound if not renpy.mobile else None) + activate_sound gui.activate_sound + yoffset 9 +style pref_active_tab_button is pref_tab_button: + background Frame("gui/namebox-selected.png", 3, 3) + yoffset 0 +style pref_tab_label is pref_label +style pref_tab_label_text is pref_label_text: + outlines [(4, text_outline_color, 0, 0), (1, text_outline_color, 1, 1)] + layout "nobreak" + +style pref_header_label is pref_label +style pref_header_label_text is pref_label_text: + size get_variable_size(28, 38, gui.text_scale) + outlines get_scaled_outlines([(4, text_outline_color, 0, 0), (1, text_outline_color, 1, 1)], 28, 38, gui.text_scale) + +style pref_hint_text: + font gui.halogen_font + size get_variable_size(24, 30, gui.text_scale) + outlines [] + color (text_outline_color if not persistent.high_contrast else "#ffdfee") + xoffset get_variable_size(40, 60, gui.text_scale) + xmaximum 800 + style pref_vbox: xsize 225 style radio_vbox: - spacing gui.pref_button_spacing + spacing get_variable_size(gui.pref_button_spacing, gui.max_pref_button_spacing, gui.text_scale) style radio_button: properties gui.button_properties("radio_button") foreground "gui/button/check_[prefix_]foreground.png" + # yminimum get_variable_size(24, 32, gui.text_scale) + # ymaximum get_variable_size(48, 64, gui.text_scale) style radio_button_text: properties gui.button_text_properties("radio_button") font "gui/font/Halogen.ttf" outlines [] + style check_vbox: - spacing gui.pref_button_spacing + spacing get_variable_size(gui.pref_button_spacing, gui.max_pref_button_spacing, gui.text_scale) style check_button: properties gui.button_properties("check_button") foreground "gui/button/check_[prefix_]foreground.png" + # ysize get_variable_size(24, 32, gui.text_scale) style check_button_text: properties gui.button_text_properties("check_button") font "gui/font/Halogen.ttf" outlines [] + style slider_slider: xsize 350 + base_bar ("gui/scrollbar/horizontal_poem_bar_short.png" if not persistent.high_contrast else "gui/scrollbar/horizontal_poem_bar_short_hc.png") + thumb ("gui/slider/horizontal_hover_thumb.png" if not persistent.high_contrast else "gui/slider/horizontal_hover_thumb_hc.png") + yalign 0.5 style slider_button: properties gui.button_properties("slider_button") @@ -1419,21 +1659,6 @@ style slider_button_text: style slider_vbox: xsize 450 -style name_label is pref_label -style name_label_text is pref_label_text - -style name_text: - font "gui/font/Halogen.ttf" - size 24 - color gui.idle_color - outlines [] - -style value_text: - size 18 - color "#000" - outlines [] - yalign 0.65 - ## History screen ############################################################## ## ## This is a screen that displays the dialogue history to the player. While @@ -1448,17 +1673,15 @@ screen history(): ## Avoid predicting this screen, as it can be very large. predict False - use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport")): - + use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0): style_prefix "history" for h in _history_list: window: - ## This lays things out properly if history_height is None. - has fixed: - yfit True + has fixed + yfit True if h.who: @@ -1471,15 +1694,13 @@ screen history(): if "color" in h.who_args: text_color h.who_args["color"] - $ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags) - text what: - substitute False + text h.what: + ypos (gui.history_text_ypos if h.who else gui.history_name_ypos) + null height get_variable_size(12, 15) if not _history_list: label _("The dialogue history is empty.") -define gui.history_allow_tags = set() - style history_window is empty style history_name is gui_label @@ -1501,9 +1722,11 @@ style history_name: ypos gui.history_name_ypos xsize gui.history_name_width -style history_name_text: +style history_name_text is namebox_label: min_width gui.history_name_width text_align gui.history_name_xalign + xalign 0.0 + layout "nobreak" style history_text: xpos gui.history_text_xpos @@ -1684,18 +1907,27 @@ style history_label_text: ################################################################################ screen name_input(message, ok_action): - ## Ensure other screens do not get input while this screen is displayed. modal True - zorder 200 - style_prefix "confirm" + default inv = VariableInputValue("player") + add "gui/overlay/confirm.png" - key "K_RETURN" action [Play("sound", gui.activate_sound), ok_action] + if renpy.mobile: + key "K_RETURN" action [inv.Disable(), Play("sound", gui.activate_sound), ok_action] + else: + key "K_RETURN" action [Play("sound", gui.activate_sound), ok_action] + if renpy.mobile: + button: + xysize (1.0, 1.0) + action [inv.Disable(), Function(FinishEnterName)] frame: + if renpy.mobile: + yalign 0.0 + yoffset 10 vbox: xalign .5 @@ -1706,8 +1938,17 @@ screen name_input(message, ok_action): style "confirm_prompt" xalign 0.5 - input default "" value VariableInputValue("player") length 12 allow "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя" - #additionally added Cyrillic characters to support Russian names for MC + fixed: + fit_first True + yminimum 10 + + xalign 0.5 + input default "" value VariableInputValue("player") length 12 allow "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя" + if renpy.mobile: + button: + action inv.Enable() + xysize (get_variable_size(280, 400, gui.text_scale), 90) + align (0.5, 0.5) hbox: xalign 0.5 @@ -1716,10 +1957,8 @@ screen name_input(message, ok_action): textbutton _("OK") action ok_action screen dialog(message, ok_action): - ## Ensure other screens do not get input while this screen is displayed. modal True - zorder 200 style_prefix "confirm" @@ -1727,21 +1966,20 @@ screen dialog(message, ok_action): add "gui/overlay/confirm.png" frame: + has vbox + xalign .5 + yalign .5 + spacing 30 - vbox: - xalign .5 - yalign .5 - spacing 30 - - label _(message): - style "confirm_prompt" - xalign 0.5 + label _(message): + style "confirm_prompt" + xalign 0.5 - hbox: - xalign 0.5 - spacing 100 + hbox: + xalign 0.5 + spacing 100 - textbutton _("OK") action ok_action + textbutton _("OK") action ok_action image confirm_glitch: "gui/overlay/confirm_glitch.png" @@ -1757,45 +1995,29 @@ image confirm_glitch: ## ## http://www.renpy.org/doc/html/screen_special.html#confirm screen confirm(message, yes_action, no_action): - ## Ensure other screens do not get input while this screen is displayed. modal True - zorder 200 - style_prefix "confirm" add "gui/overlay/confirm.png" frame: + has vbox + xalign .5 + yalign .5 + spacing 30 - vbox: - xalign .5 - yalign .5 - spacing 30 - - ## This if-else statement either shows a normal textbox or - ## glitched textbox if you are in Sayori's Death Scene and are - ## quitting the game. - # if in_sayori_kill and message == layout.QUIT: - # add "confirm_glitch" xalign 0.5 - # else: - label _(message): - style "confirm_prompt" - xalign 0.5 + label _(message): + style "confirm_prompt" + xalign 0.5 - hbox: - xalign 0.5 - spacing 100 + hbox: + xalign 0.5 + spacing 100 - ## This if-else statement disables quitting from the quit box - ## if you are in Sayori's Death Scene, else normal box. - # if in_sayori_kill and message == layout.QUIT: - # textbutton _("Yes") action NullAction() - # textbutton _("No") action Hide("confirm") - # else: - textbutton _("Yes") action yes_action - textbutton _("No") action no_action + textbutton _("Yes") action yes_action + textbutton _("No") action no_action ## Right-click and escape answer "no". #key "game_menu" action no_action @@ -1808,8 +2030,7 @@ style confirm_button is gui_medium_button style confirm_button_text is gui_medium_button_text style confirm_frame: - background Frame("gui/frame.png", gui.confirm_frame_borders, tile=gui.frame_tile) - # background Frame(recolorize("gui/frame.png"), gui.confirm_frame_borders, tile=gui.frame_tile) + background Frame([ "gui/confirm_frame.png", "gui/frame.png"], gui.confirm_frame_borders, tile=gui.frame_tile) padding gui.confirm_frame_borders.padding xalign .5 yalign .5 @@ -1822,7 +2043,7 @@ style confirm_prompt_text: style confirm_button: properties gui.button_properties("confirm_button") - hover_sound gui.hover_sound + hover_sound (gui.hover_sound if not renpy.mobile else None) activate_sound gui.activate_sound style confirm_button_text is navigation_button_text: @@ -1839,13 +2060,12 @@ screen fake_skip_indicator(): use skip_indicator screen skip_indicator(): - zorder 100 style_prefix "skip" - frame: - - hbox: + if not renpy.mobile: + frame: + has hbox spacing 6 text _("Skipping") @@ -1879,7 +2099,7 @@ style skip_frame: padding gui.skip_frame_borders.padding style skip_text: - size gui.notify_text_size + size get_variable_size(gui.notify_text_size, gui.max_notify_text_size, gui.text_scale) style skip_triangle: # We have to use a font that has the BLACK RIGHT-POINTING SMALL TRIANGLE