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

I think the only thing I’d add to dingotush’s comments on structure is that dating sim games sometimes start with a more sandbox-y approach in the early game, but transition to fewer options later once a set ‘branch’ has been triggered; especially common if there’s an implied monogamy preventing one from pursuing multiple love interests at once - either intentionally or unintentionally. It can therefore be useful to have variables stored in such a way as to be equally valid and accessible regardless of what point in the game you’re at.

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

Don’t think this paper makes a valid point in the case of renpy script, the authors main point in this dated 1960s paper is about variable contextual safety, which renpy script has already handily thrown out the proverbial window in multiple ways. Call provides zero safety in the way that is mentioned, it adds zero safety besides the return at the end over jump. To satisfiy the author of this paper you would have to totaly forgo labels and use a true python function, to get back the contextual saftey that makes him argue aginst goto, or jump.

The only meaningful difference is in the ensured return to a certain point in the script(notice script NOT code), regardless of how you have bungled the variables, along the way. And none of this takes into account rollback and the special hell the author of that paper would put it in for violating contextual safety.

So, back to my point, which is it is only a matter of flow control and readability. Renpy script is not python, nor a programming language, if you want these things (contextual safety) use python in a code block with functions.

Return combined with jump isn’t update save game stable like call and return are because of the default launcher option to explicitly generate call return information.

At least I that’s what I read, but I might’ve misunderstood it?

I think you may be confused, only call has this problem not jump, why is in how what call can add that jump is not adding. What the problem is here is when you want to return a value or object from one renpy script callable into another renpy script call stack statement you need the original context to the call if you are to load from a save that was saved inside the called callable. Jump does not allow you to return something into a renpy script statement. The jumped to callable can only return by jumping back. Jump is relying on the writer to handle at the end of the label all architecting of what context to go to next weather that is to return or do something else. For call think of it like writing down for the save what you were doing when you called the callable you saved inside. Remember labels are not functions merely labeled blocks of script, and in renpy script context is merely what block of script am i’m currently operating on. Call because it is returning into a statement has to have some definition of context to be able to do so.

My point in the previous post is all labels don’t have separate context/scope like in python functions and variables are freely shared between them, the type of context/scope were referring to above is not about variables but rather about where we should be in the script, and if using call, how many calls deep are you and from where. To return when jumping you have to jump back.

Again back to my original post on this topic, one is implicit and the other is explicit, it would be bad to mix them however neither approach is necessarily wrong, and is rather a matter of approach and your goals. Call implicitly makes sure at the end of a block you end up where you started, jump does not.

Is c++ inherently bad compared to python becues it makes you explicitly define type for variables vs python implicitly trying to figure it out on the fly? My point originally was to say both has their pros and cons.

1 Like

Are 18 year old high school seniors against the rules? (Just to clarify for me.)

So 18 year olds aren’t, but the cohort they are in would likely include 17 year olds (which would be against the rules - or more specifically a cautious interpretation of the current law in the USA where WG is incorporated and hosted). At the start of the school year most in the cohort would be 17 (illegal), by the end of the year most (but possibly not all?) would be 18. Also, the school as a whole wouldn’t only have seniors in it. Long story short any fetish game that includes playing through senior year is best avoided and would be removed.

But I’m from .uk where we’re legal at 16 (junior/11th grade) - so I’m going on what I’ve been told. I’ve no idea if it is an offence to download or play high school games if you are in the USA - but we will try to avoid hosting or discussing them.

1 Like

I wasn’t presenting it as a solution or even a complete analysis; it’s just the important paper that sparked the debate that would lead to the elimination of goto/jump in popular programming languages such as Python and Java.

RenPy has goto/jump because it is designed to emulate the way CYOAs work. It’s a non-issue if you are implementing a story that has the structure of a directed acyclic graph. However, it isn’t necessarily the best choice for control transfer, and any sandbox/life sim is explicitly cyclic in nature - which makes where you are in the story a mute point. Like a TTRPG the player is in control and creating their story, not the developer/author. Sure, there are plot hooks and events along the way, but the player is not obliged to follow them. This is a key difference that invites a different approach.

Asking a novice programmer to create a complex program like a life-sim using only jump will likely (in my experience always) lead to bugs/errors. It takes discipline to use jump alone well and not tie the code into unmanageable knots. The prime benefit of not using goto/jump is having the developer have to understand the structure of what they are doing.

RenPy script is compiled into Python (and chains of Python objects) before it runs making it a compiled programming language (and why it will sometimes either moan or just dump a traceback file rather than fail during runtime). Python is then interpreted making it a scripting language. All scripting languages are kinds of programming language.

There are so many ways to break save-game compatibility! For the label specific ones any time you rename or remove a label and the save game was made somewhere beyond that label and before the next one there’s the possibility that RenPy can’t rollback far enough to find a point it can still identify (roughly speaking it stores a label name and the number of lines after that). It’s not really dependant on if you use jump or call.

RenPy’s call x from y looks like a PITA, but does have the advantage of introducing twice as many labels (ie x and y) rather than jump which would only require x. Call based programming tends to have the minor advantages of:

  • creating smaller blocks of script without labels
  • requires a little forethought so less likely to ever have to delete/rename a label
  • the call stack itself provides additional fall backs to useable states

FYI for call/return you can optionally create a new scope using renpy.dynamic. It’s not ideal as you have to name the variables used in the new scope (the others remaining enclosing/global - so the opposite of Python), and they don’t entirely disappear at the end of the scope (instead being marked as deleted from the renpy.store).

1 Like

I’m kinda just unsure about making days pass, randomness of events, and how to make stages and specific events upon getting fatter

Your saying because an oval (programming language) can be constrained into a circle (scripting language). Circles are therefore Ovals. This logic is flawed! We would not have defined circles differently and call circles circles, which are not a type of oval, if circles were ovals. The limitations of a scripting language are a defining characteristic of scripting languages. A program interpreting a script and converting it into its own language then compiling it, does not make it any less script. Even after translation and compiling it would still reflect all the limitations of the scripting language to start with.

Yes they are all languages that can be translated between, but the defining characteristics of the languages is what determines what can be expressed in them, and therefore weather we call them a scripting language or a programming language. To say there all one is misrepresenting some/all of them, which only leads to confusion about eachs properties and characteristics. Which is why renpy script, is not python, nor can be treated as such. It is based on/similar to so we can draw parallels and inference, but to say one is the other is not a good idea.

This doesn’t apply, what determines where you go in the program is not call or jump(the vehicals) but rather the preceding logic(the driver), and while call has a predetermined context return point(auto pilot back to where it started), jump does not, but this characteristic of jump in no way means it cannot be use in a cyclic nature or mean you cannot create the similar flow patterns. Under the assumption the creator of the game has the same good understanding of how each works, there is no reason the preceding logic determining where the story goes that the player then gives the inputs to that result in the individualized out come cannot use jump instead of call to get to that part of the script(story). Jump only adds the flexibility to go anywhere at the end(up to the writer to again write out that preceding logic) rather than only back to where you were. Will the overall architecture of the game be slightly different for each? Yes, because each can do something the other can’t. However they both at their base switch context of where you are in the script. The preceding logic written determine if is cyclical or not not call or jump.

I am an idiot and did not understand anything the previous poster said lol.

She’s a college freshman, but I believe technically a highschooler would be allowed if they were 18 yeah.

  1. The school uniform is merely anime fluff for those who like the aesthetic, the planned main character is a college student and of-age, the uniform is just there to try on for weebs and to lament being fatter since graduating. No underage characters will make any appearances.

  2. I desperately wanted to learn to make call/return formula lol. I have struggled HARD to figure out loops, so if you can explain loops basic structure to me I will be indebted. That’s been my failed goal so far.

  3. Admittedly, I dislike the kidnap and pre-arranged dates in other life sim games, no offense to the devs that do those. I want MOST things to be player choice, with story events cropping up primarily through time passing and weight gain stages.

  4. Thank you, I WILL be stealing this code.

  5. I am quite dumb, so I understood about 50% of the code and 50% of what you said after it, so I will probably ask you for help multiple times. Also, apologies for not directly responding to you until now about this post - this thread has grown wildly large very quickly, which I appreciate! Its nice to get so much help and nice to have a big thread for people trying to learn renpy!

