Wiki

Events System


Introduction

The Events System is responsible for managing all in-game events. This modular approach enables seamless integration of events into existing systems and facilitates the creation of new ones without the need for hardcoding. This design is particularly beneficial for event-driven game development, offering flexibility and scalability.

Modules

EventQueue

The EventQueue handles the queue’s creation and manages the event objects waiting in it. It handles tasks like event initialization, filtering based on requirements, wait time, and priority. Additionally, it can group events in a list and display them in menus.

EventQueue takes the following parameters:

  • id: An identifier used to distinguish this object from others; typically, it shares the same name as the variable reference.

To help with programming events in Ren’Py scope, the following global variables are used:

  • _event_queue: Provides a fast way to access the currently running event queue object.
  • _events_completed_all: A boolean indicating if all events in the current queue have been completed at least once.
  • _events_completed_any: A boolean indicating if any event in the current queue has been completed at least once.
  • _events_filtered_completed_all: A boolean indicating if all events that currently meet the requirements have been completed. (This is often used for favour events menus, as they typically have only a single requirement in the form of a character tier check.)
  • _events_filtered_completed_any: A boolean indicating if any event from the list that currently meets the requirements has been completed. (This is often used for favour events menus, as they typically have only a single requirement in the form of a character tier check.)

A few things to note:

  • Global variables for the EventQueue are not accessible for events involving the mainloop.
  • You don’t need to create EventQueue objects manually. For your convenience, this is automatically done when an event is provided with a queue parameter that doesn’t exist within the variable store.

Event

The Event class is essential for event-driven gameplay. It serves as a bridge between Python and Ren’Py, enabling the automation of event loops and event states for easier creation.

Events are automatically designated as “complete” or “failed” based on the label name provided and the context of the call. If a label contains a suffix indicating failure, it will be marked as “failed”.

Event takes the following parameters:

  • id: An identifier used to distinguish this object from others; typically, it shares the same name as the variable reference.
  • wait: An integer representing the number of days that must pass before this event becomes active.
  • priority: An integer that sets the priority of this event. Lower numbers mean higher priority, while higher numbers mean lower priority.
  • req: A string containing states that will be evaluated as booleans at runtime.
  • label: A label this event will call when it starts.
  • func: A Python function this event will call when it starts.
  • queue: The string reference of the queue object this event will use.
  • autoenqueue: A boolean indicating whether this event should be automatically enqueued in the supplied queue or added manually.
  • autodequeue: A boolean indicating whether this event should be automatically dequeued after completion or removed manually.
  • repeat: A boolean indicating whether this event should be considered when looking for repeatable events in the queue.
  • fail_suffixes: A list of strings with suffixes that will mark the event as failed when jumped to.
  • ignore_labels: A list of strings with labels that will not trigger the completion of the event when jumped to until the context is passed to the event that is not listed in this list.
  • subevents: A list of strings containing references to event objects that will be enqueued after this event is marked as complete.
  • disabled: A boolean indicating whether this event is temporarily disabled or not.

The following global variables are available:

  • _event: Provides a fast way to access the current event object.
  • _event_completed: A boolean indicating if the current event has been completed at least once in the past.
  • _event_completed_failed: A boolean indicating if the current event has previously triggered a fail state at least once.

Please note:

  • If you wish to add a new event to the mainloop, just exclude the queue parameter, and the default setting will be applied, automatically adding the event to the mainloop.

How-To

Add a new event to the mainloop

To add a new event into the mainloop, you must first define the label you want to use for the event and then define the event object.

# Create the event object with requirements for triggering only on
# Hermione Tier 1 during the night, with a 28 in-game days delay.
default ExampleMod_ev_myevent = Event(
    id="ExampleMod_ev_myevent",
    wait=28,
    req="not game.daytime and states.her.tier==1",
    label="examplemod_myevent",
    autoenqueue=True,
)

label examplemod_myevent:
    # Calls chibi object to enter room and stand in the middle of it.
    call her_walk(action="enter", xpos="mid", ypos="base")

    # Calls character sayer and displays the doll image
    her "Hello world." ("base", "base", "base", "mid")

    # Calls chibi object to exit the room.
    call her_walk(action="leave")

    # Ends the event, marking it as complete, resets common character states,
    # and marks Hermione as 'busy' until the next time period,
    # lastly returns control to the mainloop.
    jump end_hermione_event

Add a new event to an existing favour

To add new events to pre-existing favours, some additional setup is required. You need to identify the name of the event queue for the favour and include it in the “queue” parameter. Then, enable “autoenqueue” and specify any “ignore labels” that reflect the state of the events in the game files.

Here’s an example showing how to add a new event to ‘Talk to me’ favour for Hermione on Tier 1:

# Create the event object with requirements for triggering only on
# Hermione Tier 1, and insert it as the last event in the event chain.
default ExampleMod_ev_myevent = Event(
    id="ExampleMod_ev_myevent",
    label="examplemod_myevent",
    priority=10,
    req="states.her.tier==1",
    queue="her_eventqueue_talk_to_me",
    autoenqueue=True,
    autodequeue=False,
    repeat=False,
    ignore_labels=["end_hg_pf_talk"])

label examplemod_myevent:

    # Calls the setup label containing common variables
    # defined for this favour type.
    call start_hg_pf_talk

    # Calls character sayer and displays the doll image
    her "Hello world." ("base", "base", "base", "mid")
   
    # Completes the event and returns control to the mainloop
    jump end_hg_pf_talk

The code structure for each favour type may differ, so it is advisable to review the relevant game files to gain a better understanding of the specific event chain.

Add a new favour

To implement a new type of favor or event chain, you need to define Event objects and provide a name for an EventQueue. The EventQueue will be automatically defined during the event initialization process based on the supplied name.

Furthermore, during initialization, you must add the reference to the EventQueue object to an existing favour list, as this list undergoes reconstruction during initialization.

When adding favours, it often involves incorporating commonly used labels to streamline the call stack and minimize code repetition.

Here’s an example showcasing how to add a new favour type to Hermione on Tier 4:

init python:
    # Appends the new favour into the favour list
    hermione_favors.append( ("examplemod_eventqueue_footjob", "Give me a footy!") )

default ExampleMod_ev_footjob = Event(
    id="ExampleMod_ev_footjob",
    label="examplemod_footjob",
    priority=5,
    req="states.her.tier==4",
    queue="examplemod_eventqueue_footjob",
    autoenqueue=True,
    autodequeue=False,
    repeat=True
)

label examplemod_footjob_start:
    # Prompt the player for input only when they activate this type of favour for the first time.
    if not _events_completed_any:
        gen "{size=-4}(Should I ask her to use her feet on me?){/size}" ("base", xpos="far_left", ypos="head")

        menu:
            "\"(Yes, let's do it!)\"":
                pass
            "\"(Not right now.)\"":
                # Cancel the current event.
                $ _event.cancel()
                # Return back to favour menu
                jump hermione_favor_menu

    # Defines how much points will Hermione for completing this favour
    $ current_payout = 25
    return

label examplemod_footjob:

    # Calls the setup label containing common variables
    # defined for this favour type.
    call examplemod_footjob_start

    # Calls character sayer and displays the doll image
    her "Hello world." ("base", "base", "base", "mid")
   
    # Completes the event and returns control to the end label, then the mainloop
    jump examplemod_footjob_end

label examplemod_footjob_end:

    stop music fadeout 2.0
   
   # Display a black screen to prevent the repositioning of elements
   # from being visible on the screen. Uses a .3 second transition
    show screen blkfade
    with d3

    # Hide Hermione's image tag
    hide hermione_main
    # Reset Hermione's chibi
    call her_chibi("stand", flip=False)
    # Reset Genie's chibi
    call gen_chibi("sit_behind_desk")
    # Reset Hermione's image positioning
    her "" (xpos="mid", ypos="base")

    # Hide the black screen using a .3 second transition
    hide screen blkfade
    with d3

    # Grant X number of points to gryffindor as defined
    # by the current_payout variable inside the start label
    $ gryffindor += current_payout
    gen "{number=current_payout} points to Gryffindor, [name_hermione_genie]. Well done." ("base", xpos="far_left", ypos="head")

    # Present distinct dialogues based on whether her mood value is non-zero.
    if states.her.mood != 0:
        her "*Hmph*... Will this be all then?" ("annoyed", "base", "angry", "mid")
        gen "Yes, you can go now." ("base", xpos="far_left", ypos="head")
        her "*Tsk*..."
    else:
        her "Anything else you need?" ("soft", "base", "base", "mid")
        gen "No, we're done here, you can go now." ("base", xpos="far_left", ypos="head")
        her "Thank you, [name_genie_hermione]."

    # Call Hermione's chibi to walk towards the door
    call her_walk("door", "base")

    # Display inner monologue for Hermione if it's her first event from this favour type
    if not _events_completed_any:
        her "(A footjob? I wasn't aware that people considered feet sexual.)" ("upset", "base", "angry", "down", flip=True)
        her "(I wonder if it will make my feet softer...)" ("soft", "base", "angry", "L", flip=True)

    # Call Hermione's chibi to exit the room
    call her_chibi("leave")

    # Increase level until all events from the chain are complete
    if not _events_filtered_completed_all
        $ states.her.level += 1

    jump end_hermione_event