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

dropping in oop,

not gonna read the whole thread (unless… it’s relevant to do so, lmao)
but yeah! drop in here what you have, and tell what’s messing up or what you wanna add exactly

1 Like

this is the current script for the game. I’m trying to make a looping daily choice pattern, that adds choices/changes scenes as weight grows and variables add up, similar to weighting game or fill me up.

# The script of the game goes in this file.

# Declare characters used by this game. The color argument colorizes the
# name of the character.

define s = Character("Sophie")

#Sophie is a bottom-heavy blonde with secret feedee and lesbian preferences

define a = Character("Anna")

#Anna is a busty and fit girl with long crimson hair.

default sophiefatness = 0

init python:
    def tick(amount=1):
        global day, dayOfWeek, time, time_map, timestr
        time += amount
        if time > len(time_map) - 1:
            day += time // len(time_map)        # Advance day
            dayOfWeek += time // len(time_map)  # Advance day of week
            dayOfWeek %= len(daysOfWeek)        # Wrap day of week back to Monday
            # dayOfWeekStr = daysOfWeek[dayOfWeek] # update if you are using this
            time %= len(time_map)     
        timestr = time_map[time]

    def isWeekday():
        return dayOfWeek <= 4    # Mon thru Fri

    def isWeekend():
        return dayOfWeek >= 5    # Sat, Sun

default dayOfWeek = 6

define  daysOfWeek = [
        "Monday",       # 0
        "Tuesday",      # 1
        "Wednesday",    # 2
        "Thusday",      # 3
        "Friday",       # 4
        "Saturday",     # 5
        "Sunday",       # 6
    ]
            
default time = 3

define time_map = [
    "morning", #0
    "day", #1
    "noon", #2
    "afternoon", #3
    "evening", #4
    "night", #5
    "latenight" #6
]

# The game starts here.

label start:

s "My first day on-campus. You can do this, Sophie. It's just college, almost everybody has done it."

s "I've just gotta study hard, make some friends-"

s "-and avoid that freshman 15 my mom kept warning me about..."

"My cheeks start to turn red, warmth rushing to my face."

s "Why am I blushing over that? W-whatever..."

s "Made it to the apartment I found online. That ad really came in just when I needed it most!" 

s "A place this big would be a lot more expensive usually, but there's another roommate here to split the rent with!"

s "I wonder what my roommates will be like?"

s "Time to find out, I guess."

s "Hello? Anybody home?"

a "Yes? Who are you?"

s "Oh! I'm Sophie!"

"The girl stares at me blankly for a moment."

s "I'm uh... your new roommate!"

a "Ohh! You're the one who responded to my ad on Greg's List!"

a "You know, before you came along, I wasn't sure anyone was gonna respond to that ad."

a "I almost forgot about it and planned on posting ads locally instead."

s "W-well I'm glad you didn't! I uh, really needed this place."

"Now that the introductions were done, I took a moment to look at my new roommate for the first time."

"Anna was gorgeous. Seriously, she could be a model! Those beautiful crimson locks, those piercing green eyes, that flawless skin..."

"AND THOSE HUGE TITS! LOOK AT THE SIZE OF THOSE THINGS!"

a "Earth to Sophie? Hello?"

s "Huh? S-sorry I'm here! What did you say, again?"

a "I asked if it was okay to go over a few house rules with you. But if you're so distracted by something right now..."

s "N-no I'm fine! I promise!"

a "Then good. Alright, well, each day, feel free to do whatever you want around here."

a "Though I recommend eating meals and going to class, obviously."

a "If you ever want to hangout or anything like that, just let me know!" 

a "Though, I really like my beauty sleep, so I won't be down to hangout in the early morning or late at night."

a "Some things just aren't doable at different times, ya know?"

s "R-right, I guess."

a "With that tutorial, I mean explanation, out of the way, why don't we get something to eat?"

s "Huh?"

a "It's already noon! Surely you need to stop and get some lunch, you've been busy all-day moving!"

s "W-well, maybe..."

"Admittedly, I had already had a decent enough lunch just an hour or two before. I made a sandwich or two back at my parent's place."

"But I wasn't sure if I wanted to pass up an opportunity to get to know my new roommate on the first day, before classes even started!"

menu:
    a "So, yes or no on the lunch break?"
    "Sounds good to me!":
        jump firstbinge

    "I ate before coming, sorry. Maybe another day?":
        jump dayonedeny


label firstbinge:
a "Awesome! You're gonna love this!"
"Anna jumped from where she was standing and rushed off to the kitchen, immediately pulling things out of the fridge."
a "I may be a Psychology major, but my REAL passion is cooking!"
s "Oh wow!"
"My mouth began to water a bit as I smelled the distinct scent of cooking meat."
s "Whatcha makin'?"
a "Burgers! I had some frozen ones lying around, and I can make 'em extra special!"
s "Looking forward to it!"
"I waited for around 10-15 minutes while Anna cooked."
a "I hope you're hungry!"
"Anna reappeared with a platter of burger patties stacked high, about 7 of them."
s "This is... a lot of food, Anna!"
a "Sorry, I'm awful at cooking smaller portions of food..."
a "Anything we don't finish is leftovers, okay?"
s "Alright..."
"And so we got to talking a bit. I learned she was a psychology major and culinary arts minor."
"I learned her family owned this property, so she got to live here for basically free."
"I told her that I came here to study art and communications."
"She was a sophomore, I was a freshman."
"And so we kept talking and eating, splitting the tray of burgers..." 
"I say 'Splitting' but in reality she only ate two of them. The other five... my bad."
s "Getting... full... ugh."
a "Sorry about that, Sophie."
a "I maybe went overboard for today."
s "It's okay."
a "Just relax a bit and let that digest okay? Won't do you any good to get an upset tummy on the first day here."
s "Fair point..."
"Anna got up and cleaned the whole mess up, before giving me one last look and smiling."
a "I think you and I are gonna get along perfectly, Sophie."
"For some reason, this sent a chill up my spine."
$ sophiefatness += 1
jump daily_choice


