Ren'Py Where do I go wrong with persistence in this gallery implementation?

Jul 22, 2019
247
369
Don't have much experience with Ren'py but looking at the documentation, it seems you need to use something like

Code:
default persistent.gallery_elements = []
Add this line before the start label. And then afterwards Ren'py will save any changes to this variable persistently. Basically Ren'py treats all variables associated with this "persistent" object as persistent data.

Again I might be wrong haven't tested this out.

 
Last edited:

DelinquentProductions

Degenerate
Game Developer
Oct 24, 2020
146
960
I think your compilation safety measures are actually working against you here. There may also be some weirdness if you haven't started the game but are unlocking things. Pulling from the RenPy docs on save states:

Python variables that are not changed before the game begins will not be saved.
What bipedal posted also tracks with some important caveats:

All data reachable through fields on persistent is saved when Ren'Py terminates, or when renpy.save_persistent() is called. Persistent data is loaded when Ren'Py starts, and when Ren'Py detects that the persistent data has been updated on disk.

As persistent data is loaded before init python blocks are run, persistent data should only contain types that are native to Python or Ren'Py. Alternatively, classes that are defined in python early blocks can be used, provided those classes can be pickled and implement equality.
Good work on that gallery implementation though.
 
Jul 22, 2019
247
369
I could only access the gallery_elements list if I referred to it as persistent.gallery_elements.
That is expected, the gallery_elements member exists only within the persistent object, so that's only how it can be accessed.

Its hard to tell what could be going wrong, but try to do the appending after the start label instead of in an init block. (Use a $ sign before the python statements when inside a label, like
Code:
label start:
    $ persistent.gallery_elements.append(blah blah)
 

DelinquentProductions

Degenerate
Game Developer
Oct 24, 2020
146
960
RenPy's debugging options and docs aren't the best just due to the nature of RenPy. One thing that might be worth checking is this order of events:
  1. Reach unlockable portion
  2. Save
  3. Close game
  4. Open and load the specific save
  5. Check gallery.
In theory if the data is being saved but not loaded on game start you could end up in a real weird state, but I'm not very familiar with RenPy's save/load/rollback mechanics.
 
  • Like
Reactions: Madmanator99

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,226
After that, we have a few utility methods for resizing the images (so that there is no need for separate image sizes for thumbnails and the high res image).
You don't need that. Frame automatically resize the image for you.
Therefore, don't need this :
Code:
image gallery_img_1 = max_scale("gallery/gallery_img_1.jpg")
[...]
image gallery_thumbnail_1 = min_scale("gallery/gallery_img_1.jpg", GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT)
Also, Ren'py automatically create an image object with the name of all the images in "game/images" and its sub-directories ; unless the name is duplicated, then only the first one will be created. Therefore, you just need that :
Python:
gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_1,["gallery_img_1", "gallery_img_1b"]))
Then depending if you want to display the thumbnail or the full image, you use Frame( gallery_elements[i].image_list[0], size=( GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT) ) or directly gallery_elements[i].image_list[0].


As for your problem, it come from there :
Python:
# This block is responsible for declaring the elements which should be available in the gallery
init python:
    gallery_elements = []
    gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_1,["gallery_img_1", "gallery_img_1b"],"gallery_thumbnail_1"))
    gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_2,["gallery_img_2"],"gallery_thumbnail_2"))
    gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_3,["gallery_img_3"],"gallery_thumbnail_3"))
The objects are created at init time, therefore they'll not be saved.

This should solve the problem :
Python:
label start:
    call initGallery
    [...]

label initGallery:
    python:
        gallery_elements = []
        gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_1,["gallery_img_1", "gallery_img_1b"],"gallery_thumbnail_1"))
        gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_2,["gallery_img_2"],"gallery_thumbnail_2"))
        gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_3,["gallery_img_3"],"gallery_thumbnail_3"))
    return
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,226
- I've tried putting the list into a "default" declaration as follows:
[...]
Shouldn't this produce the same result? I had zero success with this approach.
Hmm.
Alright, wrote like this, it totally change the meaning of your initial message. The code you provided in it absolutely not use persistent data. So I, wrongly, assumed that your "If you open it up again. you just lost all the unlockables' states" had an implied and loaded a save file.
So, yes, this will not works more than your initial code or the one in my answer.

It's late, so I can forget something, but as far as my brain is working, persistent data are saved whatever the moment they are defined and whatever if they are object or scalar-like. So, it shouldn't matter that you are creating the persistent list in an init block.
The problem, in addition to the fact that you initially didn't used the persistent prefix, come from the fact that you don't verify it the list exist before creating it. Therefore, every time Ren'py is launched, it reset the said list ; things are persistent, as long as you don't write over them.

Python:
init python:
    if persistent.gallery_elements is None:
        persistent.gallery_elements = []
        persistent.gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_1,[...] )
    [...]
 
Jul 22, 2019
247
369
Ok, just as general rule, I would suggest:

1) Before actually initialiazing the list in a label, check if it already exists or not (hasattr()). You might be overwriting it without knowing it.

2) After making your changes, call renpy.save_persistent() just to ensure it gets saved.

I have no idea if persistent variables are available in the context of the main menu screen or not. If they aren't then maybe make the gallery in-game somehow?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,226
1) Before actually initialiazing the list in a label, check if it already exists or not (hasattr()). You might be overwriting it without knowing it.
This don't works with persistent values. Due to its destination, the object handling them is designed to never throw an AttributeError exception, therefore hasattr() will always return True.
As I said above, to test if a persistent variable exist, you need to test if it's value is None or not.
 

Paz

Active Member
Aug 9, 2016
923
1,521
If anybody got any ideas for improvement and how to fix the spacing issues, please share so I can update the code.
Not sure if you've changed it, but the default Frame style has a 6px padding (defined as Border in gui.rpy if I recall correctly) that can mess it up if you're trying pixel-perfect UI.
You can check if that's the case for you with Style Inspector (Shift + I) while hovering over a displayable.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,226
Not sure if you've changed it, but the default Frame style has a 6px padding (defined as Border in gui.rpy
You're mixing two different things here. There's , that was my advice, which is a displayable, and there's which is a screen statement and the one that have the padding.
 

Paz

Active Member
Aug 9, 2016
923
1,521
You're mixing two different things here. There's , that was my advice, which is a displayable, and there's which is a screen statement and the one that have the padding.
Ah, right. That will teach me trying to read code snippets semi-awake.