How can I create many different objects with the same timer code

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.
Post Reply
Message
Author
User avatar
renardjap
Regular
Posts: 75
Joined: Sun Aug 05, 2018 1:08 pm
Location: France ! Cocorico
Contact:

How can I create many different objects with the same timer code

#1 Post by renardjap » Tue Apr 28, 2020 4:02 am

Hi guys

I wrote a code to create a building when the timer ends. My buildings are a class where I define the time needed to create the building (build_time) and a timeleft for each building.
I know how to write a timer for a specific object like this one:

Code: Select all

init python:
    
    class Building:
        def __init__(self, name, img, loc = 0, timeleft = 0, time_build = 0):
            self.name = name
            self.img = img
            self.timeleft = timeleft
            self.time_build = time_build # the time needed to build the building 
            Building.loc = loc #not important for my question 

        def start_building_timer(self):
            self.timeleft = self.time_build

        def end_building_timer(self):
            global nbr_building
            nbr_building += 1
            self.timeleft = 0


screen timer_screen:
    on "show" action Function(renpy.retain_after_load)
    zorder 3

    if building01.timeleft>1:
        timer 1.0 action SetVariable("building01.timeleft", building01.timeleft-1) repeat True
    elif building01.timeleft == 1:
        timer 1.0 action Function(building01.end_building_timer)

    vbox:
        text "building01.time_build: [building01.time_build]"
        text "building01.timeleft: [building01.timeleft]"
        text "nbr_building: [nbr_building]"
        textbutton "create building" action Function(building01.start_building_timer)


define building01 = Building("house", "",0, 0, 15)
define nbr_building = 0

label start:

    show screen timer_screen
    "You've created a new Ren'Py game."

    return
If I want to create 2 buildings, I copy-past the timer and I change the building01.timeleft by building02.timeleft (for exemple).
But I love buildings and I want to create 10'000 different buildings :lol: :lol: I cannot copy-past 10'000 timers code...
Also, I want to use the same timer code for my 10'000 buildings...

I tried to do that, but it doesn't work because it's not y python class but a renpy screen language. And to be honest, I'm not really sure of what I did...

Code: Select all

   if self.timeleft>1:
        timer 1.0 action SetVariable("self.timeleft", self.timeleft-1) repeat True
    elif self.timeleft == 1:
        timer 1.0 action Function(self.end_building_timer)
Also, How can I create many different objects with the same timer code ? And how can I do to creating different objects with different build_time in same time with the same timer code (I want to start building the timer building01, and if the timeleft of building01 is not finish I can start creating building02, like if I had different timer code).

One more question: does exist a Statement Equivalents in python of the renpy timer ? (perhaps something like renpy.timer() )

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Projects: The Button Man
Organization: NILA
Github: hell-oh-world
Location: Philippines
Contact:

Re: How can I create many different objects with the same timer code

#2 Post by hell_oh_world » Tue Apr 28, 2020 5:35 am

renardjap wrote:
Tue Apr 28, 2020 4:02 am
Hi guys

I wrote a code to create a building when the timer ends. My buildings are a class where I define the time needed to create the building (build_time) and a timeleft for each building.
I know how to write a timer for a specific object like this one:

If I want to create 2 buildings, I copy-past the timer and I change the building01.timeleft by building02.timeleft (for exemple).
But I love buildings and I want to create 10'000 different buildings :lol: :lol: I cannot copy-past 10'000 timers code...
Also, I want to use the same timer code for my 10'000 buildings...

I tried to do that, but it doesn't work because it's not y python class but a renpy screen language. And to be honest, I'm not really sure of what I did...

Also, How can I create many different objects with the same timer code ? And how can I do to creating different objects with different build_time in same time with the same timer code (I want to start building the timer building01, and if the timeleft of building01 is not finish I can start creating building02, like if I had different timer code).

One more question: does exist a Statement Equivalents in python of the renpy timer ? (perhaps something like renpy.timer() )
Again, looks like can be solved by class attributes and a loop... I haven't tested it yet but should work I guess...

Code: Select all