So, I have a custom map screen I was planning to use to let the player decide what to do, rather than a typically ren’py script menu.

I cannot think of any good way to use call and return though with this. I can have a place in the script with some sort of infinite loop that does nothing. Then the map buttons can jump to location based sections of the script, then jump back to some sort of time advance and garbage collection script section then jump to the infinite loop style holding pattern again to wait for the player to browse their custom menus…

But is there a way to do what I’ve just described using call and return instead?

It may take some reorganization of your script as to whats in each label however all of your labels for the most part would need to be designed to be used with call(mixing call and jump is where you get into trouble, while it is safe to call out of a jumped to label its not safe to jump out of a called label). You would then use the call screen action to call one of your main actions labels. I would also add you can have multiple screen actions for a screen element, by putting them in a list, so you can hide your map and call the action with one button and click

label infinitloop:
     while True:
          show screen screenmap #some one clicking on it would either call action1 or action2

label action1:
     #doing stuff
     call subaction2
     #doing morediffernt things
     call subaction3
     #finsihing up
     call cleanup
     call timeadvace
     return


label actoin2:
     #doing stuff
     call subaction
     #doing morediffernt things
     call subaction2
     #finsihing up
     call cleanup
     call timeadvace
     return

label subaction:
     #doing things not specific to an main action
     return

label subaction2:
     #doing things not specific to an main action
     return

label subaction3:
     #doing things not specific to an main action
     return

label cleanup:
     #doing things to cleanup
     return

label timeadvance:
     #doing things to advace the time
     return

I would end with unless you have a problem your trying to solve that needs call its not worth the effort to change how you context switch, unless you expect to need call.

The custom menus are already coded and the buttons trigger 3or4 actions because they have to do things like show the close menu button or swap between the map and stats screen, etc.

All the custom menus use no jump’s and just use calls. But I am considering making them use jumps instead of calls because it might make the base script simpler. Although your example seems to make sense, too.

The while True: Wouldn’t need anything inside it though as there’s a menu overlay custom screen with all the buttons to navigate to places like the map. The custom map also blocks the main script with modal True.

I cannot seem to upload gifs to the forum, but I can get embedded deviantart gifs to play.

This is the early prototype on how the menu would look/work, the map already has some prototypes for how mouse hover would highlight/etc. Not shown here because I was trying to get the gif to be a small enough size to forum embed.

I haven’t had time to reply in detail, but in short - yes you can.

Is this a separate question, or are you both working on the same game?

There’s a logical problem with giving the player access to a map/navigation screen at any point they choose from a menu bar. During an interaction with an NPC if it’s not playing out how they like they could pick the map and just GTF somewhere else, so to make sure that doesn’t happen a lot of the time you are going to have to have the map disabled/inactive. While you could work around this by having a global map enabled flag, it’s honestly going to be easier to make the map a callable screen when the script reaches the appropriate points.

There’s another, deeper, problem too. Displayed screens (such as your typical stat screen) run off the underlying event dispatch system and are quasi-asynchronous. Called screens (of which menu and say are types) run off the scripted steps, and effectively “block” until the event dispatch sends them an appropriate interaction making them appear synchronous. It’s complicated, but running story script off events can get things tied in knots.

My project is 100% unrelated to this thread and to muffintopmanor1’s game. I just thought of a question based on zdeerzzz’s comments.

There’s already a global flag for the map button I named freedom. The custom menus like the map are callable screens. It seemed like it would be okay to allow the player to view the stats screen during an event, but the map screen is blocked. Although rather than blocking the map screen, I could also just force shut the overlay. The buttons on the overlay only function when it is visible, if it is shut then clicking those regions doesn’t work.

I was thinking the game flow would be similar to thicker treat. But I actually didn’t copy the code from thicker treat I wrote it, quite possibly ineptly, myself.

