[Solved] Nested Variables in Statements, Screens, Images

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

[Solved] Nested Variables in Statements, Screens, Images

#1 Post by HB38 » Sat Jan 09, 2016 5:06 am

Hi all,

I'm trying to use some variables in a few unusual ways to save myself having to duplicate a lot of code, but I can't quite figure out how to get it to work. Here's an example of a simple say statement with a 'nested' variable:

Code: Select all


$ curr_char = c1

$ c1_first_name = Bob
$ c1_last_name = Smith

label character_1_debug:

    "[+curr_char+_first_name] [+curr_char+_last_name] is here."

Based on a few other threads, I thought this would work but it either throws an error or doesn't trigger at all (and you just get "is here"). I'm clearly just missing the syntax to get that to work, if it's possible…

And then I'm also trying to use them in an in If statement:

Code: Select all


$ c1_alive = "True"

if (+curr_char+_alive == "True"):
     > Stuff
else:
     > Other Stuff
Again, I'm guessing I'm just mucking up the syntax… Thanks for any help you can provide!
Last edited by HB38 on Mon Jan 11, 2016 9:54 pm, edited 1 time in total.

User avatar
Evildumdum
Regular
Posts: 191
Joined: Sun Jan 18, 2015 8:49 am
Projects: ApoclypseZ
Contact:

Re: Variables in other Variables and If statements

#2 Post by Evildumdum » Sat Jan 09, 2016 6:49 am

Ah the nostalgia. I tried doing a similar thing not too long back. Simply put, you cant add two variables that are strings together to make up the name of a third variable. It just doesn't work no matter what the syntax. You can achieve it with lists though.

Code: Select all

init python:
    current_characterlist = []
    character1_first = Bob
    character1_last = Smith
    character2_first = Lenny
    character2_last = Henry


label switch_to_bob:
    $ del current_characterlist[:]
    $ current_characterlist.append("[character1_first]")
    $ current_characterlist.append("[character1_last]")
    return

label switch_to_lenny:
    $ del current_characterlist[:]
    $ current_characterlist.append("[character2_first]")
    $ current_characterlist.append("[character2_last]")
    return

label conversation:
    call switch_to_bob
    "Hi [current_characterlist[0]] [current_characterlist[1]]. Its Nice to meet you."
    call switch_to_lenny
    "Hi [current_characterlist[0]] [current_characterlist[1]]. Its Nice to meet you."
"If at first you don't succeed, try hitting it with a shoe."

philat
Eileen-Class Veteran
Posts: 1763
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Variables in other Variables and If statements

#3 Post by philat » Sat Jan 09, 2016 12:08 pm

Evildumdum wrote:Simply put, you cant add two variables that are strings together to make up the name of a third variable.
Technically not true, but a bit of a moot point since this is a problem for data structures, not dynamic variables. I think it's probably simplest to use classes here.

Code: Select all

init python:
    class CharInfo():
        def __init__(self, first, last, alive):
            self.first = first
            self.last = last
            self.alive = alive

label start:
    $ adam = CharInfo("Adam", "Smith", False)
    $ bob = CharInfo("Bob", "Johnson", True)

    $ c1 = adam
    if c1.alive:
        "[c1.first] [c1.last] is alive."
    else:
        "[c1.first] [c1.last] is dead."

    $ c1 = bob
    if c1.alive:
        "[c1.first] [c1.last] is alive."
    else:
        "[c1.first] [c1.last] is dead."

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#4 Post by HB38 » Sat Jan 09, 2016 1:06 pm

philat wrote:
Evildumdum wrote:Simply put, you cant add two variables that are strings together to make up the name of a third variable.
Technically not true, but a bit of a moot point since this is a problem for data structures, not dynamic variables. I think it's probably simplest to use classes here.

Code: Select all

init python:
    class CharInfo():
        def __init__(self, first, last, alive):
            self.first = first
            self.last = last
            self.alive = alive

label start:
    $ adam = CharInfo("Adam", "Smith", False)
    $ bob = CharInfo("Bob", "Johnson", True)

    $ c1 = adam
    if c1.alive:
        "[c1.first] [c1.last] is alive."
    else:
        "[c1.first] [c1.last] is dead."

    $ c1 = bob
    if c1.alive:
        "[c1.first] [c1.last] is alive."
    else:
        "[c1.first] [c1.last] is dead."