init python:
    
    class Building:
    	buildings = []
    
        def __init__(self, name, img, loc = 0, time_build = 0): # removed the timeleft param...
            self.name = name
            self.img = img
            
            # self.timeleft = timeleft
            self.timeleft = time_build # set the timeleft directly to the time build
            
            self.time_build = time_build # the time needed to build the building 
            
            self.started = False # flag to set when the build process starts
            
            Building.loc = loc #not important for my question 
            Building.buildings.append(self)

        def start_building_timer(self):
            # self.timeleft = self.time_build looks like not necessary...
            
            self.started = True

        def end_building_timer(self):
            store.nbr_building += 1
            
            self.started = False
            
            # self.timeleft = 0 looks like not necessary...


screen timer_screen:
    on "show" action Function(renpy.retain_after_load)
    zorder 3
    
    for i in range(Building.buildings):
    	if Building.buildings[i].timeleft > 0 and Building.buildings[i].started:
    		timer 1.0 repeat True action [
    			SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1),
    			If(
    				Building.buildings[i].timeleft == 0,
    				Function(Building.buildings[i].end_building_timer)
    			)
    		]
        
	$ i_ = i + 1
    	vbox:
        	text ("building[i_].time_build: " + str(Building.buildings[i].time_build))
        	text ("building[i_].timeleft: " + str(Building.buildings[i].timeleft))
        	text "nbr_building: [nbr_building]"
       		textbutton "create building" action Function(Building.buildings[i].start_building_timer)


# define building01 = Building("house", "",0, 0, 15)
# define nbr_building = 0
default building01 = Building("house", "",0, 0, 15)
default nbr_building = 0

label start:

    show screen timer_screen
    "You've created a new Ren'Py game."

    return
May I also tell that if you want your objects to participate in game load you should default them instead of defining them. Defines are meant for constants and fixed values, and variables that you do not plan to change at all...

drKlauz
Veteran
Posts: 237
Joined: Mon Oct 12, 2015 3:04 pm
Contact:

Re: How can I create many different objects with the same timer code

#3 Post by drKlauz » Tue Apr 28, 2020 6:52 am

I believe better option would be having one always-running repeatable timer which will call update_buildings function, which in turn will process buildings states.
I may be available for hire, check my thread: viewtopic.php?f=66&t=51350

User avatar
renardjap
Regular
Posts: 75
Joined: Sun Aug 05, 2018 1:08 pm
Location: France ! Cocorico
Contact:

Re: How can I create many different objects with the same timer code

#4 Post by renardjap » Tue Apr 28, 2020 6:56 am

hell_oh_world wrote:
Tue Apr 28, 2020 5:35 am

Again, looks like can be solved by class attributes and a loop... I haven't tested it yet but should work I guess...

Code: Select all

init python:
    
    class Building:
    	buildings = []
    
        def __init__(self, name, img, loc = 0, time_build = 0): # removed the timeleft param...
            self.name = name
            self.img = img
            
            # self.timeleft = timeleft
            self.timeleft = time_build # set the timeleft directly to the time build
            
            self.time_build = time_build # the time needed to build the building 
            
            self.started = False # flag to set when the build process starts
            
            Building.loc = loc #not important for my question 
            Building.buildings.append(self)

        def start_building_timer(self):
            # self.timeleft = self.time_build looks like not necessary...
            
            self.started = True

        def end_building_timer(self):
            store.nbr_building += 1
            
            self.started = False
            
            # self.timeleft = 0 looks like not necessary...


screen timer_screen:
    on "show" action Function(renpy.retain_after_load)
    zorder 3
    
    for i in range(Building.buildings):
    	if Building.buildings[i].timeleft > 0 and Building.buildings[i].started:
    		timer 1.0 repeat True action [
    			SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1),
    			If(
    				Building.buildings[i].timeleft == 0,
    				Function(Building.buildings[i].end_building_timer)
    			)
    		]
        
	$ i_ = i + 1
    	vbox:
        	text ("building[i_].time_build: " + str(Building.buildings[i].time_build))
        	text ("building[i_].timeleft: " + str(Building.buildings[i].timeleft))
        	text "nbr_building: [nbr_building]"
       		textbutton "create building" action Function(Building.buildings[i].start_building_timer)


# define building01 = Building("house", "",0, 0, 15)
# define nbr_building = 0
default building01 = Building("house", "",0, 0, 15)
default nbr_building = 0

label start:

    show screen timer_screen
    "You've created a new Ren'Py game."

    return
