/*************************************************************************/ /* visual_script.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "visual_script.h" #include "core/config/project_settings.h" #include "core/core_string_names.h" #include "core/os/os.h" #include "scene/main/node.h" #include "visual_script_nodes.h" // Used by editor, this is not really saved. void VisualScriptNode::set_breakpoint(bool p_breakpoint) { breakpoint = p_breakpoint; } bool VisualScriptNode::is_breakpoint() const { return breakpoint; } void VisualScriptNode::ports_changed_notify() { emit_signal(SNAME("ports_changed")); } void VisualScriptNode::set_default_input_value(int p_port, const Variant &p_value) { ERR_FAIL_INDEX(p_port, default_input_values.size()); default_input_values[p_port] = p_value; #ifdef TOOLS_ENABLED if (script_used.is_valid()) { script_used->set_edited(true); } #endif } Variant VisualScriptNode::get_default_input_value(int p_port) const { ERR_FAIL_INDEX_V(p_port, default_input_values.size(), Variant()); return default_input_values[p_port]; } void VisualScriptNode::_set_default_input_values(Array p_values) { default_input_values = p_values; } void VisualScriptNode::validate_input_default_values() { default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize // Actually validate on save. for (int i = 0; i < get_input_value_port_count(); i++) { Variant::Type expected = get_input_value_port_info(i).type; if (expected == Variant::NIL || expected == default_input_values[i].get_type()) { continue; } else { // Not the same, reconvert. Callable::CallError ce; Variant existing = default_input_values[i]; const Variant *existingp = &existing; Variant::construct(expected, default_input_values[i], &existingp, 1, ce); if (ce.error != Callable::CallError::CALL_OK) { //could not convert? force.. Variant::construct(expected, default_input_values[i], nullptr, 0, ce); } } } } Array VisualScriptNode::_get_default_input_values() const { // Validate on save, since on load there is little info about this. Array values = default_input_values; values.resize(get_input_value_port_count()); return values; } String VisualScriptNode::get_text() const { return ""; } void VisualScriptNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visual_script"), &VisualScriptNode::get_visual_script); ClassDB::bind_method(D_METHOD("set_default_input_value", "port_idx", "value"), &VisualScriptNode::set_default_input_value); ClassDB::bind_method(D_METHOD("get_default_input_value", "port_idx"), &VisualScriptNode::get_default_input_value); ClassDB::bind_method(D_METHOD("ports_changed_notify"), &VisualScriptNode::ports_changed_notify); ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualScriptNode::_set_default_input_values); ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualScriptNode::_get_default_input_values); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values"); ADD_SIGNAL(MethodInfo("ports_changed")); } VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inputs, int p_output) const { ERR_FAIL_COND_V(get_output_value_port_count() <= p_output, TypeGuess()); PropertyInfo pinfo = get_output_value_port_info(p_output); TypeGuess tg; tg.type = pinfo.type; if (pinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { tg.gdclass = pinfo.hint_string; } return tg; } Ref VisualScriptNode::get_visual_script() const { return script_used; } VisualScriptNode::VisualScriptNode() { } //////////////// ///////////////////// VisualScriptNodeInstance::VisualScriptNodeInstance() { } VisualScriptNodeInstance::~VisualScriptNodeInstance() { if (sequence_outputs) { memdelete_arr(sequence_outputs); } if (input_ports) { memdelete_arr(input_ports); } if (output_ports) { memdelete_arr(output_ports); } } void VisualScript::add_function(const StringName &p_name, int p_func_node_id) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!String(p_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_name)); ERR_FAIL_COND(variables.has(p_name)); ERR_FAIL_COND(custom_signals.has(p_name)); functions[p_name] = Function(); functions[p_name].func_id = p_func_node_id; } bool VisualScript::has_function(const StringName &p_name) const { return functions.has(p_name); } void VisualScript::remove_function(const StringName &p_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!functions.has(p_name)); // Let the editor handle the node removal. functions.erase(p_name); } void VisualScript::rename_function(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!functions.has(p_name)); if (p_new_name == p_name) { return; } ERR_FAIL_COND(!String(p_new_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_new_name)); ERR_FAIL_COND(variables.has(p_new_name)); ERR_FAIL_COND(custom_signals.has(p_new_name)); functions[p_new_name] = functions[p_name]; functions.erase(p_name); } void VisualScript::set_scroll(const Vector2 &p_scroll) { scroll = p_scroll; } Vector2 VisualScript::get_scroll() const { return scroll; } void VisualScript::get_function_list(List *r_functions) const { for (const KeyValue &E : functions) { r_functions->push_back(E.key); } } int VisualScript::get_function_node_id(const StringName &p_name) const { ERR_FAIL_COND_V(!functions.has(p_name), -1); return functions[p_name].func_id; } void VisualScript::_node_ports_changed(int p_id) { Ref vsn = nodes[p_id].node; vsn->validate_input_default_values(); // Must revalidate all the functions. { List to_remove; for (const SequenceConnection &E : sequence_connections) { if (E.from_node == p_id && E.from_output >= vsn->get_output_sequence_port_count()) { to_remove.push_back(E); } if (E.to_node == p_id && !vsn->has_input_sequence_port()) { to_remove.push_back(E); } } while (to_remove.size()) { sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } { List to_remove; for (const DataConnection &E : data_connections) { if (E.from_node == p_id && E.from_port >= vsn->get_output_value_port_count()) { to_remove.push_back(E); } if (E.to_node == p_id && E.to_port >= vsn->get_input_value_port_count()) { to_remove.push_back(E); } } while (to_remove.size()) { data_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } #ifdef TOOLS_ENABLED set_edited(true); // Something changed, let's set as edited. emit_signal(SNAME("node_ports_changed"), p_id); #endif } void VisualScript::add_node(int p_id, const Ref &p_node, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script. ERR_FAIL_COND(p_node.is_null()); NodeData nd; nd.node = p_node; nd.pos = p_pos; Ref vsn = p_node; vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed), varray(p_id)); vsn->script_used = Ref(this); vsn->validate_input_default_values(); // Validate when fully loaded. nodes[p_id] = nd; } void VisualScript::remove_node(int p_id) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!nodes.has(p_id)); { List to_remove; for (const SequenceConnection &E : sequence_connections) { if (E.from_node == p_id || E.to_node == p_id) { to_remove.push_back(E); } } while (to_remove.size()) { sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } { List to_remove; for (const DataConnection &E : data_connections) { if (E.from_node == p_id || E.to_node == p_id) { to_remove.push_back(E); } } while (to_remove.size()) { data_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed)); nodes[p_id].node->script_used.unref(); nodes.erase(p_id); } bool VisualScript::has_node(int p_id) const { return nodes.has(p_id); } Ref VisualScript::get_node(int p_id) const { ERR_FAIL_COND_V(!nodes.has(p_id), Ref()); return nodes[p_id].node; } void VisualScript::set_node_position(int p_id, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!nodes.has(p_id)); nodes[p_id].pos = p_pos; } Point2 VisualScript::get_node_position(int p_id) const { ERR_FAIL_COND_V(!nodes.has(p_id), Point2()); return nodes[p_id].pos; } void VisualScript::get_node_list(List *r_nodes) const { for (const KeyValue &E : nodes) { r_nodes->push_back(E.key); } } void VisualScript::sequence_connect(int p_from_node, int p_from_output, int p_to_node) { ERR_FAIL_COND(instances.size()); SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; ERR_FAIL_COND(sequence_connections.has(sc)); sequence_connections.insert(sc); } void VisualScript::sequence_disconnect(int p_from_node, int p_from_output, int p_to_node) { SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; ERR_FAIL_COND(!sequence_connections.has(sc)); sequence_connections.erase(sc); } bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const { SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; return sequence_connections.has(sc); } void VisualScript::get_sequence_connection_list(List *r_connection) const { for (const SequenceConnection &E : sequence_connections) { r_connection->push_back(E); } } void VisualScript::data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) { ERR_FAIL_COND(instances.size()); DataConnection dc; dc.from_node = p_from_node; dc.from_port = p_from_port; dc.to_node = p_to_node; dc.to_port = p_to_port; ERR_FAIL_COND(data_connections.has(dc)); data_connections.insert(dc); } void VisualScript::data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) { DataConnection dc; dc.from_node = p_from_node; dc.from_port = p_from_port; dc.to_node = p_to_node; dc.to_port = p_to_port; ERR_FAIL_COND(!data_connections.has(dc)); data_connections.erase(dc); } bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { DataConnection dc; dc.from_node = p_from_node; dc.from_port = p_from_port; dc.to_node = p_to_node; dc.to_port = p_to_port; return data_connections.has(dc); } bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const { for (const DataConnection &E : data_connections) { if (E.to_node == p_node && E.to_port == p_port) { return true; } } return false; } bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const { for (const DataConnection &E : data_connections) { if (E.to_node == p_node && E.to_port == p_port) { *r_node = E.from_node; *r_port = E.from_port; return true; } } return false; } void VisualScript::get_data_connection_list(List *r_connection) const { for (const DataConnection &E : data_connections) { r_connection->push_back(E); } } void VisualScript::set_tool_enabled(bool p_enabled) { is_tool_script = p_enabled; } void VisualScript::add_variable(const StringName &p_name, const Variant &p_default_value, bool p_export) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!String(p_name).is_valid_identifier()); ERR_FAIL_COND(variables.has(p_name)); Variable v; v.default_value = p_default_value; v.info.type = p_default_value.get_type(); v.info.name = p_name; v.info.hint = PROPERTY_HINT_NONE; v._export = p_export; variables[p_name] = v; #ifdef TOOLS_ENABLED _update_placeholders(); #endif } bool VisualScript::has_variable(const StringName &p_name) const { return variables.has(p_name); } void VisualScript::remove_variable(const StringName &p_name) { ERR_FAIL_COND(!variables.has(p_name)); variables.erase(p_name); #ifdef TOOLS_ENABLED _update_placeholders(); #endif } void VisualScript::set_variable_default_value(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND(!variables.has(p_name)); variables[p_name].default_value = p_value; #ifdef TOOLS_ENABLED _update_placeholders(); #endif } Variant VisualScript::get_variable_default_value(const StringName &p_name) const { ERR_FAIL_COND_V(!variables.has(p_name), Variant()); return variables[p_name].default_value; } void VisualScript::set_variable_info(const StringName &p_name, const PropertyInfo &p_info) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!variables.has(p_name)); variables[p_name].info = p_info; variables[p_name].info.name = p_name; #ifdef TOOLS_ENABLED _update_placeholders(); #endif } PropertyInfo VisualScript::get_variable_info(const StringName &p_name) const { ERR_FAIL_COND_V(!variables.has(p_name), PropertyInfo()); return variables[p_name].info; } void VisualScript::set_variable_export(const StringName &p_name, bool p_export) { ERR_FAIL_COND(!variables.has(p_name)); variables[p_name]._export = p_export; #ifdef TOOLS_ENABLED _update_placeholders(); #endif } bool VisualScript::get_variable_export(const StringName &p_name) const { ERR_FAIL_COND_V(!variables.has(p_name), false); return variables[p_name]._export; } void VisualScript::_set_variable_info(const StringName &p_name, const Dictionary &p_info) { PropertyInfo pinfo; if (p_info.has("type")) { pinfo.type = Variant::Type(int(p_info["type"])); } if (p_info.has("name")) { pinfo.name = p_info["name"]; } if (p_info.has("hint")) { pinfo.hint = PropertyHint(int(p_info["hint"])); } if (p_info.has("hint_string")) { pinfo.hint_string = p_info["hint_string"]; } if (p_info.has("usage")) { pinfo.usage = p_info["usage"]; } set_variable_info(p_name, pinfo); } Dictionary VisualScript::_get_variable_info(const StringName &p_name) const { PropertyInfo pinfo = get_variable_info(p_name); Dictionary d; d["type"] = pinfo.type; d["name"] = pinfo.name; d["hint"] = pinfo.hint; d["hint_string"] = pinfo.hint_string; d["usage"] = pinfo.usage; return d; } void VisualScript::get_variable_list(List *r_variables) const { for (const KeyValue &E : variables) { r_variables->push_back(E.key); } } void VisualScript::set_instance_base_type(const StringName &p_type) { ERR_FAIL_COND(instances.size()); base_type = p_type; } void VisualScript::rename_variable(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!variables.has(p_name)); if (p_new_name == p_name) { return; } ERR_FAIL_COND(!String(p_new_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_new_name)); ERR_FAIL_COND(variables.has(p_new_name)); ERR_FAIL_COND(custom_signals.has(p_new_name)); variables[p_new_name] = variables[p_name]; variables.erase(p_name); List ids; get_node_list(&ids); for (int &E : ids) { Ref nodeget = get_node(E); if (nodeget.is_valid()) { if (nodeget->get_variable() == p_name) { nodeget->set_variable(p_new_name); } } else { Ref nodeset = get_node(E); if (nodeset.is_valid()) { if (nodeset->get_variable() == p_name) { nodeset->set_variable(p_new_name); } } } } } void VisualScript::add_custom_signal(const StringName &p_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!String(p_name).is_valid_identifier()); ERR_FAIL_COND(custom_signals.has(p_name)); custom_signals[p_name] = Vector(); } bool VisualScript::has_custom_signal(const StringName &p_name) const { return custom_signals.has(p_name); } void VisualScript::custom_signal_add_argument(const StringName &p_func, Variant::Type p_type, const String &p_name, int p_index) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); Argument arg; arg.type = p_type; arg.name = p_name; if (p_index < 0) { custom_signals[p_func].push_back(arg); } else { custom_signals[p_func].insert(0, arg); } } void VisualScript::custom_signal_set_argument_type(const StringName &p_func, int p_argidx, Variant::Type p_type) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); custom_signals[p_func].write[p_argidx].type = p_type; } Variant::Type VisualScript::custom_signal_get_argument_type(const StringName &p_func, int p_argidx) const { ERR_FAIL_COND_V(!custom_signals.has(p_func), Variant::NIL); ERR_FAIL_INDEX_V(p_argidx, custom_signals[p_func].size(), Variant::NIL); return custom_signals[p_func][p_argidx].type; } void VisualScript::custom_signal_set_argument_name(const StringName &p_func, int p_argidx, const String &p_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); custom_signals[p_func].write[p_argidx].name = p_name; } String VisualScript::custom_signal_get_argument_name(const StringName &p_func, int p_argidx) const { ERR_FAIL_COND_V(!custom_signals.has(p_func), String()); ERR_FAIL_INDEX_V(p_argidx, custom_signals[p_func].size(), String()); return custom_signals[p_func][p_argidx].name; } void VisualScript::custom_signal_remove_argument(const StringName &p_func, int p_argidx) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); custom_signals[p_func].remove_at(p_argidx); } int VisualScript::custom_signal_get_argument_count(const StringName &p_func) const { ERR_FAIL_COND_V(!custom_signals.has(p_func), 0); return custom_signals[p_func].size(); } void VisualScript::custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); ERR_FAIL_INDEX(p_with_argidx, custom_signals[p_func].size()); SWAP(custom_signals[p_func].write[p_argidx], custom_signals[p_func].write[p_with_argidx]); } void VisualScript::remove_custom_signal(const StringName &p_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_name)); custom_signals.erase(p_name); } void VisualScript::rename_custom_signal(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_name)); if (p_new_name == p_name) { return; } ERR_FAIL_COND(!String(p_new_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_new_name)); ERR_FAIL_COND(variables.has(p_new_name)); ERR_FAIL_COND(custom_signals.has(p_new_name)); custom_signals[p_new_name] = custom_signals[p_name]; custom_signals.erase(p_name); } void VisualScript::get_custom_signal_list(List *r_custom_signals) const { for (const KeyValue> &E : custom_signals) { r_custom_signals->push_back(E.key); } r_custom_signals->sort_custom(); } int VisualScript::get_available_id() const { // This is infinitely increasing, // so one might want to implement a better solution, // if the there is a case for huge number of nodes to be added to visual script. int max = -1; for (const KeyValue &E : nodes) { if (E.key > max) { max = E.key; } } return (max + 1); } ///////////////////////////////// bool VisualScript::can_instantiate() const { return true; // ScriptServer::is_scripting_enabled(); } StringName VisualScript::get_instance_base_type() const { return base_type; } Ref