Philat - I think this is really close to what I need. I'm going to throw a bit of a wrench into this though… as most of these variables are chosen at random upon game creation.

In my particular use case, I've got 'character 1'. His name could be any of numerous names that are picked using the following (each character has about 15 random variables (and 2 or 3 static ones), but I've removed most for this example):

Code: Select all

label names_generator:
    
    $ first_names = ['Bob, James, John']
    $ last_names = ['Jones, Smith, Young']
    
    $ char_1_first_name = renpy.random.choice(first_names)
    $ first_names.remove(char_1_first_name)
    $ char_1_last_name = renpy.random.choice(last_names)
    $ last_names.remove(char_1_last_name)

    $ char_2_first_name = renpy.random.choice(first_names)
    $ first_names.remove(char_2_first_name)
    $ char_2_last_name = renpy.random.choice(last_names)
    $ last_names.remove(char_2_last_name)
    
    return
Is it at all possible to use that with the statement you made?

Code: Select all

$ char_1_info = CharInfo("[char_1_first_name]", "[char_1_last_name]", False)
$ char_2_info = CharInfo("[char_2_first_name]", "[char_2_last_name]", False)

label example_char_1:

     $ curr_char = char_1

     "[curr_char.first] [curr_char.last]."

label example_char_2:

     $ curr_char = char_2

     "[curr_char.first] [curr_char.last]."

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#5 Post by HB38 » Sun Jan 10, 2016 9:12 pm

Alright, I found a way to get what I want to happen mostly:

Code: Select all

label curr_char_update:
    
    $ curr_char_firstname = char_1_first_name
    $ curr_char_lastname = char_1_last_name
    $ curr_char_alive = char_1_alive

    return
So that sets the variables I want for the scene I'm using and it works quite well - and then, when I'm done I can have them update back to the main list:

Code: Select all

label curr_char_cleanup:
    
    $ char_1_first_name  = curr_char_firstname
    $ char_1_last_name  = curr_char_lastname
    $ char_1_alive = curr_char_alive

    return
This way anything that changes doesn't get mucked up (say, that character dies!) - again, pretty clean and super obvious!

However, I'm still a bit stuck since I'd have to build a separate one of these for every character which I'd rather not do (and just use a global "curr_char" variable and flip that individually as needed). Is there some way to make the '1' in those fields a variable? If so that'll solve 95% or my issues. Thanks again!

philat
Eileen-Class Veteran
Posts: 1763
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Variables in other Variables and If statements

#6 Post by philat » Sun Jan 10, 2016 10:46 pm

...why are you back to using variables rather than classes again? Randomizing the names is just a matter of building the choices into the initialization of the object.

Code: Select all

init python:
    class CharInfo():
        def __init__(self, first, last, alive=True):
            self.first = first
            self.last = last
            self.full = first + " " + last
            self.alive = alive

    first_names = ['Bob', 'James', 'John']
    last_names = ['Jones', 'Smith', 'Young']    

label start:
    "start"

    ## short way of generating random characters
    
    $ char_list = []
    python:
        for i in range(len(first_names)):
            char_list.append(
                            CharInfo(
                                renpy.random.choice(first_names), 
                                renpy.random.choice(last_names)
                                )
                            )
            first_names.remove(char_list[i].first)
            last_names.remove(char_list[i].last)
        first_names = ['Bob', 'James', 'John'] ## restore lists as otherwise rollback gets screwed up
        last_names = ['Jones', 'Smith', 'Young']

    "[char_list[0].full]"
    "[char_list[1].full]"
    "[char_list[2].full]"

    ## long way

    python:
        char_1 = CharInfo(renpy.random.choice(first_names), renpy.random.choice(last_names))
        first_names.remove(char_1.first)
        last_names.remove(char_1.last)
        ## repeat as necessary

    "[char_1.full]"

    ## testing for save data retention

    $ char_list[0].alive = False
    $ char_1.alive = False

    "save/load here"

    "[char_list[0].alive]\n[char_1.alive]"

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#7 Post by HB38 » Mon Jan 11, 2016 12:04 am

philat wrote:...why are you back to using variables rather than classes again? Randomizing the names is just a matter of building the choices into the initialization of the object.
Philat - you're awesome! This code was very clean and easy to read, I really appreciate it. Just a few last questions and I think I can mark this one Solved! For one, when you run through this and it kicks you back to the menu (or go back to the menu manually)… and start again it errors out:

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/script.rpy", line 56, in script
    "[char_list[0].full]"
