1 /*************************************************************************/
2 /*  animation_state_machine_editor.cpp                                   */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 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 
31 #include "animation_state_machine_editor.h"
32 
33 #include "core/io/resource_loader.h"
34 #include "core/math/delaunay.h"
35 #include "core/os/input.h"
36 #include "core/os/keyboard.h"
37 #include "core/project_settings.h"
38 #include "editor/editor_scale.h"
39 #include "scene/animation/animation_blend_tree.h"
40 #include "scene/animation/animation_player.h"
41 #include "scene/gui/menu_button.h"
42 #include "scene/gui/panel.h"
43 #include "scene/main/viewport.h"
44 
can_edit(const Ref<AnimationNode> & p_node)45 bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) {
46 
47 	Ref<AnimationNodeStateMachine> ansm = p_node;
48 	return ansm.is_valid();
49 }
50 
edit(const Ref<AnimationNode> & p_node)51 void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
52 
53 	state_machine = p_node;
54 
55 	if (state_machine.is_valid()) {
56 
57 		selected_transition_from = StringName();
58 		selected_transition_to = StringName();
59 		selected_node = StringName();
60 		_update_mode();
61 		_update_graph();
62 	}
63 }
64 
_state_machine_gui_input(const Ref<InputEvent> & p_event)65 void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) {
66 
67 	Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
68 	if (playback.is_null())
69 		return;
70 
71 	Ref<InputEventKey> k = p_event;
72 	if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) {
73 		if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) {
74 			_erase_selected();
75 			accept_event();
76 		}
77 	}
78 
79 	Ref<InputEventMouseButton> mb = p_event;
80 
81 	//Add new node
82 	if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == BUTTON_LEFT))) {
83 		menu->clear();
84 		animations_menu->clear();
85 		animations_to_add.clear();
86 		List<StringName> classes;
87 		classes.sort_custom<StringName::AlphCompare>();
88 
89 		ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
90 		menu->add_submenu_item(TTR("Add Animation"), "animations");
91 
92 		AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree();
93 		ERR_FAIL_COND(!gp);
94 		if (gp && gp->has_node(gp->get_animation_player())) {
95 			AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player()));
96 			if (ap) {
97 				List<StringName> names;
98 				ap->get_animation_list(&names);
99 				for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
100 					animations_menu->add_icon_item(get_icon("Animation", "EditorIcons"), E->get());
101 					animations_to_add.push_back(E->get());
102 				}
103 			}
104 		}
105 
106 		for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
107 
108 			String name = String(E->get()).replace_first("AnimationNode", "");
109 			if (name == "Animation")
110 				continue; // nope
111 			int idx = menu->get_item_count();
112 			menu->add_item(vformat("Add %s", name), idx);
113 			menu->set_item_metadata(idx, E->get());
114 		}
115 		Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard();
116 
117 		if (clipb.is_valid()) {
118 			menu->add_separator();
119 			menu->add_item(TTR("Paste"), MENU_PASTE);
120 		}
121 		menu->add_separator();
122 		menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
123 
124 		menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position()));
125 		menu->popup();
126 		add_node_pos = mb->get_position() / EDSCALE + state_machine->get_graph_offset();
127 	}
128 
129 	// select node or push a field inside
130 	if (mb.is_valid() && !mb->get_shift() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
131 
132 		selected_transition_from = StringName();
133 		selected_transition_to = StringName();
134 		selected_node = StringName();
135 
136 		for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
137 
138 			if (node_rects[i].play.has_point(mb->get_position())) { //edit name
139 				if (play_mode->get_selected() == 1 || !playback->is_playing()) {
140 					//start
141 					playback->start(node_rects[i].node_name);
142 				} else {
143 					//travel
144 					playback->travel(node_rects[i].node_name);
145 				}
146 				state_machine_draw->update();
147 				return;
148 			}
149 
150 			if (node_rects[i].name.has_point(mb->get_position())) { //edit name
151 
152 				Ref<StyleBox> line_sb = get_stylebox("normal", "LineEdit");
153 
154 				Rect2 edit_rect = node_rects[i].name;
155 				edit_rect.position -= line_sb->get_offset();
156 				edit_rect.size += line_sb->get_minimum_size();
157 
158 				name_edit->set_global_position(state_machine_draw->get_global_transform().xform(edit_rect.position));
159 				name_edit->set_size(edit_rect.size);
160 				name_edit->set_text(node_rects[i].node_name);
161 				name_edit->show_modal();
162 				name_edit->grab_focus();
163 				name_edit->select_all();
164 
165 				prev_name = node_rects[i].node_name;
166 				return;
167 			}
168 
169 			if (node_rects[i].edit.has_point(mb->get_position())) { //edit name
170 				call_deferred("_open_editor", node_rects[i].node_name);
171 				return;
172 			}
173 
174 			if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
175 				selected_node = node_rects[i].node_name;
176 
177 				Ref<AnimationNode> anode = state_machine->get_node(selected_node);
178 				EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
179 				state_machine_draw->update();
180 				dragging_selected_attempt = true;
181 				dragging_selected = false;
182 				drag_from = mb->get_position();
183 				snap_x = StringName();
184 				snap_y = StringName();
185 				_update_mode();
186 				return;
187 			}
188 		}
189 
190 		//test the lines now
191 		int closest = -1;
192 		float closest_d = 1e20;
193 		for (int i = 0; i < transition_lines.size(); i++) {
194 
195 			Vector2 s[2] = {
196 				transition_lines[i].from,
197 				transition_lines[i].to
198 			};
199 			Vector2 cpoint = Geometry::get_closest_point_to_segment_2d(mb->get_position(), s);
200 			float d = cpoint.distance_to(mb->get_position());
201 			if (d > transition_lines[i].width) {
202 				continue;
203 			}
204 
205 			if (d < closest_d) {
206 				closest = i;
207 				closest_d = d;
208 			}
209 		}
210 
211 		if (closest >= 0) {
212 			selected_transition_from = transition_lines[closest].from_node;
213 			selected_transition_to = transition_lines[closest].to_node;
214 
215 			Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest);
216 			EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
217 		}
218 
219 		state_machine_draw->update();
220 		_update_mode();
221 	}
222 
223 	//end moving node
224 	if (mb.is_valid() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
225 
226 		if (dragging_selected) {
227 
228 			Ref<AnimationNode> an = state_machine->get_node(selected_node);
229 			updating = true;
230 			undo_redo->create_action(TTR("Move Node"));
231 			undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE);
232 			undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node));
233 			undo_redo->add_do_method(this, "_update_graph");
234 			undo_redo->add_undo_method(this, "_update_graph");
235 			undo_redo->commit_action();
236 			updating = false;
237 		}
238 		snap_x = StringName();
239 		snap_y = StringName();
240 
241 		dragging_selected_attempt = false;
242 		dragging_selected = false;
243 		state_machine_draw->update();
244 	}
245 
246 	//connect nodes
247 	if (mb.is_valid() && ((tool_select->is_pressed() && mb->get_shift()) || tool_connect->is_pressed()) && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
248 
249 		for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
250 			if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
251 				connecting = true;
252 				connecting_from = node_rects[i].node_name;
253 				connecting_to = mb->get_position();
254 				connecting_to_node = StringName();
255 				return;
256 			}
257 		}
258 	}
259 
260 	//end connecting nodes
261 	if (mb.is_valid() && connecting && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
262 
263 		if (connecting_to_node != StringName()) {
264 
265 			if (state_machine->has_transition(connecting_from, connecting_to_node)) {
266 				EditorNode::get_singleton()->show_warning(TTR("Transition exists!"));
267 
268 			} else {
269 
270 				Ref<AnimationNodeStateMachineTransition> tr;
271 				tr.instance();
272 				tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()));
273 
274 				updating = true;
275 				undo_redo->create_action(TTR("Add Transition"));
276 				undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr);
277 				undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node);
278 				undo_redo->add_do_method(this, "_update_graph");
279 				undo_redo->add_undo_method(this, "_update_graph");
280 				undo_redo->commit_action();
281 				updating = false;
282 
283 				selected_transition_from = connecting_from;
284 				selected_transition_to = connecting_to_node;
285 
286 				EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
287 				_update_mode();
288 			}
289 		}
290 		connecting_to_node = StringName();
291 		connecting = false;
292 		state_machine_draw->update();
293 	}
294 
295 	Ref<InputEventMouseMotion> mm = p_event;
296 
297 	//pan window
298 	if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
299 
300 		h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
301 		v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
302 	}
303 
304 	//move mouse while connecting
305 	if (mm.is_valid() && connecting) {
306 
307 		connecting_to = mm->get_position();
308 		connecting_to_node = StringName();
309 		state_machine_draw->update();
310 
311 		for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
312 			if (node_rects[i].node_name != connecting_from && node_rects[i].node.has_point(connecting_to)) { //select node since nothing else was selected
313 				connecting_to_node = node_rects[i].node_name;
314 				return;
315 			}
316 		}
317 	}
318 
319 	//move mouse while moving a node
320 	if (mm.is_valid() && dragging_selected_attempt) {
321 
322 		dragging_selected = true;
323 		drag_ofs = mm->get_position() - drag_from;
324 		snap_x = StringName();
325 		snap_y = StringName();
326 		{
327 			//snap
328 			Vector2 cpos = state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE;
329 			List<StringName> nodes;
330 			state_machine->get_node_list(&nodes);
331 
332 			float best_d_x = 1e20;
333 			float best_d_y = 1e20;
334 
335 			for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
336 				if (E->get() == selected_node)
337 					continue;
338 				Vector2 npos = state_machine->get_node_position(E->get());
339 
340 				float d_x = ABS(npos.x - cpos.x);
341 				if (d_x < MIN(5, best_d_x)) {
342 					drag_ofs.x -= cpos.x - npos.x;
343 					best_d_x = d_x;
344 					snap_x = E->get();
345 				}
346 
347 				float d_y = ABS(npos.y - cpos.y);
348 				if (d_y < MIN(5, best_d_y)) {
349 					drag_ofs.y -= cpos.y - npos.y;
350 					best_d_y = d_y;
351 					snap_y = E->get();
352 				}
353 			}
354 		}
355 
356 		state_machine_draw->update();
357 	}
358 
359 	//put ibeam (text cursor) over names to make it clearer that they are editable
360 	if (mm.is_valid()) {
361 
362 		state_machine_draw->grab_focus();
363 
364 		bool over_text_now = false;
365 		String new_over_node = StringName();
366 		int new_over_node_what = -1;
367 		if (tool_select->is_pressed()) {
368 
369 			for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
370 
371 				if (node_rects[i].name.has_point(mm->get_position())) {
372 					over_text_now = true;
373 					break;
374 				}
375 
376 				if (node_rects[i].node.has_point(mm->get_position())) {
377 					new_over_node = node_rects[i].node_name;
378 					if (node_rects[i].play.has_point(mm->get_position())) {
379 						new_over_node_what = 0;
380 					}
381 					if (node_rects[i].edit.has_point(mm->get_position())) {
382 						new_over_node_what = 1;
383 					}
384 				}
385 			}
386 		}
387 
388 		if (new_over_node != over_node || new_over_node_what != over_node_what) {
389 			over_node = new_over_node;
390 			over_node_what = new_over_node_what;
391 			state_machine_draw->update();
392 		}
393 
394 		if (over_text != over_text_now) {
395 
396 			if (over_text_now) {
397 				state_machine_draw->set_default_cursor_shape(CURSOR_IBEAM);
398 			} else {
399 				state_machine_draw->set_default_cursor_shape(CURSOR_ARROW);
400 			}
401 
402 			over_text = over_text_now;
403 		}
404 	}
405 }
406 
_file_opened(const String & p_file)407 void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) {
408 
409 	file_loaded = ResourceLoader::load(p_file);
410 	if (file_loaded.is_valid()) {
411 		_add_menu_type(MENU_LOAD_FILE_CONFIRM);
412 	}
413 }
414 
_add_menu_type(int p_index)415 void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) {
416 
417 	String base_name;
418 	Ref<AnimationRootNode> node;
419 
420 	if (p_index == MENU_LOAD_FILE) {
421 
422 		open_file->clear_filters();
423 		List<String> filters;
424 		ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters);
425 		for (List<String>::Element *E = filters.front(); E; E = E->next()) {
426 			open_file->add_filter("*." + E->get());
427 		}
428 		open_file->popup_centered_ratio();
429 		return;
430 	} else if (p_index == MENU_LOAD_FILE_CONFIRM) {
431 		node = file_loaded;
432 		file_loaded.unref();
433 	} else if (p_index == MENU_PASTE) {
434 
435 		node = EditorSettings::get_singleton()->get_resource_clipboard();
436 
437 	} else {
438 		String type = menu->get_item_metadata(p_index);
439 
440 		Object *obj = ClassDB::instance(type);
441 		ERR_FAIL_COND(!obj);
442 		AnimationNode *an = Object::cast_to<AnimationNode>(obj);
443 		ERR_FAIL_COND(!an);
444 
445 		node = Ref<AnimationNode>(an);
446 		base_name = type.replace_first("AnimationNode", "");
447 	}
448 
449 	if (!node.is_valid()) {
450 		EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed."));
451 		return;
452 	}
453 
454 	if (base_name == String()) {
455 
456 		base_name = node->get_class().replace_first("AnimationNode", "");
457 	}
458 
459 	int base = 1;
460 	String name = base_name;
461 	while (state_machine->has_node(name)) {
462 		base++;
463 		name = base_name + " " + itos(base);
464 	}
465 
466 	updating = true;
467 	undo_redo->create_action(TTR("Add Node"));
468 	undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos);
469 	undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
470 	undo_redo->add_do_method(this, "_update_graph");
471 	undo_redo->add_undo_method(this, "_update_graph");
472 	undo_redo->commit_action();
473 	updating = false;
474 
475 	state_machine_draw->update();
476 }
477 
_add_animation_type(int p_index)478 void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) {
479 
480 	Ref<AnimationNodeAnimation> anim;
481 	anim.instance();
482 
483 	anim->set_animation(animations_to_add[p_index]);
484 
485 	String base_name = animations_to_add[p_index];
486 	int base = 1;
487 	String name = base_name;
488 	while (state_machine->has_node(name)) {
489 		base++;
490 		name = base_name + " " + itos(base);
491 	}
492 
493 	updating = true;
494 	undo_redo->create_action(TTR("Add Node"));
495 	undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos);
496 	undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
497 	undo_redo->add_do_method(this, "_update_graph");
498 	undo_redo->add_undo_method(this, "_update_graph");
499 	undo_redo->commit_action();
500 	updating = false;
501 
502 	state_machine_draw->update();
503 }
504 
_connection_draw(const Vector2 & p_from,const Vector2 & p_to,AnimationNodeStateMachineTransition::SwitchMode p_mode,bool p_enabled,bool p_selected,bool p_travel,bool p_auto_advance)505 void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance) {
506 
507 	Color linecolor = get_color("font_color", "Label");
508 	Color icon_color(1, 1, 1);
509 	Color accent = get_color("accent_color", "Editor");
510 
511 	if (!p_enabled) {
512 		linecolor.a *= 0.2;
513 		icon_color.a *= 0.2;
514 		accent.a *= 0.6;
515 	}
516 
517 	Ref<Texture> icons[6] = {
518 		get_icon("TransitionImmediateBig", "EditorIcons"),
519 		get_icon("TransitionSyncBig", "EditorIcons"),
520 		get_icon("TransitionEndBig", "EditorIcons"),
521 		get_icon("TransitionImmediateAutoBig", "EditorIcons"),
522 		get_icon("TransitionSyncAutoBig", "EditorIcons"),
523 		get_icon("TransitionEndAutoBig", "EditorIcons")
524 	};
525 
526 	if (p_selected) {
527 		state_machine_draw->draw_line(p_from, p_to, accent, 6, true);
528 	}
529 
530 	if (p_travel) {
531 		linecolor = accent;
532 		linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v());
533 	}
534 	state_machine_draw->draw_line(p_from, p_to, linecolor, 2, true);
535 
536 	Ref<Texture> icon = icons[p_mode + (p_auto_advance ? 3 : 0)];
537 
538 	Transform2D xf;
539 	xf.elements[0] = (p_to - p_from).normalized();
540 	xf.elements[1] = xf.elements[0].tangent();
541 	xf.elements[2] = (p_from + p_to) * 0.5 - xf.elements[1] * icon->get_height() * 0.5 - xf.elements[0] * icon->get_height() * 0.5;
542 
543 	state_machine_draw->draw_set_transform_matrix(xf);
544 	state_machine_draw->draw_texture(icon, Vector2(), icon_color);
545 	state_machine_draw->draw_set_transform_matrix(Transform2D());
546 }
547 
_clip_src_line_to_rect(Vector2 & r_from,Vector2 & r_to,const Rect2 & p_rect)548 void AnimationNodeStateMachineEditor::_clip_src_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) {
549 
550 	if (r_to == r_from)
551 		return;
552 
553 	//this could be optimized...
554 	Vector2 n = (r_to - r_from).normalized();
555 	while (p_rect.has_point(r_from)) {
556 		r_from += n;
557 	}
558 }
559 
_clip_dst_line_to_rect(Vector2 & r_from,Vector2 & r_to,const Rect2 & p_rect)560 void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) {
561 
562 	if (r_to == r_from)
563 		return;
564 
565 	//this could be optimized...
566 	Vector2 n = (r_to - r_from).normalized();
567 	while (p_rect.has_point(r_to)) {
568 		r_to -= n;
569 	}
570 }
571 
_state_machine_draw()572 void AnimationNodeStateMachineEditor::_state_machine_draw() {
573 
574 	Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
575 
576 	Ref<StyleBox> style = get_stylebox("state_machine_frame", "GraphNode");
577 	Ref<StyleBox> style_selected = get_stylebox("state_machine_selectedframe", "GraphNode");
578 
579 	Ref<Font> font = get_font("title_font", "GraphNode");
580 	Color font_color = get_color("title_color", "GraphNode");
581 	Ref<Texture> play = get_icon("Play", "EditorIcons");
582 	Ref<Texture> auto_play = get_icon("AutoPlay", "EditorIcons");
583 	Ref<Texture> edit = get_icon("Edit", "EditorIcons");
584 	Color accent = get_color("accent_color", "Editor");
585 	Color linecolor = get_color("font_color", "Label");
586 	linecolor.a *= 0.3;
587 	Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode");
588 
589 	bool playing = false;
590 	StringName current;
591 	StringName blend_from;
592 	Vector<StringName> travel_path;
593 
594 	if (playback.is_valid()) {
595 		playing = playback->is_playing();
596 		current = playback->get_current_node();
597 		blend_from = playback->get_blend_from_node();
598 		travel_path = playback->get_travel_path();
599 	}
600 
601 	if (state_machine_draw->has_focus()) {
602 		state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false);
603 	}
604 	int sep = 3 * EDSCALE;
605 
606 	List<StringName> nodes;
607 	state_machine->get_node_list(&nodes);
608 
609 	node_rects.clear();
610 	Rect2 scroll_range;
611 
612 	//snap lines
613 	if (dragging_selected) {
614 
615 		Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE;
616 		if (snap_x != StringName()) {
617 			Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
618 			state_machine_draw->draw_line(from, to, linecolor, 2);
619 		}
620 		if (snap_y != StringName()) {
621 			Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
622 			state_machine_draw->draw_line(from, to, linecolor, 2);
623 		}
624 	}
625 
626 	//pre pass nodes so we know the rectangles
627 	for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
628 
629 		Ref<AnimationNode> anode = state_machine->get_node(E->get());
630 		String name = E->get();
631 		bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr());
632 		Ref<StyleBox> sb = E->get() == selected_node ? style_selected : style;
633 
634 		Size2 s = sb->get_minimum_size();
635 		int strsize = font->get_string_size(name).width;
636 		s.width += strsize;
637 		s.height += MAX(font->get_height(), play->get_height());
638 		s.width += sep + play->get_width();
639 		if (needs_editor) {
640 			s.width += sep + edit->get_width();
641 		}
642 
643 		Vector2 offset;
644 		offset += state_machine->get_node_position(E->get()) * EDSCALE;
645 		if (selected_node == E->get() && dragging_selected) {
646 			offset += drag_ofs;
647 		}
648 		offset -= s / 2;
649 		offset = offset.floor();
650 
651 		//prepre rect
652 
653 		NodeRect nr;
654 		nr.node = Rect2(offset, s);
655 		nr.node_name = E->get();
656 
657 		scroll_range = scroll_range.merge(nr.node); //merge with range
658 
659 		//now scroll it to draw
660 		nr.node.position -= state_machine->get_graph_offset() * EDSCALE;
661 
662 		node_rects.push_back(nr);
663 	}
664 
665 	transition_lines.clear();
666 
667 	//draw connecting line for potential new transition
668 	if (connecting) {
669 		Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
670 		Vector2 to;
671 		if (connecting_to_node != StringName()) {
672 			to = (state_machine->get_node_position(connecting_to_node) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
673 		} else {
674 			to = connecting_to;
675 		}
676 
677 		for (int i = 0; i < node_rects.size(); i++) {
678 			if (node_rects[i].node_name == connecting_from) {
679 				_clip_src_line_to_rect(from, to, node_rects[i].node);
680 			}
681 			if (node_rects[i].node_name == connecting_to_node) {
682 				_clip_dst_line_to_rect(from, to, node_rects[i].node);
683 			}
684 		}
685 
686 		_connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false);
687 	}
688 
689 	Ref<Texture> tr_reference_icon = get_icon("TransitionImmediateBig", "EditorIcons");
690 	float tr_bidi_offset = int(tr_reference_icon->get_height() * 0.8);
691 
692 	//draw transition lines
693 	for (int i = 0; i < state_machine->get_transition_count(); i++) {
694 
695 		TransitionLine tl;
696 		tl.from_node = state_machine->get_transition_from(i);
697 		Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2();
698 		tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE;
699 
700 		tl.to_node = state_machine->get_transition_to(i);
701 		Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2();
702 		tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;
703 
704 		Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
705 		tl.disabled = tr->is_disabled();
706 		tl.auto_advance = tr->has_auto_advance();
707 		tl.advance_condition_name = tr->get_advance_condition_name();
708 		tl.advance_condition_state = false;
709 		tl.mode = tr->get_switch_mode();
710 		tl.width = tr_bidi_offset;
711 
712 		if (state_machine->has_transition(tl.to_node, tl.from_node)) { //offset if same exists
713 			Vector2 offset = -(tl.from - tl.to).normalized().tangent() * tr_bidi_offset;
714 			tl.from += offset;
715 			tl.to += offset;
716 		}
717 
718 		for (int j = 0; j < node_rects.size(); j++) {
719 			if (node_rects[j].node_name == tl.from_node) {
720 				_clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node);
721 			}
722 			if (node_rects[j].node_name == tl.to_node) {
723 				_clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node);
724 			}
725 		}
726 
727 		bool selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node;
728 
729 		bool travel = false;
730 
731 		if (blend_from == tl.from_node && current == tl.to_node) {
732 			travel = true;
733 		}
734 
735 		if (travel_path.size()) {
736 
737 			if (current == tl.from_node && travel_path[0] == tl.to_node) {
738 				travel = true;
739 			} else {
740 				for (int j = 0; j < travel_path.size() - 1; j++) {
741 					if (travel_path[j] == tl.from_node && travel_path[j + 1] == tl.to_node) {
742 						travel = true;
743 						break;
744 					}
745 				}
746 			}
747 		}
748 
749 		bool auto_advance = tl.auto_advance;
750 		StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name);
751 		if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) {
752 			tl.advance_condition_state = true;
753 			auto_advance = true;
754 		}
755 		_connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance);
756 
757 		transition_lines.push_back(tl);
758 	}
759 
760 	//draw actual nodes
761 	for (int i = 0; i < node_rects.size(); i++) {
762 
763 		String name = node_rects[i].node_name;
764 		Ref<AnimationNode> anode = state_machine->get_node(name);
765 		bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode);
766 		Ref<StyleBox> sb = name == selected_node ? style_selected : style;
767 		int strsize = font->get_string_size(name).width;
768 
769 		NodeRect &nr = node_rects.write[i];
770 
771 		Vector2 offset = nr.node.position;
772 		int h = nr.node.size.height;
773 
774 		//prepre rect
775 
776 		//now scroll it to draw
777 		state_machine_draw->draw_style_box(sb, nr.node);
778 
779 		if (playing && (blend_from == name || current == name || travel_path.find(name) != -1)) {
780 			state_machine_draw->draw_style_box(playing_overlay, nr.node);
781 		}
782 
783 		bool onstart = state_machine->get_start_node() == name;
784 		if (onstart) {
785 			state_machine_draw->draw_string(font, offset + Vector2(0, -font->get_height() - 3 * EDSCALE + font->get_ascent()), TTR("Start"), font_color);
786 		}
787 
788 		if (state_machine->get_end_node() == name) {
789 
790 			int endofs = nr.node.size.x - font->get_string_size(TTR("End")).x;
791 			state_machine_draw->draw_string(font, offset + Vector2(endofs, -font->get_height() - 3 * EDSCALE + font->get_ascent()), TTR("End"), font_color);
792 		}
793 
794 		offset.x += sb->get_offset().x;
795 
796 		nr.play.position = offset + Vector2(0, (h - play->get_height()) / 2).floor();
797 		nr.play.size = play->get_size();
798 
799 		Ref<Texture> play_tex = onstart ? auto_play : play;
800 
801 		if (over_node == name && over_node_what == 0) {
802 			state_machine_draw->draw_texture(play_tex, nr.play.position, accent);
803 		} else {
804 			state_machine_draw->draw_texture(play_tex, nr.play.position);
805 		}
806 		offset.x += sep + play->get_width();
807 
808 		nr.name.position = offset + Vector2(0, (h - font->get_height()) / 2).floor();
809 		nr.name.size = Vector2(strsize, font->get_height());
810 
811 		state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent()), name, font_color);
812 		offset.x += strsize + sep;
813 
814 		if (needs_editor) {
815 			nr.edit.position = offset + Vector2(0, (h - edit->get_height()) / 2).floor();
816 			nr.edit.size = edit->get_size();
817 
818 			if (over_node == name && over_node_what == 1) {
819 				state_machine_draw->draw_texture(edit, nr.edit.position, accent);
820 			} else {
821 				state_machine_draw->draw_texture(edit, nr.edit.position);
822 			}
823 			offset.x += sep + edit->get_width();
824 		}
825 	}
826 
827 	scroll_range.position -= state_machine_draw->get_size();
828 	scroll_range.size += state_machine_draw->get_size() * 2.0;
829 
830 	//adjust scrollbars
831 	updating = true;
832 	h_scroll->set_min(scroll_range.position.x);
833 	h_scroll->set_max(scroll_range.position.x + scroll_range.size.x);
834 	h_scroll->set_page(state_machine_draw->get_size().x);
835 	h_scroll->set_value(state_machine->get_graph_offset().x);
836 
837 	v_scroll->set_min(scroll_range.position.y);
838 	v_scroll->set_max(scroll_range.position.y + scroll_range.size.y);
839 	v_scroll->set_page(state_machine_draw->get_size().y);
840 	v_scroll->set_value(state_machine->get_graph_offset().y);
841 	updating = false;
842 
843 	state_machine_play_pos->update();
844 }
845 
_state_machine_pos_draw()846 void AnimationNodeStateMachineEditor::_state_machine_pos_draw() {
847 
848 	Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
849 
850 	if (!playback.is_valid() || !playback->is_playing())
851 		return;
852 
853 	int idx = -1;
854 	for (int i = 0; i < node_rects.size(); i++) {
855 		if (node_rects[i].node_name == playback->get_current_node()) {
856 			idx = i;
857 			break;
858 		}
859 	}
860 
861 	if (idx == -1)
862 		return;
863 
864 	const NodeRect &nr = node_rects[idx];
865 
866 	Vector2 from;
867 	from.x = nr.play.position.x;
868 	from.y = (nr.play.position.y + nr.play.size.y + nr.node.position.y + nr.node.size.y) * 0.5;
869 
870 	Vector2 to;
871 	if (nr.edit.size.x) {
872 		to.x = nr.edit.position.x + nr.edit.size.x;
873 	} else {
874 		to.x = nr.name.position.x + nr.name.size.x;
875 	}
876 	to.y = from.y;
877 
878 	float len = MAX(0.0001, current_length);
879 
880 	float pos = CLAMP(play_pos, 0, len);
881 	float c = pos / len;
882 	Color fg = get_color("font_color", "Label");
883 	Color bg = fg;
884 	bg.a *= 0.3;
885 
886 	state_machine_play_pos->draw_line(from, to, bg, 2);
887 
888 	to = from.linear_interpolate(to, c);
889 
890 	state_machine_play_pos->draw_line(from, to, fg, 2);
891 }
892 
_update_graph()893 void AnimationNodeStateMachineEditor::_update_graph() {
894 
895 	if (updating)
896 		return;
897 
898 	updating = true;
899 
900 	state_machine_draw->update();
901 
902 	updating = false;
903 }
904 
_notification(int p_what)905 void AnimationNodeStateMachineEditor::_notification(int p_what) {
906 
907 	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
908 		error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
909 		error_label->add_color_override("font_color", get_color("error_color", "Editor"));
910 		panel->add_style_override("panel", get_stylebox("bg", "Tree"));
911 
912 		tool_select->set_icon(get_icon("ToolSelect", "EditorIcons"));
913 		tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons"));
914 		tool_connect->set_icon(get_icon("ToolConnect", "EditorIcons"));
915 
916 		transition_mode->clear();
917 		transition_mode->add_icon_item(get_icon("TransitionImmediate", "EditorIcons"), TTR("Immediate"));
918 		transition_mode->add_icon_item(get_icon("TransitionSync", "EditorIcons"), TTR("Sync"));
919 		transition_mode->add_icon_item(get_icon("TransitionEnd", "EditorIcons"), TTR("At End"));
920 
921 		//force filter on those, so they deform better
922 		get_icon("TransitionImmediateBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
923 		get_icon("TransitionEndBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
924 		get_icon("TransitionSyncBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
925 		get_icon("TransitionImmediateAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
926 		get_icon("TransitionEndAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
927 		get_icon("TransitionSyncAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
928 
929 		tool_erase->set_icon(get_icon("Remove", "EditorIcons"));
930 		tool_autoplay->set_icon(get_icon("AutoPlay", "EditorIcons"));
931 		tool_end->set_icon(get_icon("AutoEnd", "EditorIcons"));
932 
933 		play_mode->clear();
934 		play_mode->add_icon_item(get_icon("PlayTravel", "EditorIcons"), TTR("Travel"));
935 		play_mode->add_icon_item(get_icon("Play", "EditorIcons"), TTR("Immediate"));
936 	}
937 
938 	if (p_what == NOTIFICATION_PROCESS) {
939 
940 		String error;
941 
942 		Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
943 
944 		if (error_time > 0) {
945 			error = error_text;
946 			error_time -= get_process_delta_time();
947 		} else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) {
948 			error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
949 		} else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) {
950 			error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason();
951 			/*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) {
952 			if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) {
953 				error = TTR("Start and end nodes are needed for a sub-transition.");
954 			}*/
955 		} else if (playback.is_null()) {
956 			error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
957 		}
958 
959 		if (error != error_label->get_text()) {
960 			error_label->set_text(error);
961 			if (error != String()) {
962 				error_panel->show();
963 			} else {
964 				error_panel->hide();
965 			}
966 		}
967 
968 		for (int i = 0; i < transition_lines.size(); i++) {
969 			int tidx = -1;
970 			for (int j = 0; j < state_machine->get_transition_count(); j++) {
971 				if (transition_lines[i].from_node == state_machine->get_transition_from(j) && transition_lines[i].to_node == state_machine->get_transition_to(j)) {
972 					tidx = j;
973 					break;
974 				}
975 			}
976 
977 			if (tidx == -1) { //missing transition, should redraw
978 				state_machine_draw->update();
979 				break;
980 			}
981 
982 			if (transition_lines[i].disabled != state_machine->get_transition(tidx)->is_disabled()) {
983 				state_machine_draw->update();
984 				break;
985 			}
986 
987 			if (transition_lines[i].auto_advance != state_machine->get_transition(tidx)->has_auto_advance()) {
988 				state_machine_draw->update();
989 				break;
990 			}
991 
992 			if (transition_lines[i].advance_condition_name != state_machine->get_transition(tidx)->get_advance_condition_name()) {
993 				state_machine_draw->update();
994 				break;
995 			}
996 
997 			if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) {
998 				state_machine_draw->update();
999 				break;
1000 			}
1001 
1002 			bool acstate = transition_lines[i].advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + String(transition_lines[i].advance_condition_name)));
1003 
1004 			if (transition_lines[i].advance_condition_state != acstate) {
1005 				state_machine_draw->update();
1006 				break;
1007 			}
1008 		}
1009 
1010 		bool same_travel_path = true;
1011 		Vector<StringName> tp;
1012 		bool is_playing = false;
1013 		StringName current_node;
1014 		StringName blend_from_node;
1015 		play_pos = 0;
1016 		current_length = 0;
1017 
1018 		if (playback.is_valid()) {
1019 			tp = playback->get_travel_path();
1020 			is_playing = playback->is_playing();
1021 			current_node = playback->get_current_node();
1022 			blend_from_node = playback->get_blend_from_node();
1023 			play_pos = playback->get_current_play_pos();
1024 			current_length = playback->get_current_length();
1025 		}
1026 
1027 		{
1028 
1029 			if (last_travel_path.size() != tp.size()) {
1030 				same_travel_path = false;
1031 			} else {
1032 				for (int i = 0; i < last_travel_path.size(); i++) {
1033 					if (last_travel_path[i] != tp[i]) {
1034 						same_travel_path = false;
1035 						break;
1036 					}
1037 				}
1038 			}
1039 		}
1040 
1041 		//update if travel state changed
1042 		if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) {
1043 
1044 			state_machine_draw->update();
1045 			last_travel_path = tp;
1046 			last_current_node = current_node;
1047 			last_active = is_playing;
1048 			last_blend_from_node = blend_from_node;
1049 			state_machine_play_pos->update();
1050 		}
1051 
1052 		{
1053 			if (current_node != StringName() && state_machine->has_node(current_node)) {
1054 
1055 				String next = current_node;
1056 				Ref<AnimationNodeStateMachine> anodesm = state_machine->get_node(next);
1057 				Ref<AnimationNodeStateMachinePlayback> current_node_playback;
1058 
1059 				while (anodesm.is_valid()) {
1060 					current_node_playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback");
1061 					next += "/" + current_node_playback->get_current_node();
1062 					anodesm = anodesm->get_node(current_node_playback->get_current_node());
1063 				}
1064 
1065 				// when current_node is a state machine, use playback of current_node to set play_pos
1066 				if (current_node_playback.is_valid()) {
1067 					play_pos = current_node_playback->get_current_play_pos();
1068 					current_length = current_node_playback->get_current_length();
1069 				}
1070 			}
1071 		}
1072 
1073 		if (last_play_pos != play_pos) {
1074 
1075 			last_play_pos = play_pos;
1076 			state_machine_play_pos->update();
1077 		}
1078 	}
1079 
1080 	if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
1081 		over_node = StringName();
1082 		set_process(is_visible_in_tree());
1083 	}
1084 }
1085 
_open_editor(const String & p_name)1086 void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) {
1087 
1088 	AnimationTreeEditor::get_singleton()->enter_editor(p_name);
1089 }
1090 
_removed_from_graph()1091 void AnimationNodeStateMachineEditor::_removed_from_graph() {
1092 
1093 	EditorNode::get_singleton()->edit_item(NULL);
1094 }
1095 
_name_edited(const String & p_text)1096 void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
1097 
1098 	const String &new_name = p_text;
1099 
1100 	ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1);
1101 
1102 	if (new_name == prev_name) {
1103 		return; // Nothing to do.
1104 	}
1105 
1106 	const String &base_name = new_name;
1107 	int base = 1;
1108 	String name = base_name;
1109 	while (state_machine->has_node(name)) {
1110 		base++;
1111 		name = base_name + " " + itos(base);
1112 	}
1113 
1114 	updating = true;
1115 	undo_redo->create_action(TTR("Node Renamed"));
1116 	undo_redo->add_do_method(state_machine.ptr(), "rename_node", prev_name, name);
1117 	undo_redo->add_undo_method(state_machine.ptr(), "rename_node", name, prev_name);
1118 	undo_redo->add_do_method(this, "_update_graph");
1119 	undo_redo->add_undo_method(this, "_update_graph");
1120 	undo_redo->commit_action();
1121 	name_edit->hide();
1122 	updating = false;
1123 
1124 	state_machine_draw->update();
1125 }
1126 
_name_edited_focus_out()1127 void AnimationNodeStateMachineEditor::_name_edited_focus_out() {
1128 
1129 	if (updating)
1130 		return;
1131 
1132 	_name_edited(name_edit->get_text());
1133 }
1134 
_scroll_changed(double)1135 void AnimationNodeStateMachineEditor::_scroll_changed(double) {
1136 
1137 	if (updating)
1138 		return;
1139 
1140 	state_machine->set_graph_offset(Vector2(h_scroll->get_value(), v_scroll->get_value()));
1141 	state_machine_draw->update();
1142 }
1143 
_erase_selected()1144 void AnimationNodeStateMachineEditor::_erase_selected() {
1145 
1146 	if (selected_node != StringName() && state_machine->has_node(selected_node)) {
1147 		updating = true;
1148 		undo_redo->create_action(TTR("Node Removed"));
1149 		undo_redo->add_do_method(state_machine.ptr(), "remove_node", selected_node);
1150 		undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node), state_machine->get_node_position(selected_node));
1151 		for (int i = 0; i < state_machine->get_transition_count(); i++) {
1152 			String from = state_machine->get_transition_from(i);
1153 			String to = state_machine->get_transition_to(i);
1154 			if (from == selected_node || to == selected_node) {
1155 				undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(i));
1156 			}
1157 		}
1158 		if (String(state_machine->get_start_node()) == selected_node) {
1159 			undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", selected_node);
1160 		}
1161 		undo_redo->add_do_method(this, "_update_graph");
1162 		undo_redo->add_undo_method(this, "_update_graph");
1163 		undo_redo->commit_action();
1164 		updating = false;
1165 		selected_node = StringName();
1166 	}
1167 
1168 	if (selected_transition_to != StringName() && selected_transition_from != StringName() && state_machine->has_transition(selected_transition_from, selected_transition_to)) {
1169 
1170 		Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to));
1171 		updating = true;
1172 		undo_redo->create_action(TTR("Transition Removed"));
1173 		undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to);
1174 		undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr);
1175 		undo_redo->add_do_method(this, "_update_graph");
1176 		undo_redo->add_undo_method(this, "_update_graph");
1177 		undo_redo->commit_action();
1178 		updating = false;
1179 		selected_transition_from = StringName();
1180 		selected_transition_to = StringName();
1181 	}
1182 
1183 	state_machine_draw->update();
1184 }
1185 
_autoplay_selected()1186 void AnimationNodeStateMachineEditor::_autoplay_selected() {
1187 
1188 	if (selected_node != StringName() && state_machine->has_node(selected_node)) {
1189 
1190 		StringName new_start_node;
1191 		if (state_machine->get_start_node() == selected_node) { //toggle it
1192 			new_start_node = StringName();
1193 		} else {
1194 			new_start_node = selected_node;
1195 		}
1196 
1197 		updating = true;
1198 		undo_redo->create_action(TTR("Set Start Node (Autoplay)"));
1199 		undo_redo->add_do_method(state_machine.ptr(), "set_start_node", new_start_node);
1200 		undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", state_machine->get_start_node());
1201 		undo_redo->add_do_method(this, "_update_graph");
1202 		undo_redo->add_undo_method(this, "_update_graph");
1203 		undo_redo->commit_action();
1204 		updating = false;
1205 		state_machine_draw->update();
1206 	}
1207 }
1208 
_end_selected()1209 void AnimationNodeStateMachineEditor::_end_selected() {
1210 
1211 	if (selected_node != StringName() && state_machine->has_node(selected_node)) {
1212 
1213 		StringName new_end_node;
1214 		if (state_machine->get_end_node() == selected_node) { //toggle it
1215 			new_end_node = StringName();
1216 		} else {
1217 			new_end_node = selected_node;
1218 		}
1219 
1220 		updating = true;
1221 		undo_redo->create_action(TTR("Set Start Node (Autoplay)"));
1222 		undo_redo->add_do_method(state_machine.ptr(), "set_end_node", new_end_node);
1223 		undo_redo->add_undo_method(state_machine.ptr(), "set_end_node", state_machine->get_end_node());
1224 		undo_redo->add_do_method(this, "_update_graph");
1225 		undo_redo->add_undo_method(this, "_update_graph");
1226 		undo_redo->commit_action();
1227 		updating = false;
1228 		state_machine_draw->update();
1229 	}
1230 }
_update_mode()1231 void AnimationNodeStateMachineEditor::_update_mode() {
1232 
1233 	if (tool_select->is_pressed()) {
1234 		tool_erase_hb->show();
1235 		tool_erase->set_disabled(selected_node == StringName() && selected_transition_from == StringName() && selected_transition_to == StringName());
1236 		tool_autoplay->set_disabled(selected_node == StringName());
1237 		tool_end->set_disabled(selected_node == StringName());
1238 	} else {
1239 		tool_erase_hb->hide();
1240 	}
1241 }
1242 
_bind_methods()1243 void AnimationNodeStateMachineEditor::_bind_methods() {
1244 
1245 	ClassDB::bind_method("_state_machine_gui_input", &AnimationNodeStateMachineEditor::_state_machine_gui_input);
1246 	ClassDB::bind_method("_state_machine_draw", &AnimationNodeStateMachineEditor::_state_machine_draw);
1247 	ClassDB::bind_method("_state_machine_pos_draw", &AnimationNodeStateMachineEditor::_state_machine_pos_draw);
1248 	ClassDB::bind_method("_update_graph", &AnimationNodeStateMachineEditor::_update_graph);
1249 
1250 	ClassDB::bind_method("_add_menu_type", &AnimationNodeStateMachineEditor::_add_menu_type);
1251 	ClassDB::bind_method("_add_animation_type", &AnimationNodeStateMachineEditor::_add_animation_type);
1252 
1253 	ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited);
1254 	ClassDB::bind_method("_name_edited_focus_out", &AnimationNodeStateMachineEditor::_name_edited_focus_out);
1255 
1256 	ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph);
1257 
1258 	ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor);
1259 	ClassDB::bind_method("_scroll_changed", &AnimationNodeStateMachineEditor::_scroll_changed);
1260 
1261 	ClassDB::bind_method("_erase_selected", &AnimationNodeStateMachineEditor::_erase_selected);
1262 	ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected);
1263 	ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected);
1264 	ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode);
1265 	ClassDB::bind_method("_file_opened", &AnimationNodeStateMachineEditor::_file_opened);
1266 }
1267 
1268 AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL;
1269 
AnimationNodeStateMachineEditor()1270 AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
1271 
1272 	singleton = this;
1273 	updating = false;
1274 
1275 	HBoxContainer *top_hb = memnew(HBoxContainer);
1276 	add_child(top_hb);
1277 
1278 	Ref<ButtonGroup> bg;
1279 	bg.instance();
1280 
1281 	tool_select = memnew(ToolButton);
1282 	top_hb->add_child(tool_select);
1283 	tool_select->set_toggle_mode(true);
1284 	tool_select->set_button_group(bg);
1285 	tool_select->set_pressed(true);
1286 	tool_select->set_tooltip(TTR("Select and move nodes.\nRMB to add new nodes.\nShift+LMB to create connections."));
1287 	tool_select->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
1288 
1289 	tool_create = memnew(ToolButton);
1290 	top_hb->add_child(tool_create);
1291 	tool_create->set_toggle_mode(true);
1292 	tool_create->set_button_group(bg);
1293 	tool_create->set_tooltip(TTR("Create new nodes."));
1294 	tool_create->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
1295 
1296 	tool_connect = memnew(ToolButton);
1297 	top_hb->add_child(tool_connect);
1298 	tool_connect->set_toggle_mode(true);
1299 	tool_connect->set_button_group(bg);
1300 	tool_connect->set_tooltip(TTR("Connect nodes."));
1301 	tool_connect->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
1302 
1303 	tool_erase_hb = memnew(HBoxContainer);
1304 	top_hb->add_child(tool_erase_hb);
1305 	tool_erase_hb->add_child(memnew(VSeparator));
1306 	tool_erase = memnew(ToolButton);
1307 	tool_erase->set_tooltip(TTR("Remove selected node or transition."));
1308 	tool_erase_hb->add_child(tool_erase);
1309 	tool_erase->connect("pressed", this, "_erase_selected");
1310 	tool_erase->set_disabled(true);
1311 
1312 	tool_erase_hb->add_child(memnew(VSeparator));
1313 
1314 	tool_autoplay = memnew(ToolButton);
1315 	tool_autoplay->set_tooltip(TTR("Toggle autoplay this animation on start, restart or seek to zero."));
1316 	tool_erase_hb->add_child(tool_autoplay);
1317 	tool_autoplay->connect("pressed", this, "_autoplay_selected");
1318 	tool_autoplay->set_disabled(true);
1319 
1320 	tool_end = memnew(ToolButton);
1321 	tool_end->set_tooltip(TTR("Set the end animation. This is useful for sub-transitions."));
1322 	tool_erase_hb->add_child(tool_end);
1323 	tool_end->connect("pressed", this, "_end_selected");
1324 	tool_end->set_disabled(true);
1325 
1326 	top_hb->add_child(memnew(VSeparator));
1327 	top_hb->add_child(memnew(Label(TTR("Transition: "))));
1328 	transition_mode = memnew(OptionButton);
1329 	top_hb->add_child(transition_mode);
1330 
1331 	top_hb->add_spacer();
1332 
1333 	top_hb->add_child(memnew(Label(TTR("Play Mode:"))));
1334 	play_mode = memnew(OptionButton);
1335 	top_hb->add_child(play_mode);
1336 
1337 	panel = memnew(PanelContainer);
1338 	panel->set_clip_contents(true);
1339 	add_child(panel);
1340 	panel->set_v_size_flags(SIZE_EXPAND_FILL);
1341 
1342 	state_machine_draw = memnew(Control);
1343 	panel->add_child(state_machine_draw);
1344 	state_machine_draw->connect("gui_input", this, "_state_machine_gui_input");
1345 	state_machine_draw->connect("draw", this, "_state_machine_draw");
1346 	state_machine_draw->set_focus_mode(FOCUS_ALL);
1347 
1348 	state_machine_play_pos = memnew(Control);
1349 	state_machine_draw->add_child(state_machine_play_pos);
1350 	state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent
1351 	state_machine_play_pos->set_anchors_and_margins_preset(PRESET_WIDE);
1352 	state_machine_play_pos->connect("draw", this, "_state_machine_pos_draw");
1353 
1354 	v_scroll = memnew(VScrollBar);
1355 	state_machine_draw->add_child(v_scroll);
1356 	v_scroll->set_anchors_and_margins_preset(PRESET_RIGHT_WIDE);
1357 	v_scroll->connect("value_changed", this, "_scroll_changed");
1358 
1359 	h_scroll = memnew(HScrollBar);
1360 	state_machine_draw->add_child(h_scroll);
1361 	h_scroll->set_anchors_and_margins_preset(PRESET_BOTTOM_WIDE);
1362 	h_scroll->set_margin(MARGIN_RIGHT, -v_scroll->get_size().x * EDSCALE);
1363 	h_scroll->connect("value_changed", this, "_scroll_changed");
1364 
1365 	error_panel = memnew(PanelContainer);
1366 	add_child(error_panel);
1367 	error_label = memnew(Label);
1368 	error_panel->add_child(error_label);
1369 	error_panel->hide();
1370 
1371 	undo_redo = EditorNode::get_undo_redo();
1372 
1373 	set_custom_minimum_size(Size2(0, 300 * EDSCALE));
1374 
1375 	menu = memnew(PopupMenu);
1376 	add_child(menu);
1377 	menu->connect("id_pressed", this, "_add_menu_type");
1378 
1379 	animations_menu = memnew(PopupMenu);
1380 	menu->add_child(animations_menu);
1381 	animations_menu->set_name("animations");
1382 	animations_menu->connect("index_pressed", this, "_add_animation_type");
1383 
1384 	name_edit = memnew(LineEdit);
1385 	state_machine_draw->add_child(name_edit);
1386 	name_edit->hide();
1387 	name_edit->connect("text_entered", this, "_name_edited");
1388 	name_edit->connect("focus_exited", this, "_name_edited_focus_out");
1389 	name_edit->set_as_toplevel(true);
1390 
1391 	open_file = memnew(EditorFileDialog);
1392 	add_child(open_file);
1393 	open_file->set_title(TTR("Open Animation Node"));
1394 	open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1395 	open_file->connect("file_selected", this, "_file_opened");
1396 	undo_redo = EditorNode::get_undo_redo();
1397 
1398 	over_text = false;
1399 
1400 	over_node_what = -1;
1401 	dragging_selected_attempt = false;
1402 	connecting = false;
1403 
1404 	last_active = false;
1405 
1406 	error_time = 0;
1407 }
1408