May I also tell that if you want your objects to participate in game load you should default them instead of defining them. Defines are meant for constants and fixed values, and variables that you do not plan to change at all...
There is a problem...

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/script.rpy", line 56, in script
    "You've created a new Ren'Py game."
  File "renpy/common/000window.rpy", line 98, in _window_auto_callback
    _window_show()
  File "renpy/common/000window.rpy", line 60, in _window_show
    renpy.with_statement(trans)
  File "game/script.rpy", line 27, in execute
    screen timer_screen:
  File "game/script.rpy", line 27, in execute
    screen timer_screen:
  File "game/script.rpy", line 31, in execute
    for i in range(Building.buildings):
TypeError: range() integer end argument expected, got RevertableList.

I use "define" when I type the code and write the program to quickly reload it. But for the finale game I use "default"

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Projects: The Button Man
Organization: NILA
Github: hell-oh-world
Location: Philippines
Contact:

Re: How can I create many different objects with the same timer code

#5 Post by hell_oh_world » Tue Apr 28, 2020 7:17 am

renardjap wrote:
Tue Apr 28, 2020 6:56 am
hell_oh_world wrote:
Tue Apr 28, 2020 5:35 am

Again, looks like can be solved by class attributes and a loop... I haven't tested it yet but should work I guess...

Code: Select all

init python:
    
    class Building:
    	buildings = []
    
        def __init__(self, name, img, loc = 0, time_build = 0): # removed the timeleft param...
            self.name = name
            self.img = img
            
            # self.timeleft = timeleft
            self.timeleft = time_build # set the timeleft directly to the time build
            
            self.time_build = time_build # the time needed to build the building 
            
            self.started = False # flag to set when the build process starts
            
            Building.loc = loc #not important for my question 
            Building.buildings.append(self)

        def start_building_timer(self):
            # self.timeleft = self.time_build looks like not necessary...
            
            self.started = True

        def end_building_timer(self):
            store.nbr_building += 1
            
            self.started = False
            
            # self.timeleft = 0 looks like not necessary...


screen timer_screen:
    on "show" action Function(renpy.retain_after_load)
    zorder 3
    
    for i in range(Building.buildings):
    	if Building.buildings[i].timeleft > 0 and Building.buildings[i].started:
    		timer 1.0 repeat True action [
    			SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1),
    			If(
    				Building.buildings[i].timeleft == 0,
    				Function(Building.buildings[i].end_building_timer)
    			)
    		]
        
	$ i_ = i + 1
    	vbox:
        	text ("building[i_].time_build: " + str(Building.buildings[i].time_build))
        	text ("building[i_].timeleft: " + str(Building.buildings[i].timeleft))
        	text "nbr_building: [nbr_building]"
       		textbutton "create building" action Function(Building.buildings[i].start_building_timer)


# define building01 = Building("house", "",0, 0, 15)
# define nbr_building = 0
default building01 = Building("house", "",0, 0, 15)
default nbr_building = 0

label start:

    show screen timer_screen
    "You've created a new Ren'Py game."

    return
May I also tell that if you want your objects to participate in game load you should default them instead of defining them. Defines are meant for constants and fixed values, and variables that you do not plan to change at all...
There is a problem...

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/script.rpy", line 56, in script
    "You've created a new Ren'Py game."
  File "renpy/common/000window.rpy", line 98, in _window_auto_callback
    _window_show()
  File "renpy/common/000window.rpy", line 60, in _window_show
    renpy.with_statement(trans)
  File "game/script.rpy", line 27, in execute
    screen timer_screen:
  File "game/script.rpy", line 27, in execute
    screen timer_screen:
  File "game/script.rpy", line 31, in execute
    for i in range(Building.buildings):
TypeError: range() integer end argument expected, got RevertableList.

I use "define" when I type the code and write the program to quickly reload it. But for the finale game I use "default"
should be...

Code: Select all

range(Building.buildings) --> range(len(Building.buildings))

User avatar
renardjap
Regular
Posts: 75
Joined: Sun Aug 05, 2018 1:08 pm
Location: France ! Cocorico
Contact:

Re: How can I create many different objects with the same timer code

#6 Post by renardjap » Tue Apr 28, 2020 7:24 am

