Ren'Py [Solved] Bug with renpy.call ?

guest1492

Member
Apr 28, 2018
321
269
Is bugged?

Python:
label start:
    python:
        narrator('This shows')
        renpy.call('foobar')
        # the rest of the indented code block does not run
        narrator('This does not show')
    python:
        # have to add another python block right afterwards as a workaround
        narrator('This shows too')
    return

label foobar:
    'This is the called label'
    return
Documentation said:
When the jump returns, control will be passed to the statement following the current statement.
Does statement here mean a renpy statement? As in, starting a python block is considered a statement but not the code inside.
If so, then what's the point of this function at all? Why use it instead of the ?
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,548
8,684
Does statement here mean a renpy statement?
yes
Causes the current Ren'Py statement to terminate, and a jump to a label to occur. When the jump returns, control will be passed to the statement following the current statement.
If so, then what's the point of this function at all? Why use it instead of the ?
they are the same thing
The equivalent of the is the function.
I guess that's just how call statement is implemented. Smarter and/or less lazy people can probably explain how it works better than me but basically for every renpy statement there's a python function that has actual code that gets executed
 
  • Like
Reactions: guest1492

guest1492

Member
Apr 28, 2018
321
269
Sorry, I was just feeling frustrated because I spent a couple hours trying to debug something before finding out this was the culprit.

I had a label that was something like this:
Python:
label example:
    python:
        # do some calculations here

        if something:
            # call_screen works like I expected
            a = renpy.call_screen('screen_a')
            b = renpy.call_screen('screen_b')
            renpy.call('some_label', a, b)

        # do more calculations here
    return
I tried splitting off all the python code into its own function and then calling that, but the same problem. Control didn't return to the function after the call. So the only use for renpy.call() seems to be at the very end of a function. (It could be used to end a python block inside a label, but then why not just use the call statement?)
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,569
2,196
I'm reasonably sure it's due to the python: block.

A little testing, and this works as intended:

Python:
label start:

    $ narrator('This shows')
    $ renpy.call('foobar')
    $ narrator('This does not show (does now!)')

    $ narrator('This shows too')

    return

label foobar:
    '(foobar) This is the called label'
    return

I agree it's counterintuitive. Since $ and python: are supposed to be more or less the same thing.

Keep in mind that RenPy games are supposed to be written in RenPy, not python.

Python:
label start:

    "This shows"
    call foobar
    "This does not show (does now!)"

    "This shows too"

    return

label foobar:
    "(foobar) This is the called label"
    return

or to use your other example:

Python:
label example:
    python:
        # do some calculations here

    if something:
        call screen screen_a
        a = _return
     
        call screen screen_b
        b = _return
     
        call some_label (a, b)

    return

A lot of RenPy's supporting infrastructure isn't executed when running purely python code (I'm thinking of the rollback functions specifically). RenPy only does it's housekeeping when it executes the next RenPy statement. The more you dip into python, the more you risk this catching up with you.

As you already know, the same also happens if you code things as a python function...

Python:
default corruption_points = 90

init python:
    def update_corruption_points(n):
        # renpy.call("foobar")
        store.corruption_points += n
        store.corruption_points = max(0, min(store.corruption_points, 100))


label start:

    "This shows. CP = [corruption_points]."

    $ update_corruption_points(15)
    "This doesn't show the correct value. CP = [corruption_points]. (unless you comment out the renpy.call)"

    "This is the final bit of dialogue"

    return


label foobar:
    "(foobar) This is the called label. CP = [corruption_points]"
    return
 
Last edited:
  • Like
Reactions: guest1492

0x52

Ren'Py Magician
Modder
Donor
Game Developer
May 23, 2019
1,591
6,032
Keep in mind that RenPy games are supposed to be written in RenPy, not python.

Python:
label start:

"This shows"
call foobar
"This does not show (does now!)"

"This shows too"

return

label foobar:
"(foobar) This is the called label"
return
This is the way to go!

I agree it's counterintuitive. Since $ and python: are supposed to be more or less the same thing.
$ and python: are indeed the same, but I will try to explain the counterintuitive behavior you're seeing.

Python:
label start:
    $ narrator('This shows')
    $ renpy.call('foobar')
    # We resume here after the "foobar" call returns
    $ narrator('This does not show (does now!)')
    return

# Above it the same as
label start:
    python:
        narrator('This shows')
    python:
        renpy.call('foobar')
    # We resume here after the "foobar" call returns
    python:
        narrator('This does not show (does now!)')
    return

# This is different
label start:
    python:
        narrator('This shows')
        renpy.call('foobar')
        narrator('This does not show (does now!)')
    # We resume here after the "foobar" call returns
    return
What call actually does is raise an exception, which is caught by Ren'Py and you will be transfered to the called label. Code after a call exception will not run, but the next codeblock will.

So, in reality, this is what happens:
Python:
label start:
    python:
        narrator('This shows')
        raise Exception('trying to jump/call')
        narrator('This does not show (does now!)') # This is never run, because an exception was raised
    return
If you need multiline Python before and after the call, you could do this:
Python:
label start:
    python:
        narrator('This shows')
        # More code
    $ renpy.call('foobar')
    # We resume here after the "foobar" call returns
    python:
        narrator('This does not show (does now!)')
        # More code
    return
 
Last edited: