Ren'Py Renpy Event Trigger

-DEV-Phil-

Newbie
Game Developer
Jan 14, 2021
95
343
Hello friends,

After reaching out to this forum for help several times now and always being kindly assisted, I would like to try again.

I have the following problem:

I want to trigger an event after a certain number of days, for example:

Code:
label last_talk:
    show endpicture001
    get current day
    if currentday +3 trigernr3 = True
return
I know that this code doesn't quite work, it's just an example. Since I would like to use this function frequently in my game, I would like to make it as reusable as possible.

I am already using a custom daytime file for my game, which I will attach as it may be important.


Code:
## tables used by CurrentDayTime class
define dow_names=[
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
  "Sunday",
  ]

define dow_short_names=[
  "Mon",
  "Tue",
  "Wed",
  "Thu",
  "Fri",
  "Sat",
  "Sun",
  ]

define tod_names=[
  "Morning",
  "Afternoon",
  "Evening",
  "Night",
  ]

define tod_image_suffix=[
  "_morning",
  "_afternoon",
  "_evening",
  "_night",
  ]

init python:
  def display_new_tod():
    ## make sure we queue/show it only once, even if now.day_advance() was called
    if "display_new_tod" not in queued_events:
      queued_events.append("display_new_tod")

  def process_time_advanced():
    ## this function is called to update other parts of game when game time is advanced
    ## called once for every tod passed
    ## advance_quests()
    ## advance_characters()
    display_new_tod()

  class CurrentDayTime(object):
    def __init__(self,start_day=1,start_tod=0):
      super(CurrentDayTime,self).__init__()
      self.day=start_day
      self.tod=start_tod
    ## will return number of day-of-week, Monday is 0, Saturday is 5, Sunday is 6
    @property
    def dow(self):
      return (self.day-1)%7
    ## will return name of day-of-week, Monday, Saturday, Sunday
    @property
    def dow_name(self):
      return dow_names[self.dow]
    ## will return short name of day-of-week, Mon, Sat, Sun
    @property
    def dow_short_name(self):
      return dow_short_names[self.dow]
    ## will return name of time-of-day, Morning, Evening
    @property
    def tod_name(self):
      return tod_names[self.tod%len(tod_names)]
    ## will return image suffix associated with current time-of-day, used to find most appropriate image
    @property
    def tod_image_suffix(self):
      return tod_image_suffix[self.tod]
    ## advance time, by 1 tod by default
    ## will switch to new day when overlapping
    ## will call process_time_advanced function every tod advance
    def advance(self,time_to_pass=1):
      while time_to_pass>0:
        time_to_pass-=1
        self.tod+=1
        if self.tod>=len(tod_names):
          self.day+=1
          self.tod=0
        process_time_advanced()
    ## will advance time to next(by default) day, first tod(morning)
    def day_advance(self,days_to_pass=1):
      tods_to_pass=days_to_pass*len(tod_names)-self.tod
      self.advance(tods_to_pass)
    ## instance can be called to check current day/time
    ## can check day number, day of week, time of day and combinations
    ## if one of arguments is list/tuple then check if any element inside is valid
    ## now("morning") will check if current time-of-day is morning
    ## now(5) will check if current day is 5
    ## now(10,"evening") will check if current day is 10 and current tod is evening
    ## now(["morning","afternoon"],["saturday","sunday"]) will check if it is
    ## morning or after noon of saturday or sunday
    def __call__(self,*args):
      for arg in args:
        if isinstance(arg,basestring):
          arg=arg.lower()
          if arg not in (self.tod_name.lower(),self.dow_name.lower(),self.dow_short_name.lower()):
            return False
        elif isinstance(arg,(list,tuple)):
          arg_list=arg
          found=False
          for arg in arg_list:
            if isinstance(arg,basestring):
              arg=arg.lower()
              if arg in (self.tod_name.lower(),self.dow_name.lower(),self.dow_short_name.lower()):
                found=True
            else:
              if arg==self.day:
                found=True
          if not found:
            return False
        else:
          if arg!=self.day:
            return False
      return True

default now=CurrentDayTime()

## labels to be used by HUD during roaming mode
## avoid calling now.advance() directly from HUD as rollback/saving may become quirky
label advance_time:
  $now.advance()
  return

label advance_day:
  $now.day_advance()
  return

style new_tod_title:
  color "#FFF"
  size 64
  text_align 0.5
  align (0.5,0.5)

## black bg+white "TIME-OF-DAY"
label display_new_tod:
  scene black with Dissolve(0.25)
  if now("morning"):
    show expression Text(now.dow_name+"\n"+now.tod_name,style="new_tod_title") with Dissolve(0.25)
  else:
    show expression Text(now.tod_name,style="new_tod_title") with Dissolve(0.25)
  pause 1.0
  return

Maybe someone knows the solution, I myself am a beginner in programming.

Thank you in advance.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,302
15,174
Maybe someone knows the solution, I myself am a beginner in programming.
Something like this should do it:
/!\ Wrote on the fly, so typo and all are possible /!\
Python:
  class CurrentDayTime(object):
    def __init__(self,start_day=1,start_tod=0):
        self.alarmDay = 0
        self.alarmLabel = None
    [...]
    def reminder( self, delay, label ):
        self.alarmDay = self.day + delay
        self.alarmLabel = label

    @property
    def haveEvent(self):
        if self.day == self.alarmDay:
            self.alarmDay = 0
            return self.alarmLabel
        else:
            return None

label advance_day:
  $now.day_advance()
  if now.haveEvent:
      call expression now.haveEvent
  return
 
  • Like
Reactions: -DEV-Phil-

-DEV-Phil-

