Ren'Py How to save player clothes sets

Alt-0069

Member
Aug 27, 2017
108
151
Hello everyone.
I want to create a clothes customisation option for my game but I dont know how to save the preference choose by the player.

I have create differents variables for the differents clothes and I set a variable for the whole set like this:

Code:
    default LilyWearRef = ['base','base','base','shoes_1','jeans','top_1','none','none']
   
    $ LBra =  LilyWearRef [0] #none/base
    $ LPanties = LilyWearRef [1] #none/base
    $ LSock = LilyWearRef [2] #none/base
    $ LShoes = LilyWearRef [3] #none/shoes_1
    $ LBottom = LilyWearRef [4] #none/jeans
    $ LTop = LilyWearRef [5] #none/top_1
    $ LCoat = LilyWearRef [6] #none FOR THE MOMENT
    $ Lglasses = LilyWearRef [7] #none FOR THE MOMENT
and then I write this:

Code:
    #CLOTHES SET
   
    if LilyWearSet == 'naked':
        $ LilyWearRef = ['none','none','none','none','none','none','none','none']
    elif LilyWearSet == 'casual_1':
        $ LilyWearRef = ['base','base','base','shoes_1','jeans','top_1','none','none']
    else:
        $ LilyWearRef = ['base','base','base','shoes_1','jeans','top_1','none','none']
   
    return
But I don't know how to create an option for the player to make a new custom entry for LilyWearSet

Should I create some empty LilyWearSet and name them custom 1,custom 2, ... , custom 10 and then the player will change the variables ?
Is there an other option?

Thanks
 
Last edited:

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,773
You'll need to change your variables a bit and then all you have to do is fill the variables:

Python:
"Yawn... another day, hmm what am I gonna wear, a shirt or a sweater?"
menu:
    "The shirt":
        $ top_1 = "shirt"
    "Sweater":
        $ top_1 = "sweater"
"Yeah, that looks good, now about the pants..."
menu:
    "Jeans!":
        $ jeans = "Jeans"
    "Nah, it's hot, I'm gonna take shorts."
        $ jeans = "shorts"
        
#etc.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,192
But I don't know how to create an option for the player to make a new custom entry for LilyWearSet[/I]
Like recreation said, it need a change in your variables structure.

I would say that a dictionary is what you need here. Something that would looks like :
Code:
default LilyWearRef = { "default":  ['base','base','base','shoes_1','jeans','top_1','none','none'],
                        "casual_1": ['base','base','base','shoes_1','jeans','top_1','none','none'],
                        "naked": ['none','none','none','none','none','none','none','none'] }
Then to add a user defined set, you just have to add an entry to the dictionary ; bonus point because the player can name it.
Code:
label whatever:
    $ LilyWearRef["something"] = ['base','base','base','shoes_1','jeans','top_1','none','none']
If the player want to change this or that clothe, but not save the set, you can simply store the changes in the set named "default", or create a set named "actual" for this.


As for using it, it's as simple as :
Code:
label whatever:
    $ LilyActualClothes = LilyWearRef[LilyWearSet]


This being said, personally I would add the use of constants-like to ease the development :
Code:
define CLOTHE_BRA = 0
define CLOTHE_PANTIES = 1
define CLOTHE_SOCK =  2
define CLOTHE_SHOES = 3
define CLOTHE_BOTTOM = 4
define CLOTHE_TOP = 5
define CLOTHE_COAT = 6
define CLOTHE_GLASSES = 7
Then when manipulating the list of wear sets, you'll just have to use :
Code:
label whatever:
    $ LTop = LilyWearRef["casual_1"][CLOTHE_TOP]
What prevent the possibility of an error when entering the index you want to address.



