Chapter Selection Screen

When the player gets to pick how to advance

Note: Lines added or changed have #*** at the end of the line.
Lines removed are commented out, starting with #***.

Maybe they could have a different color? Cyan, something that stands out?


Part one: Setting up the labels.

label start():

    "Start of the game."

    call chapterOne

    "First chapter finished."

    call chapterTwo

    "Second chapter finished."

    "The game has reached the end."

    return

label chapterOne():

    "Chapter One: The Village."

    "Chapter where the hero rescues the cat."

    return

label chapterTwo():

    "Chapter Two: The Castle."

    "Chapter about the hero training with the knights."

    return

Part two: Screen that can call the chapters.

label start():

    "Start of the game."

    #*** call chapterOne

    #*** "First chapter finished."

    #*** call chapterTwo

    #*** "Second  chapter finished."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():                      #***
                                                #***
    vbox:                                       #***
                                                #***
        align (0.5, 0.5)                        #***
        spacing 10                              #*** 
                                                #*** 
        textbutton "Chapter One: The Village.": #***
            action Call("chapterOne")           #*** 
                                                #***
        textbutton "Chapter Two: The Castle.":  #***
            action Call("chapterTwo")           #***
                                                #***
        textbutton "Finish the game.":          #***
            action Return()                     #***

label chapterOne():

    #*** "Chapter One: The Village."

    "Chapter where the hero rescues the cat."

    call screen chapterSelection

    return

label chapterTwo():

    #*** "Chapter Two: The Castle."

    "Chapter about the hero training with the knights."

    call screen chapterSelection

    return

Part three: Adding chapterProgress and sensitive property.

default chapterProgress = 0 #***

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        textbutton "Chapter One: The Village.":
            sensitive chapterProgress == 0 #***
            action Call("chapterOne")

        textbutton "Chapter Two: The Castle.":
            sensitive chapterProgress == 1 #***
            action Call("chapterTwo")

        textbutton "Finish the game.":
            sensitive chapterProgress == 2 #***
            action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1 #***

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2 #***

    call screen chapterSelection

    return

Part four: if statement to hide chapter button before it can be played.

default chapterProgress = 0

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        if chapterProgress >= 0: #***
            textbutton "Chapter One: The Village.":
                sensitive chapterProgress == 0
                action Call("chapterOne")

        if chapterProgress >= 1: #***
            textbutton "Chapter Two: The Castle.":
                sensitive chapterProgress == 1
                action Call("chapterTwo")

        if chapterProgress >= 2: #***
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2

    call screen chapterSelection

    return

Part five: Creating buttons with a for loop, replacing names with an index

default chapterProgress = 0

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index in range(2): #***

            if chapterProgress >= index: #***

                textbutton "Chapter {}".format( index + 1 ): #***
                    sensitive chapterProgress == index #***
                    action Call("chapterOne")

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2

    call screen chapterSelection

    return

Part six: chaptersLabels to fix the action.

default chapterProgress = 0
define chaptersLabels = ["chapterOne", "chapterTwo"] #***

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index in range(2):

            if chapterProgress >= index:

                textbutton "Chapter {}".format( index + 1 ):
                    sensitive chapterProgress == index
                    action Call( chaptersLabels[index] ) #***

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2

    call screen chapterSelection

    return

Part seven: Removing range by adding enumerate.

default chapterProgress = 0
define chaptersLabels = ["chapterOne", "chapterTwo"]

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index, chapterLabel in enumerate(chaptersLabels): #***

            if chapterProgress >= index:

                textbutton "Chapter {}".format( index + 1 ):
                    sensitive chapterProgress == index
                    action Call( chapterLabel ) #***

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2

    call screen chapterSelection

    return

Part eight: Changing chaptersLabels to chaptersTuples

default chapterProgress = 0
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), #*** 
                        ("Chapter Two: The Castle.", "chapterTwo") ]   #***

label start():

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index, chapterTuple in enumerate(chaptersTuples): #***

            if chapterProgress >= index:

                textbutton chapterTuple[0]: #***
                    sensitive chapterProgress == index
                    action Call( chapterTuple[1] ) #***

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    $ chapterProgress = 1

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    $ chapterProgress = 2

    call screen chapterSelection

    return

Powered by TinyTake Screen Capture

Part nine: Preparing the chaptersStatus dict so that chapters can be done in any order.

default chapterProgress = 0
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), 
                        ("Chapter Two: The Castle.", "chapterTwo") ] 

default chaptersStatus = {} #***

