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

That’s great news!

It will happen when you call tick enough to roll over to the next day. See this post for some detail. If you don’t have a nextDay() function, this line will advance to the first period of the next day:

$ tick(len(time_map) - time)

“Semi-random” is the key phrase here. There are three basic techniques: roll-of-the-dice, draw-from-a-deck, and least-recently-used.

Roll-of-the-dice is the simplest.

  • Advantages: If you just want random flavour, and aren’t concerned about getting the same option several time in a row, it’s great.
  • Disadvantages: the number of options is fixed (or very hard to change) so it doesn’t work well if new options become available, or old ones disappear. If an option is critical to advancing the plot it may never be chosen.
$ rnd = renpy.random.randint(1,3)
if rnd == 1:
    "Treadmill"
elif rnd == 2:
    "Weights"
else:
    "Stepper"

Draw-from-a-deck is like drawing cards from a shuffled deck.

  • Advantages: guarantees all options will be seen before any is repeated, can handle new options and disappearing options. (actually used in electronic slot machines to guarantee odds)
  • Disadvantages: New options not (easily) added until deck is empty. Possibly too predictable?
default gymDeck = []

label gym:
    if len(gymDeck) == 0:
        # Add options in when the deck is empty.
        $ gymDeck.update([1, 2, 3])        # Add options 1, 2, and 3
        if anneMet:
            $ gymDeck.append(4)            # If anne has been met add her as option 4
    # Pick one card from the deck and discard it
    $ rnd = renpy.random.choice(gymDeck)  
    $ gymDeck.remove(rnd)
    if rnd == 1:
        "Treadmill"
    elif rnd == 2:
        "Weights"
    elif rnd == 3:
        "Stepper"
    else: # rnd == 4
        "Anne"

Least-recently-used

  • Advantages: guarantees an option isn’t repeated twice in a row, can handle new options and disappearing options
  • Disadvantages: a critical option that advances the plot may still not get chosen, as picks are random, but still more likely than roll-of-the-dice
default lastGym = None

label gym:
    $ options = [1, 2, 3]
    if anneMet:
        $ options.append(4)                # If anne has been met add her as option 4
    if lastGym != None:
        $ options.remove(lastGym)          # Remove the one picked last time
    $ rnd = renpy.random.choice(options)   # Pick one
    $ lastGym = rnd                        # Remember it for next time
    if rnd == 1:
        "Treadmill"
    elif rnd == 2:
        "Weights"
    elif rnd == 3:
        "Stepper"
    else: # rnd == 4
        "Anne"

The draw-from-a-deck and least-recently-used don’t have to work with numbers. You can add labels instead and use jump expression or call expression. For example (using jumps):

default gymDeck = []

label gym:
    if len(gymDeck) == 0:
        # Add options in when the deck is empty.
        $ gymDeck.update(["gymTreadmill", "gymWeights", "gymStepper"])        # Add options
        if anneMet:
            $ gymDeck.append("gymAnne")            # If anne has been met add her as an option
    # Pick one card from the deck and discard it
    $ rnd = renpy.random.choice(gymDeck)  
    $ gymDeck.remove(rnd)
    jump expression rnd                            # Jump to picked label

label gymAnne:
    "Anne"
    jump main

label gymStepper:
    "Stepper"
    jump main

label gymTreadmill:
    "Treadmill"
    jump main

label gymWeights:
    "Weights"
    jump main

In Tramp I use least-recently-used more often than not (with some draw-from-a-deck), and have classes to make these operations simpler. Tramp’s least-recently-used can give options different weights and priorities and eliminate one or more recently used options.

Note: On the whole I’d recommend using call/return over jump in a life-sim. See Some notes about RenPy.

So for random events with my game Bob I took a slightly different approach to, managing and deciding which event to fire. I set out with two goals in mind, one is that the system must be modular such that i could put events in a file and import them into the game(when the game is finished i want it to be mod-able), and two that i don’t have to write out giant if then trees for figuring out which event to fire out of 5-50 events, and this would also allow for added events to be be able to be added to the decision matrix.

The solutions i came up with is to store the events in lists with a list of properties that are used to determine if an event should fire. The limitations of this are that all events must cohere to the predefined properties used to decide which event to fire. So for the properties i have things like how rare is the event, a tuple for weight range the pc needs to be for the event to fire, a list for times it needs to be for the event to fire…

At first i started with events being contained in a label. Storing the label name as a string in the list. Then using the renpy python equivalent to “Call Label” renpy.call(*label*,**args*,*from_current=False*,***kwargs* ) in my event firing function. Later the system was switched to events being contained in python functions(not recommended unless you want to write your events in python using renpy python equivalents and not renpy script, i had other good reasons to, mainly mod-ability). They were stored in the list and directly called from the list. The detail to know for this to work is the function has to be defined before you can store it in the list. To make sure this happens i used init offset = x (x is a number higher is later) in the script to make sure the python block defining the functions initiated before the list was defined so each area of my script has different init offset = x to make sure all pieces of my game initiated in the correct order.