IndexError: list index out of range

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

Full traceback:
  File "game/script.rpy", line 56, in script
    "[char_list[0].full]"
  File "/Applications/renpy-6.99.6-sdk/renpy/ast.py", line 594, in execute
    renpy.exports.say(who, what, interact=self.interact)
  File "/Applications/renpy-6.99.6-sdk/renpy/exports.py", line 1032, in say
    who(what, interact=interact)
  File "/Applications/renpy-6.99.6-sdk/renpy/character.py", line 817, in __call__
    what = what_pattern.replace("[what]", sub(what, translate=translate))
  File "/Applications/renpy-6.99.6-sdk/renpy/character.py", line 799, in sub
    return renpy.substitutions.substitute(s, scope=scope, force=force, translate=translate)[0]
  File "/Applications/renpy-6.99.6-sdk/renpy/substitutions.py", line 229, in substitute
    s = formatter.vformat(s, (), kwargs)
  File "/home/tom/ab/x64lucid-deps/install/lib/python2.7/string.py", line 543, in vformat
  File "/home/tom/ab/x64lucid-deps/install/lib/python2.7/string.py", line 565, in _vformat
  File "/home/tom/ab/x64lucid-deps/install/lib/python2.7/string.py", line 634, in get_field
IndexError: list index out of range

Darwin-14.5.0-x86_64-i386-64bit
Ren'Py 6.99.6.739
demo 0.0
I'm not sure what is being left over that could cause that error - the only thing I did was remove the "long way" bit as it seemed redundant (I also added my code for non-random variables, but it's not being used here):

Code: Select all

init python:
    
    class NonUniformRandom(object):
        def __init__(self, list_of_values_and_probabilities):
            """
            expects a list of [ (value, probability), (value, probability),...]
            """
            self.the_list = list_of_values_and_probabilities
            self.the_sum = sum([ v[1] for v in list_of_values_and_probabilities])

        def pick(self):
            """
            return a random value taking into account the probabilities
            """
            import random
            r = random.uniform(0, self.the_sum)
            s = 0.0
            for k, w in self.the_list:
                s += w
                if r < s: return k
            return k
    
    class CharInfo():
        def __init__(self, first, last, alive=True):
            self.first = first
            self.last = last
            self.full = first + " " + last

            self.alive = alive

    first_names = ['Bob', 'James', 'John']
    last_names = ['Jones', 'Smith', 'Young']    

label start:
    "start"

    ## short way of generating random characters
    
    $ char_list = []
    python:
        for i in range(len(first_names)):
            char_list.append(
                            CharInfo(
                                renpy.random.choice(first_names), 
                                renpy.random.choice(last_names),
                                
                                )
                            )
            first_names.remove(char_list[i].first)
            last_names.remove(char_list[i].last)
            
            
        first_names = ['Bob', 'James', 'John'] ## restore lists as otherwise rollback gets screwed up
        last_names = ['Jones', 'Smith', 'Young']

    "[char_list[0].full]"
    "[char_list[1].full]"
    "[char_list[2].full]"

    ## long way


    ## testing for save data retention

    $ char_list[0].alive = False

    "save/load here"

    "[char_list[0].alive]"
A few other quickies - what is the testing for save data retention bit mean? I see you flag what was true to false there, but other than that not sure what it's supposed to be doing?

And lastly, what would the syntax be for creating an if/elseif/else statement in the code for the generator? Like, if the random generator pulls the name "Bob" for instance, his age is a random variable between 20 and 30, but elif his name is james, his age is 30 and 40, and else his age is 40 to 50? Don't worry about the coding for the ages, I just am not sure the proper syntax for the if/elseif/else statements in that section of Python…

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#8 Post by HB38 » Mon Jan 11, 2016 12:33 am

Oh, I also remembered I still need a way to be able to make the current character variable, so I can have generic screens for things such as this:

Code: Select all


$ current_char = 1

"[char_list[current_char].full] is whom is in the room right now."


philat
Eileen-Class Veteran
Posts: 1763
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Variables in other Variables and If statements

#9 Post by philat » Mon Jan 11, 2016 1:49 am

It's the same as using if/else anywhere else...?

Code: Select all

