VN Ren'Py Royal Switch [Development Thread]

ffive

Forum Fanatic
Jun 19, 2022
4,953
10,731
Regarding question how to add stuff to the game's options menu, per you'd be basically adjusting with your own additions to the preferences screen (labels and checkboxes)
 
Dec 16, 2022
233
303
I've had some time today to take a look at the script and this is what I came up with. It has a several neat features...
  • No need to repeat the same animation code for every animation, making development faster and less error prone
  • Can change behavior of all animations in one place
  • There should be no weird roll bugs, can roll over animations smoothly
  • No hard pause
  • No return labels required, just one line in the script
The whole motivation was to implement a autoplay feature, which I did, I've tested it quite a bit and this version works well for me. From what I can tell, with 'default anim_autoplay = False' it behaves the exact same, but more testing is required.
Also, save compatibility should be carefully considered. This shouldn't mess with old saves, unless someone made a save during an animation, then it might. Again, more testing required.

There's still some things that can be improved, but I thought I'd share it anyways. Take a look at it if you want and let me know what you think.

Python:
# These two can be hooked into the settings menu.
# For some reason setting anim_autoplay = True will change the layout of the anim_start / anim_end screens...
default anim_autoplay = False
default anim_duration = 8.0

# The time to wait before starting any animations,
default anim_timeout = 3.0

default anim_start_btn = "imagebuttons/animbutton_%s.webp"
default anim_end_btn = "imagebuttons/finishedbutton_%s.png"


# Generic start/stop animation screens with optional timer
screen anim_start(filename):
    frame:
        xalign 0.02
        yalign 0.95
        background None
       
        $button_action = [Hide("anim_start", dissolve), Call("play_anim", filename)]
       
        imagebutton auto anim_start_btn:
            action button_action
       
        if anim_autoplay:
            timer anim_timeout action button_action
       

screen anim_end():
    modal True
    frame:
        xalign 0.02
        yalign 0.01
        background None
       
        $button_action = [Hide("anim_end", dissolve), Return()]
       
        imagebutton auto anim_end_btn:
            action button_action
           
        if anim_autoplay:
            timer anim_duration action button_action
           

# This label is called from the script, e.g. call animation("m_dhmf2a")
label animation(filename):
    # Suspend rollback, otherwise pause will create a checkpoint and rollback won't work as expected.
    $renpy.suspend_rollback(True)
   
    show screen anim_start(filename) with dissolve
   
    python:
        # This is so that we can forward roll over this screen without blocking...
        # I haven't fully figured out why this is needed, but may be related to:
        # https://github.com/renpy/renpy/issues/2794
        if not renpy.in_rollback():
            renpy.pause()
            # If afm and autoplay are on avoid skipping the animation entirely. There's probably a better way to do this.
            if renpy.game.preferences.afm_enable and anim_autoplay:
                renpy.pause(60)
   
    hide screen anim_start with dissolve
   
    $renpy.suspend_rollback(False)
    return


label play_anim(filename):
    window hide  
    show image filename with dissolve  
    show screen anim_end with dissolve  
    pause
    # In case we somehow manage to jump out of the animation early, we hide the screen and return.
    hide screen anim_end with dissolve  
    return
This can then be called from the script like this:
Python:
call animation("m_dhmf2a")
I did finish my universal patch for the current version too, which basically mimics the same autoplay feature, but injects some code at runtime.
 
  • Like
Reactions: ffive

DeepBauhaus

ALL GLORY TO THE HYPNOTOAD
Game Developer
Oct 14, 2018
113
300
I've had some time today to take a look at the script and this is what I came up with. It has a several neat features...
  • No need to repeat the same animation code for every animation, making development faster and less error prone
  • Can change behavior of all animations in one place
  • There should be no weird roll bugs, can roll over animations smoothly
  • No hard pause
  • No return labels required, just one line in the script
The whole motivation was to implement a autoplay feature, which I did, I've tested it quite a bit and this version works well for me. From what I can tell, with 'default anim_autoplay = False' it behaves the exact same, but more testing is required.
Also, save compatibility should be carefully considered. This shouldn't mess with old saves, unless someone made a save during an animation, then it might. Again, more testing required.

There's still some things that can be improved, but I thought I'd share it anyways. Take a look at it if you want and let me know what you think.

Python:
# These two can be hooked into the settings menu.
# For some reason setting anim_autoplay = True will change the layout of the anim_start / anim_end screens...
default anim_autoplay = False
default anim_duration = 8.0

# The time to wait before starting any animations,
default anim_timeout = 3.0

default anim_start_btn = "imagebuttons/animbutton_%s.webp"
default anim_end_btn = "imagebuttons/finishedbutton_%s.png"


# Generic start/stop animation screens with optional timer
screen anim_start(filename):
    frame:
        xalign 0.02
        yalign 0.95
        background None
      
        $button_action = [Hide("anim_start", dissolve), Call("play_anim", filename)]
      
        imagebutton auto anim_start_btn:
            action button_action
      
        if anim_autoplay:
            timer anim_timeout action button_action
      

screen anim_end():
    modal True
    frame:
        xalign 0.02
        yalign 0.01
        background None
      
        $button_action = [Hide("anim_end", dissolve), Return()]
      
        imagebutton auto anim_end_btn:
            action button_action
          
        if anim_autoplay:
            timer anim_duration action button_action
          