label dayonedeny: 
a "Oh uh, okay! Fair enough."
a "Feel free to hit me up to hangout, okay?"
"I felt a little bad ditching her, but I'd already eaten lunch."
"Time to start my first day!"
s "Although, it's already after noon!"

s "It's Sunday, so I don't have to worry about classes, but..."

s "What should I do today?"

jump daily_choice

#Example menu#

label daily_choice:

menu dailychoices:
#Includes Mirror and Shower
    "Go to the bathroom":
        jump bathroom_choices
#Includes Breakfast, Lunch, and Dinner
    "Make something to eat":
        jump kitchen_choices
    "Go to class" if isWeekday() and (time == 0 or time == 1 or time == 2 or time == 3):
        jump class
    "Go to work" if isWeekday() and (time == 1 or time == 2 or time == 3):
        jump work
    "Hangout with Anna" if (time == 1 or time == 2 or time == 3 or time == 4 or time == 5):
        jump hangout
#Includes Trying-On Clothes and Changing Clothes
    "Go to my wardrobe":
        jump wardrobe_choices
#Includes Shopping, Beach, Gym, Running, or Restaurant 
    "Go outside" if (time == 0 or time == 1 or time == 2 or time == 3 or time == 4):
        jump outside_choices
#Late Night Options
    "Go out partying" if (time == 5 or time == 6):
        jump parties
    "Wait":
        jump wait
    "Go to bed" if (time == 5 or time == 6):
        jump sleep

label wait:

"You sit around, not doing much, for an hour or two."
$ tick(1)

jump daily_choice


label bathroom_choices:

s "I'm in the bathroom, now what?"

menu bathroomchoices:

    "Look in the mirror":
        jump mirror_choices

    "Take a shower":
        jump Shower

label mirror_choices:

s "Well, there I am."

s "Should I look at anything in particular?"

menu mirrorchoices:
    "Look at my face":
        jump mirrorface
    "Check out my body":
        jump mirrorbody
    "Examine my belly":
        jump mirrorbelly
    "Check out ass":
        jump mirrorass

label kitchen_choices:

s "I guess I am pretty hungry."

s "So I guess I'll make something to eat..."

menu kitchenchoices:

    "Make breakfast" if (time == 0 or time == 1):
        jump breakfast

    "Make lunch" if (time == 2 or time == 3):
        jump lunch

    "Make dinner" if (time == 4 or time == 5):
        jump dinner

    "Get a snack" if (time == 6):
        jump midnightsnack

label wardrobe_choices:

s "My wardrobe!"

s "What should I try-on?"

menu wardrobechoices:

    "School uniform":
        jump schoolgirl

    "Cheer uniform":
        jump oldcheer

    "Workout clothes":
        jump workoutclothes

    "Swimsuit":
        jump tryonswimsuit

    "Fancy dress":
        jump formaldress

    "Party fit":
        jump partyclothes

label outside_choices:

s "Okay, where to today?"

menu outsidechoices:

    "Restaurant":
        jump restaurant

    "Shopping":
        jump shoppingstart

    "The gym":
        jump gymstart

    "Go running":
        jump runningstart

    "Beach":
        jump beachstart

    "Go exploring":
        jump explorestart

return



Wanna make sure how each jump and call work is clear, because to me its an it depends on what your doing, you can totaly never use call and always use jump, why, read on.

Jump and Call both transfer control to the label. While jump only would return to the originating calling place if the jumped to label had a jump to return(reminder that labels don’t require a return statement), call by default always returns control to the originating calling place.

Neither require a return statement to work, while jump would only return control to the originating calling place if you did include a jump statement to return to the jumped from to label.

Call is useful to force return of control regardless of the labels design. While jump is useful for following the labels explicit intent(and is unforgiving of failing to explicitly define your intent).

An example such as two labels written one after the other, the second having a return the first does not, that are two “halves” of a scene. You couldn’t “call” the first and auto enter the second to play both halves of the scene auto-magicly, where with jump you can jump to the first knowing renpy will auto execute the second with the return by jumping back at the end allowing you to return control back. Why use this structure, maybe you have a alternate first half that jumps to the second half label which would then return via jump. Call does not allow for this structure without explicit calling of each label.

My point is, your choice on which to use is about where and when you want to explicitly define flow control.

Edited to clarify some poor word choice.

Haven’t quite had the time to test things on pc yet, but I do have some ideas that might make it a bit more smooth so you can put out your checks

Yet - at first glance - nothing seems to really fall short on functionality, as far as time goes at least
What are the things you are struggling with exactly?

Functionalities on the time system?
The if statements checking whether a choice should appear or not?

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.