Ren'Py Boolean True or False

mickydoo

Fudged it again.
Game Developer
Jan 5, 2018
2,446
3,557
Hey all, I have a simple map in my game.
map.jpg
The map icons are turned on by a simple True statement, and turned off and go grey with a simple False statement. When you have visited them all another simple visit statement sends you home automatically. Now that's all fine and dandy.

In this chapter however, one day you go visit the places, and some random events happen, the following day when the map comes up 2,3,4 or all 5 places may or may not show, depending on the random event.

So I don't know when the player has visited everything as I have no idea what was on their map. The only thing I do know, is whatever came up on their map was triggered by a True statement, or it would not have showed up.

So what I want to know -

Player 1 has three places to visit, so he has triggered 3 location = True statements
Player 2 has four places to visit, so he has triggered 4 location = True statements
Player 3 has five places to visit, so he has triggered 5 location = True statements

Is there way to tell renpy when all location = True statements have been triggered to do something, regardless of how many there is.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,566
7,383
So, you really have two related problems here. You need to know which places the player MAY have visited (i.e. which ones were offered to him/her), and then which of those places the player has actually visited.

If you stop and think about it, your "places" really have three states, not two:
  • Not available to the player
  • Available, but not yet visited
  • Available and visited
Thus, rather than using a boolean, I'd be inclined to use an integer variable for each of the places:
  • 0 = Not available.
  • 1 = Available but not yet visited
  • 2 = Available and visited
So, let's say that your variables are named place1 through place5, and each of those uses the 0,1,2 notation above. In this case you can do:
Code:
def visited_all_available():
    return 1 not in [place1, place2, place3, place4, place5]
This function will return True if every place that was available was visited. (The logic essentially says "none of the places are in the state "1=Available but not yet visited") If any one of the five places is in the "1=Available but not yet visited", then it will return False

There's a nice side effect - because, in Python, a zero evaluates as False and any other number as True, the statement
Code:
if place1:
evaluates as False if place1 is zero and True otherwise. Thus, you can use that "if" statement to determine whether a particular place is visible. In other words:
Code:
if place1:
and
Code:
if place1 != 0:
are equivalent in this case. (Some people don't like using that kind of Pythonista stuff. If you prefer the second form, no biggie.)

Anyway, that's far from the only way to do this, but it's one relatively simple way.
 
  • Like
Reactions: mickydoo

mickydoo

Fudged it again.
Game Developer
Jan 5, 2018
2,446
3,557
So, you really have two related problems here. You need to know which places the player MAY have visited (i.e. which ones were offered to him/her), and then which of those places the player has actually visited.

If you stop and think about it, your "places" really have three states, not two:
  • Not available to the player
  • Available, but not yet visited
  • Available and visited
Thus, rather than using a boolean, I'd be inclined to use an integer variable for each of the places:
  • 0 = Not available.
  • 1 = Available but not yet visited
  • 2 = Available and visited
So, let's say that your variables are named place1 through place5, and each of those uses the 0,1,2 notation above. In this case you can do:
Code:
def visited_all_available():
    return 1 not in [place1, place2, place3, place4, place5]
This function will return True if every place that was available was visited. (The logic essentially says "none of the places are in the state "1=Available but not yet visited") If any one of the five places is in the "1=Available but not yet visited", then it will return False

There's a nice side effect - because, in Python, a zero evaluates as False and any other number as True, the statement
Code:
if place1:
evaluates as False if place1 is zero and True otherwise. Thus, you can use that "if" statement to determine whether a particular place is visible. In other words:
Code:
if place1:
and
Code:
if place1 != 0:
are equivalent in this case. (Some people don't like using that kind of Pythonista stuff. If you prefer the second form, no biggie.)

Anyway, that's far from the only way to do this, but it's one relatively simple way.
I don't completely understand but it makes perfect sense (unlike that statement). I think I will go and and watch some tuts on integer variables first as I like knowing how and why things work, not just the simple fact if they do or not.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
Code:
def visited_all_available():
    return 1 not in [place1, place2, place3, place4, place5]
Hmm, what is "not in" ? Shouldn't there be a variable somewhere ?


This function will return True if every place that was available was visited. (The logic essentially says "none of the places are in the state "1=Available but not yet visited") If any one of the five places is in the "1=Available but not yet visited", then it will return False
Therefore, shouldn't it be something like :
Python:
default allVisitedPlaces = Set( [] )
default placeToVisit = []

label whatever:
    #  Reset the list of visited places.
    $ allVisitedPlaces = Set( [] )
    #  Define the list of place that have to be visited.
    # Need to be some computation and not an effective direct assignation.
    $ placeToVisit = [ place1, place2, ...]
    [...]

screen map():
    imagebutton:
        idle "ThisPlace.png"
        #  Tell that this place have been visited.
        # Like it's a set, it will be added only once.
        action [ AddToSet( "allVisitedPlaces", "ThisPlace" ), [...] ]

init python:
    def visited_all_available():
        # Get a working copy of the set to not alter the original.
        tmp = store.placeToVisit.copy()
        # For each place visited...
        for placeName in store.allVisitedPlaces:
            # if the place is in the list of places to visit...
            if placeName in tmp:
                # remove it.
                tmp.remove( placeName )
        # Return if the set is empty or not.
        return tmp == Set( [] )
At the end of the for loop, only left in the list the places that have to be visited, but haven't been. Therefore, if the list is empty, it will return "it's True, all places have been visited", else it will return "it's False, not all places have been visited yet".


Anyway, that's far from the only way to do this, but it's one relatively simple way.
But there's an easiest way to do ; one that don't need to rely on an external function, whatever it's code:
Python:
default placeToVisit = []

label whatever:
    #  Define the list of place that have to be visited.
    # Need to be some computation and not an effective direct assignation.
    $ placeToVisit = Set( [ place1, place2, ...] )
    [...]

screen map():
    imagebutton:
        idle "ThisPlace.png"
        #  Tell that this place have been visited, by removing it
        # from the list of places to visit.
        action [ RemoveFromSet( "placeToVisit ", "ThisPlace" ), [...] ]
Then :
Code:
if placeToVisit != Set( [] ):
    "You've still at least one place to visit."
else
    "You visited everything that needed to be visited."

Edit: Typo, in the last example, placeToVisit have to be a Set.
 
Last edited:
  • Like
Reactions: Rich

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,566
7,383
Hmm, what is "not in" ? Shouldn't there be a variable somewhere ?
Python supports an "in" operation. a in b is a boolean expression, which tests whether the value "a" is in "b", where "b" is any iteratable. It is most commonly used with "b" being a list, a set, or a dict. (For a dict, it tests the keys, so this is a way of finding out whether or not a particular key is in a dict.)

"not in" is the opposite of "in". So
Code:
1 in [place1, place2, place3, place4, place5]
builds a list with the values currently in the "place" variables, and then tests to see if there's a "1" anywhere in there.
Code:
1 not in [place1, place2, place3, place4, place5]
tests for the opposite condition - that there is not a 1 anywhere in the list.

Granted, it's more common to see a in b or "some string" in b, but the value 1 is legal and works perfectly well, since the "a" part can be any legal Python expression.
 
  • Like
Reactions: anne O'nymous

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
Granted, it's more common to see a in b or "some string" in b, but the value 1 is legal and works perfectly well, since the "a" part can be any legal Python expression.
It's your syntax that I didn't understood, misreading it as "return 1 if a missing value is present in the given list", when it should be read as "return the fact that the value 1 is, or not, present in the given list".

Probably past too much time coding in Perl recently, I forgot that Python is a way more literal language, even in its syntax shortcuts.