#include "visual_script.h" #include "visual_script_nodes.h" #include "scene/main/node.h" #include "globals.h" #define SCRIPT_VARIABLES_PREFIX "script_variables/" //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::_notification(int p_what) { if (p_what==NOTIFICATION_POSTINITIALIZE) { int dvc = get_input_value_port_count(); for(int i=0;i=default_input_values.size()) { Variant::CallError ce; saved_values.push_back(Variant::construct(expected,NULL,0,ce,false)); } else { if (expected==Variant::NIL || expected==default_input_values[i].get_type()) { saved_values.push_back(default_input_values[i]); } else { //not the same, reconvert Variant::CallError ce; Variant existing = default_input_values[i]; const Variant *existingp=&existing; saved_values.push_back( Variant::construct(expected,&existingp,1,ce,false) ); } } } return saved_values; } void VisualScriptNode::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_visual_script:VisualScript"),&VisualScriptNode::get_visual_script); ObjectTypeDB::bind_method(_MD("set_default_input_value","port_idx","value:Variant"),&VisualScriptNode::set_default_input_value); ObjectTypeDB::bind_method(_MD("get_default_input_value:Variant","port_idx"),&VisualScriptNode::get_default_input_value); ObjectTypeDB::bind_method(_MD("_set_default_input_values","values"),&VisualScriptNode::_set_default_input_values); ObjectTypeDB::bind_method(_MD("_get_default_input_values"),&VisualScriptNode::_get_default_input_values); ADD_PROPERTY(PropertyInfo(Variant::ARRAY,"_default_input_values",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_default_input_values"),_SCS("_get_default_input_values")); ADD_SIGNAL(MethodInfo("ports_changed")); } Ref VisualScriptNode::get_visual_script() const { if (scripts_used.size()) return Ref(scripts_used.front()->get()); return Ref(); } VisualScriptNode::VisualScriptNode() { breakpoint=false; } //////////////// ///////////////////// VisualScriptNodeInstance::VisualScriptNodeInstance() { sequence_outputs=NULL; input_ports=NULL; } VisualScriptNodeInstance::~VisualScriptNodeInstance() { if (sequence_outputs) { memdelete(sequence_outputs); } if (input_ports) { memdelete(input_ports); } if (output_ports) { memdelete(output_ports); } } void VisualScript::add_function(const StringName& p_name) { ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!String(p_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_name)); functions[p_name]=Function(); functions[p_name].scroll=Vector2(-50,-100); } 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)); for (Map::Element *E=functions[p_name].nodes.front();E;E=E->next()) { E->get().node->disconnect("ports_changed",this,"_node_ports_changed"); E->get().node->scripts_used.erase(this); } 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_function_scroll(const StringName& p_name, const Vector2& p_scroll) { ERR_FAIL_COND(!functions.has(p_name)); functions[p_name].scroll=p_scroll; } Vector2 VisualScript::get_function_scroll(const StringName& p_name) const { ERR_FAIL_COND_V(!functions.has(p_name),Vector2()); return functions[p_name].scroll; } void VisualScript::get_function_list(List *r_functions) const { for (const Map::Element *E=functions.front();E;E=E->next()) { r_functions->push_back(E->key()); } r_functions->sort_custom(); } int VisualScript::get_function_node_id(const StringName& p_name) const { ERR_FAIL_COND_V(!functions.has(p_name),-1); return functions[p_name].function_id; } void VisualScript::_node_ports_changed(int p_id) { StringName function; for (Map::Element *E=functions.front();E;E=E->next()) { if (E->get().nodes.has(p_id)) { function=E->key(); break; } } ERR_FAIL_COND(function==StringName()); Function &func = functions[function]; Ref vsn = func.nodes[p_id].node; //must revalidate all the functions { List to_remove; for (Set::Element *E=func.sequence_connections.front();E;E=E->next()) { if (E->get().from_node==p_id && E->get().from_output>=vsn->get_output_sequence_port_count()) { to_remove.push_back(E->get()); } if (E->get().to_node==p_id && !vsn->has_input_sequence_port()) { to_remove.push_back(E->get()); } } while(to_remove.size()) { func.sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } { List to_remove; for (Set::Element *E=func.data_connections.front();E;E=E->next()) { if (E->get().from_node==p_id && E->get().from_port>=vsn->get_output_value_port_count()) { to_remove.push_back(E->get()); } if (E->get().to_node==p_id && E->get().to_port>=vsn->get_input_value_port_count()) { to_remove.push_back(E->get()); } } while(to_remove.size()) { func.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("node_ports_changed",function,p_id); #endif } void VisualScript::add_node(const StringName& p_func,int p_id, const Ref& p_node, const Point2 &p_pos) { ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!functions.has(p_func)); for (Map::Element *E=functions.front();E;E=E->next()) { ERR_FAIL_COND(E->get().nodes.has(p_id)); //id can exist only one in script, even for different functions } Function &func = functions[p_func]; if (p_node->cast_to()) { //the function indeed ERR_EXPLAIN("A function node already has been set here."); ERR_FAIL_COND(func.function_id>=0); func.function_id=p_id; } Function::NodeData nd; nd.node=p_node; nd.pos=p_pos; Ref vsn = p_node; vsn->connect("ports_changed",this,"_node_ports_changed",varray(p_id)); vsn->scripts_used.insert(this); func.nodes[p_id]=nd; } void VisualScript::remove_node(const StringName& p_func,int p_id){ ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; ERR_FAIL_COND(!func.nodes.has(p_id)); { List to_remove; for (Set::Element *E=func.sequence_connections.front();E;E=E->next()) { if (E->get().from_node==p_id || E->get().to_node==p_id) { to_remove.push_back(E->get()); } } while(to_remove.size()) { func.sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } { List to_remove; for (Set::Element *E=func.data_connections.front();E;E=E->next()) { if (E->get().from_node==p_id || E->get().to_node==p_id) { to_remove.push_back(E->get()); } } while(to_remove.size()) { func.data_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } if (func.nodes[p_id].node->cast_to()) { func.function_id=-1; //revert to invalid } func.nodes[p_id].node->disconnect("ports_changed",this,"_node_ports_changed"); func.nodes[p_id].node->scripts_used.erase(this); func.nodes.erase(p_id); } bool VisualScript::has_node(const StringName& p_func,int p_id) const { ERR_FAIL_COND_V(!functions.has(p_func),false); const Function &func = functions[p_func]; return func.nodes.has(p_id); } Ref VisualScript::get_node(const StringName& p_func,int p_id) const{ ERR_FAIL_COND_V(!functions.has(p_func),Ref()); const Function &func = functions[p_func]; ERR_FAIL_COND_V(!func.nodes.has(p_id),Ref()); return func.nodes[p_id].node; } void VisualScript::set_node_pos(const StringName& p_func,int p_id,const Point2& p_pos) { ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; ERR_FAIL_COND(!func.nodes.has(p_id)); func.nodes[p_id].pos=p_pos; } Point2 VisualScript::get_node_pos(const StringName& p_func,int p_id) const{ ERR_FAIL_COND_V(!functions.has(p_func),Point2()); const Function &func = functions[p_func]; ERR_FAIL_COND_V(!func.nodes.has(p_id),Point2()); return func.nodes[p_id].pos; } void VisualScript::get_node_list(const StringName& p_func,List *r_nodes) const{ ERR_FAIL_COND(!functions.has(p_func)); const Function &func = functions[p_func]; for (const Map::Element *E=func.nodes.front();E;E=E->next()) { r_nodes->push_back(E->key()); } } void VisualScript::sequence_connect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node){ ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; SequenceConnection sc; sc.from_node=p_from_node; sc.from_output=p_from_output; sc.to_node=p_to_node; ERR_FAIL_COND(func.sequence_connections.has(sc)); func.sequence_connections.insert(sc); } void VisualScript::sequence_disconnect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node){ ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; SequenceConnection sc; sc.from_node=p_from_node; sc.from_output=p_from_output; sc.to_node=p_to_node; ERR_FAIL_COND(!func.sequence_connections.has(sc)); func.sequence_connections.erase(sc); } bool VisualScript::has_sequence_connection(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node) const{ ERR_FAIL_COND_V(!functions.has(p_func),false); const Function &func = functions[p_func]; SequenceConnection sc; sc.from_node=p_from_node; sc.from_output=p_from_output; sc.to_node=p_to_node; return func.sequence_connections.has(sc); } void VisualScript::get_sequence_connection_list(const StringName& p_func,List *r_connection) const { ERR_FAIL_COND(!functions.has(p_func)); const Function &func = functions[p_func]; for (const Set::Element *E=func.sequence_connections.front();E;E=E->next()) { r_connection->push_back(E->get()); } } void VisualScript::data_connect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) { ERR_FAIL_COND( instances.size() ); ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; 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( func.data_connections.has(dc)); func.data_connections.insert(dc); } void VisualScript::data_disconnect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) { ERR_FAIL_COND(!functions.has(p_func)); Function &func = functions[p_func]; 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( !func.data_connections.has(dc)); func.data_connections.erase(dc); } bool VisualScript::has_data_connection(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) const { ERR_FAIL_COND_V(!functions.has(p_func),false); const Function &func = functions[p_func]; 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 func.data_connections.has(dc); } bool VisualScript::is_input_value_port_connected(const StringName& p_func,int p_node,int p_port) const { ERR_FAIL_COND_V(!functions.has(p_func),false); const Function &func = functions[p_func]; for (const Set::Element *E=func.data_connections.front();E;E=E->next()) { if (E->get().to_node==p_node && E->get().to_port==p_port) return true; } return false; } void VisualScript::get_data_connection_list(const StringName& p_func,List *r_connection) const { ERR_FAIL_COND(!functions.has(p_func)); const Function &func = functions[p_func]; for (const Set::Element *E=func.data_connections.front();E;E=E->next()) { r_connection->push_back(E->get()); } } void VisualScript::add_variable(const StringName& p_name,const Variant& p_default_value) { 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; variables[p_name]=v; script_variable_remap[SCRIPT_VARIABLES_PREFIX+String(p_name)]=p_name; #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); script_variable_remap.erase(SCRIPT_VARIABLES_PREFIX+String(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_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){ for (Map::Element *E=variables.front();E;E=E->next()) { r_variables->push_back(E->key()); } r_variables->sort_custom(); } 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); } 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][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][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(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][p_argidx], custom_signals[p_func][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 Map >::Element *E=custom_signals.front();E;E=E->next()) { r_custom_signals->push_back(E->key()); } r_custom_signals->sort_custom(); } int VisualScript::get_available_id() const { int max_id=0; for (Map::Element *E=functions.front();E;E=E->next()) { if (E->get().nodes.empty()) continue; int last_id = E->get().nodes.back()->key(); max_id=MAX(max_id,last_id+1); } return max_id; } ///////////////////////////////// bool VisualScript::can_instance() const { return true;//ScriptServer::is_scripting_enabled(); } StringName VisualScript::get_instance_base_type() const { return base_type; } #ifdef TOOLS_ENABLED void VisualScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { placeholders.erase(p_placeholder); } void VisualScript::_update_placeholders() { if (placeholders.size()==0) return; //no bother if no placeholders List pinfo; Map values; for (Map::Element *E=variables.front();E;E=E->next()) { PropertyInfo p = E->get().info; p.name=SCRIPT_VARIABLES_PREFIX+String(E->key()); pinfo.push_back(p); values[p.name]=E->get().default_value; } for (Set::Element *E=placeholders.front();E;E=E->next()) { E->get()->update(pinfo,values); } } #endif ScriptInstance* VisualScript::instance_create(Object *p_this) { #ifdef TOOLS_ENABLED if (!ScriptServer::is_scripting_enabled()) { PlaceHolderScriptInstance *sins = memnew( PlaceHolderScriptInstance(VisualScriptLanguage::singleton,Ref