Ren'Py Variables in an ATL block

Architecter

Newbie
Jun 28, 2022
37
96
Hello everyone!
Help me please with changing variables in an ATL block (although there might be another solution).
I want to create a sex animation whose speed is changed by the player.
The problem is the smoothness of the animation change.
The visualization of sex in an ATL block:
Code:
image s_2_q2 1_1:
    "day_1_5/s_2_q2_1_1.webp"
    choice( my_video_speed_0039 == 1 ):
        pause 1
    choice( my_video_speed_0039 == 2 ):
        pause 0.04
    "day_1_5/s_2_q2_1_2.webp"
    choice( my_video_speed_0039 == 1 ):
        pause 1
    choice( my_video_speed_0039 == 2 ):
        pause 0.04
...and many more like this...
    repeat
The problem is that the ATL block uses "my_video_speed_0039 " variable on initial launch and after changing it, the animation speed does not change (I checked in the game options - the variable has indeed changed).
I guess I have to restart the ATL block to update the speed, but this option is not suitable because the smoothness of the animation is lost.
As an alternative, of course, I can create two ATL blocks with different speeds and launch them depending on the speed, but in the case the animation also twitching (for example, the transition from the 8th frame to the 1st).
Please tell me if there is a solution with a smooth change speed inside the ATL block or maybe there is another solution?
Thank you!
 
Last edited:

Architecter

Newbie
Jun 28, 2022
37
96
Found a possible solution through ConditionSwitch:
Code:
image s_2_q2 1_1:
    "day_1_5/s_2_q2_1_1.webp"
    ConditionSwitch(                                                        
        "my_video_speed_0039 == 1", "day_1_5/111b.webp",
        "my_video_speed_0039 == 2", "day_1_5/111f.webp",
        ), 
    pause 0.055        
....
It works with pictures, there's not much left...
Does anyone know how correctly replace the images "day_1_5/111b.webp" & "day_1_5/111f.webp" with a pause in this code?
Unfortunately this doesn't work: "my_video_speed_0039 == 2", pause 0.07,
 
Last edited:

Architecter

Newbie
Jun 28, 2022
37
96
I forgot to say.
I initially tried the simplest option - directly substitute the value of the variable into the pause:
Code:
image s_2_q2 1_1:
    "day_1_5/s_2_q2_1_1.webp"
    pause my_video_speed_0039          
    "day_1_5/s_2_q2_1_2.webp"   
    pause my_video_speed_0039          
    "day_1_5/s_2_q2_1_3.webp"
    pause my_video_speed_0039
Here, as well as in the first case, the pause uses the initial value of the variable and is not updated when the variable is changed. Of course I can restart the ATL block, but it causes a jerk in the animation.
Maybe it's possible to update the ATL block? For example, to update the screen is used "renpy.restart_interaction".
Maybe something can be applied here?

Damn perfectionism, it will drive me insane
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
Help me please with changing variables in an ATL block (although there might be another solution).
Changing a variable is not what you want to do here.
What you want is to change the time past waiting, and this can be done through the ATL statement. Initially intended to have personalized transform, it can also act as a variable pause.

Python:
init python:
    class VariablePause( renpy.python.RevertableObject ):
        def __init__( self, speedVariable ):
            self.switch = True
            self.var = speedVariable

        def pause( self, trans, st, at ):
            self.switch = not self.switch
            return getattr( store, self.var) if self.switch else None

define myPause = VariablePause( "my_video_speed_0039" )
default my_video_speed_0039 = 1.0

image myAnim:
   "day_1_5/s_2_q2_1_1.webp"
    function myPause.pause
    "day_1_5/s_2_q2_1_2.webp"   
    function myPause.pause
    "day_1_5/s_2_q2_1_3.webp"
    function myPause.pause
    repeat
With "my_video_speed_0039" being the waiting time in second.
 

Architecter

Newbie
Jun 28, 2022
37
96
Changing a variable is not what you want to do here.
What you want is to change the time past waiting, and this can be done through the ATL statement. Initially intended to have personalized transform, it can also act as a variable pause...
Thanks a lot for the advice!
Now it started to show the error "ATL appears to be in an infinite loop", but at least it became clear what needs to be done.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
Now it started to show the error "ATL appears to be in an infinite loop", but at least it became clear what needs to be done.
Yeah, it's the problem sometimes with ATL. It's difficult to define clearly what is an infinite loop and what isn't when things are expected to loop infinitely ;)
Generally I solve it by adding some pause 0 here and there. It change nothing since there's no pause, but it's enough to trick Ren'Py.
 
  • Like
Reactions: Architecter

Saki_Sliz

