1 /*************************************************************************/
2 /*  visibility_notifier_2d.cpp                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
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 #include "visibility_notifier_2d.h"
31 
32 #include "particles_2d.h"
33 #include "scene/2d/animated_sprite.h"
34 #include "scene/2d/physics_body_2d.h"
35 #include "scene/animation/animation_player.h"
36 #include "scene/scene_string_names.h"
37 
_enter_viewport(Viewport * p_viewport)38 void VisibilityNotifier2D::_enter_viewport(Viewport *p_viewport) {
39 
40 	ERR_FAIL_COND(viewports.has(p_viewport));
41 	viewports.insert(p_viewport);
42 
43 	if (is_inside_tree() && get_tree()->is_editor_hint())
44 		return;
45 
46 	if (viewports.size() == 1) {
47 		emit_signal(SceneStringNames::get_singleton()->enter_screen);
48 
49 		_screen_enter();
50 	}
51 	emit_signal(SceneStringNames::get_singleton()->enter_viewport, p_viewport);
52 }
53 
_exit_viewport(Viewport * p_viewport)54 void VisibilityNotifier2D::_exit_viewport(Viewport *p_viewport) {
55 
56 	ERR_FAIL_COND(!viewports.has(p_viewport));
57 	viewports.erase(p_viewport);
58 
59 	if (is_inside_tree() && get_tree()->is_editor_hint())
60 		return;
61 
62 	emit_signal(SceneStringNames::get_singleton()->exit_viewport, p_viewport);
63 	if (viewports.size() == 0) {
64 		emit_signal(SceneStringNames::get_singleton()->exit_screen);
65 
66 		_screen_exit();
67 	}
68 }
69 
set_rect(const Rect2 & p_rect)70 void VisibilityNotifier2D::set_rect(const Rect2 &p_rect) {
71 
72 	rect = p_rect;
73 	if (is_inside_tree()) {
74 		get_world_2d()->_update_notifier(this, get_global_transform().xform(rect));
75 		if (get_tree()->is_editor_hint()) {
76 			update();
77 			item_rect_changed();
78 		}
79 	}
80 
81 	_change_notify("rect");
82 }
83 
get_item_rect() const84 Rect2 VisibilityNotifier2D::get_item_rect() const {
85 
86 	return rect;
87 }
88 
get_rect() const89 Rect2 VisibilityNotifier2D::get_rect() const {
90 
91 	return rect;
92 }
93 
_notification(int p_what)94 void VisibilityNotifier2D::_notification(int p_what) {
95 
96 	switch (p_what) {
97 		case NOTIFICATION_ENTER_TREE: {
98 
99 			//get_world_2d()->
100 			get_world_2d()->_register_notifier(this, get_global_transform().xform(rect));
101 		} break;
102 		case NOTIFICATION_TRANSFORM_CHANGED: {
103 
104 			//get_world_2d()->
105 			get_world_2d()->_update_notifier(this, get_global_transform().xform(rect));
106 		} break;
107 		case NOTIFICATION_DRAW: {
108 
109 			if (get_tree()->is_editor_hint()) {
110 
111 				draw_rect(rect, Color(1, 0.5, 1, 0.2));
112 			}
113 		} break;
114 		case NOTIFICATION_EXIT_TREE: {
115 
116 			get_world_2d()->_remove_notifier(this);
117 		} break;
118 	}
119 }
120 
is_on_screen() const121 bool VisibilityNotifier2D::is_on_screen() const {
122 
123 	return viewports.size() > 0;
124 }
125 
_bind_methods()126 void VisibilityNotifier2D::_bind_methods() {
127 
128 	ObjectTypeDB::bind_method(_MD("set_rect", "rect"), &VisibilityNotifier2D::set_rect);
129 	ObjectTypeDB::bind_method(_MD("get_rect"), &VisibilityNotifier2D::get_rect);
130 	ObjectTypeDB::bind_method(_MD("is_on_screen"), &VisibilityNotifier2D::is_on_screen);
131 
132 	ADD_PROPERTY(PropertyInfo(Variant::RECT2, "rect"), _SCS("set_rect"), _SCS("get_rect"));
133 
134 	ADD_SIGNAL(MethodInfo("enter_viewport", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport")));
135 	ADD_SIGNAL(MethodInfo("exit_viewport", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport")));
136 	ADD_SIGNAL(MethodInfo("enter_screen"));
137 	ADD_SIGNAL(MethodInfo("exit_screen"));
138 }
139 
VisibilityNotifier2D()140 VisibilityNotifier2D::VisibilityNotifier2D() {
141 
142 	rect = Rect2(-10, -10, 20, 20);
143 }
144 
145 //////////////////////////////////////
146 
_screen_enter()147 void VisibilityEnabler2D::_screen_enter() {
148 
149 	for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
150 
151 		_change_node_state(E->key(), true);
152 	}
153 
154 	if (enabler[ENABLER_PARENT_FIXED_PROCESS] && get_parent())
155 		get_parent()->set_fixed_process(true);
156 	if (enabler[ENABLER_PARENT_PROCESS] && get_parent())
157 		get_parent()->set_process(true);
158 
159 	visible = true;
160 }
161 
_screen_exit()162 void VisibilityEnabler2D::_screen_exit() {
163 
164 	for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
165 
166 		_change_node_state(E->key(), false);
167 	}
168 
169 	if (enabler[ENABLER_PARENT_FIXED_PROCESS] && get_parent())
170 		get_parent()->set_fixed_process(false);
171 	if (enabler[ENABLER_PARENT_PROCESS] && get_parent())
172 		get_parent()->set_process(false);
173 
174 	visible = false;
175 }
176 
_find_nodes(Node * p_node)177 void VisibilityEnabler2D::_find_nodes(Node *p_node) {
178 
179 	bool add = false;
180 	Variant meta;
181 
182 	if (enabler[ENABLER_FREEZE_BODIES]) {
183 
184 		RigidBody2D *rb2d = p_node->cast_to<RigidBody2D>();
185 		if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_CHARACTER || (rb2d->get_mode() == RigidBody2D::MODE_RIGID && !rb2d->is_able_to_sleep())))) {
186 
187 			add = true;
188 			meta = rb2d->get_mode();
189 		}
190 	}
191 
192 	if (enabler[ENABLER_PAUSE_ANIMATIONS]) {
193 
194 		AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>();
195 		if (ap) {
196 			add = true;
197 		}
198 	}
199 
200 	if (enabler[ENABLER_PAUSE_ANIMATED_SPRITES]) {
201 
202 		AnimatedSprite *as = p_node->cast_to<AnimatedSprite>();
203 		if (as) {
204 			add = true;
205 		}
206 	}
207 
208 	if (enabler[ENABLER_PAUSE_PARTICLES]) {
209 
210 		Particles2D *ps = p_node->cast_to<Particles2D>();
211 		if (ps) {
212 			add = true;
213 		}
214 	}
215 
216 	if (add) {
217 
218 		p_node->connect(SceneStringNames::get_singleton()->exit_tree, this, "_node_removed", varray(p_node), CONNECT_ONESHOT);
219 		nodes[p_node] = meta;
220 		_change_node_state(p_node, false);
221 	}
222 
223 	for (int i = 0; i < p_node->get_child_count(); i++) {
224 		Node *c = p_node->get_child(i);
225 		if (c->get_filename() != String())
226 			continue; //skip, instance
227 
228 		_find_nodes(c);
229 	}
230 }
231 
_notification(int p_what)232 void VisibilityEnabler2D::_notification(int p_what) {
233 
234 	if (p_what == NOTIFICATION_ENTER_TREE) {
235 
236 		if (get_tree()->is_editor_hint())
237 			return;
238 
239 		Node *from = this;
240 		//find where current scene starts
241 		while (from->get_parent() && from->get_filename() == String())
242 			from = from->get_parent();
243 
244 		_find_nodes(from);
245 
246 		if (enabler[ENABLER_PARENT_FIXED_PROCESS] && get_parent())
247 			get_parent()->set_fixed_process(false);
248 		if (enabler[ENABLER_PARENT_PROCESS] && get_parent())
249 			get_parent()->set_process(false);
250 	}
251 
252 	if (p_what == NOTIFICATION_EXIT_TREE) {
253 
254 		if (get_tree()->is_editor_hint())
255 			return;
256 
257 		for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
258 
259 			if (!visible)
260 				_change_node_state(E->key(), true);
261 			E->key()->disconnect(SceneStringNames::get_singleton()->exit_tree, this, "_node_removed");
262 		}
263 
264 		nodes.clear();
265 	}
266 }
267 
_change_node_state(Node * p_node,bool p_enabled)268 void VisibilityEnabler2D::_change_node_state(Node *p_node, bool p_enabled) {
269 
270 	ERR_FAIL_COND(!nodes.has(p_node));
271 
272 	{
273 		RigidBody2D *rb = p_node->cast_to<RigidBody2D>();
274 		if (rb) {
275 
276 			rb->set_sleeping(!p_enabled);
277 		}
278 	}
279 
280 	{
281 		AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>();
282 
283 		if (ap) {
284 
285 			ap->set_active(p_enabled);
286 		}
287 	}
288 	{
289 		AnimatedSprite *as = p_node->cast_to<AnimatedSprite>();
290 
291 		if (as) {
292 
293 			if (p_enabled)
294 				as->play();
295 			else
296 				as->stop();
297 		}
298 	}
299 
300 	{
301 		Particles2D *ps = p_node->cast_to<Particles2D>();
302 
303 		if (ps) {
304 
305 			ps->set_emitting(p_enabled);
306 		}
307 	}
308 }
309 
_node_removed(Node * p_node)310 void VisibilityEnabler2D::_node_removed(Node *p_node) {
311 
312 	if (!visible)
313 		_change_node_state(p_node, true);
314 	//changed to one shot, not needed
315 	//p_node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed");
316 	nodes.erase(p_node);
317 }
318 
get_configuration_warning() const319 String VisibilityEnabler2D::get_configuration_warning() const {
320 #ifdef TOOLS_ENABLED
321 	if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) {
322 		return TTR("VisibilityEnable2D works best when used with the edited scene root directly as parent.");
323 	}
324 #endif
325 	return String();
326 }
327 
_bind_methods()328 void VisibilityEnabler2D::_bind_methods() {
329 
330 	ObjectTypeDB::bind_method(_MD("set_enabler", "enabler", "enabled"), &VisibilityEnabler2D::set_enabler);
331 	ObjectTypeDB::bind_method(_MD("is_enabler_enabled", "enabler"), &VisibilityEnabler2D::is_enabler_enabled);
332 	ObjectTypeDB::bind_method(_MD("_node_removed"), &VisibilityEnabler2D::_node_removed);
333 
334 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/pause_animations"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATIONS);
335 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/freeze_bodies"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_FREEZE_BODIES);
336 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/pause_particles"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_PAUSE_PARTICLES);
337 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/pause_animated_sprites"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATED_SPRITES);
338 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/process_parent"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_PARENT_PROCESS);
339 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "enabler/fixed_process_parent"), _SCS("set_enabler"), _SCS("is_enabler_enabled"), ENABLER_PARENT_FIXED_PROCESS);
340 
341 	BIND_CONSTANT(ENABLER_FREEZE_BODIES);
342 	BIND_CONSTANT(ENABLER_PAUSE_ANIMATIONS);
343 	BIND_CONSTANT(ENABLER_PAUSE_PARTICLES);
344 	BIND_CONSTANT(ENABLER_PAUSE_ANIMATED_SPRITES);
345 	BIND_CONSTANT(ENABLER_PARENT_PROCESS);
346 	BIND_CONSTANT(ENABLER_PARENT_FIXED_PROCESS);
347 	BIND_CONSTANT(ENABLER_MAX);
348 }
349 
set_enabler(Enabler p_enabler,bool p_enable)350 void VisibilityEnabler2D::set_enabler(Enabler p_enabler, bool p_enable) {
351 
352 	ERR_FAIL_INDEX(p_enabler, ENABLER_MAX);
353 	enabler[p_enabler] = p_enable;
354 }
is_enabler_enabled(Enabler p_enabler) const355 bool VisibilityEnabler2D::is_enabler_enabled(Enabler p_enabler) const {
356 
357 	ERR_FAIL_INDEX_V(p_enabler, ENABLER_MAX, false);
358 	return enabler[p_enabler];
359 }
360 
VisibilityEnabler2D()361 VisibilityEnabler2D::VisibilityEnabler2D() {
362 
363 	for (int i = 0; i < ENABLER_MAX; i++)
364 		enabler[i] = true;
365 	enabler[ENABLER_PARENT_PROCESS] = false;
366 	enabler[ENABLER_PARENT_FIXED_PROCESS] = false;
367 
368 	visible = false;
369 }
370