Finally, I would also add an abstract class to facilitate the use in layered images :
Python:
init python:
    # Don't handle variables, so no need to be rollback compliant or anything else.
    class LilyClothes():
        def _get( self, rank ):
            return LilyWearRef[LilyWearSet][rank]

         #  They are properties, so they are addressed as regular attributes ( /object.bra/ )
        # not as methods ( /object.bra()/ ).
        @property
        def bra( self ):
            return self._get( CLOTHE_BRA )

        @property
        def panties( self ):
            return self._get( CLOTHE_PANTIES )

       [...]

    lilyClothes = LilyClothes()
This will lead to layered images that would looks more or less like this :
Code:
layeredimage lily:
    always:
        "lily_body"

    group bra:
        "lily_bra_[lilyClothes.bra]"
    group panties:
        "lily_bra_[lilyClothes.panties]"
    [...]
What would imply less manual computation to define what clothes have to be used, since you just need to know the set definition and the actually worn set.
 
Apr 24, 2020
192
257
Here's my take on it, assuming it's python/renpy.
Python:
default clothingSetsLily = {
    "casual": {"shirt": "top_1", "pants": "jeans", "shoes": "shoes_1"},
    "naked": {"shirt": "none", "pants": "none", "shoes": "none"}
}

default clothingLily = {"shirt": "top_1", "pants": "jeans", "shoes": "shoes_1"}

init python
    def AddNewSetForLily():
        while True:
            _setName = renpy.input("What would you call this set?")
            if _setName not in clothingSetsLily:
                break
        _newSet = {}
        for _key in clothingLily:
            _newSet[_key] = clothingLily[_key]
        clothingSets.append(_newSet)
Basically you have a dictionary of the sets, which are dictionaries of clothing items (easier to change things later, and also easier to remember that pants are trousers rather than index 4 being trousers).

To change what set Lily is wearing you just have to reference a different set.
Code:
clothingLily  = clothingSetsLily["casual"]
Adding a new set is basically just copying what she's currently wearing and adding it to the dictionary of sets (also checking that we aren't overwriting any existing sets).
 

Alt-0069

Member
Aug 27, 2017
108
151
Finally I've tried this:

Code:
    default LilyclothingSets =  ["naked",
    "casual","custom"
    ]
   
    $ LilyclothingSets [0] = ['none','none','none','none','none','none','none','none']
    $ LilyclothingSets [1] = ['base','base','base','shoes_1','jeans','top_1','none','none']
    $ LilyclothingSets [2] = [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses]
   

    default LilyWearRef = LilyclothingSets [0]
   
    $ LBra = LilyWearRef [0] #none/base
    $ LPanties = LilyWearRef [1] #none/base
    $ LSock = LilyWearRef [2] #none/base
    $ LShoes = LilyWearRef [3] #none/shoes_1
    $ LBottom = LilyWearRef [4] #none/jeans
    $ LTop = LilyWearRef [5] #none/top_1
    $ LCoat = LilyWearRef [6] #none FOR THE MOMENT
    $ Lglasses = LilyWearRef [7] #none FOR THE MOMENT
But each time I have this error:

Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/_lily_/Lily_chat.rpy", line 25, in script call
    call LilyRef
  File "game/_lily_/Lily_reference.rpy", line 120, in script
    $ LTop = LilyWearRef [5] #none/top_1
  File "game/_lily_/Lily_reference.rpy", line 120, in <module>
    $ LTop = LilyWearRef [5] #none/top_1
IndexError: string index out of range

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "game/_lily_/Lily_chat.rpy", line 25, in script call
    call LilyRef
  File "game/_lily_/Lily_reference.rpy", line 120, in script
    $ LTop = LilyWearRef [5] #none/top_1
  File "D:\renpy-7.3.5-sdk\renpy\ast.py", line 914, in execute
    renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
  File "D:\renpy-7.3.5-sdk\renpy\python.py", line 2028, in py_exec_bytecode
    exec bytecode in globals, locals
  File "game/_lily_/Lily_reference.rpy", line 120, in <module>
    $ LTop = LilyWearRef [5] #none/top_1
