[SOLVED] Game hangs in event handler loop

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
LofnBard
Newbie
Posts: 14
Joined: Sun Jan 26, 2014 6:37 am
Projects: Herb Witch
Location: Quebec, Canada
Contact:

[SOLVED] Game hangs in event handler loop

#1 Post by LofnBard »

Hey folks,
I'm trying to make a team selection screen, with a loop that checks for flags that get set by my screens, and makes the appropriate changes to my active team array (a list of three class Character elements). It works if I put a "pause" inside the event handler loop, but forces the user to click somewhere else between clicking buttons. Very inconvenient. If I don't put "pause", the loop makes the game hang. I'm guessing it freezes because it loops so fast it crashes before the screens have a chance to update flags, but I don't know how else to do this.

I've worked three solid days to fix this, first in Ren'Py, then converting the whole handler to Python without success. I could use some pointers while there's still hair left to rip out... Thanks! Below is a simplified version of my code.

Code: Select all

show screen Select_Character  	# sets active_char = 1,2 or 3, by clicking on character imagebutton
show screen Character_Stats   	# displays stats of selected character
show screen Team_Roster		# Has buttons to ADD current character to roster, CLEAR roster, 
				# or START BATTLE. Pushing the button sets the flag to True. Also displays roster. 

while (start_battle_flag != True)
    if add_flag:
        overfull = Add_Char(Active_Team_Array,active_char) # If ADD is successful, returns False
        if overfull:                                       # If already full, returns True
            call screen Ask("Do you want to battle now or keep choosing roster?")  # Can jump out of loop
    if clear_flag:
        Clear(Active_Team_Array)
    if start_battle_flag:
        jump NextPhase
    pause   # Only works when this is here, inconvenient

Last edited by LofnBard on Wed Mar 19, 2014 12:43 am, edited 1 time in total.

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Game hangs in event handler loop

#2 Post by Asceai »

Yes, if you have an infinite loop your game will hang.

I'd suggest keeping the pause, but using a label before / instead of your while loop. Modal screens will prevent that pause from ever being passed and you can just have your screens do a Jump() when you want something to happen.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Game hangs in event handler loop

#3 Post by xela »

You could alternatively put ui.interact() or result = ui.interact() instead of that pause, it should do the job marvelously :)

You code setup looks a bit weird but if it works, it should be fine.
Like what we're doing? Support us at:
Image

User avatar
LofnBard
Newbie
Posts: 14
Joined: Sun Jan 26, 2014 6:37 am
Projects: Herb Witch
Location: Quebec, Canada
Contact:

Re: Game hangs in event handler loop

#4 Post by LofnBard »

I have a version with labels and one in Python with while that both hang. I really don't understand what the reference manual means by modal. What does a modal screen do? It seems to say that modal screens don't allow interaction... I have two screens that both require interaction. I'm not sure how preventing any other interactions will help me.

ui.interact() just made my screen unresponsive in the Python version of the handler, and the docs say I then have to manually add rollback. I can't use ui in the Renpy version either.
If there's a non-weird way to make this work Xela, I'd be happy to do it that way, this is just what I figured out. :)

