Ren'Py Set variables to default

Playstorepers

Member
May 24, 2020
160
79
Hi there,

I was just wondering, whether there is a command, that sets variables to their default value.

E.g.: I have:

default testvariable = [pic1, pic2, pic3, ...]

Then this shit gets shuffled, appended and all the other crazy shit.
Then I want to reset it.

I know, that I can just declare testvariable again, but I was curious, whether there was a more elegant way. Otherwise I'd have to use a label, since I'd have to reset it so often. Nothing too bad, I guess, but I was curious, whether there was a simple command like testvariable.reset or something like this.

Thanks in advance
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
I was just wondering, whether there is a command, that sets variables to their default value.

Unless Anne has some clever internal RenPy voodoo - I'm 99.9% sure the answer is no.

Best you could do would be something like:

Python:
define init_testvariable = ["pic1", "pic2", "pic3", ...]

default testvariable = init_testvariable.copy()

label start:

# some time later...

    $ testvariable = init_testvariable.copy()
    scene image(testvariable[0]) with dissolve

Perhaps I'm missing something, but I couldn't get the value of either a define or a default to be an actual displayable during startup. Hence my use of strings and image() instead. Not that it was your question... just something I ran into while trying to test my answer before replying.

Edit: Made my usual python error and forgot that = is effectively create symbolic link between two variables. Added .copy() to correct things.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Just a quick update. Python is not my normal language and sometimes there's stuff I know... but forget.

Like in this case. $ var1 = var2 doesn't copy var2 into var1... it instead creates a link between them. Changes to one affect the other. The correct version of my code requires .copy().
 
  • Like
Reactions: Playstorepers

shark_inna_hat

Active Member
Game Developer
Dec 25, 2018
705
2,733
I imagine you can make a simple custom class for that
Python:
class ListWithDefault(list):
    def __init__(self, *default_values):
        self._default_values = default_values
        self.reset()

    def reset(self):
        self.clear()
        for value in self._default_values:
            self.append(value)
..but I'm a ren'py idiot and could not tell you how to use it in the engine.
 
  • Like
Reactions: 79flavors

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,368
15,282
Unless Anne has some clever internal RenPy voodoo - I'm 99.9% sure the answer is no.
I would like to have that, and I guess that PyTom also since it would simplify the rollback. But as you said, the only way is to have a copy on your own, and use it as base anytime you need to revert the state.

After, do you keep the copy and works with the original (what you presented), or do you works with a copy, and keep the original, it totally depend of the logic behind what you try to do.
A card game would use the first approach:
Code:
define allCards = [...]

label gameStart:
    $ actualCards = allCards.copy()
    [...]
    $ actualCards.remove( ... )
    [...]
But sometimes the opposite feel better:
Python:
default myList = [...]

label whatever:
    $ workingList = myList[:] # Another way to do the copy for a list
    while len( workingList ) > 0:
        [...]
 
  • Like
Reactions: Playstorepers

Playstorepers

Member
May 24, 2020
160
79
Thanks for the .copy thingy, it fucked me over and I never knew, why.

And as for:

I would like to have that, and I guess that PyTom also since it would simplify the rollback. But as you said, the only way is to have a copy on your own, and use it as base anytime you need to revert the state.

After, do you keep the copy and works with the original (what you presented), or do you works with a copy, and keep the original, it totally depend of the logic behind what you try to do.
A card game would use the first approach:
Code:
define allCards = [...]

label gameStart:
    $ actualCards = allCards.copy()
    [...]
    $ actualCards.remove( ... )
    [...]
But sometimes the opposite feel better:
Python:
default myList = [...]

label whatever:
    $ workingList = myList[:] # Another way to do the copy for a list
    while len( workingList ) > 0:
        [...]
It still eludes me, when to use which of the two methods. The first one makes sense to me, since it would be the same way I'd implement it.

