Ren'Py Using events to get rid of if statements in Ren'py

Chasmer

New Member
Jun 15, 2021
4
8
Not so long ago I decided to create a sandbox-VN game, inspired by games like Corrupted Kingdoms, Hero's Harem Guild or Harem Hotel. I wanted to implement quests and automatically show scenes (I will call VN blocks scenes to separate them from events that I'm using in programming sense) whenever player fulfilled conditions.

Let's look at some example scenario: When player goes to Kitchen at night, a scene should start playing.

The simplest and most common way (at least from what I've seen and read) is to create if statement in location:

Python:
label Kitchen:
    #some location code
    if time == "night":
        jump some_scene
    #some location code
For single scene it works and there's no problem, but what if we have more scenes that starts in the Kitchen location, or more conditions, or maybe the scene can be triggered in kitchen and dining room. All of those things will eventually lead to the "if hell" as I call it. 10+ if statements called every time player enters a location even if the scenes were played and will never trigger again. It may not cause performance issues but it's probably gonna cause some headaches in case something goes wrong or the dev changes his mind about something.

The solution I found is to mimic the C# events in python and use them to trigger whatever we want.

For our scenario I created a VNScene class in python.

Python:
init python:
    class VNScene(store.object):
        def __init__(self, label_name, conditions):
            self.label_name = label_name #name of the label with the scene we want to play
            self.conditions = conditions #list of conditions to trigger scene, remember that it should be dictionary
       
    def check_conditions(self):
        if all(eval(c) for c in self.conditions.values()):
            self.start_scene()
           
    def start_scene(self):
        renpy.jump(self.label_name)
Then we create object from our class.

Python:
define conditions = {
    "location": "location == \"Kitchen\"",
    "time": "time == \"night\""
}

default scene_obj = VNScene("some_scene", conditions)
Now, let's create our events.

Python:
init python:
    class Event(store.object):
        def __init__(self): #constructor
            self.__event_handlers = []

        def __iadd__(self, handler):
            if all(not id(handler) == id(h) for h in self.__event_handlers):
                self.__event_handlers.append(handler)
            return self

        def __isub__(self, handler):
            self.__event_handlers.remove(handler)
            return self

        def __call__(self, *args, **keywargs):
            for event_handler in self.__event_handlers:
                event_handler(*args, **keywargs)

default on_location_change = Event()
default on_time_change = Event()
iadd and isub methods allow us to use += and -= operators

Now lets go back to our VNScene class, add two new method and modify start_scene method.

Python:
init python:
    class VNScene(store.object):
        def __init__(self, label_name, conditions):
            self.label_name = label_name #name of the label with the scene we want to play
            self.conditions = conditions #list of conditions to trigger scene, remember that it should be dictionary    

        def check_conditions(self):
            if all(eval(c) for c in self.conditions.values()):
                self.start_scene()

        def start_scene(self):
            self.unregister()
            renpy.jump(self.label_name
           
        def register(self):
            for c in self.conditions:
                if c == "location":
                    store.on_location_change += self.check_conditions
                if c == "time":
                    store.on_time_change += self.check_conditions
           
        def unregister(self):
            for c in self.conditions:
                if c == "location":
                    store.on_location_change -= self.check_conditions
                if c == "time":
                    store.on_time_change -= self.check_conditions
Now whenever we want that scene to be available for player, we call
Python:
scene_obj.register()
After scene has been played, we unregister events, so we no longer track them.

Now to use events we use them as methods.
Python:
store.on_location_change() #whenever we change location
store.on_time_change() #whenever time changes
I hope this tutorial was in any way helpful.
 

YaYa_UnTIN2

Misunderstood Thinker
Donor
Dec 19, 2017
623
706
Hi Chasmer,

I think there's a small issue on this line of code under:

Now lets go back to our VNScene class, add two new method and modify start_scene method.

Code:
renpy.jump(self.label_name
Should be:

Code:
renpy.jump(self.label_name) #<-- original code is missing an ending --> )
===



===

Thanks for the visual information, sometimes it's better to see code (and make it work in my brain), than just reading about what some random code is supposed to do. Having it "together" ready to build from is nice. :)

Good luck in your Game Dev.
 

Chasmer

New Member
Jun 15, 2021
4
8
Hi Chasmer,

I think there's a small issue on this line of code under:

Now lets go back to our VNScene class, add two new method and modify start_scene method.

Code:
renpy.jump(self.label_name
Should be:

Code:
renpy.jump(self.label_name) #<-- original code is missing an ending --> )
Yes, thanks for pointing that out. I didn't see it while writing the post. I will probably make more comprehensive guide with a lot of improvements I made to this system.
 
  • Like
Reactions: YaYa_UnTIN2