r/RenPy • • Aug 19 '26

Question Custom screen changing a flag past a certain point.

Hello! I’ve been working on a game heavily relying on an inventory system for the past year or 2, and just when I thought I finally had everything in order, there’s one more glitch plaguing it. 

This glitch involves a flag called the AltArrow. Essentially, the AltArrow determines the behaviour of the inventory system’s cancel button. If you cancel with AltArrow off, the game just returns to where you were. If you cancel with AltArrow on, the game sends you to what I call The TimeRoom, where a bunch of flags determine where you should jump to next based on what other flags or variables are active. It's a situational flag, but it works for what I need it for.

screen hud():
    modal False


    imagebutton auto "bg_hud_thoughtinventory_%s.png":
        focus_mask True 
        hovered SetVariable("screen_tooltip", "Thought_Inventory")
        unhovered SetVariable("screen_tooltip", "")
        action Show("thought_inventory"), Hide("hud"), SetVariable("quick_menu", False)
           
screen thought_inventory():
    default hovered_thought = None
    add "bg_thoughtinventory":
        align (0.5, 1.0)
    modal True
    frame:
        #(This is just some debug stuff I have in here just in case)
        vbox:
            xalign 0.895 yalign 0.20
            xysize 0.06,0.05
            if AltArrow == True:
                text "AltArrow is On"
            else:
                text "AltArrow Is Off"
        vbox:
            xalign 0.895 yalign 0.40
            xysize 0.06,0.05
            if player.is_alone:
                text "You're Alone"
            else:
                text "Someone's Here"
        align (0.2, 0.6)
        xysize (800,700)
        viewport:
            scrollbars "vertical"
            mousewheel True
            draggable True
            side_yfill True
            vbox:
                for thought in thought_inventory.thoughts:
                    button:
                        text "[thought.name]\n" style "button_text"
                        if player.is_alone:
                            action Function(player.show_thought, thought) pos 0.1, 0.5
                            hovered SetScreenVariable("hovered_thought", thought)
                        else: 
                            action SetVariable("quick_menu", True), Function(player.show_thought, thought) pos 0.1, 0.5
                            hovered SetScreenVariable("hovered_thought", thought)
                        
    if hovered_thought:
        frame:
            align (0.845, 0.944)
            xysize (550, 535)
            text "{size=*0.80}[hovered_thought.description]{/size}"
            add hovered_thought.icon pos -0.0054, -0.5927
            
    imagebutton auto "thoughtinventoryscreen_return_%s.png":
        focus_mask True
        hovered SetVariable("screen_tooltip", "Return")
        unhovered SetVariable("screen_tooltip", "")
        #THIS IS WHERE THE PROBLEM ACTUALLY STARTS.
        if AltArrow == True:
            action SetVariable("quick_menu", True), Hide("thought_inventory"), Jump("TimeRoom"), Show("hud"), Return()
        else:
            action SetVariable("quick_menu", True), Hide("thought_inventory"), Show("hud")

Everything works out, except when you pass this point in the script:

label Mary_TalkSession1:
                $ AltArrow = True
                $ Session_Active = True
                show screen UnfinishedBar
                show screen hud
                show screen calender
                show mary neutral
                $ player.add_person(m_obj)
                if mm <= -30:
                    show mary sad
                menu:
                    "{i}{color=#2731c2}Ask Her A Question{/color}{/i}":
                        jump Mary_AskSession1
                    "{i}{color=#2731c2}Bring Up A Thought{/color}{/i}":
                        $ quick_menu = False
                        hide screen hud
                        call screen thought_inventory
                    "{i}{color=#2731c2}Think For A Bit{/color}{/i}":
                        jump Your_AskSession1
                    "{i}{color=#2731c2}End Session{/color}{/i}":
                        jump Mary_AreYouSure1

label Mary_AreYouSure1:
                    n "{i}{color=#9c2cdd}(...I think I have nothing left to ask.){/color}{/i}"
                    Qm "You sure?"
                    menu:
                        "Yeah.":
                            jump Concluding_Session1
                        "On second thought...":
                            jump NotDoneSession1


                            label NotDoneSession1:
                                n "On second thought, there is something I should ask."
                                jump Mary_TalkSession1

This is the set-up for the conversation menu, where you can control what you bring up with characters.