IndexError: string index out of range

Windows-8-6.2.9200
Ren'Py 7.3.5.606
WitchTrainerRemake 1.0
Sun Dec 20 13:24:37 2020
I don't understand
 
Apr 24, 2020
192
257
Everything with a $ needs to be in an init: or label block. Looking at what you have, you can basically replace $ with default and it should work.

Do be careful about using a variable before it is announced though. Some programming languages aren't very fond of that.

EDIT: However, I would strongly recommend that you do as either anne O'nymous or I have suggested, and have a reference to the clothing slot, rather than a variable that references a clothing article that was in that slot.
As your code is now, I fear that LBra (and the rest of them) will not change if Lily changes her set of clothes.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Finally I've tried this:

You don't have permission to view the spoiler content. Log in or register now.


I don't understand

Firstly, I'm no expert on Python lists. (arrays, sets, dicts, etc). So I may be misunderstanding something too.

It all kinda depends on where all that code actually is within your script.

default lines are processed while the game is starting. RenPy scans through all your scripts and deals with them before the game reaches the main menu. For reference: they are processed AFTER init: and init python: stages - which are are also done as the game is starting.

define: and image: statements are treated the same way, though I haven't tested whether they are processed before or after the init: code. (probably before would be my guess).

So as I see it (based on your default values), the initial values of both your main variables, as it reaches the main menu will be:

LilyclothingSets = ["naked", "casual","custom"]
LilyWearRef = "naked"

python:, aka $ lines are processed at runtime, usually somewhere after label start:. Although they could be part of the game's init: code. You haven't included where in your code those lines are.

But let's assume for the moment that those lines are part of the main game flow and not part of the startup init: processes. And it executes:

Python:
    $ LilyclothingSets [0] = ['none','none','none','none','none','none','none','none']
    $ LilyclothingSets [1] = ['base','base','base','shoes_1','jeans','top_1','none','none']
    $ LilyclothingSets [2] = [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses]

    $ LBra = LilyWearRef [0]
    $ LPanties = LilyWearRef [1]
    $ LSock = LilyWearRef [2]
    $ LShoes = LilyWearRef [3]
    $ LBottom = LilyWearRef [4]
    $ LTop = LilyWearRef [5]
    $ LCoat = LilyWearRef [6]
    $ Lglasses = LilyWearRef [7]

Okay, this is where I start to have problems. Because as far as I can see LBra, LPanties, etc haven't been created as variables at the time it does $ LilyclothingSets [2] = .... But that isn't the error you're getting ($ LTop = LilyWearRef [5] / IndexError: string index out of range).

Also you've taken the time to set up defaults for LilyclothingSets ("naked", "casual", "custom"), and then your code overwrites those values. "naked" will be replaced by ['none','none','none','none','none','none','none','none']. "casual" will be replaced by ['base','base','base','shoes_1','jeans','top_1','none','none'] and as I've already said, I have a problem understanding why that 3rd line doesn't actually crash.

