1/**************************************************************************/
2/* timer.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "timer.h"
32
33void Timer::_notification(int p_what) {
34 switch (p_what) {
35 case NOTIFICATION_READY: {
36 if (autostart) {
37#ifdef TOOLS_ENABLED
38 if (is_part_of_edited_scene()) {
39 break;
40 }
41#endif
42 start();
43 autostart = false;
44 }
45 } break;
46
47 case NOTIFICATION_INTERNAL_PROCESS: {
48 if (!processing || timer_process_callback == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
49 return;
50 }
51 time_left -= get_process_delta_time();
52
53 if (time_left < 0) {
54 if (!one_shot) {
55 time_left += wait_time;
56 } else {
57 stop();
58 }
59
60 emit_signal(SNAME("timeout"));
61 }
62 } break;
63
64 case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
65 if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
66 return;
67 }
68 time_left -= get_physics_process_delta_time();
69
70 if (time_left < 0) {
71 if (!one_shot) {
72 time_left += wait_time;
73 } else {
74 stop();
75 }
76 emit_signal(SNAME("timeout"));
77 }
78 } break;
79 }
80}
81
82void Timer::set_wait_time(double p_time) {
83 ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero.");
84 wait_time = p_time;
85 update_configuration_warnings();
86}
87
88double Timer::get_wait_time() const {
89 return wait_time;
90}
91
92void Timer::set_one_shot(bool p_one_shot) {
93 one_shot = p_one_shot;
94}
95
96bool Timer::is_one_shot() const {
97 return one_shot;
98}
99
100void Timer::set_autostart(bool p_start) {
101 autostart = p_start;
102}
103
104bool Timer::has_autostart() const {
105 return autostart;
106}
107
108void Timer::start(double p_time) {
109 ERR_FAIL_COND_MSG(!is_inside_tree(), "Timer was not added to the SceneTree. Either add it or set autostart to true.");
110
111 if (p_time > 0) {
112 set_wait_time(p_time);
113 }
114 time_left = wait_time;
115 _set_process(true);
116}
117
118void Timer::stop() {
119 time_left = -1;
120 _set_process(false);
121 autostart = false;
122}
123
124void Timer::set_paused(bool p_paused) {
125 if (paused == p_paused) {
126 return;
127 }
128
129 paused = p_paused;
130 _set_process(processing);
131}
132
133bool Timer::is_paused() const {
134 return paused;
135}
136
137bool Timer::is_stopped() const {
138 return get_time_left() <= 0;
139}
140
141double Timer::get_time_left() const {
142 return time_left > 0 ? time_left : 0;
143}
144
145void Timer::set_timer_process_callback(TimerProcessCallback p_callback) {
146 if (timer_process_callback == p_callback) {
147 return;
148 }
149
150 switch (timer_process_callback) {
151 case TIMER_PROCESS_PHYSICS:
152 if (is_physics_processing_internal()) {
153 set_physics_process_internal(false);
154 set_process_internal(true);
155 }
156 break;
157 case TIMER_PROCESS_IDLE:
158 if (is_processing_internal()) {
159 set_process_internal(false);
160 set_physics_process_internal(true);
161 }
162 break;
163 }
164 timer_process_callback = p_callback;
165}
166
167Timer::TimerProcessCallback Timer::get_timer_process_callback() const {
168 return timer_process_callback;
169}
170
171void Timer::_set_process(bool p_process, bool p_force) {
172 switch (timer_process_callback) {
173 case TIMER_PROCESS_PHYSICS:
174 set_physics_process_internal(p_process && !paused);
175 break;
176 case TIMER_PROCESS_IDLE:
177 set_process_internal(p_process && !paused);
178 break;
179 }
180 processing = p_process;
181}
182
183PackedStringArray Timer::get_configuration_warnings() const {
184 PackedStringArray warnings = Node::get_configuration_warnings();
185
186 if (wait_time < 0.05 - CMP_EPSILON) {
187 warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
188 }
189
190 return warnings;
191}
192
193void Timer::_bind_methods() {
194 ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time);
195 ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time);
196
197 ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &Timer::set_one_shot);
198 ClassDB::bind_method(D_METHOD("is_one_shot"), &Timer::is_one_shot);
199
200 ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart);
201 ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart);
202
203 ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1));
204 ClassDB::bind_method(D_METHOD("stop"), &Timer::stop);
205
206 ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused);
207 ClassDB::bind_method(D_METHOD("is_paused"), &Timer::is_paused);
208
209 ClassDB::bind_method(D_METHOD("is_stopped"), &Timer::is_stopped);
210
211 ClassDB::bind_method(D_METHOD("get_time_left"), &Timer::get_time_left);
212
213 ClassDB::bind_method(D_METHOD("set_timer_process_callback", "callback"), &Timer::set_timer_process_callback);
214 ClassDB::bind_method(D_METHOD("get_timer_process_callback"), &Timer::get_timer_process_callback);
215
216 ADD_SIGNAL(MethodInfo("timeout"));
217
218 ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_timer_process_callback", "get_timer_process_callback");
219 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_RANGE, "0.001,4096,0.001,or_greater,exp,suffix:s"), "set_wait_time", "get_wait_time");
220 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot");
221 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart");
222 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paused", "is_paused");
223 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_left", PROPERTY_HINT_NONE, "suffix:s", PROPERTY_USAGE_NONE), "", "get_time_left");
224
225 BIND_ENUM_CONSTANT(TIMER_PROCESS_PHYSICS);
226 BIND_ENUM_CONSTANT(TIMER_PROCESS_IDLE);
227}
228
229Timer::Timer() {}
230