Okay, so here's the actual code I'm using, Renpy version because that one *sometimes* works without hanging, IF you click slowly and in the right places (starting to sweat, the games fair where I'm showing this off starts in a few hours...)

Code: Select all

#======= THIS PART IS IN script.rpy ===============
# ■██▓▒░ TEAM SELECTION ░▒▓██████████████████████████████████████████████████████████████████████████████████████■
label team_selector:
    
    #window hide                 # hide the bottom dialog window
    call screen bigMessage("Choose Your Team For This Battle","     Your spiritual power is only sufficient to sustain two\nallies at first.\n", "     Win battles to unlock more slots for your team\nand more allies to choose from.")
    
    "You only have 2 slots on your team to start with." #without this message, the game hangs
    $ slot_unlocked = [True,True,False]         # Only the first two slots are open at first 
    $ Ateam = [noPowers,noPowers,noPowers]      # Start with empty roster, showing the None icons
    $ p_angelica.active = True                  # Set each plant to active. Active means they have not yet been added to the roster
    $ p_elder.active = True                     # Though this changes nothing to whether they are locked or not. Put in FUNCTION
    $ p_elecampane.active = True
    $ Pteam = [noPowers,p_angelica, p_elder, p_elecampane] # Potential Team / Plant Team: List of all plants in the game

    $ ally = noPowers                           # Selected ally starts with the dummy plant
    
    $ clear_Ateam = False                       # Set all action flags to False
    $ add_Ateam = False
    $ start_battle = False
    $ overfull = False
    $ answer =""

    show screen choose_team
    show screen roster_screen
    show screen plant_info_screen

    window show                 # show the bottom dialog window
    "Instructions: \n1) Click on a plant ally on the right,\n2) Click Add on the bottom left. Repeat 1 & 2 until your roster is full.\nWhen you're done, click Start Battle on the bottom left.\nClick on the screen to hide these instructions."
    window hide                 # hide the bottom dialog window
    
label team_selection_loop:              # Check the three flags controlled by roster_screen, act if needed, then loop back here. 
    # Flags set by team_selection screens: add_Ateam, clear_Ateam, start_battle ## class Plant: ally (Ateam is used but not changed by screens)

    # ■██▓▒░ CLEAR ROSTER ░▒▓████████████████████████████████████■

    if clear_Ateam:
        $ clear_Ateam = teamselect_clear(clear_Ateam, Ateam, Pteam) #Clears all and returns False, clearing the flag in one shot
        $ allyN = 0             # Clear index of currently selected plant
        $ ally = noPowers       # Clear redsquare selection on right side of menu. 

    # ■██▓▒░ ADD to ROSTER ░▒▓████████████████████████████████████■
 
    if (add_Ateam == True):             # Player has clicked on ADD. Add "ally" to the roster, if add_ally has been set to "ally"
        $ overfull = teamselect_add (add_Ateam, Ateam, Pteam, allyN, slot_unlocked) # returns True if trying to add while roster is full
        $ add_Ateam = False             # Clear Add flag after handling event
        $ allyN = 0                     # Clear ally, otherwise clicking add again will add it a second time, even if it's greyed.
        $ ally = noPowers               # Clear selected plant ally on right side of menu. 
        if overfull:                         # Adding when roster is full
            $ overfull = False                      # Clear flag so we don't loop back here
            $ start_battle = roster_full(add_Ateam) # Ask player if they want to battle now or return to selection. Sets start_battle flag to True/False

    # ■██▓▒░ START BATTLE ░▒▓████████████████████████████████████■
    if (start_battle):                                                          # When start_battle becomes true, jump to the battle's prep
        hide screen choose_team                                                 # Hide all the screens
        hide screen roster_screen
        hide screen plant_info_screen 
        call screen bigMessage("Ready yourself, the battle is about to begin.") # Give clear status message, click on Continue
        jump prep                                                               # Go to prep and then battle
    else: 
        pause 3                              # (Only loop once per two seconds)  ##CHANGE##
        jump team_selection_loop            # Keep Looping otherwise
    pause 1

label roster_full_dialog:                      # Dialog called by team_selection_loop when trying to add more to a full roster
        hide screen plant_info_screen
        call screen bigChoice("Your roster is full", "Would you like to start the battle now? Click yes to battle.", "Click 'No' to continue choosing your team. Then use the Clear button in the bottom left to select a different team.")
        pause 1                             # only check every second
        $ answer = _return                  # Use returned value. If yes, set start_battle to True
        if answer == "y": 
            jump prep
        elif answer == "n":
            show screen plant_info_screen
            jump team_selection_loop 
I don't think you'll need to check the screens code, but here it is anyway.

Code: Select all

#======= THIS PART IS IN screens2.rpy ===============

screen bigMessage (msg1,msg2="",msg3="", size1=40,size2=30): #Has Hide() so can use show screen bigMessage to call it
    frame: 
        xalign 0.5 yalign 0.3
        vbox:
           text msg1 size size1 first_indent 70
           text "" size 15
           text msg2 size size2 first_indent 70
           text msg3 size size2 first_indent 70
           text "" size size1
           textbutton "{size=+10}Continue{/size}" action [Hide("bigMessage"),Return()]

screen choose_team:
    $ x_base =900
    $ y_base =150
    default tt = Tooltip("")
    frame:
        pos (315,0) 
        has vbox: 
            text tt.value
    text("PLANT ALLIES") xpos (x_base-45) ypos (y_base-65) size 30 underline True       # Right side top title
    text("TEAM") xpos 60 ypos (y_base-65) size 30 underline True                        # Left side top title
    
    text("Angelica") xpos (x_base) ypos (y_base-30)                                     # Names above plant icon, right side
    text("   Elder") xpos (x_base) ypos (y_base+120)
    text("Elecampane") xpos (x_base) ypos (y_base+270)
    
    text("Choose plants to \nadd to your team") xpos (x_base+20) ypos (y_base+420) size 25     #Static info text, right side
    text("Unlock the third plant\nby winning a battle") xpos (x_base+20) ypos (y_base+480) size 25
        
                             # RIGHT SIDE, ALL ALLIES   # When plant clicked, set ally = 
    if (p_angelica.unlocked and p_angelica.active):      # Show available if unlocked AND not in roster yet             Pteam[1] is Angelica
        imagebutton auto (p_angelica.img+"%s.png") xpos (x_base) ypos (y_base) action [SetVariable("ally",p_angelica), SetVariable("allyN",Pteam[1].index), Hide("tooltip_team")] hovered tt.Action("Angelica Powers: "+p_angelica.txt[1]) activate_sound ClickSound  
    else: 
        imagebutton auto (p_angelica.img+"%s.png") xpos (x_base) ypos (y_base) action None
    if (p_elder.unlocked and p_elder.active):
        imagebutton auto (p_elder.img+"%s.png") xpos (x_base) ypos (y_base+150) action [SetVariable("ally",p_elder), SetVariable("allyN",Pteam[2].index), Hide("tooltip_team") ] hovered tt.Action("Elder Powers: "+p_elder.txt[1]) activate_sound ClickSound  
    else: 
        imagebutton auto (p_elder.img+"%s.png") xpos (x_base) ypos (y_base+150) action None
    if (p_elecampane.unlocked and p_elecampane.active):
        imagebutton auto (p_elecampane.img+"%s.png") xpos (x_base) ypos (y_base+300) action [SetVariable("ally",p_elecampane), SetVariable("allyN",Pteam[3].index), Hide("tooltip_team") ] hovered tt.Action("Elecampane Powers: "+p_elecampane.txt[1]) activate_sound ClickSound  
    else: 
        imagebutton auto (p_elecampane.img+"%s.png") xpos (x_base) ypos (y_base+300) action None
 
screen plant_info_screen:
    frame:                                      #all coordinates below this take the upper left corner of frame as (0,0)
        default tt1 = Tooltip("")
        pos (250,60) minimum (600,650) maximum (600,650)    
        add (ally.img+"idle.png") xalign 1.0 yalign 0.0
        text("Plant Ally Information") xpos 10 size 40
        $ x_basex = 150
        $ y_basex =275
        
        if ally.hasPower(HARVEST): 
            imagebutton auto "gui/iHarvest_%s.png" xpos (x_basex+100) ypos (y_basex+100) hovered tt1.Action("Harvest is a Nutritive Tonic. Increases Vitality") action NullAction()
        if ally.hasPower(FIRE):
            imagebutton auto "gui/iFire_%s.png" xpos (x_basex) ypos (y_basex) hovered tt1.Action("Fire is Warming & Stimulating\nCounters Earth (cold stagnant) conditions") action NullAction()
        if ally.hasPower(EARTH):                                                      
            imagebutton auto "gui/iEarth_%s.png" xpos (x_basex) ypos (y_basex+100) hovered tt1.Action("Earth is Cooling\nCounters Fire (overheated) conditions") action NullAction()
        if ally.hasPower(STORM): 
            imagebutton auto "gui/iStorm_%s.png" xpos (x_basex+100) ypos (y_basex) hovered tt1.Action("Storm is Antiseptic and Detoxifying\nCounters Swamp (stagnant, rotting & toxic) conditions") action NullAction()
        if ally.hasPower(AIR): 
            imagebutton auto "gui/iAir_%s.png" xpos (x_basex+200) ypos (y_basex) hovered tt1.Action("Air is Drying & Toning\nCounters River (mucus flow/diarrhea) conditions") action NullAction() 
        if ally.hasPower(RIVER): 
            imagebutton auto "gui/iRiver_%s.png" xpos (x_basex+200) ypos (y_basex+100) hovered tt1.Action("River is Hydrating (water)\nCounters Air (dry & atrophy) conditions") action NullAction() 
        if ally.hasPower(STONE):          
            imagebutton auto "gui/iStone_%s.png" xpos (x_basex) ypos (y_basex+200) hovered tt1.Action("Stone is Slowing (Sedative)\nCounters Fire (overstimulated & exhausted) conditions") action NullAction()
        if ally.hasPower(MOUNT): 
            imagebutton auto "gui/iMount_%s.png" xpos (x_basex+100) ypos (y_basex+200) hovered tt1.Action("Mountain is Relaxing\nCounters Storm (stress & spasm) conditions") action NullAction()
        if ally.hasPower(SWAMP): 
            imagebutton auto "gui/iSwamp_%s.png" xpos (x_basex+200) ypos (y_basex+200) hovered tt1.Action("Swamp is Moistening (oily)\nCounters Air (dry & atrophy) conditions") action NullAction()

        vbox: 
            xpos 10 ypos 60
            text ("Name:")
            text ("Latin Name:")
            text ("Powers:\n")
            text ('"'+ally.txt[2]+ '"')
            text (tt1.value) ypos 10
        vbox: 
            xpos 150 ypos 60            
            text (ally.name)
            text (ally.txt[0])
            text (ally.txt[1])

screen roster_screen:                           ############################################ LEFT SIDE, BATTLE TEAM  
    default tt3 = Tooltip("")
    frame:                                      #all coordinates below this take the upper left corner of frame as (0,0)
        pos (25,60) minimum (200,650) maximum (200,650)
        $ x_basez = 40
        $ y_basez = 90 
        text("Roster") xalign 0.5 size 40
        add (Ateam[0].img+"idle.png") xpos (x_basez) ypos (y_basez)
        add (Ateam[1].img+"idle.png") xpos (x_basez) ypos (y_basez+150)
        add (Ateam[2].img+"idle.png") xpos (x_basez) ypos (y_basez+300)                  

        vbox: 
            xpos 10 ypos 500
            textbutton "ADD" action SetVariable("add_Ateam", True) hovered tt3.Action("Add Ally to Roster")
            textbutton "CLEAR" action SetVariable("clear_Ateam", True) hovered tt3.Action("Empty the Roster")
            textbutton "Start Battle" action [SetVariable("start_battle", True), Return(True)] hovered tt3.Action("Exit & Start Battle")

        # Flags set by screens: clear_Ateam, add_Ateam, start_battle ## class Plant: ally (Ateam is used but not changed by screens)

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Game hangs in event handler loop

#5 Post by xela »

Am I writing in riddles? :?:

Substitute "pause 3" with "$ ui.interact()"


You way is not weird enough to rewrite the whole code. I am planning to write a guide on how to use Python in VNs, Sims and Ren'Py and suggest a few coding practices in cookbook that worked well for me after I release an Alpha version of my game and unless someone does something similar by then. You case will be covered there.
Like what we're doing? Support us at:
Image

User avatar
LofnBard
Newbie
Posts: 14
Joined: Sun Jan 26, 2014 6:37 am
Projects: Herb Witch
Location: Quebec, Canada
Contact:

Re: Game hangs in event handler loop

#6 Post by LofnBard »

I can't believe it never occurred to me to use Ren'Py Python commands in Ren'Py with a $. :P
Either way, I did just try with the ui command in the Ren'Py version as you suggested.

'pause 3' lets me make 1 change to the roster, then the game crashes.
$ ui.interact() lets me make 1 change to the roster, then ADD and CLEAR stop working, though choose_team and plant_info_screen remain responsive.
Putting "hello" there makes the roster work correctly, but I have to click to dismiss the text between each action for it to take effect.
So, not yet solved.

I look forward to your cookbook guide, though it sounds like it's months away, and I'll hopefully figure this out before then.
I omitted the roster feature for the games demo last night, and the rest worked out very well. Now that the mentoring program's over I'll be setting my own deadlines for making the full game.

Regardless, I do appreciate your help Xela and Asceai. :)

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Game hangs in event handler loop