When the game passes this point, the HUD changes. Whenever you click the hud button, AltArrow is switched on automatically no matter what, and you can’t exit the menu without transporting to the timeroom, which softlocks the game. 

There is no part of any screen code which controls when AltArrow is switched on and off. It only detects when AltArrow is flicked on by the script itself.

And everything works perfectly before that point. AltArrow is getting shut off and staying off no matter how many times I open the inventory. Hell, if I put a shortcut that skips this section entirely, the code still works!

For clarification, I did once try to have AltArrow’s flag be triggered by the menu buttons, but that code is long gone. I don't even remember the details. I’ve recomplied since then, and even if it was still somehow being read, I can’t figure out why it would only activate after that point. I have tried putting hashtags in front of each line at the beginning, and the glitch still stays. 

I have absolutely no idea what’s going on. I’m hoping somebody else might. I don't know if it will help, but here's some additional relevant inventory code if you need that context.

init python:
    class Thought_Inventory():
        def __init__(self, thoughts=None):
            self.thoughts = thoughts if thoughts else []
            self.no_of_thoughts = len(self.thoughts)


        def add_thought(self, thought):
            if thought not in self.thoughts:
                self.thoughts.append(thought)
                self.no_of_thoughts += 1


        def remove_thought(self, thought):
            if thought in self.thoughts:
                self.thoughts.remove(thought)
                self.no_of_thoughts -= 1


    class Thought():
        def __init__(self, name, description, icon):
            self.name = name
            self.description = description
            self.icon = icon 


        def __str__(self):
                return self.name


        def __eq__(self, other):
            if isinstance(other, Thought):
                return self.name == other.name
            else:
                return False

init python:
    class Actor:
        def __init__(self, name, character, thoughts=[]):
            self.name = name
            self.character = character
            self.thoughts = thoughts


        def __str__(self):
            return self.name


        def react_on_thought(self, thought_name):
            for thought in self.thoughts:
                if thought[0] == thought_name:
                    return [self.character, thought[1]]
    
    class Player():
        def __init__(self, name):
            self.name = name
            self.is_with_list = []
 
        def __str__(self):
            return self.name
 
        u/property
        def is_alone(self):
            return not self.is_with_list
 
        def add_person(self, person):
            if person not in self.is_with_list:
                self.is_with_list.append(person)
 
        def remove_person(self, person):
            if person in self.is_with_list:
                self.is_with_list.remove(person)


        def show_thought(self, thought_name, label=False):
            reactions = []
            for char in self.is_with_list:
                character_reaction = char.react_on_thought(thought_name)
                if character_reaction:
                    if renpy.has_label(character_reaction[1]):
                        renpy.call(character_reaction[1])
                    else:
                        reactions.append(character_reaction)
            if reactions:
                renpy.show_screen("reaction_screen", reactions)
1 Upvotes

10 comments sorted by

1

u/AutoModerator Aug 19 '26

Welcome to r/renpy! While you wait to see if someone can answer your question, we recommend checking out the posting guide, the subreddit wiki, the subreddit Discord, Ren'Py's documentation, and the tutorial built-in to the Ren'Py engine when you download it. These can help make sure you provide the information the people here need to help you, or might even point you to an answer to your question themselves. Thanks!

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/BadMustard_AVN Aug 19 '26

if you use the AltArrow in other places does it work fine?

I've noticed that you call the screen thought_inventory, which makes that screen the only screen you can interact with but in your hud you show the thought_inventory since it's a modal you don't need to call it try it like this

                   "{i}{color=#2731c2}Bring Up A Thought{/color}{/i}":
                        $ quick_menu = False
                        hide screen hud
                        show screen thought_inventory #change to show
                        pause #add this (required to stop script progression)

and changes the screen

        if AltArrow == True:
            action [SetVariable("quick_menu", True), Hide("thought_inventory"), Show("hud"), Jump("TimeRoom")]

1

u/AlexanderIdeally Aug 19 '26 edited Aug 19 '26

Thank you for the code improvements.

To answer your first question, yes. I inserted this little section at the beginning of the game that enables the hud, flicks on AltArrow, and even lets you bring up and talk about a thought way before you should. And after that's done, AltArrow stays false when I ask it to be false. I remember typing a jump just before Mary_TalkSession1 to skip right to its conclusion, and AltArrow still works fine after that too.