The second one is still hazy to me, could you elaborate, what exactly is happening and why/when I should prefer that method?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
The second one is still hazy to me, could you elaborate, what exactly is happening and why/when I should prefer that method?
Python:
label whatever:
    $ workingList = myList[:]
    while len( workingList ) > 0:
        [...]

The while code is just a way of looping around the contents of workingList until it's empty.
It's basically "Do [...] until the condition is False".

So take a copy of myList and store it in workingList. Then loop around until workingList is empty.
I'd expect the undeclared code in [...] to include something like a workingList.pop() or workingList.remove("name") towards the end of the code block, so that eventually len (workingList) would be zero. len() is short of "length of"... if you had 6 entries... len (workingList) would be 6.

$ workingList = myList[:] is the same as $ workingList = myList.copy(). Except, for reasons which would only make sense to a python guru... myList[:] is faster than myList.copy(). myList[:] is probably best practice for a commercial environment, and therefore a good habit to pick up early. Whereas I tend to write myList.copy() because it makes sense when you're just starting out.
 

Playstorepers

Member
May 24, 2020
160
79
First of all: Thanks for your answer and for telling me, that [:] is a more fancy way to use .copy(). I didn't know that. I mean, I knew it was, thanks to anne's comment, but I didn't know it was the super-hyper-deluxe version.

Second: I do get the while loop and that I try to loop through every variable in the list. I just don't understand, what I'm trying to achieve with it.
After reading anne's message again, I think, that we all misunderstood something?

Why do I even have to remove entries, when I can simply use: workingList = myList[:] to overwrite my list back to the default?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,368
15,282
The second one is still hazy to me, could you elaborate, what exactly is happening and why/when I should prefer that method?
There isn't really a "why", it's more a "when" situation ; basically speaking it mostly depend if it's a long run or a short run.

Take a poker game by example.
You start with the full set of cards, and slowly draw cards from it. It's something that will spread through many parts of your code.
There's the part where you shuffle the cards and the one where you distribute them to the player. If you follow the Texas holdem rules, there's then the five cards drawn in the middle.
It feel logic to have somewhere a list that contain all the cards, then works with at copy of it that will be "the packet used for this play". Then, at the end of the said play, you reset the packet.
After all, it's what happen in real life. You remove cards, and at the end you rebuild the packet.

At the opposite, you've the temporary use of the variable.
I don't have a better example that cross my mind, but still the while situation is a good practical example.
Ren'py language do not have a for loop, so if you've to pass through a list in Ren'py language, you've to do this:
Python:
label whatever:
    $ workingList = theList[:]
    while len( workingList ) > 0: # As long as the list contain something.
        $ var = workingList.pop()  # Get the first entry in the list.
        [ ... ]                         # Do whatever you've to do with it.
Here, it feel more logic to "destroy" a copy of the list, than to restore the list once you've finished.

While the result will be the same with both approach, the logic, and therefore the way you'll apprehend your code, differ.
I'm not sure if it will talk to everyone, decades of practice make me perceive the difference, but I don't remember what where my thought when I started. But globally I would say that it's a question of what is easier/more natural. Is it "to restore the original", or is it "to damage a copy you don't care about".
The way you'll write your code will differ, and sometime be easier with one way than with the other. This partly because your mind will be set on a different direction, and so see the problem from a different angle.

If you want, it's like talking about the duration of a car trip.
If it's a trip in a plain, you'll say that it's a X miles/kilometers long trip. But if it's in mountain, you'll say that it's a X hour long trip. This because the speed is relatively constant, and higher, on plain, while on mountain it will totally depend of the number of turns, the size of the road, is it going up or going down, and so on.
The two approach lead to the same result, but each one put you in a different state of mind, that make you apprehend the situation in a better way.
 
  • Like
Reactions: Playstorepers

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Second: I do get the while loop and that I try to loop through every variable in the list. I just don't understand, what I'm trying to achieve with it.
After reading anne's message again, I think, that we all misunderstood something?

Why do I even have to remove entries, when I can simply use: workingList = myList[:] to overwrite my list back to the default?