# This label is called from the script, e.g. call animation("m_dhmf2a")
label animation(filename):
    # Suspend rollback, otherwise pause will create a checkpoint and rollback won't work as expected.
    $renpy.suspend_rollback(True)
  
    show screen anim_start(filename) with dissolve
  
    python:
        # This is so that we can forward roll over this screen without blocking...
        # I haven't fully figured out why this is needed, but may be related to:
        # https://github.com/renpy/renpy/issues/2794
        if not renpy.in_rollback():
            renpy.pause()
            # If afm and autoplay are on avoid skipping the animation entirely. There's probably a better way to do this.
            if renpy.game.preferences.afm_enable and anim_autoplay:
                renpy.pause(60)
  
    hide screen anim_start with dissolve
  
    $renpy.suspend_rollback(False)
    return


label play_anim(filename):
    window hide 
    show image filename with dissolve 
    show screen anim_end with dissolve 
    pause
    # In case we somehow manage to jump out of the animation early, we hide the screen and return.
    hide screen anim_end with dissolve 
    return
This can then be called from the script like this:
Python:
call animation("m_dhmf2a")
I did finish my universal patch for the current version too, which basically mimics the same autoplay feature, but injects some code at runtime.
Thank you so much for this! I will play around with it and see if I can get it to work. I would like to include you in the game credits! If you're interested, DM me.
 
Dec 16, 2022
233
303
Thank you so much for this! I will play around with it and see if I can get it to work. I would like to include you in the game credits! If you're interested, DM me.
That's nice of you to offer, but not really necessary. I'm just having a bit of fun working with renpy and if something comes of it that you want to use, that's great.

I have been thinking about potential save game issues today and just done some more testing. It turns out it doesn't matter, because saves taken during animations are broken already, for the same reason that rolling doesn't work properly I believe.

If you save during an animation subroutine like this...
Python:
label play_m_dhmf7A:
    show screen fin_dhmf_7A with dissolve
    window hide
    $ renpy.show("m_dhmf7a")
    $ renpy.transition(Dissolve(.5))
    $ renpy.pause(12, hard=True)
... renpy doesn't know where to return to and doesn't find any instruction to execute, so it just goes back to main menu.

Adding a jump instruction at the end fixes this.
Python:
label play_m_dhmf7A:
    show screen fin_dhmf_7A with dissolve
    window hide
    $ renpy.show("m_dhmf7a")
    $ renpy.transition(Dissolve(.5))
    $ renpy.pause(12, hard=True)
    jump dhmf_cs
But tbh this approach isn't very good imo, it's not robust enough and requires a lot of manual labor, I think ultimately changing how animations are handled would be for the best. I would avoid using jump at all, unless you're jumping to labels within the same scope and instead use call for calling subroutines.
 
  • Like
Reactions: DeepBauhaus

DeepBauhaus

ALL GLORY TO THE HYPNOTOAD
Game Developer
Oct 14, 2018
113
300
On Breaking Saves.

Developing a VN (or game) is a strange journey. On one hand, it can be very much like writing a serial novel, like Charles Dickens in the Literary Gazette. On the other hand, it involves the development of a (hopefully) finished product, where you are continually releasing beta (or, let’s be honest) alpha versions for public consumption.

If you are able to manage the serial model, you can simply release a chapter after chapter, and in the end you have Nicholas Nikleby. That was my goal, my hope, my aspiration. Alas, I am no Charles Dickens.

When you are developing a finished product, you occasionally encounter certain aspects of your work that simply do not fit into the greater milieu, or that, in the experience of the reader/player, are unsatisfactory (I will call these, for the sake of argument, “mistakes”). For these, you might need to go back and make some corrections. These might be minor, like changing a little bit of dialogue, or they might be more substantial, like rewriting entire scenes.

Which is all well and good for the finished product. But what about the faithful early adopters who have stuck with you from the early stages? Well, those people will end up with the dreaded broken saves.

Which brings me to the point of this post: it’s coming.

With version 0.09, I am going to be breaking saves in earnest. This will be an ongoing effort.

For those of you inconvenienced by this: I am sorry. But I truly believe it is for the better of the project.

In v. 0.09, I have re-written the “Puppy Play” scene from v. 0.08. It is (in relative terms) a minor rewrite, but it makes the story flow better, and it makes some of the later events more coherent. These changes will almost certainly break any saves made during that scene. I do not know how it will affect saves after that (though I am fairly sure earlier saves will be unaffected).

In upcoming versions (either 0.10 or 0.11) you will see some major re-writes from earlier in the VN. Specifically, I plan to rework the first three of Brigitte’s tasks/challenges. It has been pointed out that it is rather unsatisfying to choose the “bratty” path only to have our heroine eventually do the task anyway. Now, I have (or had) a reason for this. Not an excuse, but a reason. I initially thought Brigitte’s journey would involve a slow descent into submission, where she really had no choice, but she would fight it to varying degrees. This turned out to be unsatisfying.

In version 0.07, we started to move into more clearly defined routes for our heroines. It soon became clear that, while more work for your humble dev, this created more satisfying experiences for readers. So, as much as it pains me, I am going back to edit and (I hope) improve some of the earlier paths. Specifically, I will be making significant changes to Brigitte’s “Clean Your Room,” “Clean the Barracks” and “Clean the Mess Hall” stories. These will almost certainly break a bunch of saves. For that, I apologize. However, I think in the long run it will make for a better project.

I also plan to edit the way animations are handled (see earlier posts in this thread), and I'll probably go back to try to improve my earlier animations as time goes on (though I continue to assert that mine is more of a visual novel than a crummy movie, so don't expect the world from me in that regard).

Thanks to everyone for your support! I hope future versions will live up to your expectations.