label start():

    python:                                           #***
        for chapterTuple in chaptersTuples:           #***
            chaptersStatus[ chapterTuple[1] ] = False #***

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index, chapterTuple in enumerate(chaptersTuples):

            if chapterProgress >= index:

                textbutton chapterTuple[0]:
                    sensitive chapterProgress == index
                    action Call( chapterTuple[1] )

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    python:                                  #***
        chapterProgress = 1                  #***
        chaptersStatus["chapterOne"] = True  #***

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    python:                                  #***
        chapterProgress = 2                  #***
        chaptersStatus["chapterTwo"] = True  #***

    call screen chapterSelection

    return

Part ten: Function to check whether all chapters have been done.

default chapterProgress = 0
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), 
                        ("Chapter Two: The Castle.", "chapterTwo") ] 

default chaptersStatus = {} 

init python:                                            #***
    def allChaptersFinished():                          #***
        return ( False not in chaptersStatus.values() ) #***

label start():

    python:                                           
        for chapterTuple in chaptersTuples:           
            chaptersStatus[ chapterTuple[1] ] = False 

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        for index, chapterTuple in enumerate(chaptersTuples):

            if chapterProgress >= index:

                textbutton chapterTuple[0]:
                    sensitive chapterProgress == index
                    action Call( chapterTuple[1] )

        if chapterProgress >= 2:
            textbutton "Finish the game.":
                sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    python:                                  
        chapterProgress = 1                  
        chaptersStatus["chapterOne"] = True  

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    python:                                  
        chapterProgress = 2                  
        chaptersStatus["chapterTwo"] = True  

    call screen chapterSelection

    return

Part eleven: Tying chaptersStatus to the chapterSelection screen, deleting chapterProgress and ifs. 
The chapters can now be picked in any order.
index of enumerate has become obsolete.

#*** default chapterProgress = 0
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), 
                        ("Chapter Two: The Castle.", "chapterTwo") ] 

default chaptersStatus = {} 

init python:                                            
    def allChaptersFinished():                          
        return ( False not in chaptersStatus.values() ) 

label start():

    python:                                           
        for chapterTuple in chaptersTuples:           
            chaptersStatus[ chapterTuple[1] ] = False 

    "Start of the game."

    call screen chapterSelection

    "The game has reached the end."

    return

screen chapterSelection():

    vbox:

        align (0.5, 0.5)
        spacing 10

        #*** for index, chapterTuple in enumerate(chaptersTuples): 
        for chapterTuple in chaptersTuples: 

            #*** if chapterProgress >= index:

            textbutton chapterTuple[0]:
                sensitive chaptersStatus[ chapterTuple[1] ] is False #***
                action Call( chapterTuple[1] )

        #*** if chapterProgress >= 2:
        if allChaptersFinished(): #***
            textbutton "Finish the game.":
                #*** sensitive chapterProgress == 2
                action Return()

label chapterOne():

    "Chapter where the hero rescues the cat."

    #*** python:                                  
    #***    chapterProgress = 1                  
    $ chaptersStatus["chapterOne"] = True #***

    call screen chapterSelection

    return

label chapterTwo():

    "Chapter about the hero training with the knights."

    #*** python:                                  
    #***    chapterProgress = 2                 
    $ chaptersStatus["chapterTwo"] = True #***

    call screen chapterSelection

    return

Part twelve: Final cleanup and adding comments.

# List of tuples of (chapter title, chapter label).
# chapter title is how it's called in the chapter selection screen.
# chapter label is the label of the chapter. Duh.
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), 
                        ("Chapter Two: The Castle.", "chapterTwo") ] 

# Maps chapter labels to True or False.
# False if they haven't been done yet, True if they have.
default chaptersStatus = {} 

# Function that checks all values in chaptersStatus,
# to see if there are still any False ones.
init python:                                            
    def allChaptersFinished():                          
        return ( False not in chaptersStatus.values() ) 

# The game starts here.
label start():

    # Go through all the labels mentioned in chaptersTuples,
    # and set their status in chaptersStatus to False.
    python:                                           
        for chapterTuple in chaptersTuples:           
            chaptersStatus[ chapterTuple[1] ] = False 

    "Start of the game."

    # First call of the selection screen.
    call screen chapterSelection
    # Return()ing from the chapterSelection takes us here.

    "The game has reached the end."

    # Return to the main menu.
    return

# Chapter selection screen.
screen chapterSelection():

    # vbox placed in the middle of the screen.
    vbox:

        align (0.5, 0.5)
        spacing 10

        # For every chapter mentioned in chaptersTuples:
        for chapterTuple in chaptersTuples: 

            # Create a textbutton.
            # Text from the (chapter title, chapter label) tuple.
            # Can be clicked if status of this chapter label is False.
            # Calls chapter label from the (chapter title, chapter label) tuple.
            textbutton chapterTuple[0]:
                sensitive chaptersStatus[ chapterTuple[1] ] is False
                action Jump( chapterTuple[1] )

        # Textbutton that appears after all chapters have their status
        # set to True. Takes us to where the chapterSelection
        # was first called.
        if allChaptersFinished():
            textbutton "Finish the game.":
                action Return() 

