So I'm doing a thing where I'm picking one of the pages in the documentation (or a few elements if the page is a particularly complicated thing) and making a test project where I do everything on the page, then adding cool tricks or whatever else might need to be added that I learn here.
The last tester I finished was for the Screens and Screen Language page. I just did the "high level" stuff, so the subsections: Screen Language, Control Statements, Showif Statements, Screen Statements. And I threw in a few other bits and pieces. One section I did was on Nested screens and returning information. I think the things I'm experimenting with here address what your underlying questions are.
So as to your first question, why doesn't call screen create a new context? I don't know...it just doesn't. But there is probably a good reason for it having to do with what happens when you enter a new context. Xela would know about this more than I would. But looking at the documentation, I notice that renpy.get_game_runtime() only counts time spent in the top level context, so if your calling a screen took you out of the top-level context, it would result in an inaccurate game_runtime stat. Similarly, the tag entry to screen language notes that two screens with the same tag will replace each other in the same context only. So if you had a screen tagged map up and called another map screen and calling resulted in a new context, then that first map wouldn't be replaced...which I also don't think is what is wanted by default.
As to your second question, why doesn't call screen add to the call stack? I don't know...it just doesn't. The documentation for the call statement specifically says that it adds to the call stack: "The call statement is used to transfer control to the given label. It also pushes the next statement onto the call stack, allowing the return statement to return control to the statement following the call." (
http://www.renpy.org/doc/html/label.html#call-statement). The call screen statement doesn't mention that at all. So...so...no call stack. But I don't suppose you really need it. When you call a screen it waits for your interaction and then goes back from whence you came...so it is the same effect.
Okay, anyway, here it part of my larger screen tester that deals with nesting screens and passing information back and forth:
Code: Select all
#####
#Calling Nested Screens & Returning Data
#There are only 3 ways to get data into _return: from the return statement, from Return() after using call screen, or when input is invoked using Call Screen
init python:
class Char():
def __init__(self, name, mhp, skills=None):
self.name = name
self.curHP = mhp
self.maxHP = mhp
self.skills = skills
self.active_skill = None
screen battle(char):
frame align (1.0, 0.0):
vbox:
label "[char.name]"
text "[char.curHP]/[char.maxHP] HP"
text "Skill to Use: [char.active_skill]"
text "_return: [_return]"
text "-----"
textbutton "Choose Skill (with Show)" action Show("choose", char=char)
textbutton "Choose Skill (with ShowTransient)" action ShowTransient("chooseT", char=char)
textbutton "Choose Skill (with renpy.call_screen...)" action Function(renpy.call_screen, "choose2", char)
textbutton "Choose Skill (with renpy.call_in_new...)" action Function(renpy.call_in_new_context, "choosetest", char)
textbutton "Choose Skill (with ui.callsinnew...)" action ui.callsinnewcontext("choosetest", char)
textbutton "End Choice" action [Hide("battle")]
screen ns_input():
vbox:
text "Enter a word."
input
screen choose(char):
modal True #I added this for my new experiment
frame align (0.0, 0.0):
vbox:
label "[char.name]'s Skills"
for sk in char.skills:
textbutton "[sk]" action [SetField(char, "active_skill", sk), Hide("choose")]
screen chooseT(char):#This only hides when the current interaction completes...which means forwarding the text
frame align (0.0, 0.0):
vbox:
label "[char.name]'s Skills"
for sk in char.skills:
textbutton "[sk]" action [SetField(char, "active_skill", sk)]
screen chooseL(char):#This one if for calling from a lable
modal True #Put this here if you don't want people to be able to click on the battle screen while this is up
frame align (0.0, 0.0):
vbox:
label "[char.name]'s Skills"
for sk in char.skills:
textbutton "[sk]" action Return(sk)
screen choose2(char):
modal True #I added this for my new experiment
frame align (0.0, 0.0):
vbox:
label "[char.name]'s Skills"
for sk in char.skills:
textbutton "[sk]" action [SetField(char, "active_skill", sk), Return(sk)]
label check():
"Hello"
return("Goodbye")
label choosetest(char):
call screen choose2(char)
"Returning the _return from the choose 2 screen: [_return]"
return(_return)
label start:
$pc = Char("Tim", 15, ["Attack", "Defend", "Talk"])
"This section of the tester deals with passing information back and forth."
"Generally speaking a lot of your screens will not pass information around, but set varaibles directly."
"According to the Documentation, there are 3 ways to return: (which is stored in the _return variable)\n
-Supplying a varible to the return statement.\n
-Returned from the Return() screen action *when the screen has been called with call screen*\n
-Rturned from the Input user interface when placed in a screen that has been invoked through call screen"
"I want to start off with just the passing around of information."
show screen battle(pc)
"Do me a favor and don't click any of the buttons on the now showing battle screen just yet."
"Testing return with expression."
call check
"Returning from the check label: [_return]"
"If you notice, the result of that return is up there on the battle screen."
$pc.curHP = 10
"Most of the time though, you'll manually change the variable, like now I'll remove 5HP."
"Let's do the passing of info from an input."
call screen ns_input()
"Notice the change in the return."
"Now let's look at the difference between calling and showing a screen in order to grab input from within this label."
"First the proper way with call. Please choose Attack."
call screen chooseL(pc)
$pc.active_skill = _return
"The return is [_return]"
"Now let's try it with show. Show the first way, with a regular manual show. Choose Defend."
show screen chooseL(pc)
"First thing to note, the screen didn't disappear. Return doesn't do that if you don't call. So you'd have to add a
Hide screen action to the text button in order to hide it. So what did the Return do? It just advanced the screen.
Click on a button again."
"The return is [_return]--this is the return from the call. Your button pushing doesn't do anything but have an interaction
advancing your text, this is because you showed the screen rather than called it. Click a button one more time so we can
hide this modal screen."
hide screen chooseL
"So there is another way to show a screen, wait for info, and then get rid of it other, with the ui.interact command. Choose Defend."
show screen chooseL(pc)
$result = ui.interact()
$pc.active_skill = result
hide screen chooseL
"Did I catch a result? I was able to store the result: [result], but it doesn't store it into _return: [_return]"
"Now that we've dealt with returning calling/showing from within the label, let's do it from the screen up there."
"So click on each of the options and see how it works out. \n
The first option shows a modal screen that sets the variable directly and then hides the screen. No _return."
"The second option shows the screen with Show Transient. This shows the screen, sets the variable directly (no _return). \n
It doesn't have a hide function because show transient will hide it with the next interaction..."
"Which is when you advance the text, not when you press buttons."
"Okay the third button uses the renpy.call_screen funtion...but if you press it, it will throw an error that says:\n
Exception: Cannot start an interaction in the middle of an interaction, without creating a new context.\n
So don't press that button! If you want to call a new screen you'll have to use the call in context functions."
"The fourth button uses the modern Function renpy.calls_in_new_context. Note, this calls a label, not a screen.\n
The label calls a screen that both sets a variable and returns a varaible. You'll notice the _return is caught in the label,\n
however, because it is a new context, that _return never makes it back to the context of the battle screen, so _return there is not changed."
"The fifth button uses the older and out of fasion version of the fourth button ui.callsinnewcontext. It works the same way as the fourth button."
"The last button closes your screen."
"This section of the tester is over."
return