My assumption is that it's unrelated to actually resetting the contents and more about dealing with what happens next.

To take the "deck of card" as an example. You'd normally have "full reset" of 52 cards, stored however you want to store them.
But the assumption is that for a "reset" to be needed, the game must have ended. For a very simple card game - that might be because there are no cards left.

that is:

Python:
define starter_deck = ["HA", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "H10", "HJ", "HQ", "HK", "DA", "D2", [...] ]

default current_deck = []

label start_new_game:

    current_deck = renpy.random.sample(starter_deck[:], len(starter_deck))

    while len(current_deck) > 0:
        # play the game

I've used renpy.random.sample() here for a couple of reasons. Firstly renpy.random honors rollback. It still creates random numbers, but in such a way that rolling back and then forward again still gets you the same results. Likewise with save files. So no save-scumming your way out of a bad card draw. Secondly google tells me that random.shuffle() updates the original list, whereas random.sample() does not. Given that starter_deck is supposed to be fixed and unchangeable (hence the define), .sample seemed the better answer.

All of which are solving a card game issues... when you aren't designing a card game. Sorry... mainly explaining for future lurkers who stumble on this thread for other reasons.

I guess the assumption is that if you're needing to reset your list, then the list must have been changed since the game started. The card game is one example of how that might happen, and the while loop fits that usage. I can also imagine a list of pictures you want to appear during the game, but after the player has encountered all/most of them, you want to reset things. Even something a simple of a list of sexual positions. I dunno, I'm grasping here.

TL;DR If while doesn't fit your needs... ignore it.
 

Playstorepers

Member
May 24, 2020
160
79
There isn't really a "why", it's more a "when" situation ; basically speaking it mostly depend if it's a long run or a short run.

Take a poker game by example.
You start with the full set of cards, and slowly draw cards from it. It's something that will spread through many parts of your code.
There's the part where you shuffle the cards and the one where you distribute them to the player. If you follow the Texas holdem rules, there's then the five cards drawn in the middle.
It feel logic to have somewhere a list that contain all the cards, then works with at copy of it that will be "the packet used for this play". Then, at the end of the said play, you reset the packet.
After all, it's what happen in real life. You remove cards, and at the end you rebuild the packet.

At the opposite, you've the temporary use of the variable.
I don't have a better example that cross my mind, but still the while situation is a good practical example.
Ren'py language do not have a for loop, so if you've to pass through a list in Ren'py language, you've to do this:
Python:
label whatever:
    $ workingList = theList[:]
    while len( workingList ) > 0: # As long as the list contain something.
        $ var = workingList.pop()  # Get the first entry in the list.
        [ ... ]                         # Do whatever you've to do with it.
Here, it feel more logic to "destroy" a copy of the list, than to restore the list once you've finished.

While the result will be the same with both approach, the logic, and therefore the way you'll apprehend your code, differ.
I'm not sure if it will talk to everyone, decades of practice make me perceive the difference, but I don't remember what where my thought when I started. But globally I would say that it's a question of what is easier/more natural. Is it "to restore the original", or is it "to damage a copy you don't care about".
The way you'll write your code will differ, and sometime be easier with one way than with the other. This partly because your mind will be set on a different direction, and so see the problem from a different angle.

If you want, it's like talking about the duration of a car trip.
If it's a trip in a plain, you'll say that it's a X miles/kilometers long trip. But if it's in mountain, you'll say that it's a X hour long trip. This because the speed is relatively constant, and higher, on plain, while on mountain it will totally depend of the number of turns, the size of the road, is it going up or going down, and so on.
The two approach lead to the same result, but each one put you in a different state of mind, that make you apprehend the situation in a better way.
I see...

I don't completely get what you're saying, but I get what you're trying to say. Basically both methods work, but they "feel differently", even though, they're basically the same thing.

Here, it feel more logic to "destroy" a copy of the list, than to restore the list once you've finished.
A big thanks to you both and your input.