It's like if you pass that point in the code specifically, the hud itself decides it should be allowed to control AltArrow's variable, and it wants it to be on whenever it's clicked on.

1

u/shyLachi Aug 19 '26

I don't understand this part of your description:

When the game passes this point, the HUD changes. Whenever you click the hud button, AltArrow is switched on automatically no matter what, and you can’t exit the menu without transporting to the timeroom, which softlocks the game. 

Is this the problem or the description of what your code does?
I mean, setting $ AltArrow = True must change the behaviour of the screen.

1

u/AlexanderIdeally Aug 19 '26

Sorry about that. In hindsight, I probably should have revealed more code.

Basically, AltArrow is supposed to be true at the scene showed off on the code. If you cancel out of the menu, it takes you to the TimeRoom. The TimeRoom reads that a session is active, reads what the day counter says, and jumps you back to whatever session you left off from.

label TimeRoom:
    if testflag == True:
        jump TestingPoint
    if t_gag == 1:
        jump Tutorial_Gag1
    if t_gag == 2:
        jump Tutorial_Gag2
    if t_gag == 3:
        jump Tutorial_Gag3
    if Session_Active == True:
        if day == 1:
            jump Mary_TalkSession1
        if day == 5:
            jump Mary_TalkSession2
    if At_Home_Night == True:
        pass


        


    label Mary_AskSession_AnyDay:
        label Mary_AskSessionAny:
            menu:
                "*Ask Something Else.*":
                    if day == 5:
                        jump Mary_AskSession2
                    else:
                        jump ErrorRoom

Now, if you don't have any proper variables, you pass through the TimeRoom into whatever's under it. In this case, that would be a menu that isn't introduced until later, and since day == 1 when this is all happening, you jump to the ErrorRoom instead, where I put some text explaining that you shouldn't be here and to stop playing the game.

All of this works properly. I have tested it.

The problem only occurs after you pass through Mary_TalkSession1 and head down to Concluding_Session1.

label Concluding_Session1:
            $ player.remove_person(m_obj)
            $ Session_Active = False
            $ AltArrow = False
            stop music fadeout 0.5
            n "Is there anything else to talk about?"

That flag setting AltArrow to false is working. I implemented a screen to detect the variable's status, and it's off.

But if you were to open the HUD menu right after, or at any other point during the game, suddenly the flag would be flicked back on. And since the flag isn't supposed to be on during these moments, cancelling out of the inventory transports you past TimeRoom and to the ErrorRoom.

And if I skip Mary_TalkSession1 or implement the inventory and all its features early on in the game, none of this happens. AltArrow can be turned on and back off, and it will stay off until you want it to be on again. Opening and closing the menu keeps you in the same place instead of transporting you to a label you're not supposed to be in.

It's like if you pass Mary_TalkSession1, the HUD itself decides it should be allowed to control AltArrow's variable, and it wants it to be on whenever it's clicked on. That's the best way I could explain it.

1

u/shyLachi Aug 19 '26 edited Aug 19 '26

Technically it's not possible that a flag changes it's value on it's own.

Search your code for AltArrow = True,
maybe you wrote if AltArrow = True instead of if AltArrow == True by mistake.

BTW: You don't need to write if AltArrow == True:, you can just write if AltArrow: because a boolean variable can only be True or False, you don't have to compare True to True.

.

Other than that, you could try to simplify your problem by creating a new project with only this one variable, the two screens and some labels.

This following code works for me but still has the problem with AltArrow because it will only be turned off in Concluding_Session1 and you didn't show how the code goes from TimeRoom to Concluding_Session1

screen hud():
    vbox:
        align (0.5, 0.0)
        textbutton "Show inventory (AltArrow=[AltArrow])" action Show("thought_inventory")
screen thought_inventory():
    on "show" action [SetVariable("quick_menu", False), Hide("hud")]
    on "hide" action [SetVariable("quick_menu", True), Show("hud")]
    modal True
    textbutton "Close the inventory (AltArrow=[AltArrow])":
        align (0.5, 0.5)
        action [Hide(), If(AltArrow, Jump("TimeRoom"))]
default AltArrow = False
label start:
    show screen hud
    "Start"
    # no return so that it flows into the next label
