Random events via objects and methods?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Message
Author
DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Random events via objects and methods?

#1 Post by DoubleProlix »

I'd like to create a random event system that runs through a list of potential events, checking to see if they're currently possible, and against each's probability. One method to do this would be a list with two items per event, one label with each event's requirements and probability (returning true or false) and the other with the label to jump to if it triggers.

That seems a bit clumsy, though, and I was wondering if I could do something with objects and methods.

Each list item would be an object, and each object would have a method to test to see if it was possible and how probably (obj.canHappen or something). If this passes, then the object's executeMe label is jumped to.

Is this a thing I can do? How would I code it?

And would it be possible to dynamically construct the list from all objects of a given type, or would I need to use something more static?

Thanks.
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

User avatar
RicharDann
Veteran
Posts: 286
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Random events via objects and methods?

#2 Post by RicharDann »

Objects seem like a good idea to me, it's way more organized and easier for you to mantain and debug, it should be possible since you can fully integrate Python code into Ren'Py.

As for how would you code it it depends on how complex you want the system to be, what kind of conditions there are (i.e. if we have to check strings, numbers, or True/False booleans), and how the game reacts to those conditions. Here's an example system I made some time ago, maybe it could give you an idea on how to code something similar:

Code: Select all

init python:
    
    class Event(object):
        
        def __init__(self, name = '', requirement = '', probability=0, label=''):
            self.name = name #This should be a string
            self.requirement = requirement #This should be a list of ['condition', required_value]
            self.probability = probability #This is a number (or maybe a percentage) used to check if the event may happen. 
            self.label = label #And this would be the label to call when requirement is met
            
        def checkRequirement(self): #Check the requirement
            if self.requirement[0] == 'intelligence': #If the requirement is intelligence
                global mc_intelligence
                if mc_intelligence >= self.requirement[1]: 
                    return True
                else:
                    return False
        
        def execute(self):
            if self.checkRequirement:
                renpy.call(self.label)

default studyCheck = Event('Study Check', ['intelligence', 10], 0, 'study_check')

default mc_intelligence = 0 #I plan to make a Player object to hold his/her stats, this will do for now.

define mc = Character("MC")

# The game starts here.

label start:
    
    mc "I should probably study a little."
    
    menu:
        
        mc "Shouldn't I?"
        
        "Yeah, studying is good":
            $ mc_intelligence += 10
        
        "Nah, I don't wanna.":
            pass
            
    "The next day in class..."
    
    mc "The teacher is looking at me..."
    
    $ studyCheck.execute()

    mc "The end"

    return

label study_check:
       
    $ chance = renpy.random.randint(0,1) #This will be 0 or 1, for 50% change of happening
    
    if chance == studyCheck.probability: #If chance == 1, this will execute
        
        
        "Teacher" "It's your turn today. Answer this question..."
        
        if mc_intelligence >= 10:
        
            mc "Good thing I studied."
        else:
            
            mc "Damn, I should have studied."
        
    else:
        mc "He looked elsewhere. Phew..."


The most important step is always the next one.

DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Re: Random events via objects and methods?

#3 Post by DoubleProlix »

Looks great.

How would I make a dynamic list of events whose requirement was currently returning True? I'd like to do this at runtime every time a check had to be made - gather up all the currently possible events and run one of them.
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

User avatar
RicharDann
Veteran
Posts: 286
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Random events via objects and methods?

#4 Post by RicharDann »

You may have to use a function like this (written on the fly, might need tweaking):

Code: Select all


default eventList = [studyCheck, beautyCheck, charismaCheck] # This should be a list of all events

init python:
    #This goes inside an init python block, but outside of the class Event
    def getPossibleEvents():
        global eventList
        
        possible_events = []
        
        for event in eventList:
            if event.checkRequirement():
                possible_events.append(event)
                
        return possible_events
        
    def executeRandomEvent(): #This is what you'd call when you want to run a random event
        
        p_events = getPossibleEvents()
        
        if len(p_events) > 1:
            event_id = renpy.random.randint(0, (len(p_events) -1)) 
            
        else:
            event_id = 0
        
        p_events[event_id].execute()
This function doesn't handle each event's probability.
Last edited by RicharDann on Fri Oct 20, 2017 11:21 am, edited 1 time in total.
The most important step is always the next one.

DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Re: Random events via objects and methods?

#5 Post by DoubleProlix »

Ah, so there really isn't a way to just construct a list of all objects of class Events?
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

User avatar
RicharDann
Veteran
Posts: 286
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Random events via objects and methods?

#6 Post by RicharDann »

Ah, I understand now, you want to simply get all instances of an object?

That's a little advanced, I've never done it before so I don't really know if it's possible in Ren'Py. This link refers to Python itself, but it may help you:

http://effbot.org/pyfaq/how-do-i-get-a- ... -class.htm
The most important step is always the next one.

DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Re: Random events via objects and methods?

#7 Post by DoubleProlix »

Great!

One other object related question, though. Say I wanted the requirement to be more complex; for example, to trigger, it has to be at night time, the player has to have met Mary, and they need $10 on hand.

I think it'd be easiest to have a method to check ~whatever~ in each object, returning True or False when called, but I have no idea how to implement that. Any ideas?

In pseudocode:

1. The handler picking the event selects an item from the event list at random.
2. If that random event can happen (the requirement checker method returns True), it happens.
3. Else it picks a different event from the list.

Or, when constructing the list, the listbuilding function checks to see if the requirement method returns True before adding it... I think I can figure that out from the link you provided, if I can figure out how to implement a method when defining the event objects.
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

