Ren'Py Displaying a message from inside a function

The Rogue Trader

Active Member
Sep 12, 2021
510
747
Hello, I've coded a very simple card game in Renpy to test it while the team coder works in Unity.

Everything works fine, except that I want to give the player a message if he plays a card without the required prerequisites. Unfortunately, that happens from a function in a python block. Said function is launched from a called screen (and not a shown screen).

So far:
- Adding a displayable like a textbutton is just plain not allowed.
- Renpy.say doesn't work because the resulting message is covered by the elements in the screen.
- Calling a new screen doesn't work because the function is called from a called screen, and the new screen needs to be called in a new context because "interaction in the middle of an interaction".
- Calling in a new context doesn't work because it doesn't have a mean to pass the text to the new screen. I tried to create a different screen for each different message, but it still doesn't work because the function can't find the screen label ("name not defined").
- Setting a variable, putting the message in it and then displaying it in a text-box in the original screen when the function has been closed sounds a good idea but I've cluttered all the screen and have no place where to put this text. Not tested.

I've run out of ideas. Some suggestions?

init block:

Python:
init python:
    <other needed classes>

    # Option Cards
    class Option( renpy.python.RevertableObject ):
        def __init__( self, face, element, cost, prereq_train, prereq_rank, effect, description ):
            # Face: image
            self.face = face
            # Element: Fire, Earth, Air, Water, Neutral
            self.element = element
            # Cost
            self.cost = cost           
            # Needed Training: Fighter, Scout, Diplomat
            self.prereq_train = prereq_train
            self.prereq_rank = prereq_rank
            # Effect: code to call when playing the card.
            self.effect = effect
            self.description = description

    player_deck = []
    
    
        
    def Play_Card ():
        global shown_card
    <other needed global variables>
        # checking prerequisites
        if (option_deck[shown_card].prereq_train == "Fighter" and pr_fighter >= option_deck[shown_card].prereq_rank) or (option_deck[shown_card].prereq_train == "Scour" and pr_scout >= option_deck[shown_card].prereq_rank) or (option_deck[shown_card].prereq_train == "Diplomat" and pr_diplomat >= option_deck[shown_card].prereq_rank):
            # checking elemental affinity
            if ( option_deck[shown_card].element == "Fire" and aff_fire >= option_deck[shown_card].cost ) or (option_deck[shown_card].element == "Earth" and aff_earth >= option_deck[shown_card].cost) or (option_deck[shown_card].element == "Air" and aff_air >= option_deck[shown_card].cost) or (option_deck[shown_card].element == "Water" and aff_water >= option_deck[shown_card].cost):
                # paying cost
                if option_deck[shown_card].element == "Fire":
                    aff_fire -= option_deck[shown_card].cost
                elif option_deck[shown_card].element == "Earth":
                    aff_earth -= option_deck[shown_card].cost
                elif option_deck[shown_card].element == "Air":
                    aff_air -= option_deck[shown_card].cost
                elif option_deck[shown_card].element == "Water":
                    aff_water -= option_deck[shown_card].cost
                else:
                    renpy.show_display_say(None,"This shouldn't be happening")
                # calling card effects
                eval (option_deck[shown_card].effect)
                player_play.append( option_deck[shown_card] )
                option_deck.pop ( shown_card )
                shown_card -= 1
            else:
                renpy.invoke_in_new_context(notify,"You don't have enough Elemental Affinity to play this card.")
        else:
            renpy.invoke_in_new_context(notify,"You don't meet the prerequisites to play this card.")




game block:
Python:
screen card_battle():

    #stuff happens here#

    imagebutton idle option_deck[shown_card].face xpos 1614 ypos 440 action Function( Play_Card )
    
    #other stuff#
    textbutton "Commit" xpos 1653 ypos 910 action Return()

(I tried moving the offending function to the game block but it didn't help).
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
- Setting a variable, putting the message in it and then displaying it in a text-box in the original screen when the function has been closed sounds a good idea but I've cluttered all the screen and have no place where to put this text. Not tested.
You already have all the place you want to put the text:

Python:
default errorMessage = None

screen whatever():

    [whatever is your screen]

    # Will only be shown when there's an errorMessage
    if not errorMessage is None:
        # Fake an error box
        frame:
            # center the frame.
            xalign 0.5
            yalign 0.5
            # Give it a background for it stand out on top of your screen.
            background "#000"
            # Probably add some padding or margin for a better effect.
            # And finally display your text
            vbox:
                # Probably need some vertical spacing.
                text "Oops, an error happened:"
                text "[errorMessage"]
                text "Click anywhere to continue."

        #  Then put a fully transparent clickable covering the whole screen
        # on top of everything, in order to be nice with the player and 
        # permit him to easily close the error box.
        imagebutton:
            idle Solid( "#00000000" )
            xpos 0 ypos 0
            xsize config.screen_width
            ysize config.screen_height
            action SetVariable( "errorMessage", None )

I've run out of ideas. Some suggestions?
Well, there's also the function used when you want to display a notification message to the player... .


Code:
    def Play_Card ():
        global shown_card
    <other needed global variables>
This is totally useless. All the game variables are already directly available. Just remember to prefix them with store. when it's an assignation.
Code:
default whatever = "ABCDEF"
default someOtherVariable = "Nothing"

init python:

    def someCode():
        store.someOtherVariable = whatever
        store.whatever = "something else"

label start:
    "[whatever] - [someOtherVariable]"
    #  Needed for the demonstration due to possible optimization conflict
    # depending of the version of Ren'Py used.
    pause 
    $ someCode()
    "[whatever] - [someOtherVariable]"
    "END"
    return
 

The Rogue Trader

Active Member
Sep 12, 2021
510
747
You already have all the place you want to put the text:
Sigh... You're right.
For some reason, I never noticed this easy construction for opening a text 'window'. Now that you told me, it jumps out from everywhere.
That's exactly what I wanted to achieve but...

Well, there's also the function used when you want to display a notification message to the player... .
... This one is so simple that I've to use it just out of pique.
It's probably the only such function I didn't try when reading the documentation. And being the only one not tried, it's the only one that works.
I'll probably want to go back later and implement your first suggestion, as it allows more customization of the UI, but now I just want to put a render in the oven and go out and enjoy this sunny afternoon.

This is totally useless. All the game variables are already directly available. Just remember to prefix them with store. when it's an assignation.
:oops:
Why has nobody ever told me this? I swear I never saw it before: all the code and the forum threads I did read used declaring global variables.
Anyway, it works perfectly and saves a lot of inelegance.

Thanks, once again you saved my day! (y)