Newbie
Game Developer
Jan 14, 2021
95
343
Something like this should do it:
/!\ Wrote on the fly, so typo and all are possible /!\
Python:
  class CurrentDayTime(object):
    def __init__(self,start_day=1,start_tod=0):
        self.alarmDay = 0
        self.alarmLabel = None
    [...]
    def reminder( self, delay, label ):
        self.alarmDay = self.day + delay
        self.alarmLabel = label

    @property
    def haveEvent(self):
        if self.day == self.alarmDay:
            self.alarmDay = 0
            return self.alarmLabel
        else:
            return None

label advance_day:
  $now.day_advance()
  if now.haveEvent:
      call expression now.haveEvent
  return



First of all, thank you very much for the quick response, but I think I need to be a bit more specific with my question. In my game, I use an open world, and I have a specific file for each location to keep track of things. I want to use this function to unlock a new location if, for example, someone has visited another location after 3 days.

To make this work the way I envisioned it: At the end of the first location, for example, I would need a

Python:
label this_is_the_ent_of_event01:
    start trigger 001
    jump openworldmap
And then, when this timer starts, the location should be unlocked after 3 days, like this:

Python:
menu:
    "Visit Sarah" if eventtrigger01 ==True ------------This menu item should only be possible if 3 days or another number of days have passed since the last event. 
        jump letsvisit_Sarah
    "Nevermind"
        jump Openworl
Is this feasible?

Best regards.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,576
2,204
Anything is possible, but a lot of it will come down to how much you understand programming, the various parts of the RenPy language and the underlying use of Python.

We could come up with a perfect, elegant solution - but if you don't follow it... you won't be able to adapt it to your project.

I tend to stick to the advice for those who don't understand some of the more complex solutions.
You could for example, just have a countdown timer (a variable) that is set at a specific part of the story. Then when the variable hits zero, it sets another variable to be True instead of false.

One approach would be to have a common hub (a label) within your code that you always return back to. Then the code within that label does all your housekeeping tasks and then jumps to whatever location the player is currently at (at whatever time of day, etc).

Another approach would be to have a similar hub, but per location, then invoke the housekeeping logic at the beginning of each hub, either by repeating the code (bad) or calling a common bit of code held elsewhere in the script (a called label or a perhaps a function).

Something vaguely like:

Python:
default unlock_kitchen_countdown = -1
default kitchen_unlocked = False

# ...

label my_hub:

    if kitchen_unlocked == False and unlock_kitchen_countdown > 0:
        $ unlock_kitchen_countdown -= 1
        if unlock_kitchen_countdown == 0:
            $ kitchen_unlocked = True

It's up to you when and how you want to set the value of unlock_kitchen_countdown to 3 to start the ball rolling. It's initial value of -1 will stop that logic being triggered until you start the countdown.

Obviously, this solution is very low tech - with variables needed for each and every event you want to organize.
The other consideration is your use of time periods each day. This logic would be something you'd run only at the end of day. Or perhaps, given 4 time periods per day, you'd want the coundown to be 12 instead of 3... and unlock things in 3 days, depending on the time of day when the event was unlocked.

Realistically though... your use of python lists is already more complex than my alternative solution. So perhaps I'm low-balling my answer TOO low.
 
  • Like
Reactions: anne O'nymous

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,302
15,174
And then, when this timer starts, the location should be unlocked after 3 days, like this:

Python:
menu:
    "Visit Sarah" if eventtrigger01 ==True ------------This menu item should only be possible if 3 days or another number of days have passed since the last event.
        jump letsvisit_Sarah
    "Nevermind"
        jump Openworl
Is this feasible?
Yes, you just need to take few steps back.

Instead of linking the menu directly to the trigger, make the menu depend on a flag ; flag that, it, will be linked to the trigger:

Python:
default sarahAvailable = False

label whatever:
    [...]
    $ now.reminder( 3, "openSarah" )
    [...]

label openSarah:
    $ sarahAvailable = True
    return

label somethingElse:
    [...]
    menu:
        "Visit Sarah" if sarahAvailable
        jump letsvisit_Sarah
    "Nevermind"
        jump Openworl
    [...]

label advance_day:
  $now.day_advance()
  if now.haveEvent:
      call expression now.haveEvent
  return
The use of labels for the event isn't the best here, but still the easiest. If you've the knowledge to use more advanced, you should also have the knowledge to do the adaptation by yourself.


Python:
default unlock_kitchen_countdown = -1
default kitchen_unlocked = False

# ...

label my_hub:

    if kitchen_unlocked == False and unlock_kitchen_countdown > 0:
        $ unlock_kitchen_countdown -= 1
        if unlock_kitchen_countdown == 0:
            $ kitchen_unlocked = True
Your way is proper, but testing the flag isn't necessary here. Since it's automatically raised when the counter reach 0, a simple if unlock_kitchen_countdown > 0: would have the same result.

It's a really small, and unnoticeable optimization. But there's few times, like a big if structure called really often, where it can be interesting to have even this kind of small optimization.
But, the most interesting point here is that it remove a dependency to the flag name. Therefore less risk of typos, and one less thing to care about if you change the way you proceed (move the flags to an object or a dict by example).

On a secondary note, you can also remove the second if. It will not effectively optimize the code, but it can optimize its writing when you've a lot to write: $ kitchen_unlocked = not unlock_kitchen_countdown
The flag will be forced at False as long as the counter isn't "0", and passed to True when the counter will reach "0". Like this will not anymore be processed once the counter reach 0, it works without side effect.


Realistically though... your use of python lists is already more complex than my alternative solution. So perhaps I'm low-balling my answer TOO low.
Or the lists use came with the code he use, and you offered him an alternative that he will fully understand.