I suppose it's possible you've listed the code here in a different order than it's in the game and the $ LBra = ... type code is executed before the $ LilyclothingSets = ... code. But even that can't be right, since the value of LilyWearRef (based on it's default value) isn't a python list and is just "naked".

OH JEEZ.... I got it. Fuck me... I told you I barely understand python lists...

The value is "naked".
So LilyWearRef [0] is "n".
So LilyWearRef [1] is "a".
So LilyWearRef [2] is "k".

See where I'm going with this?

LilyWearRef [3] is "e".
LilyWearRef [4] is "d".

... AND LilyWearRef [5] is longer than the value it's pointing to... so you get index out of range.

Okay... so that answers your question... but I think you've still got some underlying issues.
I would recommend looking at this link and try to get your head around the difference between lists, sets, tuples and dictionaries (they are all variations of a python list).


It's basically where I learned the stuff I've been using to try to get my head around these sort of problems.

To completely oversimplify things... Dictionaries are accessed by a key value (like "naked" or "casual" or "custom" ... hint, hint) and are defined using the { } brackets. Lists are accessed by a number 0, 1, 2, 3, etc and are defined using the [ ] brackets. I think you've missed that distinction when reading through some of the earlier posts.

My gut feel is you only need dicts and lists (or actually a dict containing a group of lists). But it's difficult to explain why I think that, without knowing my assumptions of what you are trying to do. Perhaps it would be better for me to actually ask, rather than assume...


So answers me ask two simple core questions about your clothing sets...

Are you planning to only have 3 sets of clothes for Lily? (naked, casual and custom)... or can the player (or the game) add/remove extra sets based on new clothing acquired while playing (I dunno, "ballroom", "BDSM", "swimwear", "nightwear").

Because I suspect the solution will change depending on whether it's just 2 predefined sets + 1 "custom" set... or whether any or all of those sets can be altered by the player (or the game acting for the player).

Is Lily the protagonist of your game? Or are you going to have clothing sets for other characters too?
 
Last edited:
  • Like
Reactions: anne O'nymous
Apr 24, 2020
192
257
OH JEEZ.... I got it. Fuck me... I told you I barely understand python lists...

The value is "naked".
So LilyWearRef [0] is "n".
So LilyWearRef [1] is "a".
So LilyWearRef [2] is "k".

See where I'm going with this?

LilyWearRef [3] is "e".
LilyWearRef [4] is "d".
Holy... great catch.

I tried running the codes line by line to see what was wrong. But the way I did it, it always ended up overwriting "naked" with the list of "none", so there should be enough elements.
Also, my attempt at recreating the code also failed whenever it reached the variables that had not yet been defined.

The whole setup the names of the clothing sets, but then overwriting them with lists, feels like an attempt at setting up a dictionary, but treating it like a list.
Lists take an index, dictionaries take a key/string.

But yeah, the whole "strings are just a list of characters" is something you have to get your head around for weakly typed languages.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
If it were me, I'd probably do something like this:

A list of what the character is currently wearing, combined with a dict of the various sets of possible clothing.

I wouldn't have a "custom" set, instead I would create new sets as the game progresses (or let the player create their own extra sets based on what the character is currently wearing).

The system would work by using the "current" list of clothing most of the time, but copying the whole list of what is currently being worn to and from the dictionary (array) of available sets.

I wrote this code to prototype the idea...

Python:
# Bra, Panties, Socks, Shoes, Bottom, Top, Coat, Glasses
default clothing_sets = {
    "naked" : ["none", "none", "none", "none", "none", "none", "none", "none"]
    "casual" : ["base", "base", "base", "shoes_1", "jeans", "top_1", "none", "none"]
    }

default current_clothes = list(clothing_sets["casual"])
default clothing_set_name = ""
default available_sets = []

define MAX_CLOTHING_SETS = 8

label start:

    scene black with fade
    "Start:.... "

label clothing_menu:

    menu:
        "Change clothing...":
            menu:
                "Change Bra (currently '[current_clothes[0]]')":
                    menu:
                        "Casual Bra":
                            $ current_clothes [0] = "base"
                        "Lacy Bra":
                            $ current_clothes [0] = "lacy"
                        "Peek-a-boo Bra":
                            $ current_clothes [0] = "peek"
                        "No change":
                            jump clothing_menu

                "Change Panties (currently '[current_clothes[1]]')":
                    menu:
                        "Casual Panties":
                            $ current_clothes [1] = "base"
                        "Lacy Panties":
                            $ current_clothes [1] = "lacy"
                        "Crotchless Panties":
                            $ current_clothes [1] = "crotchless"
                        "No change":
                            jump clothing_menu

                "(Same for ... Socks, Shoes, Lowerbody, Upperbody, Coat and Glasses)":
                    jump clothing_menu

                "No change":
                    jump clothing_menu

        "*** Too Many Clothing Sets Exist - Delete One To Free Up A Slot ***" if len(clothing_sets) >= MAX_CLOTHING_SETS:
            jump clothing_menu

        "Save Current Clothing As New Set..." if len(clothing_sets) < MAX_CLOTHING_SETS:
            $ clothing_set_name = renpy.input("What would you like to name this new set?", length=10, allow="abcdefghijklmnopqrstuvwxyz1234567890-.#")
            $ clothing_set_name = clothing_set_name.lower().strip()

            if clothing_set_name == "":
                "You must provide a valid name for the clothing set."
                jump clothing_menu

            if  clothing_set_name in clothing_sets.keys():
                "Sorry, a clothing set called '[clothing_set_name]' already exists."
                jump clothing_menu

            $ clothing_sets[clothing_set_name] = list(current_clothes)
            "New set named '[clothing_set_name]' created."

        "Swap To Clothing Set...":

            $ available_sets = clothing_sets.keys()

            menu:
                "[available_sets[0]]":
                    $ current_clothes = list(clothing_sets [available_sets [0]])    # the first two sets are always available (naked, casual)

                "[available_sets[1]]":
                    $ current_clothes = list(clothing_sets [available_sets [1]])

                "[available_sets[2]]" if len(available_sets) > 2:
                    $ current_clothes = list(clothing_sets [available_sets [2]])

                "[available_sets[3]]" if len(available_sets) > 3:
                    $ current_clothes = list(clothing_sets [available_sets [3]])

                "[available_sets[4]]" if len(available_sets) > 4:
                    $ current_clothes = list(clothing_sets [available_sets [4]])

                "[available_sets[5]]" if len(available_sets) > 5:
                    $ current_clothes = list(clothing_sets [available_sets [5]])

                "[available_sets[6]]" if len(available_sets) > 6:
                    $ current_clothes = list(clothing_sets [available_sets [6]])

                "[available_sets[7]]" if len(available_sets) > 7:
                    $ current_clothes = list(clothing_sets [available_sets [7]])

                "Cancel":
                    jump clothing_menu

            "Swapped to : [current_clothes]"

        "Stop Playing Dressup":
            jump finished_clothing

    jump clothing_menu

label finished_clothing:

    "Currently wearing : [current_clothes]"

    "*** THE END ***"

    return

In effect, current_clothes is a list array of the clothes the character is currently wearing.
Then clothing_sets is a dict array, keyed by the name of the clothing set which holds multiple copies of that same clothing array.
My prototype code allows the player to pick a set of clothes from the existing list to wear (by copying the data to the current_clothes variable. It can also add entries into the dictionary by copying current_clothes to one of the clothing_sets entries.

I've had to limit things to 8 clothing sets, because more than 9 menu: choices on the screen just looks odd.

I've used menu: only for prototyping purposes. A much better solution would be to have a screen: to changing the current clothes and a separate screen: for clothing sets maintenance.

I've not included code for deleting entries in the dictionary, because it would be much easier to implement using a screen. It'd need a bit of validation to avoid deleting "casual" or "naked". But looking at the documentation, it's just a simple thing like $ del clothing_sets["test1"], where "test1" is one of the entries in the dictionary.

It looks like a lot of code, but most of it is just repeated sections for different clothing pieces and dealing with the clothing sets the hard way (using menu:).


One peculiarity that tripped me up when initially trying this is that when you do something like $ list2 = list1, that doesn't actually create a new list called "list2". What it does is create a link between list1 and list2, they are effectively the same object (at least to my limited brain). So any change to list2 is also made to list1.

As a consequence, when I thought I was copying data in current_clothes to/from clothing_sets, it caused some problems I hadn't anticipated - because I thought they were actually two separate variables.

The solution is to use the list() function when shifting data around, which creates a new list object based on an existing list. There's a couple of ways of doing this same thing, but this one "felt" neater to me in the end.


This prototype code works, and can be tested by simply copy/pasting the code into a dummy RenPy project.

Anne wrote a better implementation of the clothing type array earlier up the thread, where you can access things using names like "bra" and "panties" rather than [0] and [1]. I'd probably try to use that in a "real" game, rather than a prototype.
 

Alt-0069

Member
Aug 27, 2017
108
151
Okay...
I have tried to use dictionary and after a few other tries it work.

Thank you guys
 

Alt-0069

Member
Aug 27, 2017
108
151
Okay now I have a different issue about dictionary...

How can I change only one clothes like the Top if I use this:

Code:
init:
    default LBra = "base" #none/base
    default LPanties = "base" #none/base
    default LSock = "base" #none/base
    default LShoes = "shoes_1" #none/shoes_1
    default LBottom = "jeans" #none/jeans
    default LTop = "top_1" #none/top_1
    default LCoat = "none"
    default Lglasses = "none"
   
   
label LilyRef:

    default LilyclothingSets =  {"naked": ['none','none','none','none','none','none','none','none'],
        "casual": ['base','base','base','shoes_1','jeans','top_1','none','none'],
        "custom": [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses],
    }
   
    default LilyWearRef = LilyclothingSets["casual"]
   
    $ LBra = LilyWearRef [0] #none/base
    $ LPanties = LilyWearRef [1] #none/base
    $ LSock = LilyWearRef [2] #none/base
    $ LShoes = LilyWearRef [3] #none/shoes_1
    $ LBottom = LilyWearRef [4] #none/jeans
    $ LTop = LilyWearRef [5] #none/top_1
    $ LCoat = LilyWearRef [6] #none FOR THE MOMENT
    $ Lglasses = LilyWearRef [7] #none FOR THE MOMENT
Because each time I write
Code:
$LTop = 'none'

call LilyRef
in the "chat" label it doesn't do anything. I've also tried to do:

Code:
$ LilyWearRef = LilyclothingSets["custom"]
$LTop = 'none'
call LilyRef
But it doesn't work too.

Thank for the help.
 
Last edited:

Alt-0069

Member
Aug 27, 2017
108
151
init python
def AddNewSetForLily():
while True:
_setName = renpy.input("What would you call this set?")
if _setName not in clothingSetsLily:
break
_newSet = {}
for _key in clothingLily:
_newSet[_key] = clothingLily[_key]
clothingSets.append(_newSet)[/CODE]


Adding a new set is basically just copying what she's currently wearing and adding it to the dictionary of sets (also checking that we aren't overwriting any existing sets).
So if the player choose differents clothes for the character what do I need to write to use the python code?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Because each time I write
Code:
$LTop = 'none'

call LilyRef
If I'm reading that right...

You're changing the value of LTop to "none". Then you call the label LilyRef, which immediately overwrites all the values, including LTop, with the values from the LilyWearRef list.

There's another more complex issue rattling around my head about when variables are actually variables, and when they are merely (sometimes shared) pointers to areas of memory. Ignore this for the moment and consider it a placeholder for something annoyingly complex later.

Your problem, as I see it, is you're trying to use the individual variables ("LBra", "LPanties", etc) as both "what Lily is currently wearing" AND "the items that make up the 'custom' dressup set".

Maybe I'm reading it wrong, but it feels like you're not treating the LilyWearRef as the list of what Lily is currently wearing. Instead treating the individual variables as that, even when the "casual" or "naked" sets might currently be in use.

The other more complex "pointers/references" thing, I'm going to need to do some testing to make sure I actually understand it myself before trying to explain myself here.

You don't have permission to view the spoiler content. Log in or register now.
 
Apr 24, 2020
192
257
Try rubber duck debugging, which is reading every line of code one by one and explaining what they do. Let's run through your code
Python:
init:
    default LBra = "base" #none/base
    default LPanties = "base" #none/base
    default LSock = "base" #none/base
    default LShoes = "shoes_1" #none/shoes_1
    default LBottom = "jeans" #none/jeans
    default LTop = "top_1" #none/top_1
    default LCoat = "none"
    default Lglasses = "none"
Okay, so we start by setting up a bunch of variables. There's no need for the init in the beginning, but it doesn't do any harm.
So no errors here, let's take a look at the label.
Python:
default LilyclothingSets =  {"naked": ['none','none','none','none','none','none','none','none'],
        "casual": ['base','base','base','shoes_1','jeans','top_1','none','none'],
        "custom": [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses],
    }
hmm ... this doesn't look right. We are creating a dictionary of the clothing sets. Although the code wouldn't create any errors, this really should have been part of the first init block since we aren't going to change this, so there's no reason for it to be called all the time.
Python:
default LilyWearRef = LilyclothingSets["casual"]
Okay, here's where the code seem to not do anything, as it sets Lily's current clothes to be the casual set, which is what we gave her as default later.
Python:
$ LBra = LilyWearRef [0] #none/base
    $ LPanties = LilyWearRef [1] #none/base
    $ LSock = LilyWearRef [2] #none/base
    $ LShoes = LilyWearRef [3] #none/shoes_1
    $ LBottom = LilyWearRef [4] #none/jeans
    $ LTop = LilyWearRef [5] #none/top_1
    $ LCoat = LilyWearRef [6] #none FOR THE MOMENT
    $ Lglasses = LilyWearRef [7] #none FOR THE MOMENT
Then we assign the clothing pieces to be items in the casual clothing set, just like we set up in the beginning.

So nothing in the code does anything wrong, it simply sets her Lily's current clothing to her casual set, which she is already wearing.

Here's some quick changes you could make
Python:
default LBra = "base" #none/base
default LPanties = "base" #none/base
default LSock = "base" #none/base
default LShoes = "shoes_1" #none/shoes_1
default LBottom = "jeans" #none/jeans
default LTop = "top_1" #none/top_1
default LCoat = "none"
default Lglasses = "none"
  
default LilyclothingSets =  {"naked": ['none','none','none','none','none','none','none','none'],
        "casual": ['base','base','base','shoes_1','jeans','top_1','none','none'],
        "custom": [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses],
    }
    
init python:
    def ChangeLilysSet(setName):

        global LilyWearRef
        global LBra
        global LPanties
        global LSock
        global LShoes
        global LBottom
        global LTop
        global LCoat
    
        LilyWearRef = LilyclothingSets[setName]
  
        LBra = LilyWearRef [0] #none/base
        LPanties = LilyWearRef [1] #none/base
        LSock = LilyWearRef [2] #none/base
        LShoes = LilyWearRef [3] #none/shoes_1
        LBottom = LilyWearRef [4] #none/jeans
        LTop = LilyWearRef [5] #none/top_1
        LCoat = LilyWearRef [6] #none FOR THE MOMENT
    
label LilyRef:
    "I'm wearing a casual set"
    $ ChangeLilysSet("naked")
    "Now I'm naked"
    $ ChangeLilysSet("casual")
    "Now I'm wearing the casual set again"
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,192
Code:
    default LilyWearRef = LilyclothingSets [0]
This do not do what you think.
You aren't copying LilyclothingSets[0] into LilyWearRef but you make the two point to the same place into the memory. Therefore, any change in LilyclothingSets[0], like by example LilyclothingSets[0][1] = None will be also apply to LilyWearRef, andthe opposite is also true, any change in LilyWearRef, like LilyWearRef[1] = None will apply to LilyclothingSets[0]. The second case being the worse possible scenario for you, because it will change the sets, and change it definitively.

There's many way to copy a list, one of them is to ask Python to create a new one, like this :
Code:
    default LilyWearRef = list( LilyclothingSets[0] )

This problem also exist, in the exact same way and for the exact same reason, when you write :
[/code]
default LilyWearRef = LilyclothingSets["casual"]
[/code]
Here, it's :
[/code]
default LilyWearRef = list( LilyclothingSets["casual"] )
[/code]
that you have to use.


Also, but it's a side note:
While your notation listName [index] works perfectly, it's better to write it without the additional space, so listName[index].
It's just the application of a mental bias. Like in human language, everything separated by a space represent two words, when seeing your notation, the meaning of what is wrote isn't immediately evident. It take just an instant to understand what is in front of our eyes, but it let the reader with a small touch of confusion that is unnecessary.



I wouldn't have a "custom" set, instead I would create new sets as the game progresses (or let the player create their own extra sets based on what the character is currently wearing).
I strongly recommend the use of a "custom set" as representing the clothes actually worn. It will give a reference when clothes are automatically removed for a reason or another, and have to be automatically worn again after some times.


How can I change only one clothes like the Top if I use this:

Code:
[...]
    default LilyclothingSets =  {"naked": ['none','none','none','none','none','none','none','none'],
        "casual": ['base','base','base','shoes_1','jeans','top_1','none','none'],
        "custom": [LBra, LPanties, LSock, LShoes, LBottom, LTop, LCoat, Lglasses],
    }
  
    default LilyWearRef = LilyclothingSets["casual"]

    $ LBra = LilyWearRef [0] #none/base
This is correct. Well, minus the "copy versus 'point to the same place'" thing I talk about above.

LilyclothingSets is a dictionary where each entry is a list. Therefore, whatever you write LilyWearRef = LilyclothingSets["casual"] or LilyWearRef = list( LilyclothingSets["casual"] ), you're assigning a list to LilyWearRef.
And later when you want to access only one piece of clothes, you're correctly addressing the right cell of the list.

Therefore, I guess that the problem is in fact located in LilyRef.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
[...] Although the code wouldn't create any errors, this really should have been part of the first init block since we aren't going to change this, so there's no reason for it to be called all the time.
Somewhat off topic.... But it seems you both are misunderstanding what default is. It certainly doesn't need to be in an init: block. Though it is very common for it to be at the top of the code for readability sake.

Firstly... $ is "do single python command". It's just a shorthand alternative.

Python:
    $ var1 = 99
    $ var2 = "Julia"
    $ var3 = True

# as opposed to:

    python:
        var1 = 99
        var2 = "Julia"
        var3 = True

default is used to set a variable to a specific value during the startup processes of the game, before you initially see the main menu. It is however one of the unique commands that are processed during that time regardless of where they appear in the code. (same for image, screen: and a few other statements).

During the game startup, RenPy runs a number of tasks. init: and init python: blocks are certainly included and are actually dealt with before default lines are processed. But eventually, RenPy runs a scan through all the code looking for default commands and then runs each of them. Those lines could be at the top of the code, in the middle or right at the end - they will still be dealt with before the main menu appears.

There used to be a practice of initializing variables as part of the init process using "$" - but there are some consequences for save files which is the reason why default was created as a unique command. You still see it in some games, where the author has either seen it coded that way in other games and/or just doesn't understand why default exists.

You might have seen:

Python:
init:
    $ var1 = 99
    $ var2 = "Julia"
    $ var3 = True

But more recent code would be:

Python:
default var1 = 99
default var2 = "Julia"
default var3 = True

Because of the "default can be placed anywhere", there is nothing technically wrong with it being in the middle of some other code or even right at the end - but it isn't how most programmers would do it and therefore isn't where most programmers would expect to find those lines when looking at someone else's code. Instead they would put the default commands at the top of the code (or near the top), which is where most of us would consider it runs anyway.

My point is they are independent of the code. So "put them in the init block" feels like mixing up two completely unrelated solutions ( the newer "default" solution and the older use of settings variables using "$" within init blocks).