Why first code is not work
For a question of scope.
To limit the risk of collision between variables (two variables with the same name, but a different meaning), language like Python create space in which the variables are, or not, relevant.
By default there's only one scope, the
global one. It's the lowest possible level, where everything is located. Then Python create a
local scope every time it's relevant ; globally speaking, every time you create a function/method, you also create a
local scope.
When you'll try to assign a value to a variable, Python will always works with the
current scope, that is the smallest and highest
local scope ; therefore, still globally speaking, the body of the current function/method.
Note that the same isn't totally the same when Python get a value from a variable. Python will first look if the variable exist in the
current scope, and if it don't, Python will look at the
global scope if it find it.
Python:
init python:
def loop1( idx ):
what = "not a test"
for i in range( 1, 3 ):
loop2( idx, i )
def loop2( idx1, idx2 ):
for i in range( 1, 3 ):
print( "{} - {} - {} - {}".format( what, idx1, idx2, i ) )
label start:
$ what = "my test"
python:
for i in range( 1, 3 ):
loop1( i )
"Open the console [[shift] + [[o] to see what have been printed."
"END"
The result will be:
my test 1 - 1 - 1
...
my test 2 - 1 - 2
...
my test 2 - 2 - 2
Each one of the three variables named "i" are defined in their own scope, reason why you can use them without problem. In each scope, the variable have a different value, relevant only for this scope.
In the same time, the variable "what" is defined two times. Firstly in the
global scope, and secondly in the scope
local for "loop1". But it's only in "loop2" that we use it, from the point of view of "loop2", the two only scopes that exist are
global and the
current scope.
Like it don't find a variable named "what" in the
current scope, it look for (and find) it in the
global scope. But like you can see, it totally ignore what exist in the "loop1"
local scope.
As seen from Ren'Py, the
global scope is the script of the game.
Python:
init python:
myVar1 = None
define myVar2 = None
default myVar3 = None
label whatever:
$ myVar4 = None
The four variables will be in the
global scope. But obviously, if you define a function/method, then it will have its own
local scope, as expected.
The only
local scope that exist in Ren'Py is for screens:
Python:
screen whatever():
default myLocalVar = None
# Note: I don't guaranty that this one haven't changed a bit recently,
# and that there isn't times when it will act as a local variable, especially
# when there's embedded (/use/d) screens.
$ myGlobalVar = None
Now, in your first version, you assign the value inside the "changeLocation" function:
Python:
def changeLocation(location_id: str):
[...]
cur_location = location
This mean that you are giving a value to a variable named "cur_location" and that exist in the
current scope. When you'll quit the function, this scope will disappear, and the variable with it.
This while in your second version, you assign the value inside the "change_location" label:
Python:
label change_location(location_id = None):
[...]
$ cur_location = changeLocation(location_id = location_id)
Like I said, labels works in the
global scope, therefore you assign the value to the same "cur_location" than the one created with your previous
default cur_location = None
There's a way to deal with this kind of issues. To simplify the explanation, I'll present it as "tell Python that you want to address the 'Ren'Py' scope" ; but keep somewhere in your mind that it's a simplification, not the effective truth.
For this, you just need to prefix the variable by
store.
. Therefore, for your first version to works, you just need a small bit of change:
Python:
def changeLocation(location_id: str):
for location in locations:
if location.id == location_id:
# Now you are addressing the Ren'Py variable named 'cur_location' and
# not anymore the local variable named like that.
store.cur_location = location
return
and why location_id is DELETED?
In a way, it's still a question of scope.
When you pass a value as argument to a function/method, Python create the variable in the
current scope. But Ren'Py cannot do this, since it do not have
local scope (except those particular case with screens).
Therefore, it create the variable in the
global scope, then delete it at the end of the label, because it lost all relevance.
Now a bit of "all I said above isn't totally true".
Ren'Py really don't have the notion of scopes, but it can more or less fake a "
local scope". It's possible to tell it that a given variable will only be relevant for the current label. This is done with the
You must be registered to see the links
function.
Python:
label start:
$ myVar = "ABCD"
"[[start] The content of {i}myVar{/i} is '[myVar]'"
call calledLabel
"[[start] The content of {i}myVar{/i} is back to '[myVar]'"
"END"
return
label calledLabel:
$ renpy.dynamic( "myVar" )
$ myVar = "A larch"
"[[calledLabel] The content of {i}myVar{/i} is now '[myVar]', but locally."
call calledLabel2
jump jumpedLabel
label calledLabel2:
"[[calledLabel2] The content of {i}myVar{/i} is still '[myVar]' because we are a subcontext of {i}calledLabel2{/i}."
return
label jumpedLabel:
"[[jumpedLabel] The content of {i}myVar{/i} is still '[myVar]' because we are in the same context than {i}calledLabel2{/i}."
return