Random events via objects and methods?
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.
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.
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Random events via objects and methods?
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.
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.
- RicharDann
- Veteran
- Posts: 286
- Joined: Thu Aug 31, 2017 11:47 am
- Contact:
Re: Random events via objects and methods?
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:
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.
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Re: Random events via objects and methods?
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.
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.
- RicharDann
- Veteran
- Posts: 286
- Joined: Thu Aug 31, 2017 11:47 am
- Contact:
Re: Random events via objects and methods?
You may have to use a function like this (written on the fly, might need tweaking):
This function doesn't handle each event's probability.
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()
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.
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Re: Random events via objects and methods?
Ah, so there really isn't a way to just construct a list of all objects of class Events?
- RicharDann
- Veteran
- Posts: 286
- Joined: Thu Aug 31, 2017 11:47 am
- Contact:
Re: Random events via objects and methods?
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
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.
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Re: Random events via objects and methods?
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.
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.
Re: Random events via objects and methods?
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).
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Re: Random events via objects and methods?
So I'd store the conditionals as strings?
'a == 1 && b == 2 && c == 3'
if eval(x):
foo
'a == 1 && b == 2 && c == 3'
if eval(x):
foo
Re: Random events via objects and methods?
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.
- 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?
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
Some sections of the cookbook thread need expanding, on the agenda.
Maybe play with the sample game, scan the code and see
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 }
Maybe play with the sample game, scan the code and see
Frameworks & Scriptlets:
- Speech Bubble dialogue system
- Multiple Notify with ATL and history
- (WIP) Radial Masking - needs updating to use Shader
- 7.4 - Smooth Tinting using ATL and matrixcolor
- Several other repositories there too
Re: Random events via objects and methods?
The Dating Sim Engine has a very nice event manager built in. It is very usable without the default user interface.
-
- Regular
- Posts: 33
- Joined: Thu Oct 19, 2017 8:04 pm
- Contact:
Re: Random events via objects and methods?
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:
And then later on I define instances of the Event class like this:
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.
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)
Code: Select all
default eventImplemented_obj = Event ('eventType', 'True', 'eventImplemented')
label eventImplemented:
# what the event does when triggered
return
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.
- 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?
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.
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:
- Speech Bubble dialogue system
- Multiple Notify with ATL and history
- (WIP) Radial Masking - needs updating to use Shader
- 7.4 - Smooth Tinting using ATL and matrixcolor
- Several other repositories there too
- 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?
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.
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:
- Speech Bubble dialogue system
- Multiple Notify with ATL and history
- (WIP) Radial Masking - needs updating to use Shader
- 7.4 - Smooth Tinting using ATL and matrixcolor
- Several other repositories there too
Who is online
Users browsing this forum: Google [Bot]