philat
Eileen-Class Veteran
Posts: 1895
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Random events via objects and methods?

#8 Post by philat »

You can simply use eval() on strings (that is, if eval('period=="night"') is the same as if period=="night" so you can have a complex set of conditions as strings).

DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Re: Random events via objects and methods?

#9 Post by DoubleProlix »

So I'd store the conditionals as strings?

'a == 1 && b == 2 && c == 3'
if eval(x):
foo
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

philat
Eileen-Class Veteran
Posts: 1895
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Random events via objects and methods?

#10 Post by philat »

Yes, except you'd use and instead of &&. You could also use a list of strings and iterate through them, but I don't think it would make much difference performance-wise. May potentially be easier to add or remove conditions dynamically, but whether you'd need that is up in the air.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Random events via objects and methods?

#11 Post by Remix »

You might want to look at:

Event Handler with Lexer controls

Though it is a work in progress (needs translations fixed etc) it provides most of what you require.
Basically it lets you do

Code: Select all

label some_event:
    event register:
        day "Friday"
        hour 12 13 14
        
        function:
            test_something [ *args as string ] { **kwargs as string }
Some sections of the cookbook thread need expanding, on the agenda.

Maybe play with the sample game, scan the code and see
Frameworks & Scriptlets:

User avatar
Qwxlea
Newbie
Posts: 18
Joined: Sat Jun 10, 2017 3:58 am
IRC Nick: qwxlea
Github: Qwxlea
Contact:

Re: Random events via objects and methods?

#12 Post by Qwxlea »

The Dating Sim Engine has a very nice event manager built in. It is very usable without the default user interface.

DoubleProlix
Regular
Posts: 33
Joined: Thu Oct 19, 2017 8:04 pm
Contact:

Re: Random events via objects and methods?

#13 Post by DoubleProlix »

Okay, I've got things mostly working, except for the small detail where I define my objects in init but their methods don't fire after saving and loading. I've figured out that I need to define my objects outside of init and after the start label, but then the instances throw errors because the Event object is not defined.

Whoops.

So how do I define the object class outside of init in such a way that I'm not throwing those errors?

In init I currently have:

Code: Select all

    python:
          import weakref
          import random
          class Event(object):
            _instances = set()
            def __init__(self, eventType = ' ', isActive = ' ', doEvent = ' '):
                self.eventType = eventType
                self.doEvent = doEvent
                self.isActive = isActive
                self.triggered = False
                self._instances.add(weakref.ref(self))

            def execute(self):
                if eval(self.isActive) == True and self.triggered == False:
                    renpy.call(self.label)
            @classmethod
            def getinstances(cls):
                dead = set()
                for ref in cls._instances:
                    obj=ref()
                    if obj is not None:
                        yield obj
                    else:
                        dead.add(ref)
                cls._instances -= dead  
          def chooseEvent(type): 
            list = []
            for obj in Event.getinstances():
                if eval(obj.isActive) and obj.triggered == False and obj.eventType == type:
                    list.append(obj)
            if len(list) == 0:
                return(False)
            else:
                renpy.call((renpy.random.choice(list)).doEvent)
                return(True)
And then later on I define instances of the Event class like this:

Code: Select all

default eventImplemented_obj = Event ('eventType', 'True', 'eventImplemented')

label eventImplemented:
    
    # what the event does when triggered
    return
Only after loading, instead of choosing an event the way it should (and does when playing a new game without loading or saving), nothing happens.

If I move the class definition to the start label, I just get errors that Event is undefined.

Update: Putting the class definition after an after_load: label doesn't solve the problem. I'm really not sure why events stop triggering after the game is saved and loaded.
Last edited by DoubleProlix on Fri Nov 03, 2017 7:29 pm, edited 1 time in total.
Current Project: Thaumarotica - an occult erotic thriller
SubscribeStar - Patreon - Itch.io

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Random events via objects and methods?

#14 Post by Remix »

Not entirely sure what you mean by ' their methods don't fire after saving and loading ' without seeing the class __init__ or post init configuration method calls themselves.

Basically, does __init__ initialize or does it set up the object with data too?
If it only initializes (without setting data mostly), when/where do the setup method calls occur?

What init offset are you using? How do you interact with the object? When does it get first initialized? When does the internal data get set?

Anyway, at a guess:

Use python early block in a file named with a low numeric start (so the object definition is available prior to script parsing), then either use a lexer parser to initialize during the parse (that would be when Ren'py parses the dialogue/script at startup)

or re-initialize with an after_load callback function

define config.after_load_callbacks = [ ... ]
A list of functions that are called (with no arguments) when a load occurs.

A save/load should really be pickling and storing your object in the save data (and unpickle on load) anyway. So the object should be available whenever... unless said object holds mutate-able data sets that cannot be pickled - even then, Ren'py tries to convert anything it can to a non mutating version.
Frameworks & Scriptlets:

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Random events via objects and methods?

#15 Post by Remix »

I think that using weakref to simulate a Singleton pattern might not be pickle-able...

In my understanding weakref relies on an object or filesystem-byte-index alias that is dropped as soon as the object is no longer needed. For Ren'py to save and reload all those it would need to convert each to a solidly referenced object which basically destroys the singleton... ?

Have you perhaps considered a factory or dispatcher style pattern? Personally I'd edge toward one overlord class that just holds and spawns sub classes for events, basically a handler or over-seer class. Init that in a 0 or -1 offset and just register/amend/delete events from that. Ren'py should have no probs saving/loading that.
Frameworks & Scriptlets:

Post Reply

Who is online

Users browsing this forum: Google [Bot]