The error is solved but when I click for the first time on the textbutton "create building", the timer start but when it reach 0, the nrb_building isn't increase and stay at 0.
And when I click the second time on the textbutton "create building", nothing happen.
Last edited by renardjap on Tue Apr 28, 2020 12:14 pm, edited 2 times in total.

User avatar
renardjap
Regular
Posts: 75
Joined: Sun Aug 05, 2018 1:08 pm
Location: France ! Cocorico
Contact:

Re: How can I create many different objects with the same timer code

#7 Post by renardjap » Tue Apr 28, 2020 12:08 pm

renardjap wrote:
Tue Apr 28, 2020 7:24 am
The error is solve but when I click on the first time on the textbutton "create building", the timer start but when it reach 0, the nrb_building isn't increase and stay at 0.
And when I click the second time on the textbutton "create building", nothing happen.
I finally found a solution :)

Code: Select all

init python:
    
    class Building:
        buildings = []
        
        def __init__(self, name, img, loc = 0, time_build = 0, timeleft = 0): # removed the timeleft param... <== I need the timeleft in the param to build many times the same building
            self.name = name
            self.img = img
            self.timeleft = timeleft
            self.time_build = time_build 
            
            Building.loc = loc #not important for my question 
            Building.buildings.append(self)
            
        def start_building_timer(self):
            if self.timeleft > 0:
                pass # the player can not reset the timer once started !
            else:
                self.timeleft = self.time_build
            
        def end_building_timer(self):
            store.nbr_building += 1
            self.timeleft = 0 

screen timer_screen:
    on "show" action Function(renpy.retain_after_load)
    zorder 3
    
    for i in range(len(Building.buildings)):
        #if Building.buildings[i].timeleft > 0 and Building.buildings[i].started == True:
        if Building.buildings[i].timeleft > 1: # I change this part of the code
            timer 1.0 repeat True action SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1)
        elif Building.buildings[i].timeleft == 1:
            timer 1.0 action Function(Building.buildings[i].end_building_timer)
                    
    vbox:
        text "building01.timeleft: [building01.timeleft] "
        text "building02.timeleft: [building02.timeleft] "
        text "nbr_building: [nbr_building]"
        textbutton "create building01" action Function(building01.start_building_timer)
        textbutton "create building01" action Function(building02.start_building_timer)


define building01 = Building("house", "",0, 5)
define building02 = Building("military", "",0, 8)
define nbr_building = 0

label start:
    show screen timer_screen
    "You've created a new Ren'Py game."
    return
This field was the problem

Code: Select all

    			SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1),
    			If(
    				Building.buildings[i].timeleft == 0,
    				Function(Building.buildings[i].end_building_timer)
    			)
When the timer ends, the Function(Building.buildings[].end_building_timer) is not call... I don't know why. I finally change it by my old one

Code: Select all

        if Building.buildings[i].timeleft > 1: # I change this part of the code
            timer 1.0 repeat True action SetField(Building.buildings[i], "timeleft", Building.buildings[i].timeleft - 1)
        elif Building.buildings[i].timeleft == 1:
            timer 1.0 action Function(Building.buildings[i].end_building_timer)
It's maybe less elegant, but it works fine.

Thank you very much for your help !!! You're right, I must work on the class attributes and the loop ! I will do more exercises !

I have a question: I'm not sure what does the SetField() I checked the wiki but there are not a lot of info... I don't really understand how it works and what it does...

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Projects: The Button Man
Organization: NILA
Github: hell-oh-world
Location: Philippines
Contact:

Re: How can I create many different objects with the same timer code

#8 Post by hell_oh_world » Tue Apr 28, 2020 12:46 pm

renardjap wrote:
Tue Apr 28, 2020 12:08 pm
I have a question: I'm not sure what does the SetField() I checked the wiki but there are not a lot of info... I don't really understand how it works and what it does...
https://www.renpy.org/doc/html/screen_a ... l#SetField
it just sets the attribute of an object or a class, it works like SetVariable(), but since you're dealing with objects, it's more appropriate to use SetField() as it's designed for objects and classes.

User avatar
renardjap
Regular
Posts: 75
Joined: Sun Aug 05, 2018 1:08 pm
Location: France ! Cocorico
Contact:

Re: How can I create many different objects with the same timer code

#9 Post by renardjap » Tue Apr 28, 2020 1:36 pm

ok thank you :)

Post Reply

Who is online

Users browsing this forum: Google [Bot]