Ren'Py Play Movie - Random & Looping

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,572
Within Callisto I have some repeatable lewd content where lewd videos are shot from a couple of angles. The player is able to switch between one lewd act and another (via buttons), however I would like which angle it is shown from to be random. The following content sort of works, however it changes file every time it goes through a video loop.
Python:
play movie ["crclrcg1l.webm" , "crclrcg2l.webm"] loop
I could resolve via using a variable (1 or 2) and then using if / else or potentially inserting the 1/2 variable into the video tag, however is there an easier way of doing this?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,355
15,269
[...] is there an easier way of doing this?
Yes there is, by using an effective randomization :

Python:
label randomMovie( movieList )
    # The /selected/ variable will be local to this label.
    $ renpy.dynamic( "selected" )
    # Let Python randomly choose what movie to play
    $ selected = renpy.random.choice( movieList )
    # Then let Ren'py play it.
    $ renpy.music.play( selected, loop=True )
    # And finally return to the main code flow.
    return

label whatever:
    [...]
    # Note: You MUST be passing a list, so don't forget the '[' and ']'.
    call randomMovie( [ "crclrcg1l.webm" , "crclrcg2l.webm" ] )
    [...]
It can be simplified, but there isn't real need for that.
 
  • Red Heart
Reactions: Xavster

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
My answers are theoretical. I haven't tested either or have never used tried them. I'm making some assumptions based on online documentation.
I believe one or both could work though. I'm sure Anne will correct me if I'm wrong.

Firstly, that ["crclrcg1l.webm" , "crclrcg2l.webm"] is a . Just an array of strings.

So I believe, you could store that list in a variable.

Python:
    $ my_movie_list = ["crclrcg1l.webm" , "crclrcg2l.webm"]
    play movie my_movie_list loop

Next, according to the documentation I linked to for the list, there are various things you can do to a list. In this case, I'm thinking .

However, you want random not sorted. But the sort allows for you to define a custom key for sorting your list, using a function.
The example looks like this (it sorts the list based on the length of the values):

Python:
# A function that returns the length of the value:
def myFunc(e):
  return len(e)

cars = ['Ford', 'Mitsubishi', 'BMW', 'VW']

cars.sort(key=myFunc)

So now, I'm thinking that if the key= function returned a - the list would be sorted into a random order. You might be able to do away with the function completely, since the generation of a random number is probably a function anyway. So maybe this would work?...

Python:
    $ my_movie_list = ["crclrcg1l.webm" , "crclrcg2l.webm"]
    play movie my_movie_list.sort(key=renpy.random.randint(0,99)) loop

But... you also mention that your player is able to press a button to change angles. That implies you don't want the full list of movies to play in (random) sequence and instead want a single movie to play on a loop until the player presses the button and then for the game to swap to another random movie.

For that, I'm wondering if you perhaps want instead.

Random choice picks a single random value from a list.

I'm thinking something like...

Python:
    # play 1 random movie from the list
    play movie renpy.random.choice(["crclrcg1l.webm" , "crclrcg2l.webm"]) loop

Which doesn't stop it randomly picking the same movie again.