This then allowed me to start categorizing events into lists, and having the game on the fly pull from all relevant categories to compile a list of possible events to fire.

I can go into more specific detail with examples if you want to attempt this but it is a more advanced event implantation.

Simple fix that may solve your issue is In gui.rpy change the value of gui.choice_spacing to be smaller. the number represents the number of pixels between the choices of the menu, I generally don’t go less than 5-10 on spacing for my custom screens. I think it defaults to 33 for 1080p so you may get a few more items in just by changing this.

If your going for full on custom menus go find the choice screen in screens.rpy and change it to your hearts content. One of the simper ways to get more buttons is to change to a vpgrid from a vbox that way you can have multiple columns or rows depending on what you set.

For example

screen choice(items):

    vpgrid:
        xalign 0.5
        ypos 405
        yanchor 0.5

        spacing gui.choice_spacing
        cols 2
        for i in items:
            fixed:
                xsize 600
                ysize 35
                style_prefix "choice"
                textbutton i.caption align(.5,.5) action i.action

Results in:

Hey gang, sorry for the radio silence! I got a concussion after getting punched in the face by a random drunk dude, and then college kicked my ass with classes. I’ll do everything I can to try and read through the recent messages here and try to set up the next day system and random event integration.

Hey gang, again. I got a bad sickness and went silent, but I’m still trying my best here. Might make an official post for “My Roommate is a Feeder” to get more help and input. I think I know what I need and want to do, it just sounds tedious and miserable, exponentially increased by the number of choices lol so I may cut some.

I also need to figure out how to work weight stages and plot progression.

It’s time to think about the basic structure of your game. There are roughly three types:

Branching: Every choice creates a new branch. This is much like the early CYOA books where you’d go to a different passage based on the choices made. All the state of the game is held in which branch/passage you are on. This only really works for very short stories or ones with lots of early endings (such as a horror type game) as the number of passages grows exponentially. Easy to implement just jumping between branches/passages.

Split and Join: Choices branch the story, but most then lead back to a single point, then start branching again before re-joining. Some of the state is held in which branch is running, but some is also held in variables which affect which branches are offered later in the plot. More advanced CYOA books had this structure (requiring the player to track things like inventory and health on a separate piece of paper - these are the variables, as well as in their memory “If you defeated the ogre turn to passage 96” also variables). The form is closer to, for example, a three act play or a traditional story structure. By joining paths you control the exponential growth.

Sandbox/LifeSim: All the state is held in variables and choices alter those variables. Variables in turn are used to determine what choices to offer. Games that follow a wake-up, do-stuff, sleep pattern typically follow this structure and have a main loop at the top level of the game, and require a different approach to the other two. Since the path of the story isn’t obvious from the structure of the code you’ll need notes about how the paths with each NPC works and what each variable is used for. Tends to also need lots of variety in scenes to stop it becoming repeatative. Much better suited to call/return than a jump based structure.

Plot is really down to you as the author, but it doesn’t hurt to be open to feedback once you’ve a prototype.

As for weight stages, the simplest thing is to have a defined list of the highest weight for each stage, then a function to look up the character stage based on their weight. It may be simpler to keep their base weight (unchanging) and gain (never negative) as separate quantities. Put the stages at plot-significant places (eg. weighing more than an NPC, or significant milestone).

For example (keeping gain separate from base mass):

    # Upper weight gain for each size.
    #
define sizeLims = [
     10,    # Stage 0
     20,    # Stage 1
     30,    # Stage 2
     40,    # Stage 3
     50,    # Stage 4
     60,    # Stage 5
     70,    # Stage 6
]

init python:
        # Get the size stage for a given gain.
        # Return None if gain is beyond known size limits.
        #
    def stageForGain(gain):
        result = 0
        for limGain in sizeLims:
            if gain < limGain:
                return result
            else:
                result += 1
        # Outside of known sizes.
        return None

If you are going to have art, then the more stages you have the more art you’ll need.

Also think about whether the core mechanics are going to be in metric or customary units (obviously you can present them in either - and players will want a choice). Programmatically metric is much simpler if you’re going to be dealing with calories, reporting BMI, or calculating BMR values. Customary is simpler if the plot points are based on customary unit boundaries (eg. going past 600lbs).

3 Likes

So currently, the project I’ve been using in this thread, I’d like to make the life sim type. It has a plot but is mostly dictated by players free choices.

Its like, heavily inspired by weighting game and fill me up ya know?

But I’ve just been struggling to make it lol. I can post the current code here? Its just the intro Dialogue and then the first choices, that nearly all end in broken paths, and I was too sick over the weekend to put the next day stuff in.

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