# Label of the first chapter.
label chapterOne():

    "Chapter where the hero rescues the cat."
    
    # Set this chapter's status in chaptersStatus to True.
    $ chaptersStatus["chapterOne"] = True

    # Go back to the chapter selection screen.
    call screen chapterSelection

    # Required so that Return() in chapterSelection works properly.
    return

# Label of the second chapter.
label chapterTwo():

    "Chapter about the hero training with the knights."
         
    # Set this chapter's status in chaptersStatus to True.
    $ chaptersStatus["chapterTwo"] = True

    # Go back to the chapter selection screen.
    call screen chapterSelection

    # Required so that Return() in chapterSelection works properly.
    return

Powered by TinyTake Screen Capture

Part thirteen - Modifying it so it works with ShowMenu instead, allowing us to enter it straight from the Main Menu, at the start of the game.
It works ingame, too, since Start() seems to work like Call when the game has not yet been started, and like Jump otherwise.
Using Start() also starts the game if it hasn't yet been.

Some comments changed.

# List of tuples of (chapter title, chapter label).
# chapter title is how it's called in the chapter selection screen.
# chapter label is the label of the chapter. Duh.
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), 
                        ("Chapter Two: The Castle.", "chapterTwo") ] 

# Maps chapter labels to True or False.
# False if they haven't been done yet, True if they have.
default chaptersStatus = {} 

# Function that checks all values in chaptersStatus,
# to see if there are still any False ones.
init python:                                            
    def allChaptersFinished():                          
        return ( False not in chaptersStatus.values() ) 


# Important change elsewhere:                                           #***
# In screens.rpy, in the navigation screen (Line 292),                  #***
# there is the Start button (Line 304).                                 #***
# I've changed its action from Start() to ShowMenu("chapterSelection"). #***


# As all Start() actions lead to labels other than Start,
# this will never be entered.
# However, Ren'Py games *have to* have the start label defined.
label start():

    return

    #***    # Go through all the labels mentioned in chaptersTuples,
    #***    # and set their status in chaptersStatus to False.
    #***    python:                                           
    #***        for chapterTuple in chaptersTuples:           
    #***            chaptersStatus[ chapterTuple[1] ] = False 
    #***
    #***    "Start of the game."
    #***
    #***    # First call of the selection screen.
    #***    call screen chapterSelection
    #***    # Return()ing from the chapterSelection takes us here.
    #***
    #***    "The game has reached the end."
    #***
    #***    # Return to the main menu.
    #***    return

# We still need to set up chaptersStatus.                                    #***
# This isn't a bad place. A label that is run before entering the Main Menu. #***
label before_main_menu:                                                      #***

    # Check if chaptersStatus is empty.                                      #***
    # since before_main_menu is ran when *returning* to it after game, too,  #***
    # running this every time would overwrite all statuses back to False.    #***
    if not chaptersStatus.keys():                                            #***

        # Go through all the labels mentioned in chaptersTuples,             #***
        # and set their status in chaptersStatus to False.                   #***
        python:                                                              #***
            for chapterTuple in chaptersTuples:                              #***
                chaptersStatus[ chapterTuple[1] ] = False                    #***

    # Continue to the Main Menu.                                             #***
    return                                                                   #***

# Chapter selection screen.
screen chapterSelection():

    # A background to cover Main Menu, since it's shown on top of it. #***
    # The same background used by the Main Menu by default.           #***
    add gui.main_menu_background #***

    # vbox placed in the middle of the screen.
    vbox:

        align (0.5, 0.5)
        spacing 10

        # For every chapter mentioned in chaptersTuples:
        for chapterTuple in chaptersTuples: 

            # Create a textbutton.
            # Text from the (chapter title, chapter label) tuple.
            # Can be clicked if status of this chapter label is False.
            #*** # Calls chapter label from the (chapter title, chapter label) tuple.
            # Starts the game at the chosen label.        #***
            textbutton chapterTuple[0]:
                sensitive chaptersStatus[ chapterTuple[1] ] is False

                #*** action Call( chapterTuple[1] )
                action Start( chapterTuple[1] ) #***

                # Make the text white. Default color is hard to see with the bg. #***
                text_idle_color "fff"                                            #***

        # Textbutton that appears after all chapters have their status
        # set to True. Takes us to where the chapterSelection
        # was first called.
        if allChaptersFinished():
            textbutton "Back to Main Menu.": #***
                action Return() 

                # Make the text white. Default color is hard to see with the bg. #***
                text_idle_color "fff"                                            #***