Although come to think of it, thicker treat, wholehog, weightinggame, etc, all have a weird nebulous phase in the script where they wait for the player to use a custom interface to choose what to do. Rather than using the normal default menu. I guess I liked that feature. Tramps by comparison though always uses the renpy menu system, which I didn’t find as engaging. But I can see how it would prevent a myriad of potential bugs/pitfalls a custom screen can cause. I brought this up here I guess, because well, I’d like custom menu navigation. I’ve coded the custom menus, but I’m worried about those unforseen pitfalls in my future.

Really though, making the map something that the script calls, rather than being something the player has free access to would be fine, it wouldn’t really change much. At least it wouldn’t change the code for the map screen itself much, it would of course change the code for the script a lot.

Here’s an idea for a simple navigation system following on from the idea of having a main loop (as outlined above) for @Muffintopmanor1 and a global pcLoc variable:

#
# Navigation.
# E&OE.
#
    # Allow PC to choose a new location.
    #
label navigate:
    # Default to staying put.
    #
    $ newLoc = pcLoc

    # If self voicing use spoken navigation menu,
    # otherwise use navigation screen.
    # Both just change newLoc.
    #
    if _preferences.self_voicing:
        call .navMenu
    else
        call screen navScr

    # Did they pick somewhere new?
    #
    if newLoc != pcLoc:
        # Describe how they get from pcLoc to newLoc.
        # ...
        # Put the PC at the new location.
        $ pcLoc = newLoc
    return



    # Present a list of places to go.
    #
label .navMenu:
    menu:
        "{alt}Menu. {/alt}Where should I go?"
        "Beach" if pcLoc != 'beach':
            $ newLoc = 'beach'
        "Gym" if pcLoc != 'gym':
            $ newLoc = 'gym'
        "Home" if pcLoc != 'home':
            $ newLoc = 'home'
        "Pub" if pcLoc != 'pub':
            $ newLoc = 'pub'
        "Stay here":
            pass
    return



    # Ugly AF navigation screen.
    # Replace with pretty map with markers and mouseover hot spots and put
    # in its own rpy file.
    #
screen navScr:
    vbox:
        button:
            text "Beach"
            action SetVariable('newLoc', 'beach')
        button:
            text "Gym"
            action SetVariable('newLoc', 'gym')
        button:
            text "Home"
            action SetVariable('newLoc', 'home')
        button:
            text "Pub"
            action SetVariable('newLoc', 'pub')
            

It has both a traditional RenPy menu and a custom screen that do exactly the same thing. Obviously you get to make the custom screen as fancy as you like (I haven’t to keep the example short). If self-voicing is on then it uses the menu style as it is accessible, but otherwise uses the fancy screen.

And a way to trigger it from a very rough player home:

#
# The player characters's home.
# E&OE.
#
label home:

    # Allow the player to choose what to do next in their home.
    #
label .choice:
    menu:
        "What shall I do at home?"
        "Bathroom":
            pass # Shower?
        "Bedroom":
            pass # Sleep?
        "Kitchen":
            pass # Food?
        "Outside":
            call .leaveHome
    return


    # Leave home and go elsewhere.
    #
label .leaveHome:
    "You put on your shoes and head outside."
    call navigate
    return

By changing the pcLoc and returning to the main loop it will now call the new location’s .choice label - effectively moving the PC there.

From experience with other GUI systems where there’s multithreading I must admit I get cautious about relying on inactive buttons to prevent users interacting with things when event/update race conditions are possible. I’m pretty sure this can’t happen in RenPy (locking up the “working” thread always seems to kill the event handling - but I don’t know for sure) but I’d be double checking that freedom flag in the map screen itself.

Don’t get me wrong - I like those custom map-like menus too. Tramp hasn’t had any kind of “prettification” pass at all! Some things are in-place - like the custom choice screen for the menu (as in food menu, not renpy menu). I’ve some ideas around the navigation I’d like to try, but it’s all secondary to the story stuff at the moment. But the experimentation I have done has shown that non-called screens (ie. running off event-dispatch system) with side-effects are problematic. The docs aren’t messing about when they state Screens must not cause side effects that are visible from outside the screen. Wierd stuff can happen.