I am an idiot, Please Help Me Learn Ren'Py.

First up, I see “school uniform” in the wardrobe. Weight Gaming has a strict policy about underage characters in fetish games - it will get your game removed. I’d strongly suggest starting at university age and never including school age at all.

Right, mod hat off.

I’d still strongly encourage having a main loop and using call/return for the most part. The reason for this is that life sim type games have a basic structure built around the player’s day:

  • Wake up
  • Do stuff (work, events, going places, interacting with NPCs)
  • Run out of energy or time
  • Go home
  • Sleep
  • And repeat until game over

The “Do stuff” step can include:

  • things the player has no control over - like being kidnapped at a certain time in the plot!
  • things the player previously agreed to do - like the date they set up with an NPC for Saturday night
  • things the player has to do - like their job
  • things the player has a free choice of

The main loop encapsulates this basic structure that is at the heart of the game. Without it you’ll end up having to check for the out-of-energy, too-late-in-the-day, time-to-go-to-work, is-some-event-pending all over your code! Miss one and things will either break or act wierd.

Here’s a basic outline that covers all these scenarios:

#
# Main gameplay loop.
#
default gameOver = False        # Game over flag, set by endings
default energy = 10             # Player energy
default events = Events([])     # Scheduled event class (eg. for dates)
default job = Job()             # Job class (for class/work times)
default newDay = False          # Flag to run start of day processing
default pcLoc = "home"          # Current player location

define  dbg = Character("Debug")

label main:
    while not gameOver:
        if newDay:
            call newDay                     # Set state for start of new day
            call wake                       # Have the PC wake up

        elif energy <= 0 or isTooLate():    # Check for end of this day
            if pcLoc != "home"              
                call returnHome             # Have the PC return home
            call sleep                      # Have the PC sleep (setting newDay True)

        elif events.hasEvent():             # Is there a scheduled event such as a date?
            $ renLabel = events.getLabel()
            if renpy.has_label(renLabel)
                call expression renLabel    # Run that event
            else:
                dbg "In main: event label [renLabel] does not exist."
                $ tick()

        elif job.isReady():                 # Is the PC supposed to be at work?
            $ renLabel = job.getLabel()
            if renpy.has_label(renLabel)
                call expression renLabel    # Have PC work
            else:
                dbg "In main: job label [renLabel] does not exist."
                $ tick()
        else:                               # Player has control
            $ renLabel = pcLoc + ".choice"
            if renpy.has_label(renLabel)
                call expression renLabel    # Give player a choice of what to do at their location
            else:
                dbg "In main: location [pcLoc] does not have a label [renLabel]"
                $ tick()
    return

Note: there’s absolutely no “story” in this loop. It’s all about the structure of the day and expressing the basic mechanism of the game. Every time one thing is done it returns to the loop so it can check what needs to happen next. That saves you coding all the checks at the end of each scene - you just return and have the loop take care of it.

I’ve used imaginary classes for Events and Job in this example for clarity. Whether you actually use classes is up to you, those could be complex functions, but this is where classes and OOP in general shine.

I agree, and that’s why I said “better suited” rather than “must”. Can you build this using jump alone? Yes, you can: both The Weighting Game and A Piece of Cake do exactly this. Does it create bugs/problems and hamper further development? Also yes.

We have the benefit of five and a half decades of academic research and practical experience of how to write structured code. Over-reliance on goto style programming (even RenPyTom admits in the source that he called his goto-like keyword “jump” to fudge this association) causes problems down the line with both bugs and maintainability. It even gets an XKCD strip.

Jump is great for emulating the structure of a CYOA book (ie. “turn to passage 89”) where the game state is primarily in where you are in the book. This is the kind of structure many RenPy games have. But since a life sim/sandbox is much more complex and has its game state in variables jump/goto is of very limited use and structured programming approaches should generally be preferred.

In Tramp there are currently 14 jumps (contrast with 1340 calls) where I was either lazy or it was an expedient fix (usually to maintain save-game compatibility between versions). I still plan to eliminate the lazy ones.

TLDR: Save future you headaches: avoid jump unless it’s absolutely necessary.

You absolutely can, it’s nothing to do with arriving by jump or call - this is the basic operation of the call stack. The caller provides the return “address” and return goes back to the statement after it (labels don’t define any scope of a return). However this kind of fall-through programming needs to be used with care and it is generally cleaner not to do this and have something (as you suggested) like:

label scene1:
    if goodVariant:
        call scene1aGood
    else:
        call scene1aBad
    call scene1b
    return

Which keeps the structure and narration apart - a good thing that simplifies both writing and testing.

1 Like