init python:

    class CharInfo(object):
        def __init__(self, first, last, alive=True):
            self.first = first
            self.last = last
            self.full = first + " " + last
            self.alive = alive

            if self.first == "Bob":
                self.age = renpy.random.choice(range(20,30))
            else:
                self.age = renpy.random.choice(range(30,40))

    first_names = ['Bob', 'James', 'John']
    last_names = ['Jones', 'Smith', 'Young']    
    char_list = []

label start:
    "start"

    ## short way of generating random characters

    $ first_names = ['Bob', 'James', 'John']
    $ last_names = ['Jones', 'Smith', 'Young']    
    $ char_list = []

    ## the rest of the code
The error was due to the lists not being initialized after label start, an oversight on my part. Save/load is sometimes tricky with class objects and lists, etc. (as just evidenced). Whenever you're doing something with more advanced data structures, it's a good idea to check whether save/load, rollback, and quitting and restarting all work properly. (I hadn't tested for quitting to the main menu.)

You can't 'double' interpolate variables using renpy brackets. There are several ways you can tackle your problem, but if you're using a screen, just reference char_list[current_char] directly.

Code: Select all

screen blah(cc):
    text char_list[cc].full + " is in the room."

label start:
    $ curr_char = 0
    show screen blah(curr_char)
    "Showing [char_list[0].full]"

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#10 Post by HB38 » Mon Jan 11, 2016 2:05 pm

philat wrote:It's the same as using if/else anywhere else...?

Code: Select all

init python:

    class CharInfo(object):
        def __init__(self, first, last, alive=True):
            self.first = first
            self.last = last
            self.full = first + " " + last
            self.alive = alive

            if self.first == "Bob":
                self.age = renpy.random.choice(range(20,30))
            else:
                self.age = renpy.random.choice(range(30,40))

    first_names = ['Bob', 'James', 'John']
    last_names = ['Jones', 'Smith', 'Young']    
    char_list = []

label start:
    "start"

    ## short way of generating random characters

    $ first_names = ['Bob', 'James', 'John']
    $ last_names = ['Jones', 'Smith', 'Young']    
    $ char_list = []

    ## the rest of the code
The error was due to the lists not being initialized after label start, an oversight on my part. Save/load is sometimes tricky with class objects and lists, etc. (as just evidenced). Whenever you're doing something with more advanced data structures, it's a good idea to check whether save/load, rollback, and quitting and restarting all work properly. (I hadn't tested for quitting to the main menu.)

You can't 'double' interpolate variables using renpy brackets. There are several ways you can tackle your problem, but if you're using a screen, just reference char_list[current_char] directly.

Code: Select all

screen blah(cc):
    text char_list[cc].full + " is in the room."

label start:
    $ curr_char = 0
    show screen blah(curr_char)
    "Showing [char_list[0].full]"

How would you do this in a label? Both when using it in text and when using it as a variable for like an if statement? Pretty sure that will be my last question on this issue; everything else is working great! :D

philat
Eileen-Class Veteran
Posts: 1763
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Variables in other Variables and If statements

#11 Post by philat » Mon Jan 11, 2016 8:56 pm

Using it as a variable for if/else statements works without any further issue. "if char_list[current_char].alive" will work fine. To use it in a statement, use %s or .format() interpolation instead. http://lemmasoft.renai.us/forums/viewto ... =8&t=33185

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#12 Post by HB38 » Mon Jan 11, 2016 9:36 pm

Heads up to anyone using this thread as a reference… you can use multiple variables in the same statement as follows:

Code: Select all

$ narrator ("{0} is a {1} character who is {2} years old." .format(char_list[curr_char].full, char_list[curr_char].height, char_list[curr_char].age))
To get this output:

"John Smith is a short character who is 27 years old."
Last edited by HB38 on Mon Jan 11, 2016 10:37 pm, edited 1 time in total.

User avatar
HB38
Regular
Posts: 53
Joined: Sun Apr 27, 2014 2:14 pm
Contact:

Re: Variables in other Variables and If statements

#13 Post by HB38 » Mon Jan 11, 2016 9:53 pm

And this would be how to do a character (I'm using live composites in my game):

Code: Select all

show expression ("character_%s_doll" % curr_char)
That code will pull the image defined as "character_0_doll".



Hope this helps someone else down the line!

Post Reply

Who is online

Users browsing this forum: Alex, Google [Bot]