# Label of the first chapter.
label chapterOne():

    "Chapter where the hero rescues the cat."
    
    # Set this chapter's status in chaptersStatus to True.
    $ chaptersStatus["chapterOne"] = True

    #*** # Go back to the chapter selection screen.
    #*** call screen chapterSelection

    # Go back to the chapterSelection.          #***
    $ renpy.run( ShowMenu("chapterSelection") ) #***

    # Required so that Return() in chapterSelection works properly.
    return


# Label of the second chapter.
label chapterTwo():

    "Chapter about the hero training with the knights."
         
    # Set this chapter's status in chaptersStatus to True.
    $ chaptersStatus["chapterTwo"] = True

    #*** # Go back to the chapter selection screen.
    #*** call screen chapterSelection

    # Go back to the chapterSelection.          #***
    $ renpy.run( ShowMenu("chapterSelection") ) #***

    # Required so that Return() in chapterSelection works properly.
    return

Powered by TinyTake Screen Capture

Part fourteen - Menu version (like Part #13) of Part #8.

Comments added.

# Index of the currently available chapter.
default chapterProgress = 0

# List of tuples of (chapter title, chapter label).
# chapter title is how it's called in the chapter selection screen.
# chapter label is the label of the chapter. Duh.
define chaptersTuples = [ ("Chapter One: The Village.", "chapterOne"), #*** 
                        ("Chapter Two: The Castle.", "chapterTwo") ]   #***

# As all Start() actions lead to labels other than Start,
# this will never be entered.
# However, Ren'Py games *have to* have the start label defined.
label start():

    return #***

    #*** "Start of the game."

    #*** # First call of the selection screen.
    #*** call screen chapterSelection
    #*** # Return()ing from the chapterSelection takes us here.

    #*** "The game has reached the end."

    #*** # Return to the Main Menu.
    #*** return

# Chapter selection screen.
screen chapterSelection():

    # A background to cover Main Menu, since it's shown on top of it.
    # The same background used by the Main Menu by default.          
    add gui.main_menu_background #***

    # vbox placed in the middle of the screen.
    vbox:

        align (0.5, 0.5)
        spacing 10

        # For every chapter mentioned in chaptersTuples:
        for index, chapterTuple in enumerate(chaptersTuples):

            # Visible if it's the available chapter or a finished one.
            if chapterProgress >= index:

                # Create a textbutton.                                    
                # Text from the (chapter title, chapter label) tuple.     
                # Can be clicked if it's the currently available chapter. 
                # Starts the game at the chosen label.                    
                textbutton chapterTuple[0]:
                    sensitive chapterProgress == index
                    action Start( chapterTuple[1] ) #***

                    # Make the text white. Default color is hard to see with the bg. #***
                    text_idle_color "fff"                                            #***

        # Textbutton that appears we've been through all the chapters,
        # two in our case.
        # It will return us back to the Main Menu.
        if chapterProgress >= 2:
            textbutton "Back to Main Menu.": #***
                sensitive chapterProgress == 2
                action Return()

                # Make the text white. Default color is hard to see with the bg. #***
                text_idle_color "fff"                                            #***

# Label of the first chapter.
label chapterOne():

    "Chapter about the hero training with the knights."

    # Set the progress to the next value.
    $ chapterProgress = 1

    # Action that brings up chapterSelection as a menu.
    $ renpy.run( ShowMenu("chapterSelection") ) #***

    #*** # Go back to the chapter selection screen.
    #*** call screen chapterSelection

    # Required so that Return() in chapterSelection works properly.
    return

# Label of the second chapter.
label chapterTwo():

    "Chapter about the hero training with the knights."

    # Set the progress to the next value.
    $ chapterProgress = 2

    # Action that brings up chapterSelection as a menu.
    $ renpy.run( ShowMenu("chapterSelection") ) #***

    #*** # Go back to the chapter selection screen.
    #*** call screen chapterSelection

    # Required so that Return() in chapterSelection works properly.
    return

Powered by TinyTake Screen Capture

Things to mention:

  • That chaptersStatus should probably be a persistent variable.
  • That chaptersTuples could be defaulted instead. I'm not sure if there's any point, since you won't be able to see next chapters anyway. Maybe if you wanted to hide earlier chapters.
  • Maybe use </Code>'s code for auto defining of chapters, through files, viz. discussion with them. If I include it though, it will be a huge bonus.
  • That I would use a class instead of just a list and a dict. One thing that this would allow is multiple different instances of the screen.
  • ((( Investigate the call stack, especially in Part #8. Game Starts and Ends as you go through the Part #13. )))
  • ((( Change Calls to Jumps? Currently changed in a single block, #12 )))

The final words!