#7 Post by xela »

LoL

ui.interact() basically waits for user interaction. When that occurs it allows the game to jump back to the team_selection_loop. Fault lies somewhere between your function or screens. The setup of the code itself is sound.
Like what we're doing? Support us at:
Image

User avatar
LofnBard
Newbie
Posts: 14
Joined: Sun Jan 26, 2014 6:37 am
Projects: Herb Witch
Location: Quebec, Canada
Contact:

Re: Game hangs in event handler loop

#8 Post by LofnBard »

Hmmm, the fault is either in the handler function, or in the screens. That really narrows it down. Thank you, wise one, for your guidance. LOL :P

In the meantime, I HATH FOUND THE SOLUTION!

I put the three interface screens into a single choose_team_roster screen. It's fine, a screen can have multiple frames and differently named tooltips for each. At the start of my team_selection_loop handler, I "call screen choose_team_roster" (1st change: call instead of show)

In choose_team_roster screen, buttons set the ADD, CLEAR or BATTLE flags, then execute a Return() (2nd change: adding Return() to buttons).
This gives control back to the handler, which runs only once to deal with event, then loops back up to the top where it does "call screen" again, allowing the user to continue interactions.

Summary:
1) Call interface screen. Return() when button is pressed.
2) Run handler code to alter roster.
3) Jump out if flag start_battle is True.
4) Jump to 1.

It's a bit counter intuitive, because the interface disappears when the screen returns, showing nothing at all while the handler runs.
However, the handler runs so fast that no human can see the interface is gone for a tiny fraction of a second.

SOLVED! :-D

Post Reply

Who is online

Users browsing this forum: Ocelot