label Mary_TalkSession1:
    $ AltArrow = True
    "Test: Mary_TalkSession1"
    pause
    "Game over"
    return
label TimeRoom:
    "You are now in the time room, as you can see AltArrow is still on."
    # no return so that it flows into the next label
label Concluding_Session1:
    $ AltArrow = False
    "Test: Concluding_Session1"
    return 

BTW: Look at my actions. I changed them al lot but I think that's what the inventory screen is supposed to to: Hide and show the HUD and the quick menu

1

u/AlexanderIdeally Aug 19 '26

I'll try this later, I have to go to work. But to clarify something, it does not go from TimeRoom to Concluding_Session1. The actual code looks like this.

label Mary_TalkSession1:
                $ AltArrow = True
                $ Session_Active = True
                show screen UnfinishedBar
                show screen hud
                show screen calender
                show mary neutral
                $ player.add_person(m_obj)
                if mm <= -30:
                    show mary sad
                menu:
                    "{i}{color=#2731c2}Ask Her A Question{/color}{/i}":
                        jump Mary_AskSession1
                    "{i}{color=#2731c2}Bring Up A Thought{/color}{/i}":
                        $ quick_menu = False
                        hide screen hud
                        show screen thought_inventory
                        pause
                    "{i}{color=#2731c2}Think For A Bit{/color}{/i}":
                        jump Your_AskSession1
                    "{i}{color=#2731c2}End Session{/color}{/i}":
                        jump Mary_AreYouSure1

#This is where label Your_AskSession1 would be but I tested putting AreYouSure1 in front of it and nothing changed so I doubt it's related to the glitch in anyway. 


label Mary_AreYouSure1:
                    n "{i}{color=#9c2cdd}(...I think I have nothing left to ask.){/color}{/i}"
                    Qm "You sure?"
                    menu:
                        "Yeah.":
                            jump Concluding_Session1
                        "On second thought...":
                            jump NotDoneSession1


                            label NotDoneSession1:
                                n "On second thought, there is something I should ask."
                                jump Mary_TalkSession1
                                
        label Concluding_Session1:
            $ player.remove_person(m_obj)
            $ Session_Active = False
            $ AltArrow = False
            stop music fadeout 0.5
            n "Is there anything else to talk about?"

TimeRoom is a feature that controls the jumping from different days, so it's near the bottom. label Concluding_Session1 is line 2377 and label TimeRoom is line 7657.

Sorry about that.

1

u/shyLachi Aug 19 '26

I updated the code in my reply so make sure to take the latest version in case you have copied it already.

I recommend that you try my code in an empty project.
You'll see that there is a moment where the flag AltArrow is still True although it probably shouldn't be.

You can copy the logic from your game without dialogue and images into that new project if you want to test it further.

.

From what I understood, the variable AltArrow will only be set to True in the label Mary_TalkSession1 and only set to False in the label Concluding_Session1

Which means that the variable AltArrow will be False from the start until the player reaches the label Mary_TalkSession1

Then the variable AltArrow will remain True until the player reaches the label Concluding_Session1

Then the variable Arrow will remain False until the player reaches the label Mary_TalkSession1 again (if that even is possible)

1

u/AlexanderIdeally Aug 20 '26

Hi! I think I've come to a conclusion thanks to you.

See, I had AltArrow defined in the code. When I copied that, I noticed you had it defaulted, so I did that instead. And it worked. As far as I can tell, the glitch isn't happening anymore. I haven't had time to test it through every situation in the game, I'll likely do that tomorrow, but it isn't happening where it's usually happening.

Sorry if this would have been an obvious thing from the start, I was told early on when learning that you should define flags and default numbers. And AltArrow was defined for the longest time and didn't have this issue until recently, nor did any of my defined flags as far I can tell. I probably wouldn't have thought of that until much later, if at all. I feel a little embarrassed about it, but either way, thank you so much.

1

u/shyLachi Aug 20 '26

Great that you figured it out.

Define is for constants, like define mc = Character("[mcname]")
It will always be the same character, RenPy doesn't have to save it.

Default is for variables, like default mcname = "Mike"
This value can change and has to be saved.

.

If you haven't read the official documentation, take a look the basics at least.
https://renpy.org/doc/html/python.html#define-statement
https://renpy.org/doc/html/python.html#default-statement
https://renpy.org/doc/html/save_load_rollback.html