Basic events/quests based system

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
ISAWHIM
Veteran
Posts: 318
Joined: Sun Nov 06, 2016 5:34 pm
Contact:

Basic events/quests based system

#1 Post by ISAWHIM »

I use a similar version of this code, to create various interactions.

The system uses a form of "event creator", to make the various events available, for interactions. These "events" can be manifested as "menu selections", or as some form of "clickable navigation POI", or just display as "extra dialogue", within some string of dialogue.

The concept is as follows...

1: Create the code for a set of "events", which can be added to an "event list", of "available events".

2: Something triggers the "creation" of an "event". In the sample, I create the initial events, before entering the game_loop.

3: With the events added to a list, they can be read and interacted with. In this case, I use the MENU system to show the available events.

4: "Calling" the event, will make the code run, and return once it is finished. (Do NOT "jump" to an event, or it will just end the game, or "return" to some WRONG location.)

You can replace your "script.rpy" text with this, and it should work fine. (Except the NOTE, mentioned at the bottom.)

Code: Select all

default QuestList = {}
default SleepCount = 0

label start:
    "First, we need a quest."
    call quest_add("001", "Search the woods.")
    "The quest, \"Search the woods\", was added."
    "Now we have a quest to do."
    "As a bonus, I am adding another."
    call quest_add("004", "Go to sleep.")
    "The quest, \"Go to sleep\", was added."
    "Once you sleep 4x, the sleep quest will self-terminate."
    "The chained events, ralated to the woods, will re-trigger the sleep event, if it does not exist still. However, it will not reset the sleep-count."
    "Now we will enter the MAIN game-loop."

##############################
## Game-Loop for the VN/Game
##############################

label game_loop:
    python:
        if len(QuestList) > 0:
            # This is just building the "list" of menu-choices, from the QuestList
            z = "["
            for Qkey in QuestList:
                z += "(\"" + QuestList.get(Qkey) + "\",\"" + Qkey + "\"),"
            z += ","
            z = z.replace(",,","]")
            # Narrator is the normal "None" person talking
            narrator("Here is your current list of quests.", interact=False)
            z = renpy.display_menu(eval(z))
            renpy.call("quest_" + z)
        else:
            renpy.jump("end_game")
    jump game_loop

##############################
## Game ending
##############################

# You want the "game_loop" to direct here, when the game is over.
label end_game:
    "The end."
    return

##############################
## Functions for Quests/Events
##############################

label quest_add(Qkey,Qval):
    # "not in" will stop quests from being added, unless they are unique
    if Qkey not in QuestList.keys():
        $QuestList.update({Qkey:Qval})
    return

label quest_remove(Qkey):
    # This removes the quest, if it exists
    if Qkey in QuestList.keys():
        $del QuestList[Qkey]
    return

##############################
## Dialogue for Quests/Events
##############################

label quest_001:
    call quest_remove("001")
    "You search the woods."
    call quest_add("002", "Search the fountain.")
    "Look, a fountain just appeared!"
    return

label quest_002:
    call quest_remove("002")
    "You peer into the fountain and find a tiny bottle with a scroll."
    call quest_add("003", "Read the scroll.")
    "Suddenly, the fountain disappears, as you grab the bottle!"
    return

label quest_003:
    call quest_remove("003")
    "You read the scroll."
    "Suddenly, the scroll and the bottle disappear."
    call quest_add("004", "Get some sleep.")
    "Maybe you should get some sleep, nothing else seems to happen."
    return

label quest_004:
    $SleepCount += 1
    "Zzz...{p}Zzz...{p}Zzz..."
    # This checks to see if you have seen the quest_003, in this "repeating quest".
    if renpy.seen_label("quest_003"):
        "You wake-up with a MAGIC headache!"
        # Revert the scroll quest to unseen, so you are not constantly waking with a MAGIC headache.
        $renpy.mark_label_unseen("quest_003")
        # You could now, if desired, add the "search the woods" quest back again.
    else:
        "Time to wake!"

    if SleepCount >= 4:
        $SleepCount = 0
        call quest_remove("004")
    return
NOTE: I have included how to "self-terminate", which is used in the chain of events for the "fountain". I have also included a simple "incremented event", which repeats X-times, before self-terminating. I did not include an event that terminates other events. (This would be an event which might supercede other older events, like a hard-progression. It could also be something simple like pouring a glass of milk, juice or soda. Selecting one event would naturally remove the others that are related.)

NOTE: This code should be 100% "rollback safe". Python code was only used to create the menu-display. Though, in the sample, the "history" may be a bit odd to "read". History does not include the "selection text" from a menu but the "game-loop text" (narrator text), shows from a menu. There is a way to force that, in code, unseen, if desired.

To show the choice-text, alter "screens.rpy" file to look like this...
SOURCE: viewtopic.php?t=40329

Code: Select all

## Choice screen ###############################################################
screen choice(items):
    style_prefix "choice"
    vbox:
        for i in items:
            textbutton i.caption action [i.action, Function(narrator.add_history, kind="adv",who=__("Choice:"),what=__(i.caption))]
I have a more advanced form of this setup, which uses the actual "event", as a function. Called in various ways, it "acts" in various ways. It can set itself up, and other things. It can "run", or just "return info about itself", and it can even determine if it is available or not, for display. Though, that is a more complex system. It is based off this system, using mostly RenPy's internal code and functions, with little assistance needed from "python specific code". (Things get "funny", when using python code and functions, and then trying to "roll-back" and "continue", safely.)

These events/quests can be bound to specific "people", "places", "times", "other interactions"... etc. They do NOT have to be used bluntly like this. Such that, in my example, there could have been a list of quests for "the woods", and another for "home", and another for "Bob_home". The numbers for quests are really irrelevant and may not be friendly to remember, but it does allow them to be serialized and ordered, by creation. It doesn't take much effort to scroll through them, or search for them, when needed. Often, there will rarely be times that older events will address future events, but a simple debug tracking can easily help you locate any odd events you need. Adding comments, or unseen values to the events/quests, can also help.

Since these are serialized, by number... You can split these into groups, then again into individual files easily too. (So you don't have to scroll pages and pages of "events/quests".) RenPy will give you a nice warning if you have any duplicates listed.

Post Reply

Who is online

Users browsing this forum: Google [Bot]