Ren'Py What are some good ways to manage events, plot flags, weekday and time of day in a renpy game? (coding)

hakarlman

Engaged Member
Jul 30, 2017
2,075
3,214
Can someone share clever ways to manage plot flags, weekday & time of day so things don't get too confusing in renpy code? I'm trying to come up with a methodology for handling what sexual and non-sexual events happen at what time in the game, based on plot flags, time of day and the day of the week. Any advice would be appreciated.

A rough and dirty code example would help a lot or linking to threads that discuss this topic.
 

Epadder

Programmer
Game Developer
Oct 25, 2016
567
1,045
I haven't tried to wrap my head fully around this yet, but it might be a good fit for you to look over for a 'clever' way to handle that stuff.


In what I'm working on now I decided it wasn't really necessary to drill that deep. I'm using from the Ren'py forums that extends the Ren'py 'Character' class to basically hold variables and associate them to specific characters.

I then have a 'hub' label for a location where a character is (they don't move really) and test how along in their quest you are by looking at the quest variable associated with that specific character.
Specific time isn't really looked at on a day to day basis just a limited pool of actions per day, and the Day/Week/Year are just an array of integers.

And unlocking scenes after the main quest is done is through a function that examines a different variable associated with every character.

The Stats I'm tracking:
Python:
STAT_DEFAULTS = {
            'characterstat_UIDisplayedRace' : 'Human', #Species the girl belongs to for description purposes
            'characterstat_RaceUsedForFunctions' : 'human', #Race is used by functions
            'characterstat_CurrentRelationshipPoints' : 1, #Value to unlock scenes post quest completion
            'characterstat_CurrentQuestStage' : 0, #Quest Progress
            'characterstat_DateOfLastQuestAdvancement' : [0,0,0],
            'characterstat_IsStillActive' : True, #Is still available to become a matron
            'characterstat_NumberOfSexualEncountersAfterRecruited' : 0, #sexual encounters with the player
            'characterstat_NumberOfWeeksPregnant' : 0, # number of weeks pregnant
            'characterstat_NumberOfSexualEncountersNotImpregnated' : 0, # number of sexual encounters without impregnation used to counter balance lower fertility characters.
            'characterstat_LabelsOfSexScenesForMemories' : ['none'], # Labels of sex scenes associated with character
            'characterstat_AreTheyCurrentlyPregnant' : False, # Is the character pregnant
            'characterstat_NumberOfOffspring' : 0, # Number of children had
            'characterstat_ThresholdToImpregnate' : 50, # How easy the character is to impregnate
            'characterstat_HasBeenBorn' : False, #Status determining if character has been born.
            'characterstat_TypeOfCharacter' : "Matron", # Type of Character (Matron and Daughter main two types TBI, others MNI)
            'characterstat_AssociatedCharacters' : ['none'], #List of characters that share relationship gains with this character. (TBI)
            'characterstat_GaveBirthLastWeek' : False, #Just recently gave birth flag
            'characterstat_ListOfDaughterCharacters' : ['none'], #List of children born
            'characterstat_UIDisplayedCharacterImage' : '/characters/default.webp', # Image to show on character interaction screen
            'characterstat_OptionSexScenesEnabled' : True, # Enables sex scenes can be disabled by player if they just want to ignore those interactions for any reason
            'characterstat_RelationshipThresholdsToUnlockNewScenes' : [25,50,75,100,125,150,175,200], # standard relationship thresholds to unlock new scenes for characters
            'characterstat_LabelsOfScenesToUnlockBasedOnRelationship' : ['none'], #the labels for threshold scenes
            'characterstat_LabelsOfSexScenesAfterRecruited' : ['none'], # the labels for sex scenes after harem member
            'characterstat_PersonalityType' : "Laidback", # The Basic Personality determines available generic date types.
            'characterstat_WasIntroducedToPlayer' : False, #Flag for introduction sequence for daughter characters. (TBI)
            'characterstat_NicknameForYou' : "Dear", # The nickname the character will use for you (TBI)
            'characterstat_PlayerCallsHer' : "Honey", # The nickname the character is called by you (TBI)
            'characterstat_LabelsOfPregnantSexScenes' : ['none'], # Specific Labels to Call When Bedroom Option is called when character is currently pregnant (TBI)
            'characterstat_IsAResidentCharacter' : False, #Resident of a Town (MNI)
            'characterstat_GeneratedID' : "No One", #Generation System ID to determine which images to show (MNI)
            'characterstat_ListOfGeneratedDaughters' : ['none'], #Generation System randomly generated daughters (MNI)
            'characterstat_NameOfMother' : "Unknown", # (MNI)
            'characterstat_RPGLevel' : 1, # (MNI)
            'characterstat_RPGStrength' : 1, # (MNI)
            'characterstat_RPGVitality' : 1, # (MNI)
            'characterstat_RPGCharisma' : 1, # (MNI)
            'characterstat_RPGHealthPool' : 100, # (MNI)
            'characterstat_RPGTotalExperience' : 0, # (MNI)
            'characterstat_RPGCurrentExperience' : 0, # (MNI)
            'characterstat_RPGExperienceNeedToLevel' : 1000, # (MNI)

        }
Creating a character:
Python:
define character.aura = Character("Aura", what_color="#F5F6CE", who_color="#F3F781")
default aura = CharacterStats("aura", characterstat_RaceUsedForFunctions='human', characterstat_UIDisplayedRace='Human', characterstat_PersonalityType='Laidback', characterstat_UIDisplayedCharacterImage='/characters/Aura.webp',  characterstat_LabelsOfSexScenesForMemories=['none'],characterstat_DateOfLastQuestAdvancement=[0,0,0], characterstat_CurrentQuestStage=0, characterstat_RelationshipThresholdsToUnlockNewScenes=[10000000], characterstat_LabelsOfScenesToUnlockBasedOnRelationship=['none'], characterstat_CurrentRelationshipPoints=10, characterstat_ThresholdToImpregnate=150, characterstat_HasBeenBorn=True, characterstat_GaveBirthLastWeek=False, characterstat_ListOfDaughterCharacters=['none'], characterstat_OptionSexScenesEnabled=True, characterstat_TypeOfCharacter='Matron', characterstat_LabelsOfSexScenesAfterRecruited=['hscene_Aura_Bedroom_01'], characterstat_NumberOfOffspring=0, characterstat_IsStillActive=True, characterstat_AreTheyCurrentlyPregnant=False, characterstat_WasIntroducedToPlayer=True, characterstat_NumberOfWeeksPregnant=0, characterstat_NumberOfSexualEncountersAfterRecruited=0, characterstat_AssociatedCharacters=['none'], characterstat_ListOfGeneratedDaughters=['none'])
A 'hub' label:
Python:
label locationlabel_Desert_NutAndNephthysBuriedPyramid:
    $ quick_menu = False
    $ locationsystem_DiscoveredCharacterNutAndNephthys = True
    if nut.characterstat_DateOfLastQuestAdvancement[2] + 2 < worldstat_CurrentDate[2] or nut.characterstat_DateOfLastQuestAdvancement[1] < worldstat_CurrentDate[1]:
        if nut.characterstat_CurrentQuestStage == 0:
            jump questlabel_NutAndNephthys_01_NutAsksAboutSah
        elif nut.characterstat_CurrentQuestStage == 1:
            jump questlabel_NutAndNephthys_02_AngryNut
        elif nut.characterstat_CurrentQuestStage == 2:
            jump questlabel_NutAndNephthys_03_Tripel
        elif nut.characterstat_CurrentQuestStage == 3:
            jump hscene_NutAndNephthys_01
        elif nut.characterstat_CurrentQuestStage == 4:
            jump questlabel_NutAndNephthys_04_DarkHistoryRevealed
        elif nut.characterstat_CurrentQuestStage == 5:
            if basesystem_HumanHomeAvailableSpace >= 2:
                jump questlabel_NutAndNephthys_06_InviteHome
            else:
                nar "If you intend to ask Nut and Nephthys to join you back at camp then you better make sure there is space for them."
        elif nut.characterstat_CurrentQuestStage == 10000 or nut.characterstat_CurrentQuestStage == 5000 or nut.characterstat_CurrentQuestStage == 5001:
            if nut.characterstat_CurrentQuestStage == 10000:
                nar "###REDACTED###"
            elif nut.characterstat_CurrentQuestStage == 5000:
                nar "###REDACTED###"
            elif nut.characterstat_CurrentQuestStage == 5001:
                nar "###REDACTED###"

            if rpgstat_PlayerEquippedWeapon != "Gaia's Blade":
                jump itemlabel_CaliburnAndGreen
    else:
        call systemlabel_WaitACoupleDays from _call_systemlabel_WaitACoupleDays_2
    jump locationlabel_Desert_Hub
Scene unlocking:
Python:
def relationshipfunction_CallDateInteraction(char):
        targetLabel = internalfunction_CopyJumpPoint(char)
        targetThreshold = internalfuction_CopyThresholdValue(char)
        sceneListLength = len(char.characterstat_LabelsOfScenesToUnlockBasedOnRelationship)
        thresholdLength = len(char.characterstat_RelationshipThresholdsToUnlockNewScenes)
        if thresholdLength > 1 and sceneListLength > 1:
            char.characterstat_RelationshipThresholdsToUnlockNewScenes.remove(targetThreshold)
            char.characterstat_LabelsOfScenesToUnlockBasedOnRelationship.remove(targetLabel)
            renpy.jump("%s" % (targetLabel))
        elif sceneListLength > 1:
            char.characterstat_LabelsOfScenesToUnlockBasedOnRelationship.remove(targetLabel)
            renpy.jump("%s" % (targetLabel))
        elif sceneListLength == 1:
            if targetLabel == "hscene_Generated_3P":
                if char.characterstat_AssociatedCharacters[0][1].characterstat_CurrentRelationshipPoints > 174:
                    char.characterstat_RelationshipThresholdsToUnlockNewScenes[0] = 10000000
                    renpy.jump("%s" % (targetLabel))
                else:
                    uifunction_CustomNotification("Improve relationship with her sister %s, to unlock next scene" % (char.characterstat_AssociatedCharacters[0][0].name))
                    renpy.jump("screenlabel_GenericCharacterInteractionScreen")

            else:
                char.characterstat_RelationshipThresholdsToUnlockNewScenes[0] = 10000000
                renpy.jump("%s" % (targetLabel))
        else:
            #print "Error, falling back to generic date."
            renpy.jump("relationshiplabel_DateSelectionHub")

    def internalfunction_CopyJumpPoint(char):
        jumppoint = char.characterstat_LabelsOfScenesToUnlockBasedOnRelationship[0]
        return jumppoint

    def internalfuction_CopyThresholdValue(char):
        thresholdvalue = char.characterstat_RelationshipThresholdsToUnlockNewScenes[0]
        return thresholdvalue
 

Rich

Old Fart
Modder
Respected User
Donor
Game Developer
Jun 25, 2017
2,439
6,847
Can someone share clever ways to manage plot flags, weekday & time of day so things don't get too confusing in renpy code?
It really doesn't require a ton of cleverness. More, you should develop some conventions and stick to them.
  1. Remember that all variables in Renpy are "global," so you'll want to have some kind of naming convention so that you don't accidentally re-use the same variable name for two different things. I have a tendency to break my scripts up into smaller files. For any variable that's introduced in that part of the script, I "default" the variable at the top of the file, and prefix the variable name with the file name so I know where to look for it.
  2. "default" is your friend, since it guarantees that a variable will have a value, even if you introduce the variable in release 3, but someone loads a save from release 2.
  3. Create flags to remember choices in each episode unless you're really sure the choice won't have impact in subsequent episodes. It's a pain later on (but there is a workaround) if you forgot to "remember" a choice.
  4. Suppose you have an A/B choice in episode 2 that you forgot to set a "which choice did they make" flag for, and now you discover you need that info in Episode 3. What you can do is:
    • Go ahead and create the variable for it, but default it to "None" instead of "True" or "False".
    • Update the code from Episode 2 to set it to True or False accordingly.
    • Now, when you get to the "where it matters" point, there are three possible values for the variable:
      • True, meaning they played through the updated chioce
      • False, meaning they played through the updated choice and selected the other option
      • None, meaning they're playing with a game saved at a point after the updated choice. Knowing this, you can design a work-around in your code.
Other than that, there really isn't a ton to it. Have a default value for the variable, set it appropriately when you get to "that point" in the code, and then have "if" tests when you get to where it matters.
 

Rich

Old Fart
Modder
Respected User
Donor
Game Developer
Jun 25, 2017
2,439
6,847
I neglected to ask whether your code is a 'sandbox' type game (i.e. player can wander freely) or whether it's more of a VN approach. Most of what I said above was targetted as the VN approach.
 
  • Like
Reactions: hakarlman

hakarlman

Engaged Member
Jul 30, 2017
2,075
3,214
I neglected to ask whether your code is a 'sandbox' type game (i.e. player can wander freely) or whether it's more of a VN approach. Most of what I said above was targetted as the VN approach.
Funny you mentioned this, I was thinking about it. I'm making a sandbox game.
 

Rich

Old Fart
Modder
Respected User
Donor
Game Developer
Jun 25, 2017
2,439
6,847
With sandbox games, you're somewhat less likely to have "this path vs that path" choices. Instead, you tend to have a series of (possibly interlocking) quests, each of which have a state. The state can usually be represented by an integer, indicating how far through the quest's sequence you are. Day and time of day are typically also simple integers. It may make sense to have some Python functions in various places. For example, an "advance_time" function that handles things like rolling over the day when you get to midnight, or enforcing that when you get to 2:00am, the character has to sleep. But otherwise, the core "recordkeeping concepts" aren't necessarily that different.
 
  • Like
Reactions: hakarlman