Finally, there is also something called . note the extra "s". This returns a new list of random values based on an existing list. The new list can be longer than the original list (i.e. you can get 20 random output values based on 3 input values) - and for that reason, I wonder if it would generate duplicates even when trying to randomize a fixed number of values. (guessing... something like [1,2,3] -> [3,3,2] could be possible, I don't know.)

You might want to try it. I think it would look like this...

Python:
    # play all movies from the list in random order (I hope).
    play movie renpy.random.choices(["crclrcg1l.webm" , "crclrcg2l.webm"]) loop

You could make the whole system cleverer by or values to the list of available movies to be played, and maybe avoid the same movie being picked twice in a row.

Erm... maybe...
Python:
label play_movies:

    $ movie_playlist = []

label repeat_play_movies:

    if movie_playlist == []:
        $ movie_playlist = ["crclrcg1l.webm" , "crclrcg2l.webm"]

    $ picked_movie = renpy.random.choice(movie_playlist)
    $ movie_playlist.remove(picked_movie)
    play movie picked_movie loop

    # I'm imagining a button that jumps back to "repeat_play_movies" to pick another random movie from the remaining playlist
    # once the play list is empty, it is reset back to the full list.

As I say, this is all theoretical. I went looking at the documentation for python lists a while ago for something else. While reading stuff, I saw some of the functions. Hopefully, they work how I think they do.

Edit: Just saw Anne's answer... I probably need to add renpy. in front of anything I've written using random.. I think RenPy has a way of picking random numbers ahead of time to avoid players save-scumming their way through random events or rolling back and rolling forward to get a different random answer.
 
Last edited:
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,572
Yes there is, by using an effective randomization :

Python:
label randomMovie( movieList )
    # The /selected/ variable will be local to this label.
    $ renpy.dynamic( "selected" )
    # Let Python randomly choose what movie to play
    $ selected = renpy.random.choice( movieList )
    # Then let Ren'py play it.
    $ renpy.music.play( selected, loop=True )
    # And finally return to the main code flow.
    return

label whatever:
    [...]
    # Note: You MUST be passing a list, so don't forget the '[' and ']'.
    call randomMovie( [ "crclrcg1l.webm" , "crclrcg2l.webm" ] )
    [...]
It can be simplified, but there isn't real need for that.
I sort of understand what you are trying to do, however the call doesn't seem to change the animation shown within game. Note that I am adjusting play movie to something else and then using show movie to display on screen. Not sure where I may be going wrong. Do I need to define the selected list first?

PS: 'selected' seems to be a reserved word for something, I assume this can be called anything.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,355
15,269
Edit: Just saw Anne's answer... I probably need to add renpy. in front of anything I've written using random.. I think RenPy has a way of picking random numbers ahead of time to avoid players save-scumming their way through random events or rolling back and rolling forward to get a different random answer.
Not exactly picking them ahead, it's more "keeping memory of them through rollback". Therefore, normally (never really tried to test the limits) once a random number or choice have been picked, whatever how many times you'll rollback then try again, the result should be the same. There's probably case where it don't really works, but globally it's the intent behind the few modifications made by Ren'py.


I sort of understand what you are trying to do, however the call doesn't seem to change the animation shown within game.
Hmm... My knowledge regarding movies don't goes really far, there's perhaps something I'm missing.


PS: 'selected' seems to be a reserved word for something, I assume this can be called anything.
Yes, it can be whatever name you want. I was at works when writing this, so I picked the first name that crossed my mind.
 
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,572
Tried tweaking the suggested code to get it work within game without luck. However I did manage to implement the variable method I considered in first post.
Code:
$ lewd_angle = renpy.random.choice(['1', '2'])

play movie ["crclm{}l.webm".format(lewd_angle)] loop
The various animations are contained within a while loop, so I just position the lewd_angle random choice to evaluate at the top of the while loop and then have the suitable movie play code within the position check if statements.
 
  • Like
Reactions: anne O'nymous

DelinquentProductions

Degenerate
Game Developer
Oct 24, 2020
135
892
Given that the randomness has been addressed this may be of help for swapping the currently shown videos:

Python:
init -99 python:
    current_anim = None
    anim_name = ""
    anim_choices=[]
    class AnimChoice(object):
        def __init__(self, label, anim):
            self.kind = "GameScreen"
            self.label = label
            self.anim = anim

    def set_anim_choices(choices):
        anim_choices = choices
        return

#####
# Animation inits
#####

init:
    image example_a1_mv = Movie(play="movies/example_a1.webm", loop=True, start_image="movies/example_a1.png")
    image example_a2_mv = Movie(play="movies/example_a1.webm", loop=True, start_image="movies/example_a1.png")


#####
# Animation labels
#####

label part1:

    label .a1:
        $anim_name = "example_a1_mv"
        jump animation_loop


    label .a2:
        $anim_name = "example_a2_mv"
        jump animation_loop
    label .a_full:
        $anim_name = "emaple_a1_mv"
        $anim_choices = [AnimChoice("part1.a1", "A1"),
            AnimChoice("part1.a2", "A2")]

        jump animation_loop

#####
# Core Loop
#####

label animation_loop:
    $renpy.scene()
    $renpy.show(anim_name)

    call screen anim_choice_picker
    $sa = _return
    if not sa:
        $renpy.end_replay
        return

    call expression sa from _anim_expression
    return


screen anim_choice_picker():
    style_prefix "animChoice"
    frame:
        xpadding 10
        ypadding 10
        xalign 1.0
        yalign 1.0

        vbox:
            for c in (anim_choices):
                textbutton c.anim action Return(c.label)

            textbutton "End" action Return(False)
Ok now what this actually does.

AnimChoice is the class holding all of the information we need to jump between labels holding our animations. There is probably a smoother way of doing this but I've ended up in weird states with rollbacks and jumps before and this seems to handle those cases perfectly. Also allows for gallery replays quite nicely which is an accidental feature.

Inits and Labels are fairly self explanatory. The major pitfall there is the .a_full label which is used as an entry point for that set of animations and defining what will be available within the angle change dialog.

animation_loop is where all the magic actually happens, and by magic I mean clearing the screen with a scene() call and dropping in a new webm. From there it just chills waiting for input from the anim_choice_picker dialog. If user clicks "End" the dialog returns false and things break out of the animation loop. Actually calling into this from the main script is also easy.

Python:
label .animation_a_full:
    call part1.a_full from _call_a_full_from_expression
The return from the animation_loop will drop us right back at the label immediately after the call part1.a_full, and because of the from keyword we get a free empty label to catch it and continue on nicely.