Well-Known Member
May 3, 2018
1,403
1,011
Changing a variable is not what you want to do here.
What you want is to change the time past waiting, and this can be done through the ATL statement. Initially intended to have personalized transform, it can also act as a variable pause.

Python:
init python:
    class VariablePause( renpy.python.RevertableObject ):
        def __init__( self, speedVariable ):
            self.switch = True
            self.var = speedVariable

        def pause( self, trans, st, at ):
            self.switch = not self.switch
            return getattr( store, self.var) if self.switch else None

define myPause = VariablePause( "my_video_speed_0039" )
default my_video_speed_0039 = 1.0

image myAnim:
   "day_1_5/s_2_q2_1_1.webp"
    function myPause.pause
    "day_1_5/s_2_q2_1_2.webp" 
    function myPause.pause
    "day_1_5/s_2_q2_1_3.webp"
    function myPause.pause
    repeat
With "my_video_speed_0039" being the waiting time in second.
giving the a read, I understand how the pause works, by returning some number representing seconds. What I don't understand is what is the point of the switch? why is it being toggled (ie self.switch = not self.switch) every time pause is called, disabling and re-enabling the pause function? Also interesting to see how you are able to use other variables outside the class to control the pause delay.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
What I don't understand is what is the point of the switch? why is it being toggled (ie self.switch = not self.switch) every time pause is called, disabling and re-enabling the pause function?
The function is called every "value returned by the function", unless the value returned is None, in which case Ren'Py will pass to the next statement in the ATL list. Therefore, the method is an infinite loop between its two states. Every odd calls it have to make Ren'Py wait, and every even calls, it have to let Ren'Py advance in the ATL.
Without the switch, you would be stuck in an infinite pause, because the method would always return a value, and Ren'Py would call it again after "this value" second(s).


Also interesting to see how you are able to use other variables outside the class to control the pause delay.
Here I relied on getattr() because the variable was anonymous in order to have more than one "VariablePause" object if needed ; you can by example have different type of animations and a configuration for the speed of each one.

But when you are sure that you'll always use the same variable, you can address it directly. All the content of store, therefore all the variables used in the game, is available as global variables inside a function/method.
You can read them directly, therefore this would works:
Python:
init python:
    def getValue():
        return myVar

define myVar = 12

label start:
    if getValue() == 12:
        "Hey, it works."
It's when you need to write in them that it's a bit more complicated. By default Python will think that you want to create a new local variable. Therefore either you "import" it or, better, you address it indirectly through store:
Python:
init python:
    def changeImportedValue():
        global myVar
        myVar += 1

    def changeValue():
        store.myVar += 1

define myVar = 12

label start:
    $ changeValue()
    if myVar == 13:
        "Hey there !"
    $ changeImportedValue()
    if myVar == 14:
        "Can you please stop changing me ?"
The indirect assignation works because Python don't have a local variable named "store", therefore it search in the global variables, found it and use it.
This also mean that objects, lists and dictionaries don't need the store prefix, because you are addressing a part, and just a part, of a variable that don't exist locally. The "just a part of" being the key here:
Python:
init python:
    def addToList( value ):
        # the /append/ method is a part of /myList/ list.
        myList.append( value )

    def doNotWorks():
        # A full assignation do not address a part of /myList/ list.
        myList = [ "a", "b", "c", "d" ]

    def alsoWorks():
        # Once again, you address only one entry of /myList/ list, so "just a part".
        return myList[1]

define myList = [ "a", "b" ]

label start:
    $ addToList( "c" )
    if len( myList ) == 3:
        "Hey..."
    $ doNotWorks()
    if len( myList ) == 3:
        "Thanks god, you haven't changed me."
    if alsoWorks() == "b":
        "Good, you almost know the starts of the alphabet."
/!\ all the codes are "unless I made a typo because it's really late and I should be sleeping since hours". /!\
 

Saki_Sliz

Well-Known Member
May 3, 2018
1,403
1,011
Ah, I see. I had to re-read it for the words to actually stick, trying to use my brain on a friday evening after a day in the office doesn't always work. I remember thinking it was clever the first time i read it thinking how having the function repeat would be a nice way to have one function act as a loop to do computations with... then I promptly forgot this detail. now I get where op's infinite loop error came from.

interesting that full assignation doesn't work for lists, but I'll probably avoid using global/store in most cases, that's literally spaghetti code. At most, I'll probably use the anonymous variable like how you used it, to keep things decoupled for sanity sake.

Also, interesting way to make a warning symbol, add an underline effect and its almost like a warning sign :p /!\
considering how often you post code, maybe add that last disclaimer to your signature :whistle: