diff options
Diffstat (limited to 'modules')
28 files changed, 3918 insertions, 526 deletions
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 18a4347edf..ac89c0e9e9 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -32,7 +32,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int host = enet_host_create (& address /* the address to bind the server host to */, p_max_clients /* allow up to 32 clients and/or outgoing connections */, - 2 /* allow up to 2 channels to be used, 0 and 1 */, + SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, p_in_bandwidth /* assume any amount of incoming bandwidth */, p_out_bandwidth /* assume any amount of outgoing bandwidth */); @@ -52,7 +52,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port host = enet_host_create (NULL /* create a client host */, 1 /* only allow 1 outgoing connection */, - 2 /* allow up 2 channels to be used, 0 and 1 */, + SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */, p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */); @@ -70,8 +70,8 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port unique_id=_gen_unique_id(); - /* Initiate the connection, allocating the two channels 0 and 1. */ - ENetPeer *peer = enet_host_connect (host, & address, 2, unique_id); + /* Initiate the connection, allocating the enough channels */ + ENetPeer *peer = enet_host_connect (host, & address, SYSCH_MAX, unique_id); if (peer == NULL) { enet_host_destroy(host); @@ -148,12 +148,12 @@ void NetworkedMultiplayerENet::poll(){ ENetPacket * packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]); encode_uint32(E->key(),&packet->data[4]); - enet_peer_send(event.peer,1,packet); + enet_peer_send(event.peer,SYSCH_CONFIG,packet); //send the new peer to existing peers packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]); encode_uint32(*new_id,&packet->data[4]); - enet_peer_send(E->get(),1,packet); + enet_peer_send(E->get(),SYSCH_CONFIG,packet); } } else { @@ -185,7 +185,7 @@ void NetworkedMultiplayerENet::poll(){ ENetPacket* packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_REMOVE_PEER,&packet->data[0]); encode_uint32(*id,&packet->data[4]); - enet_peer_send(E->get(),1,packet); + enet_peer_send(E->get(),SYSCH_CONFIG,packet); } } else if (!server) { emit_signal("server_disconnected"); @@ -204,10 +204,13 @@ void NetworkedMultiplayerENet::poll(){ case ENET_EVENT_TYPE_RECEIVE: { - if (event.channelID==1) { + if (event.channelID==SYSCH_CONFIG) { //some config message ERR_CONTINUE( event.packet->dataLength < 8); + // Only server can send config messages + ERR_CONTINUE( server ); + int msg = decode_uint32(&event.packet->data[0]); int id = decode_uint32(&event.packet->data[4]); @@ -226,12 +229,12 @@ void NetworkedMultiplayerENet::poll(){ } enet_packet_destroy(event.packet); - } else if (event.channelID==0){ + } else if (event.channelID < SYSCH_MAX){ Packet packet; packet.packet = event.packet; - int *id = (int*)event.peer -> data; + uint32_t *id = (uint32_t*)event.peer->data; ERR_CONTINUE(event.packet->dataLength<12) @@ -243,6 +246,8 @@ void NetworkedMultiplayerENet::poll(){ packet.from=source; if (server) { + // Someone is cheating and trying to fake the source! + ERR_CONTINUE(source!=*id); packet.from=*id; @@ -258,7 +263,7 @@ void NetworkedMultiplayerENet::poll(){ ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags); - enet_peer_send(E->get(),0,packet2); + enet_peer_send(E->get(),event.channelID,packet2); } } else if (target<0) { @@ -272,7 +277,7 @@ void NetworkedMultiplayerENet::poll(){ ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags); - enet_peer_send(E->get(),0,packet2); + enet_peer_send(E->get(),event.channelID,packet2); } if (-target != 1) { @@ -289,7 +294,7 @@ void NetworkedMultiplayerENet::poll(){ } else { //to someone else, specifically ERR_CONTINUE(!peer_map.has(target)); - enet_peer_send(peer_map[target],0,packet.packet); + enet_peer_send(peer_map[target],event.channelID,packet.packet); } } else { @@ -359,7 +364,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer,int &r_buffe incoming_packets.pop_front(); *r_buffer=(const uint8_t*)(¤t_packet.packet->data[12]); - r_buffer_size=current_packet.packet->dataLength; + r_buffer_size=current_packet.packet->dataLength-12; return OK; } @@ -369,16 +374,20 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_ ERR_FAIL_COND_V(connection_status!=CONNECTION_CONNECTED,ERR_UNCONFIGURED); int packet_flags=0; + int channel=SYSCH_RELIABLE; switch(transfer_mode) { case TRANSFER_MODE_UNRELIABLE: { packet_flags=ENET_PACKET_FLAG_UNSEQUENCED; + channel=SYSCH_UNRELIABLE; } break; case TRANSFER_MODE_UNRELIABLE_ORDERED: { packet_flags=0; + channel=SYSCH_UNRELIABLE; } break; case TRANSFER_MODE_RELIABLE: { packet_flags=ENET_PACKET_FLAG_RELIABLE; + channel=SYSCH_RELIABLE; } break; } @@ -402,7 +411,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_ if (server) { if (target_peer==0) { - enet_host_broadcast(host,0,packet); + enet_host_broadcast(host,channel,packet); } else if (target_peer<0) { //send to all but one //and make copies for sending @@ -416,18 +425,18 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_ ENetPacket* packet2 = enet_packet_create (packet->data,packet->dataLength,packet_flags); - enet_peer_send(F->get(),0,packet2); + enet_peer_send(F->get(),channel,packet2); } enet_packet_destroy(packet); //original packet no longer needed } else { - enet_peer_send (E->get(), 0, packet); + enet_peer_send (E->get(), channel, packet); } } else { ERR_FAIL_COND_V(!peer_map.has(1),ERR_BUG); - enet_peer_send (peer_map[1], 0, packet); //send to server for broadcast.. + enet_peer_send (peer_map[1], channel, packet); //send to server for broadcast.. } diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 59863c1f78..f64db4561e 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -23,6 +23,13 @@ private: SYSMSG_REMOVE_PEER }; + enum { + SYSCH_CONFIG, + SYSCH_RELIABLE, + SYSCH_UNRELIABLE, + SYSCH_MAX + }; + bool active; bool server; diff --git a/modules/enet/win32.c b/modules/enet/win32.c index d77fa9a49a..15edd7acbb 100644 --- a/modules/enet/win32.c +++ b/modules/enet/win32.c @@ -28,7 +28,9 @@ enet_initialize (void) return -1; } +#ifndef WINRT_ENABLED timeBeginPeriod (1); +#endif return 0; } @@ -36,11 +38,22 @@ enet_initialize (void) void enet_deinitialize (void) { +#ifndef WINRT_ENABLED timeEndPeriod (1); +#endif WSACleanup (); } +#ifdef WINRT_ENABLED +enet_uint32 +timeGetTime() { + ULONGLONG ticks = GetTickCount64(); + return (enet_uint32)ticks; +} +#endif + + enet_uint32 enet_host_random_seed (void) { diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index ce8b6a6ea4..2e2cbe7b29 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -662,6 +662,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; } break; + // ternary operators + case GDParser::OperatorNode::OP_TERNARY_IF: { + + // x IF a ELSE y operator with early out on failure + + int res = _parse_expression(codegen,on->arguments[0],p_stack_level); + if (res<0) + return res; + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT); + codegen.opcodes.push_back(res); + int jump_fail_pos=codegen.opcodes.size(); + codegen.opcodes.push_back(0); + + + res = _parse_expression(codegen,on->arguments[1],p_stack_level); + if (res<0) + return res; + + codegen.alloc_stack(p_stack_level); //it will be used.. + codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); + codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); + codegen.opcodes.push_back(res); + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + int jump_past_pos=codegen.opcodes.size(); + codegen.opcodes.push_back(0); + + codegen.opcodes[jump_fail_pos]=codegen.opcodes.size(); + res = _parse_expression(codegen,on->arguments[2],p_stack_level); + if (res<0) + return res; + + codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); + codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); + codegen.opcodes.push_back(res); + + codegen.opcodes[jump_past_pos]=codegen.opcodes.size(); + + return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; + + } break; //unary operators case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break; @@ -1403,6 +1443,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * #endif if (p_func) { gdfunc->_initial_line=p_func->line; +#ifdef TOOLS_ENABLED + + p_script->member_lines[func_name]=p_func->line; +#endif } else { gdfunc->_initial_line=0; } @@ -1632,6 +1676,12 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa p_script->member_indices[name]=minfo; p_script->members.insert(name); +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->variables[i].line; +#endif + + } for(int i=0;i<p_class->constant_expressions.size();i++) { @@ -1643,6 +1693,11 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa p_script->constants.insert(name,constant->value); //p_script->constants[constant->value].make_const(); +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->constant_expressions[i].expression->line; +#endif + } for(int i=0;i<p_class->_signals.size();i++) { @@ -1691,6 +1746,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa if (err) return err; +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->subclasses[i]->line; +#endif p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants p_script->subclasses.insert(name,subclass); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 5b74dab889..c3e59836a2 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -47,16 +47,14 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { Ref<Script> GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const { String _template = String()+ - "\nextends %BASE%\n\n"+ - "# member variables here, example:\n"+ - "# var a=2\n"+ - "# var b=\"textvar\"\n\n"+ + "extends %BASE%\n\n"+ + "# class member variables go here, for example:\n"+ + "# var a = 2\n"+ + "# var b = \"textvar\"\n\n"+ "func _ready():\n"+ "\t# Called every time the node is added to the scene.\n"+ "\t# Initialization here\n"+ - "\tpass\n"+ - "\n"+ - "\n"; + "\tpass\n"; _template = _template.replace("%BASE%",p_base_class_name); @@ -353,6 +351,7 @@ struct GDCompletionIdentifier { Ref<GDScript> script; Variant::Type type; Variant value; //im case there is a value, also return it + }; @@ -937,6 +936,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: return false; } + static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { @@ -2096,7 +2096,7 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base GDParser p; - Error err = p.parse(p_code,p_base_path,false,"",true); + p.parse(p_code,p_base_path,false,"",true); bool isfunction=false; Set<String> options; @@ -2143,7 +2143,18 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base GDCompletionIdentifier t; if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) { - if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") { + //native enum + Ref<GDNativeClass> gdn = t.value; + if (gdn.is_valid()) { + StringName cn = gdn->get_name(); + List<String> cnames; + ObjectTypeDB::get_integer_constant_list(cn,&cnames); + for (List<String>::Element *E=cnames.front();E;E=E->next()) { + options.insert(E->get()); + } + } + } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { Ref<GDScript> on_script; @@ -2513,3 +2524,442 @@ void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_ } } + +#ifdef TOOLS_ENABLED + +Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol,const String& p_base_path, Object*p_owner,LookupResult& r_result) { + + + //before parsing, try the usual stuff + if (ObjectTypeDB::type_exists(p_symbol)) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.class_name=p_symbol; + return OK; + } + + for(int i=0;i<Variant::VARIANT_MAX;i++) { + Variant::Type t = Variant::Type(i); + if (Variant::get_type_name(t)==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.class_name=Variant::get_type_name(t); + return OK; + } + } + + for(int i=0;i<GDFunctions::FUNC_MAX;i++) { + if (GDFunctions::get_func_name(GDFunctions::Function(i))==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name="@GDScript"; + r_result.class_member=p_symbol; + return OK; + } + } + + GDParser p; + p.parse(p_code,p_base_path,false,"",true); + + if (p.get_completion_type()==GDParser::COMPLETION_NONE) + return ERR_CANT_RESOLVE; + + GDCompletionContext context; + + context._class=p.get_completion_class(); + context.block=p.get_completion_block(); + context.function=p.get_completion_function(); + context.base=p_owner; + context.base_path=p_base_path; + bool isfunction=false; + + switch(p.get_completion_type()) { + + case GDParser::COMPLETION_NONE: { + } break; + case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name=Variant::get_type_name(p.get_completion_built_in_constant()); + r_result.class_member=p_symbol; + return OK; + + } break; + case GDParser::COMPLETION_FUNCTION: { + + + if (context._class && context._class->functions.size()) { + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->name==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=context._class->functions[i]->line; + return OK; + } + } + } + + Ref<GDScript> parent = _get_parent_class(context); + while(parent.is_valid()) { + int line = parent->get_member_line(p_symbol); + if (line>=0) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=line; + r_result.script=parent; + return OK; + + } + + parent=parent->get_base(); + } + + GDCompletionIdentifier identifier = _get_native_class(context); + print_line("identifier: "+String(identifier.obj_type)); + + if (ObjectTypeDB::has_method(identifier.obj_type,p_symbol)) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name=identifier.obj_type; + r_result.class_member=p_symbol; + return OK; + } + + + } break; + case GDParser::COMPLETION_IDENTIFIER: { + + //check if a function + if (p.get_completion_identifier_is_function()) { + if (context._class && context._class->functions.size()) { + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->name==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=context._class->functions[i]->line; + return OK; + } + } + } + + Ref<GDScript> parent = _get_parent_class(context); + while(parent.is_valid()) { + int line = parent->get_member_line(p_symbol); + if (line>=0) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=line; + r_result.script=parent; + return OK; + + } + + parent=parent->get_base(); + } + + GDCompletionIdentifier identifier = _get_native_class(context); + + + if (ObjectTypeDB::has_method(identifier.obj_type,p_symbol)) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name=identifier.obj_type; + r_result.class_member=p_symbol; + return OK; + } + } else { + + + const GDParser::BlockNode *block=context.block; + //search in blocks going up (local var?) + while(block) { + + + + for (int i=0;i<block->statements.size();i++) { + + if (block->statements[i]->line>p.get_completion_line()) + continue; + + + if (block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + + const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(block->statements[i]); + + if (lv->assign && lv->name==p_symbol) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=block->statements[i]->line; + return OK; + } + } + } + block=block->parent_block; + } + + //guess from function arguments + if (context.function && context.function->name!=StringName()) { + + for(int i=0;i<context.function->arguments.size();i++) { + + if (context.function->arguments[i]==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=context.function->line; + return OK; + } + + } + } + + //guess in class constants + + for(int i=0;i<context._class->constant_expressions.size();i++) { + + if (context._class->constant_expressions[i].identifier==p_symbol) { + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=context._class->constant_expressions[i].expression->line; + return OK; + } + } + + //guess in class variables + if (!(context.function && context.function->_static)) { + + for(int i=0;i<context._class->variables.size();i++) { + + if (context._class->variables[i].identifier==p_symbol) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=context._class->variables[i].line; + return OK; + } + } + } + + //guess in autoloads as singletons + List<PropertyInfo> props; + Globals::get_singleton()->get_property_list(&props); + + for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + String name = s.get_slice("/",1); + if (name==String(p_symbol)) { + + String path = Globals::get_singleton()->get(s); + if (path.begins_with("*")) { + String script =path.substr(1,path.length()); + + if (!script.ends_with(".gd")) { + //not a script, try find the script anyway, + //may have some success + script=script.basename()+".gd"; + } + + if (FileAccess::exists(script)) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=0; + r_result.script=ResourceLoader::load(script); + return OK; + } + } + } + } + + //global + for(Map<StringName,int>::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) { + if (E->key()==p_symbol) { + + Variant value = GDScriptLanguage::get_singleton()->get_global_array()[E->get()]; + if (value.get_type()==Variant::OBJECT) { + Object *obj = value; + if (obj) { + + if (obj->cast_to<GDNativeClass>()) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.class_name=obj->cast_to<GDNativeClass>()->get_name(); + + } else { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.class_name=obj->get_type(); + } + return OK; + } + } else { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name="@Global Scope"; + r_result.class_member=p_symbol; + return OK; + } + } + + } +#if 0 + GDCompletionIdentifier identifier; + if (_guess_identifier_type(context,p.get_completion_line(),p_symbol,identifier)) { + + print_line("var type: "+Variant::get_type_name(identifier.type)); + if (identifier.script.is_valid()) { + print_line("var script: "+identifier.script->get_path()); + } + print_line("obj type: "+String(identifier.obj_type)); + print_line("value: "+String(identifier.value)); + } +#endif + } + + } break; + case GDParser::COMPLETION_PARENT_FUNCTION: { + + } break; + case GDParser::COMPLETION_METHOD: + isfunction=true; + case GDParser::COMPLETION_INDEX: { + + const GDParser::Node *node = p.get_completion_node(); + if (node->type!=GDParser::Node::TYPE_OPERATOR) + break; + + + + + GDCompletionIdentifier t; + if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) { + + if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") { + //native enum + Ref<GDNativeClass> gdn = t.value; + if (gdn.is_valid()) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name=gdn->get_name();; + r_result.class_member=p_symbol; + return OK; + + } + } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + + Ref<GDScript> on_script; + + if (t.value.get_type()) { + Object *obj=t.value; + + + if (obj) { + + + on_script=obj->get_script(); + + if (on_script.is_valid()) { + int loc = on_script->get_member_line(p_symbol); + if (loc>=0) { + r_result.script=on_script; + r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.location=loc; + return OK; + } + } + } + } + + if (ObjectTypeDB::has_method(t.obj_type,p_symbol)) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name=t.obj_type; + r_result.class_member=p_symbol; + return OK; + + } + + bool success; + ObjectTypeDB::get_integer_constant(t.obj_type,p_symbol,&success); + if (success) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name=t.obj_type; + r_result.class_member=p_symbol; + return OK; + } + + ObjectTypeDB::get_property_type(t.obj_type,p_symbol,&success); + + if (success) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY; + r_result.class_name=t.obj_type; + r_result.class_member=p_symbol; + return OK; + } + + + } else { + + Variant::CallError ce; + Variant v = Variant::construct(t.type,NULL,0,ce); + + bool valid; + v.get_numeric_constant_value(t.type,p_symbol,&valid); + if (valid) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name=Variant::get_type_name(t.type); + r_result.class_member=p_symbol; + return OK; + } + + //todo check all inputevent types for property + + v.get(p_symbol,&valid); + + if (valid) { + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY; + r_result.class_name=Variant::get_type_name(t.type); + r_result.class_member=p_symbol; + return OK; + } + + if (v.has_method(p_symbol)) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name=Variant::get_type_name(t.type); + r_result.class_member=p_symbol; + return OK; + + } + + + } + } + + + } break; + case GDParser::COMPLETION_CALL_ARGUMENTS: { + + return ERR_CANT_RESOLVE; + } break; + case GDParser::COMPLETION_VIRTUAL_FUNC: { + + GDCompletionIdentifier cid = _get_native_class(context); + + if (cid.obj_type!=StringName()) { + List<MethodInfo> vm; + ObjectTypeDB::get_virtual_methods(cid.obj_type,&vm); + for(List<MethodInfo>::Element *E=vm.front();E;E=E->next()) { + + if (p_symbol==E->get().name) { + + r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name=cid.obj_type; + r_result.class_member=p_symbol; + return OK; + + } + } + } + } break; + case GDParser::COMPLETION_YIELD: { + + return ERR_CANT_RESOLVE; + + } break; + + } + + + return ERR_CANT_RESOLVE; +} + +#endif diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index b2cc6341c1..094e21bb4f 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -1437,7 +1437,7 @@ void GDFunctionState::_bind_methods() { ObjectTypeDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant())); ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid); - ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback")); + ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback")); } diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index a565e866d0..e82eb83773 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -840,8 +840,13 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument=0; r_ret=Variant(); + } else if(((String)(*p_args[0])).begins_with("/")) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_ret=RTR("Paths cannot start with '/', absolute paths must start with 'res://', 'user://', or 'local://'"); + } else { + r_ret=ResourceLoader::load(*p_args[0]); } - r_ret=ResourceLoader::load(*p_args[0]); } break; case INST2DICT: { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index e5a8dc0152..3c01f469f9 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -203,6 +203,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide completion_line=tokenizer->get_token_line(); completion_block=current_block; completion_found=true; + completion_ident_is_call=false; tokenizer->advance(); if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { @@ -210,6 +211,9 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide tokenizer->advance(); } + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) { + completion_ident_is_call=true; + } return true; } @@ -303,6 +307,10 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ _set_error("expected string constant as 'preload' argument."); return NULL; } + if (path.begins_with("/")) { + _set_error("Paths cannot start with '/', absolute paths must start with \'res://\', \'user://\', or \'local://\'"); + return NULL; + } if (!path.is_abs_path() && base_path!="") path=base_path+"/"+path; path = path.replace("///","//").simplify_path(); @@ -936,6 +944,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break; case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break; case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break; + case GDTokenizer::TK_CF_IF: op=OperatorNode::OP_TERNARY_IF; break; + case GDTokenizer::TK_CF_ELSE: op=OperatorNode::OP_TERNARY_ELSE; break; default: valid=false; break; } @@ -958,6 +968,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ int next_op=-1; int min_priority=0xFFFFF; bool is_unary=false; + bool is_ternary=false; for(int i=0;i<expression.size();i++) { @@ -971,6 +982,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ int priority; bool unary=false; + bool ternary=false; + bool error=false; switch(expression[i].op) { @@ -1001,25 +1014,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ case OperatorNode::OP_EQUAL: priority=8; break; case OperatorNode::OP_NOT_EQUAL: priority=8; break; + case OperatorNode::OP_IN: priority=10; break; - + case OperatorNode::OP_NOT: priority=11; unary=true; break; case OperatorNode::OP_AND: priority=12; break; case OperatorNode::OP_OR: priority=13; break; - - // ?: = 10 - - case OperatorNode::OP_ASSIGN: priority=14; break; - case OperatorNode::OP_ASSIGN_ADD: priority=14; break; - case OperatorNode::OP_ASSIGN_SUB: priority=14; break; - case OperatorNode::OP_ASSIGN_MUL: priority=14; break; - case OperatorNode::OP_ASSIGN_DIV: priority=14; break; - case OperatorNode::OP_ASSIGN_MOD: priority=14; break; - case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=14; break; - case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=14; break; - case OperatorNode::OP_ASSIGN_BIT_AND: priority=14; break; - case OperatorNode::OP_ASSIGN_BIT_OR: priority=14; break; - case OperatorNode::OP_ASSIGN_BIT_XOR: priority=14; break; + + case OperatorNode::OP_TERNARY_IF: priority=14; ternary=true; break; + case OperatorNode::OP_TERNARY_ELSE: priority=14; error=true; break; // Errors out when found without IF (since IF would consume it) + + case OperatorNode::OP_ASSIGN: priority=15; break; + case OperatorNode::OP_ASSIGN_ADD: priority=15; break; + case OperatorNode::OP_ASSIGN_SUB: priority=15; break; + case OperatorNode::OP_ASSIGN_MUL: priority=15; break; + case OperatorNode::OP_ASSIGN_DIV: priority=15; break; + case OperatorNode::OP_ASSIGN_MOD: priority=15; break; + case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=15; break; + case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=15; break; + case OperatorNode::OP_ASSIGN_BIT_AND: priority=15; break; + case OperatorNode::OP_ASSIGN_BIT_OR: priority=15; break; + case OperatorNode::OP_ASSIGN_BIT_XOR: priority=15; break; default: { @@ -1030,11 +1045,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ } if (priority<min_priority) { + if(error) { + _set_error("Unexpected operator"); + return NULL; + } // < is used for left to right (default) // <= is used for right to left next_op=i; min_priority=priority; is_unary=unary; + is_ternary=ternary; } } @@ -1075,6 +1095,62 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ } + } else if(is_ternary) { + if (next_op <1 || next_op>=(expression.size()-1)) { + _set_error("Parser bug.."); + ERR_FAIL_V(NULL); + } + + if(next_op>=(expression.size()-2) || expression[next_op+2].op != OperatorNode::OP_TERNARY_ELSE) { + _set_error("Expected else after ternary if."); + ERR_FAIL_V(NULL); + } + if(next_op>=(expression.size()-3)) { + _set_error("Expected value after ternary else."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op=expression[next_op].op; + op->line=op_line; //line might have been changed from a \n + + if (expression[next_op-1].is_op) { + + _set_error("Parser bug.."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op+1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by an unary op in a valid combination, + // due to how precedence works, unaries will always dissapear first + + _set_error("Unexpected two consecutive operators after ternary if."); + return NULL; + } + + if (expression[next_op+3].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by an unary op in a valid combination, + // due to how precedence works, unaries will always dissapear first + + _set_error("Unexpected two consecutive operators after ternary else."); + return NULL; + } + + + op->arguments.push_back(expression[next_op+1].node); //next expression goes as first + op->arguments.push_back(expression[next_op-1].node); //left expression goes as when-true + op->arguments.push_back(expression[next_op+3].node); //expression after next goes as when-false + + //replace all 3 nodes by this operator and make it an expression + expression[next_op-1].node=op; + expression.remove(next_op); + expression.remove(next_op); + expression.remove(next_op); + expression.remove(next_op); } else { if (next_op <1 || next_op>=(expression.size()-1)) { @@ -2037,6 +2113,10 @@ void GDParser::_parse_extends(ClassNode *p_class) { _set_error("'extends' constant must be a string."); return; } + if (((String)(constant)).begins_with("/")) { + _set_error("Paths cannot start with '/', absolute paths must start with \'res://\', \'user://\', or \'local://\'"); + return; + } p_class->extends_file=constant; tokenizer->advance(); @@ -3029,6 +3109,16 @@ void GDParser::_parse_class(ClassNode *p_class) { } member._export.type=cn->value.get_type(); member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE; + if (cn->value.get_type()==Variant::OBJECT) { + Object *obj = cn->value; + Resource *res = obj->cast_to<Resource>(); + if(res==NULL) { + _set_error("Exported constant not a type or resource."); + return; + } + member._export.hint=PROPERTY_HINT_RESOURCE_TYPE; + member._export.hint_string=res->get_type(); + } } } #ifdef TOOLS_ENABLED @@ -3157,6 +3247,114 @@ void GDParser::_parse_class(ClassNode *p_class) { } } break; + case GDTokenizer::TK_PR_ENUM: { + //mutiple constant declarations.. + + int last_assign = -1; // Incremented by 1 right before the assingment. + String enum_name; + Dictionary enum_dict; + + tokenizer->advance(); + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { + enum_name=tokenizer->get_token_identifier(); + tokenizer->advance(); + } + if (tokenizer->get_token()!=GDTokenizer::TK_CURLY_BRACKET_OPEN) { + _set_error("Expected '{' in enum declaration"); + return; + } + tokenizer->advance(); + + while(true) { + if(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) { + + tokenizer->advance(); // Ignore newlines + } else if (tokenizer->get_token()==GDTokenizer::TK_CURLY_BRACKET_CLOSE) { + + tokenizer->advance(); + break; // End of enum + } else if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + + if(tokenizer->get_token()==GDTokenizer::TK_EOF) { + _set_error("Unexpected end of file."); + } else { + _set_error(String("Unexpected ") + GDTokenizer::get_token_name(tokenizer->get_token()) + ", expected identifier"); + } + + return; + } else { // tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER + ClassNode::Constant constant; + + constant.identifier=tokenizer->get_token_identifier(); + + tokenizer->advance(); + + if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) { + tokenizer->advance(); + + Node *subexpr=NULL; + + subexpr = _parse_and_reduce_expression(p_class,true,true); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } + return; + } + + if (subexpr->type!=Node::TYPE_CONSTANT) { + _set_error("Expected constant expression"); + } + + const ConstantNode *subexpr_const = static_cast<const ConstantNode*>(subexpr); + + if(subexpr_const->value.get_type() != Variant::INT) { + _set_error("Expected an int value for enum"); + } + + last_assign = subexpr_const->value; + + constant.expression=subexpr; + + } else { + last_assign = last_assign + 1; + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = last_assign; + constant.expression = cn; + } + + if(tokenizer->get_token()==GDTokenizer::TK_COMMA) { + tokenizer->advance(); + } + + if(enum_name != "") { + const ConstantNode *cn = static_cast<const ConstantNode*>(constant.expression); + enum_dict[constant.identifier] = cn->value; + } + + p_class->constant_expressions.push_back(constant); + } + + } + + if(enum_name != "") { + ClassNode::Constant enum_constant; + enum_constant.identifier=enum_name; + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = enum_dict; + enum_constant.expression=cn; + p_class->constant_expressions.push_back(enum_constant); + } + + if (!_end_statement()) { + _set_error("Expected end of statement (enum)"); + return; + } + + + + + } break; default: { @@ -3376,6 +3574,11 @@ int GDParser::get_completion_argument_index() { return completion_argument; } +int GDParser::get_completion_identifier_is_function() { + + return completion_ident_is_call; +} + GDParser::GDParser() { head=NULL; diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 9e6f6e6765..75653e0916 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -247,6 +247,9 @@ public: OP_BIT_AND, OP_BIT_OR, OP_BIT_XOR, + //ternary operators + OP_TERNARY_IF, + OP_TERNARY_ELSE, }; Operator op; @@ -429,6 +432,7 @@ private: int completion_line; int completion_argument; bool completion_found; + bool completion_ident_is_call; PropertyInfo current_export; @@ -475,7 +479,7 @@ public: BlockNode *get_completion_block(); FunctionNode *get_completion_function(); int get_completion_argument_index(); - + int get_completion_identifier_is_function(); void clear(); GDParser(); diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index b97a0fcbb6..0ea10950df 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -751,7 +751,7 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { void GDScript::_bind_methods() { - ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new")); + ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new")); ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code); diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 0c3e1eb614..051e80634f 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -65,6 +65,7 @@ class GDScript : public Script { StringName setter; StringName getter; ScriptInstance::RPCMode rpc_mode; + }; friend class GDInstance; @@ -86,8 +87,11 @@ friend class GDScriptLanguage; Map<StringName,Ref<GDScript> > subclasses; Map<StringName,Vector<StringName> > _signals; + #ifdef TOOLS_ENABLED + Map<StringName,int> member_lines; + Map<StringName,Variant> member_default_values; List<PropertyInfo> members_cache; @@ -193,6 +197,16 @@ public: virtual ScriptLanguage *get_language() const; + virtual int get_member_line(const StringName& p_member) const { +#ifdef TOOLS_ENABLED + if (member_lines.has(p_member)) + return member_lines[p_member]; + else +#endif + return -1; + + } + GDScript(); ~GDScript(); }; @@ -394,6 +408,9 @@ public: virtual int find_function(const String& p_function,const String& p_code) const; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const; virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List<String>* r_options,String& r_call_hint); +#ifdef TOOLS_ENABLED + virtual Error lookup_code(const String& p_code, const String& p_symbol, const String& p_base_path, Object*p_owner, LookupResult& r_result); +#endif virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const; virtual void add_global_constant(const StringName& p_variable,const Variant& p_value); diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 47e740b227..2041ec12ad 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -95,6 +95,7 @@ const char* GDTokenizer::token_names[TK_MAX]={ "setget", "const", "var", +"enum", "preload", "assert", "yield", @@ -874,6 +875,7 @@ void GDTokenizerText::_advance() { {TK_PR_SLAVE,"slave"}, {TK_PR_SYNC,"sync"}, {TK_PR_CONST,"const"}, + {TK_PR_ENUM,"enum"}, //controlflow {TK_CF_IF,"if"}, {TK_CF_ELIF,"elif"}, @@ -1330,7 +1332,7 @@ StringName GDTokenizerBuffer::get_token_identifier(int p_offset) const{ ERR_FAIL_INDEX_V(offset,tokens.size(),StringName()); uint32_t identifier = tokens[offset]>>TOKEN_BITS; - ERR_FAIL_INDEX_V(identifier,identifiers.size(),StringName()); + ERR_FAIL_INDEX_V(identifier,(uint32_t)identifiers.size(),StringName()); return identifiers[identifier]; } @@ -1389,7 +1391,7 @@ const Variant& GDTokenizerBuffer::get_token_constant(int p_offset) const{ int offset = token+p_offset; ERR_FAIL_INDEX_V(offset,tokens.size(),nil); uint32_t constant = tokens[offset]>>TOKEN_BITS; - ERR_FAIL_INDEX_V(constant,constants.size(),nil); + ERR_FAIL_INDEX_V(constant,(uint32_t)constants.size(),nil); return constants[constant]; } diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index 1815f82894..b91229ab1e 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -102,6 +102,7 @@ public: TK_PR_SETGET, TK_PR_CONST, TK_PR_VAR, + TK_PR_ENUM, TK_PR_PRELOAD, TK_PR_ASSERT, TK_PR_YIELD, diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 58afeef3e3..ad54149b51 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -36,6 +36,7 @@ #include "visual_script_builtin_funcs.h" #include "visual_script_flow_control.h" #include "visual_script_yield_nodes.h" +#include "visual_script_expression.h" VisualScriptLanguage *visual_script_language=NULL; @@ -60,6 +61,7 @@ void register_visual_script_types() { ObjectTypeDB::register_type<VisualScriptGlobalConstant>(); ObjectTypeDB::register_type<VisualScriptClassConstant>(); ObjectTypeDB::register_type<VisualScriptMathConstant>(); + ObjectTypeDB::register_type<VisualScriptBasicTypeConstant>(); ObjectTypeDB::register_type<VisualScriptEngineSingleton>(); ObjectTypeDB::register_type<VisualScriptSceneNode>(); ObjectTypeDB::register_type<VisualScriptSceneTree>(); @@ -97,11 +99,14 @@ void register_visual_script_types() { ObjectTypeDB::register_type<VisualScriptBuiltinFunc>(); + ObjectTypeDB::register_type<VisualScriptExpression>(); + register_visual_script_nodes(); register_visual_script_func_nodes(); register_visual_script_builtin_func_node(); register_visual_script_flow_control_nodes(); register_visual_script_yield_nodes(); + register_visual_script_expression_node(); #ifdef TOOLS_ENABLED VisualScriptEditor::register_editor(); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index aa09e7f005..bd042c8989 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -3,7 +3,7 @@ #include "scene/main/node.h" #include "os/os.h" #include "globals.h" -#define SCRIPT_VARIABLES_PREFIX "script_variables/" + //used by editor, this is not really saved @@ -574,6 +574,23 @@ bool VisualScript::is_input_value_port_connected(const StringName& p_func,int p_ return false; } +bool VisualScript::get_input_value_port_connection_source(const StringName& p_func,int p_node,int p_port,int *r_node,int *r_port) const { + + ERR_FAIL_COND_V(!functions.has(p_func),false); + const Function &func = functions[p_func]; + + for (const Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) { + if (E->get().to_node==p_node && E->get().to_port==p_port) { + *r_node=E->get().from_node; + *r_port=E->get().from_port; + return true; + } + } + + return false; + +} + void VisualScript::get_data_connection_list(const StringName& p_func,List<DataConnection> *r_connection) const { ERR_FAIL_COND(!functions.has(p_func)); @@ -598,8 +615,6 @@ void VisualScript::add_variable(const StringName& p_name,const Variant& p_defaul v._export=p_export; variables[p_name]=v; - script_variable_remap[SCRIPT_VARIABLES_PREFIX+String(p_name)]=p_name; - #ifdef TOOLS_ENABLED _update_placeholders(); @@ -616,7 +631,6 @@ 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(); @@ -911,7 +925,7 @@ void VisualScript::_update_placeholders() { continue; PropertyInfo p = E->get().info; - p.name=SCRIPT_VARIABLES_PREFIX+String(E->key()); + p.name=String(E->key()); pinfo.push_back(p); values[p.name]=E->get().default_value; } @@ -947,7 +961,7 @@ ScriptInstance* VisualScript::instance_create(Object *p_this) { continue; PropertyInfo p = E->get().info; - p.name=SCRIPT_VARIABLES_PREFIX+String(E->key()); + p.name=String(E->key()); pinfo.push_back(p); values[p.name]=E->get().default_value; } @@ -1045,10 +1059,10 @@ void VisualScript::get_script_signal_list(List<MethodInfo> *r_signals) const { bool VisualScript::get_property_default_value(const StringName& p_property,Variant& r_value) const { - if (!script_variable_remap.has(p_property)) + if (!variables.has(p_property)) return false; - r_value=variables[ script_variable_remap[p_property] ].default_value; + r_value=variables[ p_property ].default_value; return true; } void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const { @@ -1116,6 +1130,7 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const { } } +#ifdef TOOLS_ENABLED bool VisualScript::are_subnodes_edited() const { for(const Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) { @@ -1129,7 +1144,7 @@ bool VisualScript::are_subnodes_edited() const { return false; } - +#endif void VisualScript::_set_data(const Dictionary& p_data) { @@ -1385,12 +1400,10 @@ VisualScript::~VisualScript() { bool VisualScriptInstance::set(const StringName& p_name, const Variant& p_value) { - const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name); - if (!remap) - return false; - Map<StringName,Variant>::Element *E=variables.find(remap->get()); - ERR_FAIL_COND_V(!E,false); + Map<StringName,Variant>::Element *E=variables.find(p_name); + if (!E) + return false; E->get()=p_value; @@ -1400,14 +1413,12 @@ bool VisualScriptInstance::set(const StringName& p_name, const Variant& p_value) bool VisualScriptInstance::get(const StringName& p_name, Variant &r_ret) const { - const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name); - if (!remap) + const Map<StringName,Variant>::Element *E=variables.find(p_name); + if (!E) return false; - const Map<StringName,Variant>::Element *E=variables.find(remap->get()); - ERR_FAIL_COND_V(!E,false); - - return E->get(); + r_ret=E->get(); + return true; } void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{ @@ -1416,21 +1427,15 @@ void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) c if (!E->get()._export) continue; PropertyInfo p = E->get().info; - p.name=SCRIPT_VARIABLES_PREFIX+String(E->key()); + p.name=String(E->key()); p_properties->push_back(p); } } Variant::Type VisualScriptInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const{ - const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name); - if (!remap) { - if (r_is_valid) - *r_is_valid=false; - return Variant::NIL; - } - const Map<StringName,VisualScript::Variable>::Element *E=script->variables.find(remap->get()); + const Map<StringName,VisualScript::Variable>::Element *E=script->variables.find(p_name); if (!E) { if (r_is_valid) *r_is_valid=false; @@ -1892,17 +1897,21 @@ Variant VisualScriptInstance::_call_internal(const StringName& p_method, void* p if (node && (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD || error_str==String())) { + if (error_str!=String()) { + error_str+=" "; + } + if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { int errorarg=r_error.argument; - error_str="Cannot convert argument "+itos(errorarg+1)+" to "+Variant::get_type_name(r_error.expected)+"."; + error_str+="Cannot convert argument "+itos(errorarg+1)+" to "+Variant::get_type_name(r_error.expected)+"."; } else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { - error_str="Expected "+itos(r_error.argument)+" arguments."; + error_str+="Expected "+itos(r_error.argument)+" arguments."; } else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { - error_str="Expected "+itos(r_error.argument)+" arguments."; + error_str+="Expected "+itos(r_error.argument)+" arguments."; } else if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) { - error_str="Invalid Call."; + error_str+="Invalid Call."; } else if (r_error.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { - error_str="Instance is null"; + error_str+="Base Instance is null"; } } @@ -2430,7 +2439,7 @@ void VisualScriptFunctionState::_bind_methods() { ObjectTypeDB::bind_method(_MD("connect_to_signal","obj","signals","args"),&VisualScriptFunctionState::connect_to_signal); ObjectTypeDB::bind_method(_MD("resume:Array","args"),&VisualScriptFunctionState::resume,DEFVAL(Variant())); ObjectTypeDB::bind_method(_MD("is_valid"),&VisualScriptFunctionState::is_valid); - ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback")); + ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback")); } VisualScriptFunctionState::VisualScriptFunctionState() { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 9e96967c6f..63b018b0c2 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -38,6 +38,8 @@ public: virtual String get_output_sequence_port_text(int p_port) const=0; + virtual bool has_mixed_input_and_sequence_ports() const { return false; } + virtual int get_input_value_port_count() const=0; virtual int get_output_value_port_count() const=0; @@ -63,7 +65,7 @@ public: Variant::Type type; InputEvent::Type ev_type; StringName obj_type; - String script_type; + Ref<Script> script; TypeGuess() { type=Variant::NIL; ev_type=InputEvent::NONE; } }; @@ -225,7 +227,6 @@ friend class VisualScriptInstance; Map<StringName,Function> functions; Map<StringName,Variable> variables; - Map<StringName,StringName> script_variable_remap; Map<StringName,Vector<Argument> > custom_signals; Map<Object*,VisualScriptInstance*> instances; @@ -279,6 +280,7 @@ public: bool has_data_connection(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) const; void get_data_connection_list(const StringName& p_func,List<DataConnection> *r_connection) const; bool is_input_value_port_connected(const StringName& p_name,int p_node,int p_port) const; + bool get_input_value_port_connection_source(const StringName& p_name,int p_node,int p_port,int *r_node,int *r_port) const; void add_variable(const StringName& p_name,const Variant& p_default_value=Variant(),bool p_export=false); bool has_variable(const StringName& p_name) const; @@ -342,7 +344,9 @@ public: virtual void get_script_property_list(List<PropertyInfo> *p_list) const; +#ifdef TOOLS_ENABLED virtual bool are_subnodes_edited() const; +#endif VisualScript(); ~VisualScript(); diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 1d0bca0b30..24a44d3506 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -65,6 +65,21 @@ const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "bytes2var", }; +VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String& p_string) { + + for(int i=0;i<FUNC_MAX;i++) { + if (p_string==func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String VisualScriptBuiltinFunc::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func,FUNC_MAX,String()); + return func_name[p_func]; +} int VisualScriptBuiltinFunc::get_output_sequence_port_count() const { @@ -88,10 +103,10 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const{ } -int VisualScriptBuiltinFunc::get_input_value_port_count() const{ +int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { - switch(func) { + switch(p_func) { case MATH_RANDOMIZE: case MATH_RAND: @@ -157,6 +172,11 @@ int VisualScriptBuiltinFunc::get_input_value_port_count() const{ } return 0; } + +int VisualScriptBuiltinFunc::get_input_value_port_count() const{ + + return get_func_argument_count(func); +} int VisualScriptBuiltinFunc::get_output_value_port_count() const{ switch(func) { @@ -560,118 +580,124 @@ VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() { r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\ r_error.argument=m_arg;\ r_error.expected=Variant::REAL;\ - return 0;\ + return;\ } -class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance { -public: - VisualScriptBuiltinFunc *node; - VisualScriptInstance *instance; +void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func,const Variant** p_inputs,Variant* r_return,Variant::CallError& r_error,String& r_error_str) { - VisualScriptBuiltinFunc::BuiltinFunc func; + switch(p_func) { + case VisualScriptBuiltinFunc::MATH_SIN: { + VALIDATE_ARG_NUM(0); + *r_return=Math::sin(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_COS: { - //virtual int get_working_memory_size() const { return 0; } - //virtual bool is_output_port_unsequenced(int p_idx) const { return false; } - //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; } + VALIDATE_ARG_NUM(0); + *r_return=Math::cos(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_TAN: { - virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { + VALIDATE_ARG_NUM(0); + *r_return=Math::tan(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_SINH: { - switch(func) { - case VisualScriptBuiltinFunc::MATH_SIN: { + VALIDATE_ARG_NUM(0); + *r_return=Math::sinh(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_COSH: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::sin(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_COS: { + VALIDATE_ARG_NUM(0); + *r_return=Math::cosh(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_TANH: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::cos(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_TAN: { + VALIDATE_ARG_NUM(0); + *r_return=Math::tanh(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ASIN: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::tan(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_SINH: { + VALIDATE_ARG_NUM(0); + *r_return=Math::asin(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ACOS: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::sinh(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_COSH: { + VALIDATE_ARG_NUM(0); + *r_return=Math::acos(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ATAN: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::cosh(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_TANH: { + VALIDATE_ARG_NUM(0); + *r_return=Math::atan(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ATAN2: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::tanh(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ASIN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::atan2(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_SQRT: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::asin(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ACOS: { + VALIDATE_ARG_NUM(0); + *r_return=Math::sqrt(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_FMOD: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::acos(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ATAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::fmod(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_FPOSMOD: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::atan(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ATAN2: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::fposmod(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_FLOOR: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::atan2(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_SQRT: { + VALIDATE_ARG_NUM(0); + *r_return=Math::floor(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_CEIL: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::sqrt(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_FMOD: { + VALIDATE_ARG_NUM(0); + *r_return=Math::ceil(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ROUND: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::fmod(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_FPOSMOD: { + VALIDATE_ARG_NUM(0); + *r_return=Math::round(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ABS: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::fposmod(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_FLOOR: { + if (p_inputs[0]->get_type()==Variant::INT) { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::floor(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_CEIL: { + int64_t i = *p_inputs[0]; + *r_return=ABS(i); + } else if (p_inputs[0]->get_type()==Variant::REAL) { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::ceil(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ROUND: { + real_t r = *p_inputs[0]; + *r_return=Math::abs(r); + } else { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::round(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ABS: { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::REAL; + + } + } break; + case VisualScriptBuiltinFunc::MATH_SIGN: { if (p_inputs[0]->get_type()==Variant::INT) { int64_t i = *p_inputs[0]; - *p_outputs[0]=ABS(i); + *r_return= i < 0 ? -1 : ( i > 0 ? +1 : 0); } else if (p_inputs[0]->get_type()==Variant::REAL) { real_t r = *p_inputs[0]; - *p_outputs[0]=Math::abs(r); + *r_return= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0); } else { r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -679,410 +705,412 @@ public: r_error.expected=Variant::REAL; } - } break; - case VisualScriptBuiltinFunc::MATH_SIGN: { + } break; + case VisualScriptBuiltinFunc::MATH_POW: { - if (p_inputs[0]->get_type()==Variant::INT) { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::pow(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_LOG: { - int64_t i = *p_inputs[0]; - *p_outputs[0]= i < 0 ? -1 : ( i > 0 ? +1 : 0); - } else if (p_inputs[0]->get_type()==Variant::REAL) { + VALIDATE_ARG_NUM(0); + *r_return=Math::log(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_EXP: { - real_t r = *p_inputs[0]; - *p_outputs[0]= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0); - } else { + VALIDATE_ARG_NUM(0); + *r_return=Math::exp(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ISNAN: { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::REAL; + VALIDATE_ARG_NUM(0); + *r_return=Math::is_nan(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_ISINF: { - } - } break; - case VisualScriptBuiltinFunc::MATH_POW: { + VALIDATE_ARG_NUM(0); + *r_return=Math::is_inf(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_EASE: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::pow(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_LOG: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::ease(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_DECIMALS: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::log(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_EXP: { + VALIDATE_ARG_NUM(0); + *r_return=Math::step_decimals(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_STEPIFY: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::exp(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ISNAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::stepify(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_LERP: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::is_nan(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_ISINF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]); + } break; + case VisualScriptBuiltinFunc::MATH_DECTIME: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::is_inf(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_EASE: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]); + } break; + case VisualScriptBuiltinFunc::MATH_RANDOMIZE: { + Math::randomize(); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::ease(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_DECIMALS: { + } break; + case VisualScriptBuiltinFunc::MATH_RAND: { + *r_return=Math::rand(); + } break; + case VisualScriptBuiltinFunc::MATH_RANDF: { + *r_return=Math::randf(); + } break; + case VisualScriptBuiltinFunc::MATH_RANDOM: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::step_decimals(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_STEPIFY: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return=Math::random(*p_inputs[0],*p_inputs[1]); + } break; + case VisualScriptBuiltinFunc::MATH_SEED: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::stepify(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_LERP: { + VALIDATE_ARG_NUM(0); + uint32_t seed=*p_inputs[0]; + Math::seed(seed); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *p_outputs[0]=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]); - } break; - case VisualScriptBuiltinFunc::MATH_DECTIME: { + } break; + case VisualScriptBuiltinFunc::MATH_RANDSEED: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *p_outputs[0]=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]); - } break; - case VisualScriptBuiltinFunc::MATH_RANDOMIZE: { - Math::randomize(); - - } break; - case VisualScriptBuiltinFunc::MATH_RAND: { - *p_outputs[0]=Math::rand(); - } break; - case VisualScriptBuiltinFunc::MATH_RANDF: { - *p_outputs[0]=Math::randf(); - } break; - case VisualScriptBuiltinFunc::MATH_RANDOM: { + VALIDATE_ARG_NUM(0); + uint32_t seed=*p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return=reta; - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - *p_outputs[0]=Math::random(*p_inputs[0],*p_inputs[1]); - } break; - case VisualScriptBuiltinFunc::MATH_SEED: { + } break; + case VisualScriptBuiltinFunc::MATH_DEG2RAD: { - VALIDATE_ARG_NUM(0); - uint32_t seed=*p_inputs[0]; - Math::seed(seed); + VALIDATE_ARG_NUM(0); + *r_return=Math::deg2rad(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_RAD2DEG: { - } break; - case VisualScriptBuiltinFunc::MATH_RANDSEED: { + VALIDATE_ARG_NUM(0); + *r_return=Math::rad2deg(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_LINEAR2DB: { - VALIDATE_ARG_NUM(0); - uint32_t seed=*p_inputs[0]; - int ret = Math::rand_from_seed(&seed); - Array reta; - reta.push_back(ret); - reta.push_back(seed); - *p_outputs[0]=reta; + VALIDATE_ARG_NUM(0); + *r_return=Math::linear2db(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::MATH_DB2LINEAR: { - } break; - case VisualScriptBuiltinFunc::MATH_DEG2RAD: { + VALIDATE_ARG_NUM(0); + *r_return=Math::db2linear(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::LOGIC_MAX: { - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::deg2rad(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_RAD2DEG: { + if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) { + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return=MAX(a,b); + } else { VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::rad2deg(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_LINEAR2DB: { + VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::linear2db(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::MATH_DB2LINEAR: { + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return=MAX(a,b); + } + } break; + case VisualScriptBuiltinFunc::LOGIC_MIN: { + + if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return=MIN(a,b); + } else { VALIDATE_ARG_NUM(0); - *p_outputs[0]=Math::db2linear(*p_inputs[0]); - } break; - case VisualScriptBuiltinFunc::LOGIC_MAX: { + VALIDATE_ARG_NUM(1); - if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) { + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - *p_outputs[0]=MAX(a,b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); + *r_return=MIN(a,b); + } + } break; + case VisualScriptBuiltinFunc::LOGIC_CLAMP: { - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; + if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) { - *p_outputs[0]=MAX(a,b); - } + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return=CLAMP(a,b,c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); - } break; - case VisualScriptBuiltinFunc::LOGIC_MIN: { + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; - if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) { + *r_return=CLAMP(a,b,c); + } + } break; + case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: { - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - *p_outputs[0]=MIN(a,b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = nearest_power_of_2(num); + } break; + case VisualScriptBuiltinFunc::OBJ_WEAKREF: { - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; + if (p_inputs[0]->get_type()!=Variant::OBJECT) { - *p_outputs[0]=MIN(a,b); - } - } break; - case VisualScriptBuiltinFunc::LOGIC_CLAMP: { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::OBJECT; - if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) { + return; - int64_t a = *p_inputs[0]; - int64_t b = *p_inputs[1]; - int64_t c = *p_inputs[2]; - *p_outputs[0]=CLAMP(a,b,c); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); + } - real_t a = *p_inputs[0]; - real_t b = *p_inputs[1]; - real_t c = *p_inputs[2]; + if (p_inputs[0]->is_ref()) { - *p_outputs[0]=CLAMP(a,b,c); + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; } - } break; - case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: { - VALIDATE_ARG_NUM(0); - int64_t num = *p_inputs[0]; - *p_outputs[0] = nearest_power_of_2(num); - } break; - case VisualScriptBuiltinFunc::OBJ_WEAKREF: { + Ref<WeakRef> wref = memnew( WeakRef ); + wref->set_ref(r); + *r_return=wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { - if (p_inputs[0]->get_type()!=Variant::OBJECT) { + return; + } + Ref<WeakRef> wref = memnew( WeakRef ); + wref->set_obj(obj); + *r_return=wref; + } - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::OBJECT; - return 0; - } - if (p_inputs[0]->is_ref()) { + } break; + case VisualScriptBuiltinFunc::FUNC_FUNCREF: { - REF r = *p_inputs[0]; - if (!r.is_valid()) { + if (p_inputs[0]->get_type()!=Variant::OBJECT) { - return 0; - } + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::OBJECT; - Ref<WeakRef> wref = memnew( WeakRef ); - wref->set_ref(r); - *p_outputs[0]=wref; - } else { - Object *obj = *p_inputs[0]; - if (!obj) { - - return 0; - } - Ref<WeakRef> wref = memnew( WeakRef ); - wref->set_obj(obj); - *p_outputs[0]=wref; - } + return; + } + if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=1; + r_error.expected=Variant::STRING; + return; - } break; - case VisualScriptBuiltinFunc::FUNC_FUNCREF: { + } - if (p_inputs[0]->get_type()!=Variant::OBJECT) { + Ref<FuncRef> fr = memnew( FuncRef); - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::OBJECT; + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); - return 0; + *r_return=fr; - } - if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) { + } break; + case VisualScriptBuiltinFunc::TYPE_CONVERT: { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=1; - r_error.expected=Variant::STRING; + VALIDATE_ARG_NUM(1); + int type=*p_inputs[1]; + if (type<0 || type>=Variant::VARIANT_MAX) { - return 0; + r_error_str=RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::INT; + return; - } + } else { - Ref<FuncRef> fr = memnew( FuncRef); - fr->set_instance(*p_inputs[0]); - fr->set_function(*p_inputs[1]); + *r_return=Variant::construct(Variant::Type(type),p_inputs,1,r_error); + } + } break; + case VisualScriptBuiltinFunc::TYPE_OF: { - *p_outputs[0]=fr; - } break; - case VisualScriptBuiltinFunc::TYPE_CONVERT: { + *r_return = p_inputs[0]->get_type(); - VALIDATE_ARG_NUM(1); - int type=*p_inputs[1]; - if (type<0 || type>=Variant::VARIANT_MAX) { + } break; + case VisualScriptBuiltinFunc::TYPE_EXISTS: { - *p_outputs[0]=RTR("Invalid type argument to convert(), use TYPE_* constants."); - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::INT; - return 0; - } else { + *r_return = ObjectTypeDB::type_exists(*p_inputs[0]); + } break; + case VisualScriptBuiltinFunc::TEXT_STR: { - *p_outputs[0]=Variant::construct(Variant::Type(type),p_inputs,1,r_error); - } - } break; - case VisualScriptBuiltinFunc::TYPE_OF: { + String str = *p_inputs[0]; + + *r_return=str; + } break; + case VisualScriptBuiltinFunc::TEXT_PRINT: { - *p_outputs[0] = p_inputs[0]->get_type(); + String str = *p_inputs[0]; + print_line(str); - } break; - case VisualScriptBuiltinFunc::TYPE_EXISTS: { + } break; - *p_outputs[0] = ObjectTypeDB::type_exists(*p_inputs[0]); + case VisualScriptBuiltinFunc::TEXT_PRINTERR: { - } break; - case VisualScriptBuiltinFunc::TEXT_STR: { + String str = *p_inputs[0]; - String str = *p_inputs[0]; + //str+="\n"; + OS::get_singleton()->printerr("%s\n",str.utf8().get_data()); - *p_outputs[0]=str; - } break; - case VisualScriptBuiltinFunc::TEXT_PRINT: { + } break; + case VisualScriptBuiltinFunc::TEXT_PRINTRAW: { + String str = *p_inputs[0]; - String str = *p_inputs[0]; - print_line(str); + //str+="\n"; + OS::get_singleton()->print("%s",str.utf8().get_data()); - } break; + } break; + case VisualScriptBuiltinFunc::VAR_TO_STR: { - case VisualScriptBuiltinFunc::TEXT_PRINTERR: { + String vars; + VariantWriter::write_to_string(*p_inputs[0],vars); + *r_return=vars; + } break; + case VisualScriptBuiltinFunc::STR_TO_VAR: { - String str = *p_inputs[0]; + if (p_inputs[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; - //str+="\n"; - OS::get_singleton()->printerr("%s\n",str.utf8().get_data()); + return; + } + VariantParser::StreamString ss; + ss.s=*p_inputs[0]; - } break; - case VisualScriptBuiltinFunc::TEXT_PRINTRAW: { - String str = *p_inputs[0]; + String errs; + int line; + Error err = VariantParser::parse(&ss,*r_return,errs,line); - //str+="\n"; - OS::get_singleton()->print("%s",str.utf8().get_data()); + if (err!=OK) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + *r_return="Parse error at line "+itos(line)+": "+errs; + return; + } + } break; + case VisualScriptBuiltinFunc::VAR_TO_BYTES: { - } break; - case VisualScriptBuiltinFunc::VAR_TO_STR: { - String vars; - VariantWriter::write_to_string(*p_inputs[0],vars); - *p_outputs[0]=vars; - } break; - case VisualScriptBuiltinFunc::STR_TO_VAR: { + ByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0],NULL,len); + if (err) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::NIL; + r_error_str="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } - if (p_inputs[0]->get_type()!=Variant::STRING) { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::STRING; + barr.resize(len); + { + ByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0],w.ptr(),len); - return 0; - } + } + *r_return=barr; + } break; + case VisualScriptBuiltinFunc::BYTES_TO_VAR: { - VariantParser::StreamString ss; - ss.s=*p_inputs[0]; + if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::RAW_ARRAY; - String errs; - int line; - Error err = VariantParser::parse(&ss,*p_outputs[0],errs,line); + return; + } + ByteArray varr=*p_inputs[0]; + Variant ret; + { + ByteArray::Read r=varr.read(); + Error err = decode_variant(ret,r.ptr(),varr.size(),NULL); if (err!=OK) { + r_error_str=RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument=0; - r_error.expected=Variant::STRING; - *p_outputs[0]="Parse error at line "+itos(line)+": "+errs; - return 0; + r_error.expected=Variant::RAW_ARRAY; + return; } - } break; - case VisualScriptBuiltinFunc::VAR_TO_BYTES: { + } + *r_return=ret; - ByteArray barr; - int len; - Error err = encode_variant(*p_inputs[0],NULL,len); - if (err) { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::NIL; - *p_outputs[0]="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; - return 0; - } + } break; + default: {} + } - barr.resize(len); - { - ByteArray::Write w = barr.write(); - encode_variant(*p_inputs[0],w.ptr(),len); +} - } - *p_outputs[0]=barr; - } break; - case VisualScriptBuiltinFunc::BYTES_TO_VAR: { - if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::RAW_ARRAY; +class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance { +public: - return 0; - } + VisualScriptBuiltinFunc *node; + VisualScriptInstance *instance; - ByteArray varr=*p_inputs[0]; - Variant ret; - { - ByteArray::Read r=varr.read(); - Error err = decode_variant(ret,r.ptr(),varr.size(),NULL); - if (err!=OK) { - *p_outputs[0]=RTR("Not enough bytes for decoding bytes, or invalid format."); - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=0; - r_error.expected=Variant::RAW_ARRAY; - return 0; - } + VisualScriptBuiltinFunc::BuiltinFunc func; - } - *p_outputs[0]=ret; + //virtual int get_working_memory_size() const { return 0; } + //virtual bool is_output_port_unsequenced(int p_idx) const { return false; } + //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; } + + virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { - } break; - default: {} - } + VisualScriptBuiltinFunc::exec_func(func,p_inputs,p_outputs[0],r_error,r_error_str); return 0; } diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index ebf227a192..000230d84f 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -68,6 +68,11 @@ public: FUNC_MAX }; + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant** p_inputs, Variant* r_return, Variant::CallError& r_error, String& r_error_str); + static BuiltinFunc find_function(const String& p_string); + private: static const char* func_name[FUNC_MAX]; BuiltinFunc func; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7f09a265fb..02d3c453c4 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3,6 +3,7 @@ #include "visual_script_nodes.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" +#include "visual_script_expression.h" #include "os/input.h" #include "tools/editor/editor_resource_preview.h" #include "os/keyboard.h" @@ -330,7 +331,7 @@ static Color _color_from_type(Variant::Type p_type) { case Variant::QUAT: color = Color::html("ec69a3"); break; case Variant::_AABB: color = Color::html("ee7991"); break; case Variant::MATRIX3: color = Color::html("e3ec69"); break; - case Variant::TRANSFORM: color = Color::html("ecd669"); break; + case Variant::TRANSFORM: color = Color::html("f6a86e"); break; case Variant::COLOR: color = Color::html("9dff70"); break; case Variant::IMAGE: color = Color::html("93f1b9"); break; @@ -502,9 +503,20 @@ void VisualScriptEditor::_update_graph(int p_only_id) { } - Label *text = memnew( Label ); - text->set_text(node->get_text()); - gnode->add_child(text); + if (node->cast_to<VisualScriptExpression>()) { + + LineEdit *line_edit = memnew( LineEdit ); + line_edit->set_text(node->get_text()); + line_edit->set_expand_to_text_length(true); + line_edit->add_font_override("font",get_font("source","EditorFonts")); + gnode->add_child(line_edit); + line_edit->connect("text_changed",this,"_expression_text_changed",varray(E->get())); + } else { + Label *text = memnew( Label ); + text->set_text(node->get_text()); + gnode->add_child(text); + } + if (node->cast_to<VisualScriptComment>()) { Ref<VisualScriptComment> vsc=node; @@ -523,19 +535,27 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->set_offset(pos*EDSCALE); slot_idx++; + + int mixed_seq_ports=0; + if (!single_seq_output) { - for(int i=0;i<node->get_output_sequence_port_count();i++) { - Label *text2 = memnew( Label ); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - gnode->add_child(text2); - gnode->set_slot(slot_idx,false,0,Color(),true,TYPE_SEQUENCE,Color(1,1,1,1),seq_port,seq_port); - slot_idx++; + if (node->has_mixed_input_and_sequence_ports()) { + mixed_seq_ports=node->get_output_sequence_port_count(); + } else { + for(int i=0;i<node->get_output_sequence_port_count();i++) { + + Label *text2 = memnew( Label ); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + gnode->add_child(text2); + gnode->set_slot(slot_idx,false,0,Color(),true,TYPE_SEQUENCE,Color(1,1,1,1),seq_port,seq_port); + slot_idx++; + } } } - for(int i=0;i<MAX(node->get_output_value_port_count(),node->get_input_value_port_count());i++) { + for(int i=0;i<MAX(node->get_output_value_port_count(),MAX(mixed_seq_ports,node->get_input_value_port_count()));i++) { bool left_ok=false; Variant::Type left_type=Variant::NIL; @@ -554,8 +574,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) { Variant::Type right_type=Variant::NIL; String right_name; - if (i<node->get_output_value_port_count()) { - PropertyInfo pi = node->get_output_value_port_info(i); + if (i>=mixed_seq_ports && i<node->get_output_value_port_count()+mixed_seq_ports) { + PropertyInfo pi = node->get_output_value_port_info(i-mixed_seq_ports); right_ok=true; right_type=pi.type; right_name=pi.name; @@ -620,6 +640,14 @@ void VisualScriptEditor::_update_graph(int p_only_id) { hbc->add_spacer(); + if (i<mixed_seq_ports) { + + Label *text2 = memnew( Label ); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + hbc->add_child(text2); + } + if (right_ok) { hbc->add_child(memnew(Label(right_name))); @@ -639,7 +667,11 @@ void VisualScriptEditor::_update_graph(int p_only_id) { gnode->add_child(hbc); - gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),right_ok,right_type,_color_from_type(right_type)); + if (i<mixed_seq_ports) { + gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),true,TYPE_SEQUENCE,Color(1,1,1,1),Ref<Texture>(),seq_port); + } else { + gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),right_ok,right_type,_color_from_type(right_type)); + } slot_idx++; } @@ -1177,6 +1209,30 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt } } +void VisualScriptEditor::_expression_text_changed(const String& p_text,int p_id) { + + Ref<VisualScriptExpression> vse = script->get_node(edited_func,p_id); + if (!vse.is_valid()) + return; + + + updating_graph=true; + + undo_redo->create_action(TTR("Change Expression"),UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(vse.ptr(),"expression",p_text); + undo_redo->add_undo_property(vse.ptr(),"expression",vse->get("expression")); + undo_redo->add_do_method(this,"_update_graph",p_id); + undo_redo->add_undo_method(this,"_update_graph",p_id); + undo_redo->commit_action(); + + Node *node = graph->get_node(itos(p_id)); + if (node->cast_to<Control>()) + node->cast_to<Control>()->set_size(Vector2(1,1)); //shrink if text is smaller + + updating_graph=false; + +} + void VisualScriptEditor::_available_node_doubleclicked() { TreeItem *item = nodes->get_selected(); @@ -2084,6 +2140,7 @@ Vector<String> VisualScriptEditor::get_functions(){ void VisualScriptEditor::set_edited_script(const Ref<Script>& p_script){ + script=p_script; signal_editor->script=p_script; signal_editor->undo_redo=undo_redo; @@ -2558,11 +2615,320 @@ void VisualScriptEditor::_graph_disconnected(const String& p_from,int p_from_slo } + void VisualScriptEditor::_graph_connect_to_empty(const String& p_from,int p_from_slot,const Vector2& p_release_pos) { + Node* node = graph->get_node(p_from); + if (!node) + return; + + GraphNode *gn = node->cast_to<GraphNode>(); + if (!gn) + return; + + Ref<VisualScriptNode> vsn = script->get_node(edited_func,p_from.to_int()); + if (!vsn.is_valid()) + return; + + if (p_from_slot<vsn->get_output_sequence_port_count()) { + + port_action_popup->clear(); + port_action_popup->add_item(TTR("Condition"),CREATE_COND); + port_action_popup->add_item(TTR("Sequence"),CREATE_SEQUENCE); + port_action_popup->add_item(TTR("Switch"),CREATE_SWITCH); + port_action_popup->add_item(TTR("Iterator"),CREATE_ITERATOR); + port_action_popup->add_item(TTR("While"),CREATE_WHILE); + port_action_popup->add_item(TTR("Return"),CREATE_RETURN); + + port_action_node=p_from.to_int(); + port_action_output=p_from_slot; + + } else { + port_action_popup->clear(); + port_action_popup->add_item(TTR("Call"),CREATE_CALL); + port_action_popup->add_item(TTR("Get"),CREATE_GET); + port_action_popup->add_item(TTR("Set"),CREATE_SET); + + + port_action_output=p_from_slot-vsn->get_output_sequence_port_count(); + port_action_node=p_from.to_int(); + + + } + + port_action_pos=p_release_pos; + port_action_popup->set_size(Size2(1,1)); + port_action_popup->set_pos(graph->get_global_pos()+p_release_pos); + port_action_popup->popup(); +} + +VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_node,int p_output,Set<int> &visited_nodes) { + + + VisualScriptNode::TypeGuess tg; + tg.type=Variant::NIL; + + if (visited_nodes.has(p_node)) + return tg; //no loop + + visited_nodes.insert(p_node); + + Ref<VisualScriptNode> node = script->get_node(edited_func,p_node); + + if (!node.is_valid()) { + + return tg; + } + + Vector<VisualScriptNode::TypeGuess> in_guesses; + + for(int i=0;i<node->get_input_value_port_count();i++) { + PropertyInfo pi = node->get_input_value_port_info(i); + VisualScriptNode::TypeGuess g; + g.type=pi.type; + + if (g.type==Variant::NIL || g.type==Variant::OBJECT) { + //any or object input, must further guess what this is + int from_node; + int from_port; + + if (script->get_input_value_port_connection_source(edited_func,p_node,i,&from_node,&from_port)) { + + g = _guess_output_type(from_node,from_port,visited_nodes); + } else { + Variant defval = node->get_default_input_value(i); + if (defval.get_type()==Variant::OBJECT) { + + Object *obj = defval; + + if (obj) { + + g.type=Variant::OBJECT; + g.obj_type=obj->get_type(); + g.script=obj->get_script(); + } + } + } + + } + + in_guesses.push_back(g); + } + + return node->guess_output_type(in_guesses.ptr(),p_output); +} + +void VisualScriptEditor::_port_action_menu(int p_option) { + + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap,snap)); + } + ofs/=EDSCALE; + + bool seq_connect=false; + + Ref<VisualScriptNode> vnode; + Set<int> vn; + + switch(p_option) { + + case CREATE_CALL: { + + Ref<VisualScriptFunctionCall> n; + n.instance(); + vnode=n; + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); + + if (tg.type==Variant::OBJECT) { + n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + + if (tg.obj_type!=StringName()) { + n->set_base_type(tg.obj_type); + } else { + n->set_base_type("Object"); + } + + if (tg.script.is_valid()) { + n->set_base_script(tg.script->get_path()); + new_connect_node_select->select_method_from_script(tg.script); + } else { + new_connect_node_select->select_method_from_base_type(n->get_base_type()); + } + + + } else { + n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); + n->set_basic_type(tg.type); + new_connect_node_select->select_method_from_basic_type(tg.type); + } + + + + } break; + case CREATE_SET: { + + Ref<VisualScriptPropertySet> n; + n.instance(); + vnode=n; + + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); + + if (tg.type==Variant::OBJECT) { + n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + + if (tg.obj_type!=StringName()) { + n->set_base_type(tg.obj_type); + } else { + n->set_base_type("Object"); + } + + if (tg.script.is_valid()) { + n->set_base_script(tg.script->get_path()); + new_connect_node_select->select_property_from_script(tg.script); + } else { + new_connect_node_select->select_property_from_base_type(n->get_base_type()); + } + + + } else { + n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); + n->set_basic_type(tg.type); + new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type); + } + } break; + case CREATE_GET: { + + Ref<VisualScriptPropertyGet> n; + n.instance(); + vnode=n; + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); + + if (tg.type==Variant::OBJECT) { + n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + + if (tg.obj_type!=StringName()) { + n->set_base_type(tg.obj_type); + } else { + n->set_base_type("Object"); + } + + if (tg.script.is_valid()) { + n->set_base_script(tg.script->get_path()); + new_connect_node_select->select_property_from_script(tg.script); + } else { + new_connect_node_select->select_property_from_base_type(n->get_base_type()); + } + + + } else { + n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); + n->set_basic_type(tg.type); + new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type); + } + + } break; + case CREATE_COND: { + + Ref<VisualScriptCondition> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + case CREATE_SEQUENCE: { + + Ref<VisualScriptSequence> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + case CREATE_SWITCH: { + + Ref<VisualScriptSwitch> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + case CREATE_ITERATOR: { + + Ref<VisualScriptIterator> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + case CREATE_WHILE: { + + Ref<VisualScriptWhile> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + case CREATE_RETURN: { + + Ref<VisualScriptReturn> n; + n.instance(); + vnode=n; + seq_connect=true; + + } break; + + } + + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs); + if (seq_connect) { + undo_redo->add_do_method(script.ptr(),"sequence_connect",edited_func,port_action_node,port_action_output,new_id); + } + undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id); + undo_redo->add_do_method(this,"_update_graph",new_id); + undo_redo->add_undo_method(this,"_update_graph",new_id); + undo_redo->commit_action(); + + port_action_new_node=new_id; } +void VisualScriptEditor::_selected_connect_node_method_or_setget(const String& p_text) { + + Ref<VisualScriptNode> vsn = script->get_node(edited_func,port_action_new_node); + + if (vsn->cast_to<VisualScriptFunctionCall>()) { + + Ref<VisualScriptFunctionCall> vsfc = vsn; + vsfc->set_function(p_text); + script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); + } + + if (vsn->cast_to<VisualScriptPropertySet>()) { + + Ref<VisualScriptPropertySet> vsp = vsn; + vsp->set_property(p_text); + script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); + } + + if (vsn->cast_to<VisualScriptPropertyGet>()) { + + Ref<VisualScriptPropertyGet> vsp = vsn; + vsp->set_property(p_text); + script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); + } + + _update_graph(port_action_new_node); + _update_graph_connections(); + +} + + void VisualScriptEditor::_default_value_changed() { @@ -2889,6 +3255,9 @@ void VisualScriptEditor::_bind_methods() { ObjectTypeDB::bind_method("_center_on_node",&VisualScriptEditor::_center_on_node); ObjectTypeDB::bind_method("_comment_node_resized",&VisualScriptEditor::_comment_node_resized); ObjectTypeDB::bind_method("_button_resource_previewed",&VisualScriptEditor::_button_resource_previewed); + ObjectTypeDB::bind_method("_port_action_menu",&VisualScriptEditor::_port_action_menu); + ObjectTypeDB::bind_method("_selected_connect_node_method_or_setget",&VisualScriptEditor::_selected_connect_node_method_or_setget); + ObjectTypeDB::bind_method("_expression_text_changed",&VisualScriptEditor::_expression_text_changed); @@ -2949,7 +3318,7 @@ VisualScriptEditor::VisualScriptEditor() { VBoxContainer *left_vb = memnew( VBoxContainer ); left_vsplit->add_child(left_vb); left_vb->set_v_size_flags(SIZE_EXPAND_FILL); - left_vb->set_custom_minimum_size(Size2(180,1)*EDSCALE); + left_vb->set_custom_minimum_size(Size2(230,1)*EDSCALE); base_type_select = memnew( Button ); left_vb->add_margin_child(TTR("Base Type:"),base_type_select); @@ -3093,6 +3462,15 @@ VisualScriptEditor::VisualScriptEditor() { method_select->connect("selected",this,"_selected_method"); error_line=-1; + new_connect_node_select = memnew( PropertySelector ); + add_child(new_connect_node_select); + new_connect_node_select->connect("selected",this,"_selected_connect_node_method_or_setget"); + + port_action_popup = memnew( PopupMenu ); + add_child(port_action_popup); + port_action_popup->connect("item_pressed",this,"_port_action_menu"); + + } VisualScriptEditor::~VisualScriptEditor() { diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 5a40822bfe..483ae1644c 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -33,6 +33,19 @@ class VisualScriptEditor : public ScriptEditorBase { EDIT_PASTE_NODES, }; + enum PortAction { + + CREATE_CALL, + CREATE_SET, + CREATE_GET, + CREATE_COND, + CREATE_SEQUENCE, + CREATE_SWITCH, + CREATE_ITERATOR, + CREATE_WHILE, + CREATE_RETURN, + }; + MenuButton *edit_menu; Ref<VisualScript> script; @@ -53,6 +66,7 @@ class VisualScriptEditor : public ScriptEditorBase { PropertyEditor *edit_signal_edit; PropertySelector *method_select; + PropertySelector *new_connect_node_select; VisualScriptEditorVariableEdit *variable_editor; @@ -114,6 +128,16 @@ class VisualScriptEditor : public ScriptEditorBase { static Clipboard *clipboard; + PopupMenu *port_action_popup; + + PortAction port_action; + int port_action_node; + int port_action_output; + Vector2 port_action_pos; + int port_action_new_node; + void _port_action_menu(int p_option); + void _selected_connect_node_method_or_setget(const String& p_text); + int error_line; @@ -144,6 +168,8 @@ class VisualScriptEditor : public ScriptEditorBase { void _member_button(Object *p_item, int p_column, int p_button); + void _expression_text_changed(const String& p_text,int p_id); + String revert_on_drag; @@ -173,6 +199,8 @@ class VisualScriptEditor : public ScriptEditorBase { void _draw_color_over_button(Object* obj,Color p_color); void _button_resource_previewed(const String& p_path,const Ref<Texture>& p_preview,Variant p_ud); + VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node,int p_port_action_output,Set<int> &visited_nodes); + protected: diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp new file mode 100644 index 0000000000..cc3b5f2174 --- /dev/null +++ b/modules/visual_script/visual_script_expression.cpp @@ -0,0 +1,1523 @@ +#include "visual_script_expression.h" + + +bool VisualScriptExpression::_set(const StringName& p_name, const Variant& p_value) { + + if (String(p_name)=="expression") { + expression=p_value; + expression_dirty=true; + ports_changed_notify(); + return true; + } + + if (String(p_name)=="out_type") { + output_type=Variant::Type(int(p_value)); + expression_dirty=true; + ports_changed_notify(); + return true; + } + if (String(p_name)=="sequenced") { + sequenced=p_value; + ports_changed_notify(); + return true; + } + + if (String(p_name)=="input_count") { + + int from=inputs.size(); + inputs.resize(int(p_value)); + for(int i=from;i<inputs.size();i++) { + inputs[i].name=String::chr('a'+i); + if (from==0) { + inputs[i].type=output_type; + } else { + inputs[i].type=inputs[from-1].type; + } + } + expression_dirty=true; + ports_changed_notify(); + _change_notify(); + return true; + } + + if (String(p_name).begins_with("input/")) { + + int idx=String(p_name).get_slice("/",1).to_int(); + ERR_FAIL_INDEX_V(idx,inputs.size(),false); + + String what=String(p_name).get_slice("/",2); + + if (what=="type") { + + inputs[idx].type=Variant::Type(int(p_value)); + } else if (what=="name") { + + inputs[idx].name=p_value; + } else { + return false; + } + + expression_dirty=true; + ports_changed_notify(); + return true; + } + + + return false; + +} + +bool VisualScriptExpression::_get(const StringName& p_name,Variant &r_ret) const { + + if (String(p_name)=="expression") { + r_ret=expression; + return true; + } + + if (String(p_name)=="out_type") { + r_ret=output_type; + return true; + } + + if (String(p_name)=="sequenced") { + r_ret=sequenced; + return true; + } + + if (String(p_name)=="input_count") { + r_ret=inputs.size(); + return true; + } + + if (String(p_name).begins_with("input/")) { + + int idx=String(p_name).get_slice("/",1).to_int(); + ERR_FAIL_INDEX_V(idx,inputs.size(),false); + + String what=String(p_name).get_slice("/",2); + + if (what=="type") { + + r_ret=inputs[idx].type; + } else if (what=="name") { + + r_ret=inputs[idx].name; + } else { + return false; + } + + return true; + } + + + return false; +} +void VisualScriptExpression::_get_property_list( List<PropertyInfo> *p_list) const { + + + String argt="Any"; + for(int i=1;i<Variant::VARIANT_MAX;i++) { + argt+=","+Variant::get_type_name(Variant::Type(i)); + } + + p_list->push_back(PropertyInfo(Variant::STRING,"expression",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT,"out_type",PROPERTY_HINT_ENUM,argt)); + p_list->push_back(PropertyInfo(Variant::INT,"input_count",PROPERTY_HINT_RANGE,"0,64,1")); + p_list->push_back(PropertyInfo(Variant::BOOL,"sequenced")); + + for(int i=0;i<inputs.size();i++) { + + p_list->push_back(PropertyInfo(Variant::INT,"input/"+itos(i)+"/type",PROPERTY_HINT_ENUM,argt)); + p_list->push_back(PropertyInfo(Variant::STRING,"input/"+itos(i)+"/name")); + } +} + +int VisualScriptExpression::get_output_sequence_port_count() const { + + return sequenced?1:0; +} +bool VisualScriptExpression::has_input_sequence_port() const{ + + return sequenced; +} + + +String VisualScriptExpression::get_output_sequence_port_text(int p_port) const{ + + return String(); +} + + +int VisualScriptExpression::get_input_value_port_count() const{ + + return inputs.size(); + +} +int VisualScriptExpression::get_output_value_port_count() const{ + + return 1; +} + + +PropertyInfo VisualScriptExpression::get_input_value_port_info(int p_idx) const{ + + return PropertyInfo(inputs[p_idx].type,inputs[p_idx].name); +} +PropertyInfo VisualScriptExpression::get_output_value_port_info(int p_idx) const{ + + return PropertyInfo(output_type,"result"); +} + +String VisualScriptExpression::get_caption() const{ + + return "Expression"; +} +String VisualScriptExpression::get_text() const{ + + return expression; +} + + +Error VisualScriptExpression::_get_token(Token& r_token) { + + while (true) { +#define GET_CHAR() (str_ofs>=expression.length()?0:expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar==0) { + r_token.type=TK_EOF; + return OK; + } + + + switch(cchar) { + + case 0: { + r_token.type=TK_EOF; + return OK; + } break; + case '{': { + + r_token.type=TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type=TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type=TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type=TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type=TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type=TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type=TK_COMMA; + return OK; + }; + case ':': { + + r_token.type=TK_COLON; + return OK; + }; + case '.': { + + r_token.type=TK_PERIOD; + return OK; + }; + case '=': { + + cchar=GET_CHAR(); + if (cchar=='=') { + r_token.type=TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs]=='=') { + r_token.type=TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type=TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs]=='=') { + r_token.type=TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs]=='>') { + r_token.type=TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type=TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs]=='=') { + r_token.type=TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs]=='<') { + r_token.type=TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type=TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type=TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type=TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type=TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type=TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type=TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs]=='&') { + r_token.type=TK_OP_AND; + str_ofs++; + } else { + r_token.type=TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs]=='|') { + r_token.type=TK_OP_OR; + str_ofs++; + } else { + r_token.type=TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type=TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type=TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + + String str; + while(true) { + + CharType ch=GET_CHAR(); + + if (ch==0) { + _set_error("Unterminated String"); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch=='"') { + break; + } else if (ch=='\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next==0) { + _set_error("Unterminated String"); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res=0; + + switch(next) { + + case 'b': res=8; break; + case 't': res=9; break; + case 'n': res=10; break; + case 'f': res=12; break; + case 'r': res=13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + + for(int j=0;j<4;j++) { + CharType c = GET_CHAR(); + + if (c==0) { + _set_error("Unterminated String"); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) { + + _set_error("Malformed hex constant in string"); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType v; + if (c>='0' && c<='9') { + v=c-'0'; + } else if (c>='a' && c<='f') { + v=c-'a'; + v+=10; + } else if (c>='A' && c<='F') { + v=c-'A'; + v+=10; + } else { + ERR_PRINT("BUG"); + v=0; + } + + res<<=4; + res|=v; + + + } + + + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str+=res; + + } else { + str+=ch; + } + } + + r_token.type=TK_CONSTANT; + r_token.value=str; + return OK; + + } break; + default: { + + if (cchar<=32) { + break; + } + + if (cchar=='-' || (cchar>='0' && cchar<='9')) { + //a number + + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading=READING_INT; + + if (cchar=='-') { + num+='-'; + cchar=GET_CHAR(); + + } + + + + CharType c = cchar; + bool exp_sign=false; + bool exp_beg=false; + bool is_float=false; + + while(true) { + + switch(reading) { + case READING_INT: { + + if (c>='0' && c<='9') { + //pass + } else if (c=='.') { + reading=READING_DEC; + is_float=true; + } else if (c=='e') { + reading=READING_EXP; + } else { + reading=READING_DONE; + } + + } break; + case READING_DEC: { + + if (c>='0' && c<='9') { + + } else if (c=='e') { + reading=READING_EXP; + + } else { + reading=READING_DONE; + } + + } break; + case READING_EXP: { + + if (c>='0' && c<='9') { + exp_beg=true; + + } else if ((c=='-' || c=='+') && !exp_sign && !exp_beg) { + if (c=='-') + is_float=true; + exp_sign=true; + + } else { + reading=READING_DONE; + } + } break; + } + + if (reading==READING_DONE) + break; + num+=String::chr(c); + c = GET_CHAR(); + + + } + + str_ofs--; + + r_token.type=TK_CONSTANT; + + if (is_float) + r_token.value=num.to_double(); + else + r_token.value=num.to_int(); + return OK; + + } else if ((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_') { + + String id; + bool first=true; + + while((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_' || (!first && cchar>='0' && cchar<='9')) { + + id+=String::chr(cchar); + cchar=GET_CHAR(); + first=false; + } + + str_ofs--; //go back one + + if (id=="in") { + r_token.type=TK_OP_IN; + } else if (id=="null") { + r_token.type=TK_CONSTANT; + r_token.value=Variant(); + } else if (id=="true") { + r_token.type=TK_CONSTANT; + r_token.value=true; + } else if (id=="false") { + r_token.type=TK_CONSTANT; + r_token.value=false; + } else if (id=="PI") { + r_token.type=TK_CONSTANT; + r_token.value=Math_PI; + } else if (id=="not") { + r_token.type=TK_OP_NOT; + } else if (id=="or") { + r_token.type=TK_OP_OR; + } else if (id=="and") { + r_token.type=TK_OP_AND; + } else if (id=="self") { + r_token.type=TK_SELF; + } else { + + for(int i=0;i<Variant::VARIANT_MAX;i++) { + if (id==Variant::get_type_name(Variant::Type(i))) { + r_token.type=TK_BASIC_TYPE; + r_token.value=i; + return OK; + break; + } + } + + VisualScriptBuiltinFunc::BuiltinFunc bifunc = VisualScriptBuiltinFunc::find_function(id); + if (bifunc!=VisualScriptBuiltinFunc::FUNC_MAX) { + r_token.type=TK_BUILTIN_FUNC; + r_token.value=bifunc; + return OK; + } + + r_token.type=TK_IDENTIFIER; + r_token.value=id; + } + + return OK; + } else { + _set_error("Unexpected character."); + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; + } + } + } + } + + r_token.type=TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char* VisualScriptExpression::token_name[TK_MAX]={ +"CURLY BRACKET OPEN", +"CURLY BRACKET CLOSE", +"BRACKET OPEN", +"BRACKET CLOSE", +"PARENTHESIS OPEN", +"PARENTHESIS CLOSE", +"IDENTIFIER", +"BUILTIN FUNC", +"SELF", +"CONSTANT", +"BASIC TYPE", +"COLON", +"COMMA", +"PERIOD", +"OP IN", +"OP EQUAL", +"OP NOT EQUAL", +"OP LESS", +"OP LESS EQUAL", +"OP GREATER", +"OP GREATER EQUAL", +"OP AND", +"OP OR", +"OP NOT", +"OP ADD", +"OP SUB", +"OP MUL", +"OP DIV", +"OP MOD", +"OP SHIFT LEFT", +"OP SHIFT RIGHT", +"OP BIT AND", +"OP BIT OR", +"OP BIT XOR", +"OP BIT INVERT", +"EOF", +"ERROR" +}; + +VisualScriptExpression::ENode* VisualScriptExpression::_parse_expression() { + + + Vector<Expression> expression; + + while(true) { + //keep appending stuff to expression + ENode*expr=NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + + + switch(tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node<DictionaryNode>(); + + + while(true) { + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs=cofs; //revert + //parse an expression + ENode* expr=_parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type!=TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr=_parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_COMMA) { + //all good + } else if (tk.type==TK_CURLY_BRACKET_CLOSE) { + str_ofs=cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr=dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node<ArrayNode>(); + + + while(true) { + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_BRACKET_CLOSE) { + break; + } + str_ofs=cofs; //revert + //parse an expression + ENode* expr=_parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_COMMA) { + //all good + } else if (tk.type==TK_BRACKET_CLOSE) { + str_ofs=cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr=an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode* e=_parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type!=TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr=e; + + } break; + case TK_IDENTIFIER: { + + String what = tk.value; + int index=-1; + for(int i=0;i<inputs.size();i++) { + if (what==inputs[i].name) { + index=i; + break; + } + } + + if (index!=-1) { + InputNode *input = alloc_node<InputNode>(); + input->index=index; + expr=input; + } else { + _set_error("Invalid input identifier '"+what+"'. For script variables, use self (locals are for inputs)."+what); + return NULL; + } + } break; + case TK_SELF: { + + SelfNode *self = alloc_node<SelfNode>(); + expr=self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value=tk.value; + expr=constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type!=TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node<ConstructorNode>(); + constructor->data_type=bt; + + while(true) { + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs=cofs; //revert + //parse an expression + ENode* expr=_parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_COMMA) { + //all good + } else if (tk.type==TK_PARENTHESIS_CLOSE) { + str_ofs=cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr=constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type!=TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); + bifunc->func=VisualScriptBuiltinFunc::BuiltinFunc(int(tk.value)); + + while(true) { + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs=cofs; //revert + //parse an expression + ENode* expr=_parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_COMMA) { + //all good + } else if (tk.type==TK_PARENTHESIS_CLOSE) { + str_ofs=cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = VisualScriptBuiltinFunc::get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '"+VisualScriptBuiltinFunc::get_func_name(bifunc->func)+"' expects "+itos(expected_args)+" arguments."); + } + + expr=bifunc; + + } break; + case TK_OP_SUB: { + + Expression e; + e.is_op=true; + e.op=Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + Expression e; + e.is_op=true; + e.op=Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + + } + + //before going to operators, must check indexing! + + while(true) { + int cofs2=str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done=false; + + switch(tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node<IndexNode>(); + index->base=expr; + + ENode* what=_parse_expression(); + if (!what) + return NULL; + + index->index=what; + + _get_token(tk); + if (tk.type!=TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr=index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type!=TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier=tk.value; + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method=identifier; + func_call->base=expr; + + while(true) { + + int cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs=cofs; //revert + //parse an expression + ENode* expr=_parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs=str_ofs; + _get_token(tk); + if (tk.type==TK_COMMA) { + //all good + } else if (tk.type==TK_PARENTHESIS_CLOSE) { + str_ofs=cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr=func_call; + } else { + //named indexing + str_ofs=cofs; + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + index->base=expr; + index->name=identifier; + expr=index; + + } + + } break; + default: { + str_ofs=cofs2; + done=true; + } break; + } + + if (done) + break; + } + + //push expression + { + Expression e; + e.is_op=false; + e.node=expr; + expression.push_back(e); + } + + //ok finally look for an operator + + + int cofs=str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + + Variant::Operator op = Variant::OP_MAX; + + switch(tk.type) { + case TK_OP_IN: op=Variant::OP_IN; break; + case TK_OP_EQUAL: op=Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op=Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op=Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op=Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op=Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op=Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op=Variant::OP_AND; break; + case TK_OP_OR: op=Variant::OP_OR; break; + case TK_OP_NOT: op=Variant::OP_NOT; break; + case TK_OP_ADD: op=Variant::OP_ADD; break; + case TK_OP_SUB: op=Variant::OP_SUBSTRACT; break; + case TK_OP_MUL: op=Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op=Variant::OP_DIVIDE; break; + case TK_OP_MOD: op=Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op=Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op=Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op=Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op=Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op=Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op=Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op==Variant::OP_MAX) { //stop appending stuff + str_ofs=cofs; + break; + } + + //push operator and go on + { + Expression e; + e.is_op=true; + e.op=op; + expression.push_back(e); + } + } + + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + + while(expression.size()>1) { + + int next_op=-1; + int min_priority=0xFFFFF; + bool is_unary=false; + + for(int i=0;i<expression.size();i++) { + + + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary=false; + + switch(expression[i].op) { + + + case Variant::OP_BIT_NEGATE: priority=0; unary=true; break; + case Variant::OP_NEGATE: priority=1; unary=true; break; + + case Variant::OP_MULTIPLY: priority=2; break; + case Variant::OP_DIVIDE: priority=2; break; + case Variant::OP_MODULE: priority=2; break; + + case Variant::OP_ADD: priority=3; break; + case Variant::OP_SUBSTRACT: priority=3; break; + + case Variant::OP_SHIFT_LEFT: priority=4; break; + case Variant::OP_SHIFT_RIGHT: priority=4; break; + + case Variant::OP_BIT_AND: priority=5; break; + case Variant::OP_BIT_XOR: priority=6; break; + case Variant::OP_BIT_OR: priority=7; break; + + case Variant::OP_LESS: priority=8; break; + case Variant::OP_LESS_EQUAL: priority=8; break; + case Variant::OP_GREATER: priority=8; break; + case Variant::OP_GREATER_EQUAL: priority=8; break; + + case Variant::OP_EQUAL: priority=8; break; + case Variant::OP_NOT_EQUAL: priority=8; break; + + case Variant::OP_IN: priority=10; break; + + case Variant::OP_NOT: priority=11; unary=true; break; + case Variant::OP_AND: priority=12; break; + case Variant::OP_OR: priority=13; break; + + + default: { + _set_error("Parser bug, invalid operator in expression: "+itos(expression[i].op)); + return NULL; + } + + } + + if (priority<min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op=i; + min_priority=priority; + is_unary=unary; + } + + } + + if (next_op==-1) { + + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op==-1,NULL); + } + + + // OK! create operator.. + if (is_unary) { + + int expr_pos=next_op; + while(expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos==expression.size()) { + //can happen.. + _set_error("Unexpected end of expression.."); + return NULL; + } + } + + //consecutively do unary opeators + for(int i=expr_pos-1;i>=next_op;i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op=expression[i].op; + op->nodes[0]=expression[i+1].node; + op->nodes[1]=NULL; + expression[i].is_op=false; + expression[i].node=op; + expression.remove(i+1); + } + + + } else { + + if (next_op <1 || next_op>=(expression.size()-1)) { + _set_error("Parser bug.."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op=expression[next_op].op; + + if (expression[next_op-1].is_op) { + + _set_error("Parser bug.."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op+1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by an unary op in a valid combination, + // due to how precedence works, unaries will always dissapear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + + op->nodes[0]=expression[next_op-1].node; //expression goes as left + op->nodes[1]=expression[next_op+1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression[next_op-1].node=op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool VisualScriptExpression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes=NULL; + root=NULL; + + } + + error_str=String(); + error_set=false; + str_ofs=0; + + root=_parse_expression(); + + if (error_set) { + root=NULL; + if (nodes) { + memdelete(nodes); + } + nodes=NULL; + return true; + } + + expression_dirty=false; + return false; +} + + +class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance { +public: + + VisualScriptInstance* instance; + VisualScriptExpression *expression; + + //virtual int get_working_memory_size() const { return 0; } + //execute by parsing the tree directly + virtual bool _execute(const Variant** p_inputs,VisualScriptExpression::ENode *p_node,Variant& r_ret,String& r_error_str,Variant::CallError &ce) { + + switch(p_node->type) { + case VisualScriptExpression::ENode::TYPE_INPUT: { + + const VisualScriptExpression::InputNode *in = static_cast<const VisualScriptExpression::InputNode*>(p_node); + r_ret=*p_inputs[in->index]; + } break; + case VisualScriptExpression::ENode::TYPE_CONSTANT: { + + const VisualScriptExpression::ConstantNode *c = static_cast<const VisualScriptExpression::ConstantNode*>(p_node); + r_ret=c->value; + + } break; + case VisualScriptExpression::ENode::TYPE_SELF: { + + r_ret=instance->get_owner_ptr(); + } break; + case VisualScriptExpression::ENode::TYPE_OPERATOR: { + + + const VisualScriptExpression::OperatorNode *op = static_cast<const VisualScriptExpression::OperatorNode*>(p_node); + + Variant a; + bool ret = _execute(p_inputs,op->nodes[0],a,r_error_str,ce); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + ret = _execute(p_inputs,op->nodes[1],b,r_error_str,ce); + if (ret) + return true; + } + + bool valid=true; + Variant::evaluate(op->op,a,b,r_ret,valid); + if (!valid) { + r_error_str="Invalid operands to operator "+Variant::get_operator_name(op->op)+": "+Variant::get_type_name(a.get_type())+" and "+Variant::get_type_name(b.get_type())+"."; + return true; + } + + } break; + case VisualScriptExpression::ENode::TYPE_INDEX: { + + const VisualScriptExpression::IndexNode *index = static_cast<const VisualScriptExpression::IndexNode*>(p_node); + + Variant base; + bool ret = _execute(p_inputs,index->base,base,r_error_str,ce); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs,index->index,idx,r_error_str,ce); + if (ret) + return true; + + bool valid; + r_ret=base.get(idx,&valid); + if (!valid) { + r_error_str="Invalid index of type "+Variant::get_type_name(idx.get_type())+" for base of type "+Variant::get_type_name(base.get_type())+"."; + return true; + } + + + + } break; + case VisualScriptExpression::ENode::TYPE_NAMED_INDEX: { + + const VisualScriptExpression::NamedIndexNode *index = static_cast<const VisualScriptExpression::NamedIndexNode*>(p_node); + + Variant base; + bool ret = _execute(p_inputs,index->base,base,r_error_str,ce); + if (ret) + return true; + + bool valid; + r_ret=base.get_named(index->name,&valid); + if (!valid) { + r_error_str="Invalid index '"+String(index->name)+"' for base of type "+Variant::get_type_name(base.get_type())+"."; + return true; + } + + } break; + case VisualScriptExpression::ENode::TYPE_ARRAY: { + const VisualScriptExpression::ArrayNode *array = static_cast<const VisualScriptExpression::ArrayNode*>(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i=0;i<array->array.size();i++) { + + Variant value; + bool ret = _execute(p_inputs,array->array[i],value,r_error_str,ce); + if (ret) + return true; + arr[i]=value; + } + + r_ret=arr; + + } break; + case VisualScriptExpression::ENode::TYPE_DICTIONARY: { + const VisualScriptExpression::DictionaryNode *dictionary = static_cast<const VisualScriptExpression::DictionaryNode*>(p_node); + + Dictionary d; + for (int i=0;i<dictionary->dict.size();i+=2) { + + Variant key; + bool ret = _execute(p_inputs,dictionary->dict[i+0],key,r_error_str,ce); + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs,dictionary->dict[i+1],value,r_error_str,ce); + if (ret) + return true; + + d[key]=value; + } + + r_ret=d; + } break; + case VisualScriptExpression::ENode::TYPE_CONSTRUCTOR: { + + const VisualScriptExpression::ConstructorNode *constructor = static_cast<const VisualScriptExpression::ConstructorNode*>(p_node); + + Vector<Variant> arr; + Vector<const Variant*> argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i=0;i<constructor->arguments.size();i++) { + + Variant value; + bool ret = _execute(p_inputs,constructor->arguments[i],value,r_error_str,ce); + if (ret) + return true; + arr[i]=value; + argp[i]=&arr[i]; + } + + + r_ret=Variant::construct(constructor->data_type,argp.ptr(),argp.size(),ce); + + if (ce.error!=Variant::CallError::CALL_OK) { + r_error_str="Invalid arguments to construct '"+Variant::get_type_name(constructor->data_type)+"'."; + return true; + } + + + } break; + case VisualScriptExpression::ENode::TYPE_BUILTIN_FUNC: { + + const VisualScriptExpression::BuiltinFuncNode *bifunc = static_cast<const VisualScriptExpression::BuiltinFuncNode*>(p_node); + + Vector<Variant> arr; + Vector<const Variant*> argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i=0;i<bifunc->arguments.size();i++) { + + Variant value; + bool ret = _execute(p_inputs,bifunc->arguments[i],value,r_error_str,ce); + if (ret) + return true; + arr[i]=value; + argp[i]=&arr[i]; + } + + + VisualScriptBuiltinFunc::exec_func(bifunc->func,argp.ptr(),&r_ret,ce,r_error_str); + + if (ce.error!=Variant::CallError::CALL_OK) { + r_error_str="Builtin Call Failed. "+r_error_str; + return true; + } + + } break; + case VisualScriptExpression::ENode::TYPE_CALL: { + + const VisualScriptExpression::CallNode *call = static_cast<const VisualScriptExpression::CallNode*>(p_node); + + + Variant base; + bool ret = _execute(p_inputs,call->base,base,r_error_str,ce); + if (ret) + return true; + + Vector<Variant> arr; + Vector<const Variant*> argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i=0;i<call->arguments.size();i++) { + + Variant value; + bool ret = _execute(p_inputs,call->arguments[i],value,r_error_str,ce); + if (ret) + return true; + arr[i]=value; + argp[i]=&arr[i]; + } + + + r_ret=base.call(call->method,argp.ptr(),argp.size(),ce); + + if (ce.error!=Variant::CallError::CALL_OK) { + r_error_str="On call to '"+String(call->method)+"':"; + return true; + } + + } break; + } + return false; + } + + virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { + + if (!expression->root || expression->error_set) { + r_error_str=expression->error_str; + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + return 0; + } + + + bool error = _execute(p_inputs,expression->root,*p_outputs[0],r_error_str,r_error); + if (error && r_error.error==Variant::CallError::CALL_OK) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + } + +#ifdef DEBUG_ENABLED + if (!error && expression->output_type!=Variant::NIL && !Variant::can_convert_strict(p_outputs[0]->get_type(),expression->output_type)) { + + r_error_str+="Can't convert expression result from "+Variant::get_type_name(p_outputs[0]->get_type())+" to "+Variant::get_type_name(expression->output_type)+"."; + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + + } +#endif + + return 0; + } + + +}; + +VisualScriptNodeInstance* VisualScriptExpression::instance(VisualScriptInstance* p_instance){ + + _compile_expression(); + VisualScriptNodeInstanceExpression *instance = memnew( VisualScriptNodeInstanceExpression ); + instance->instance=p_instance; + instance->expression=this; + return instance; +} + + +VisualScriptExpression::VisualScriptExpression() +{ + output_type=Variant::NIL; + expression_dirty=true; + error_set=true; + root=NULL; + nodes=NULL; + sequenced=false; +} + +VisualScriptExpression::~VisualScriptExpression() { + + if (nodes) { + memdelete(nodes); + } +} + + +void register_visual_script_expression_node() { + + VisualScriptLanguage::singleton->add_register_func("operators/expression",create_node_generic<VisualScriptExpression>); + +} diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h new file mode 100644 index 0000000000..4edae133c7 --- /dev/null +++ b/modules/visual_script/visual_script_expression.h @@ -0,0 +1,280 @@ +#ifndef VISUALSCRIPTEXPRESSION_H +#define VISUALSCRIPTEXPRESSION_H + +#include "visual_script.h" +#include "visual_script_builtin_funcs.h" + +class VisualScriptExpression : public VisualScriptNode { + + OBJ_TYPE(VisualScriptExpression,VisualScriptNode) +friend class VisualScriptNodeInstanceExpression; + + struct Input { + + Variant::Type type; + String name; + + Input() { type=Variant::NIL; } + }; + + Vector<Input> inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char* token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + + void _set_error(const String& p_err) { + if (error_set) + return; + error_str=p_err; + error_set=true; + } + + Error _get_token(Token& r_token); + + String error_str; + bool error_set; + + + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next=NULL; } + virtual ~ENode() { if (next) { memdelete(next); } } + }; + + struct Expression { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode* _parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type=TYPE_INPUT; + } + }; + + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type=TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode* nodes[2]; + + OperatorNode() { + type=TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + + SelfNode() { + type=TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode*base; + ENode*index; + + IndexNode() { + type=TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode*base; + StringName name; + + NamedIndexNode() { + type=TYPE_NAMED_INDEX; + } + + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector<ENode*> arguments; + + ConstructorNode() { + type=TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode*base; + StringName method; + Vector<ENode*> arguments; + + CallNode() { + type=TYPE_CALL; + } + + }; + + struct ArrayNode : public ENode { + Vector<ENode*> array; + ArrayNode() { + type=TYPE_ARRAY; + } + + }; + + struct DictionaryNode : public ENode { + Vector<ENode*> dict; + DictionaryNode() { + type=TYPE_DICTIONARY; + } + + }; + + struct BuiltinFuncNode : public ENode { + VisualScriptBuiltinFunc::BuiltinFunc func; + Vector<ENode*> arguments; + BuiltinFuncNode() { + type=TYPE_BUILTIN_FUNC; + } + }; + + template<class T> + T* alloc_node() { + T* node = memnew(T); + node->next=nodes; + nodes=node; + return node; + } + + ENode *root; + ENode *nodes; + + + + + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + + virtual int get_output_sequence_port_count() const; + virtual bool has_input_sequence_port() const; + + + virtual String get_output_sequence_port_text(int p_port) const; + + + virtual int get_input_value_port_count() const; + virtual int get_output_value_port_count() const; + + + virtual PropertyInfo get_input_value_port_info(int p_idx) const; + virtual PropertyInfo get_output_value_port_info(int p_idx) const; + + virtual String get_caption() const; + virtual String get_text() const; + virtual String get_category() const { return "operators"; } + + virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + + VisualScriptExpression(); + ~VisualScriptExpression(); +}; + + +void register_visual_script_expression_node(); + + +#endif // VISUALSCRIPTEXPRESSION_H diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index 5066a4214d..97338da187 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -616,7 +616,7 @@ bool VisualScriptSwitch::has_input_sequence_port() const{ int VisualScriptSwitch::get_input_value_port_count() const{ - return 1; + return case_values.size()+1; } int VisualScriptSwitch::get_output_value_port_count() const{ @@ -628,14 +628,15 @@ String VisualScriptSwitch::get_output_sequence_port_text(int p_port) const { if (p_port==case_values.size()) return "done"; - if (case_values[p_port].value.get_type()==Variant::NIL) - return "null"; - return case_values[p_port].value; + return String(); } PropertyInfo VisualScriptSwitch::get_input_value_port_info(int p_idx) const{ - return PropertyInfo(Variant::NIL,"input"); + if (p_idx<case_values.size()) { + return PropertyInfo(case_values[p_idx].type," ="); + } else + return PropertyInfo(Variant::NIL,"input"); } PropertyInfo VisualScriptSwitch::get_output_value_port_info(int p_idx) const{ @@ -659,7 +660,7 @@ class VisualScriptNodeInstanceSwitch : public VisualScriptNodeInstance { public: VisualScriptInstance* instance; - Vector<Variant> case_values; + int case_count; //virtual int get_working_memory_size() const { return 0; } //virtual bool is_output_port_unsequenced(int p_idx) const { return false; } @@ -668,17 +669,17 @@ public: virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { if (p_start_mode==START_MODE_CONTINUE_SEQUENCE) { - return case_values.size(); //exit + return case_count; //exit } - for(int i=0;i<case_values.size();i++) { + for(int i=0;i<case_count;i++) { - if (*p_inputs[0]==case_values[i]) { + if (*p_inputs[i]==*p_inputs[case_count]) { return i|STEP_FLAG_PUSH_STACK_BIT; } } - return case_values.size(); + return case_count; } @@ -688,10 +689,7 @@ VisualScriptNodeInstance* VisualScriptSwitch::instance(VisualScriptInstance* p_i VisualScriptNodeInstanceSwitch * instance = memnew(VisualScriptNodeInstanceSwitch ); instance->instance=p_instance; - instance->case_values.resize(case_values.size()); - for(int i=0;i<case_values.size();i++) { - instance->case_values[i]=case_values[i].value; - } + instance->case_count=case_values.size(); return instance; } @@ -708,23 +706,12 @@ bool VisualScriptSwitch::_set(const StringName& p_name, const Variant& p_value) int idx = String(p_name).get_slice("/",1).to_int(); ERR_FAIL_INDEX_V(idx,case_values.size(),false); - String what = String(p_name).get_slice("/",2); - - if (what=="type") { - case_values[idx].type=Variant::Type(int(p_value)); - Variant::CallError ce; - case_values[idx].value=Variant::construct(case_values[idx].type,NULL,0,ce); - _change_notify(); - ports_changed_notify(); - return true; - } + case_values[idx].type=Variant::Type(int(p_value)); + _change_notify(); + ports_changed_notify(); - if (what=="value") { - case_values[idx].value=p_value; - ports_changed_notify(); - return true; - } + return true; } return false; @@ -741,17 +728,9 @@ bool VisualScriptSwitch::_get(const StringName& p_name,Variant &r_ret) const { int idx = String(p_name).get_slice("/",1).to_int(); ERR_FAIL_INDEX_V(idx,case_values.size(),false); - String what = String(p_name).get_slice("/",2); - - if (what=="type") { - r_ret=case_values[idx].type; - return true; - } - if (what=="value") { - r_ret=case_values[idx].value; - return true; - } + r_ret=case_values[idx].type; + return true; } return false; @@ -767,8 +746,7 @@ void VisualScriptSwitch::_get_property_list( List<PropertyInfo> *p_list) const { } for(int i=0;i<case_values.size();i++) { - p_list->push_back(PropertyInfo(Variant::INT,"case/"+itos(i)+"/type",PROPERTY_HINT_ENUM,argt)); - p_list->push_back(PropertyInfo(case_values[i].type,"case/"+itos(i)+"/value")); + p_list->push_back(PropertyInfo(Variant::INT,"case/"+itos(i),PROPERTY_HINT_ENUM,argt)); } } diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h index e1730a2264..e0da84a534 100644 --- a/modules/visual_script/visual_script_flow_control.h +++ b/modules/visual_script/visual_script_flow_control.h @@ -203,7 +203,6 @@ class VisualScriptSwitch : public VisualScriptNode { struct Case { Variant::Type type; - Variant value; Case() { type=Variant::NIL; } }; @@ -224,6 +223,7 @@ public: virtual String get_output_sequence_port_text(int p_port) const; + virtual bool has_mixed_input_and_sequence_ports() const { return true; } virtual int get_input_value_port_count() const; diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 79910aa2bd..5a21cb40e9 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -12,7 +12,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { - if (method_cache.flags&METHOD_FLAG_CONST) + if (method_cache.flags&METHOD_FLAG_CONST || call_mode==CALL_MODE_BASIC_TYPE) return 0; else return 1; @@ -20,7 +20,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const{ - if (method_cache.flags&METHOD_FLAG_CONST) + if (method_cache.flags&METHOD_FLAG_CONST || call_mode==CALL_MODE_BASIC_TYPE) return false; else return true; @@ -417,6 +417,14 @@ void VisualScriptFunctionCall::_update_method_cache() { method_cache.return_val = mb->get_argument_info(-1); #endif + + if (mb->is_vararg()) { + //for vararg just give it 10 arguments (should be enough for most use cases) + for(int i=0;i<10;i++) { + method_cache.arguments.push_back(PropertyInfo(Variant::NIL,"arg"+itos(i))); + use_default_args++; + } + } } else if (script.is_valid() && script->has_method(function)) { method_cache = script->get_method_info(function); @@ -920,6 +928,18 @@ VisualScriptNodeInstance* VisualScriptFunctionCall::instance(VisualScriptInstanc instance->validate=validate; return instance; } + + +VisualScriptFunctionCall::TypeGuess VisualScriptFunctionCall::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + if (p_output==0 && call_mode==CALL_MODE_INSTANCE) { + return p_inputs[0]; + } + + return VisualScriptNode::guess_output_type(p_inputs,p_output); + +} + VisualScriptFunctionCall::VisualScriptFunctionCall() { validate=true; @@ -1600,6 +1620,17 @@ VisualScriptNodeInstance* VisualScriptPropertySet::instance(VisualScriptInstance return instance; } + + +VisualScriptPropertySet::TypeGuess VisualScriptPropertySet::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + if (p_output==0 && call_mode==CALL_MODE_INSTANCE) { + return p_inputs[0]; + } + + return VisualScriptNode::guess_output_type(p_inputs,p_output); + +} VisualScriptPropertySet::VisualScriptPropertySet() { call_mode=CALL_MODE_SELF; diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h index 5367deed7f..43ef276cf4 100644 --- a/modules/visual_script/visual_script_func_nodes.h +++ b/modules/visual_script/visual_script_func_nodes.h @@ -105,6 +105,9 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; + + VisualScriptFunctionCall(); }; @@ -195,6 +198,7 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; VisualScriptPropertySet(); }; diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index f5c5159af7..7ada292b13 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -553,7 +553,7 @@ void VisualScriptOperator::_bind_methods() { argt+=","+Variant::get_type_name(Variant::Type(i)); } - ADD_PROPERTY(PropertyInfo(Variant::INT,"operator_value/type",PROPERTY_HINT_ENUM,types,PROPERTY_USAGE_NOEDITOR),_SCS("set_operator"),_SCS("get_operator")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"operator_value/type",PROPERTY_HINT_ENUM,types),_SCS("set_operator"),_SCS("get_operator")); ADD_PROPERTY(PropertyInfo(Variant::INT,"typed_value/typed",PROPERTY_HINT_ENUM,argt),_SCS("set_typed"),_SCS("get_typed")); } @@ -1071,7 +1071,13 @@ PropertyInfo VisualScriptPreload::get_input_value_port_info(int p_idx) const{ PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const{ - return PropertyInfo(Variant::OBJECT,"res"); + PropertyInfo pinfo=PropertyInfo(Variant::OBJECT,"res"); + if (preload.is_valid()) { + pinfo.hint=PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string=preload->get_type(); + } + + return pinfo; } @@ -1569,6 +1575,151 @@ VisualScriptClassConstant::VisualScriptClassConstant() { } +////////////////////////////////////////// +////////////////BASICTYPECONSTANT/////////// +////////////////////////////////////////// + +int VisualScriptBasicTypeConstant::get_output_sequence_port_count() const { + + return 0; +} + +bool VisualScriptBasicTypeConstant::has_input_sequence_port() const{ + + return false; +} + +int VisualScriptBasicTypeConstant::get_input_value_port_count() const{ + + return 0; +} +int VisualScriptBasicTypeConstant::get_output_value_port_count() const{ + + return 1; +} + +String VisualScriptBasicTypeConstant::get_output_sequence_port_text(int p_port) const { + + return String(); +} + +PropertyInfo VisualScriptBasicTypeConstant::get_input_value_port_info(int p_idx) const{ + + return PropertyInfo(); +} + +PropertyInfo VisualScriptBasicTypeConstant::get_output_value_port_info(int p_idx) const{ + + return PropertyInfo(Variant::INT,"value"); +} + + +String VisualScriptBasicTypeConstant::get_caption() const { + + return "BasicConst"; +} + +String VisualScriptBasicTypeConstant::get_text() const { + + return Variant::get_type_name(type)+"."+String(name); +} + +void VisualScriptBasicTypeConstant::set_basic_type_constant(const StringName& p_which) { + + name=p_which; + _change_notify(); + ports_changed_notify(); +} + +StringName VisualScriptBasicTypeConstant::get_basic_type_constant() const { + return name; +} + + +void VisualScriptBasicTypeConstant::set_basic_type(Variant::Type p_which) { + + type=p_which; + _change_notify(); + ports_changed_notify(); +} + +Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const { + return type; +} + +class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance { +public: + + int value; + bool valid; + //virtual int get_working_memory_size() const { return 0; } + + virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { + + if (!valid) { + r_error_str="Invalid constant name, pick a valid basic type constant."; + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + } + + *p_outputs[0] = value; + return 0; + } + + +}; + +VisualScriptNodeInstance* VisualScriptBasicTypeConstant::instance(VisualScriptInstance* p_instance) { + + VisualScriptNodeInstanceBasicTypeConstant * instance = memnew(VisualScriptNodeInstanceBasicTypeConstant ); + instance->value=Variant::get_numeric_constant_value(type,name,&instance->valid); + return instance; +} + +void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo& property) const { + + if (property.name=="constant") { + + List<StringName> constants; + Variant::get_numeric_constants_for_type(type,&constants); + + if (constants.size()==0) { + property.usage=0; + return; + } + property.hint_string=""; + for(List<StringName>::Element *E=constants.front();E;E=E->next()) { + if (property.hint_string!=String()) { + property.hint_string+=","; + } + property.hint_string+=String(E->get()); + } + + } +} + +void VisualScriptBasicTypeConstant::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_basic_type","name"),&VisualScriptBasicTypeConstant::set_basic_type); + ObjectTypeDB::bind_method(_MD("get_basic_type"),&VisualScriptBasicTypeConstant::get_basic_type); + + ObjectTypeDB::bind_method(_MD("set_basic_type_constant","name"),&VisualScriptBasicTypeConstant::set_basic_type_constant); + ObjectTypeDB::bind_method(_MD("get_basic_type_constant"),&VisualScriptBasicTypeConstant::get_basic_type_constant); + + + String argt="Null"; + for(int i=1;i<Variant::VARIANT_MAX;i++) { + argt+=","+Variant::get_type_name(Variant::Type(i)); + } + + ADD_PROPERTY(PropertyInfo(Variant::INT,"basic_type",PROPERTY_HINT_ENUM,argt),_SCS("set_basic_type"),_SCS("get_basic_type")); + ADD_PROPERTY(PropertyInfo(Variant::STRING,"constant",PROPERTY_HINT_ENUM,""),_SCS("set_basic_type_constant"),_SCS("get_basic_type_constant")); +} + +VisualScriptBasicTypeConstant::VisualScriptBasicTypeConstant() { + + type=Variant::NIL; +} + ////////////////////////////////////////// @@ -1782,6 +1933,19 @@ VisualScriptNodeInstance* VisualScriptEngineSingleton::instance(VisualScriptInst return instance; } +VisualScriptEngineSingleton::TypeGuess VisualScriptEngineSingleton::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + Object *obj=Globals::get_singleton()->get_singleton_object(singleton); + TypeGuess tg; + tg.type=Variant::OBJECT; + if (obj) { + tg.obj_type=obj->get_type(); + tg.script=obj->get_script(); + } + + return tg; +} + void VisualScriptEngineSingleton::_bind_methods() { @@ -1919,6 +2083,8 @@ VisualScriptNodeInstance* VisualScriptSceneNode::instance(VisualScriptInstance* } + + #ifdef TOOLS_ENABLED static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) { @@ -1942,6 +2108,49 @@ static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const R #endif +VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + + VisualScriptSceneNode::TypeGuess tg; + tg.type=Variant::OBJECT; + tg.obj_type="Node"; + +#ifdef TOOLS_ENABLED + Ref<Script> script = get_visual_script(); + if (!script.is_valid()) + return tg; + + MainLoop * main_loop = OS::get_singleton()->get_main_loop(); + if (!main_loop) + return tg; + + SceneTree *scene_tree = main_loop->cast_to<SceneTree>(); + + if (!scene_tree) + return tg; + + Node *edited_scene = scene_tree->get_edited_scene_root(); + + if (!edited_scene) + return tg; + + Node* script_node = _find_script_node(edited_scene,edited_scene,script); + + if (!script_node) + return tg; + + Node* another = script_node->get_node(path); + + if (another) { + tg.obj_type=another->get_type(); + tg.script=another->get_script(); + } +#endif + return tg; + +} + + void VisualScriptSceneNode::_validate_property(PropertyInfo& property) const { #ifdef TOOLS_ENABLED @@ -2079,6 +2288,13 @@ VisualScriptNodeInstance* VisualScriptSceneTree::instance(VisualScriptInstance* return instance; } +VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + TypeGuess tg; + tg.type=Variant::OBJECT; + tg.obj_type="SceneTree"; + return tg; +} void VisualScriptSceneTree::_validate_property(PropertyInfo& property) const { @@ -2270,6 +2486,23 @@ VisualScriptNodeInstance* VisualScriptSelf::instance(VisualScriptInstance* p_ins return instance; } +VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess* p_inputs, int p_output) const { + + VisualScriptSceneNode::TypeGuess tg; + tg.type=Variant::OBJECT; + tg.obj_type="Object"; + + Ref<Script> script = get_visual_script(); + if (!script.is_valid()) + return tg; + + tg.obj_type=script->get_instance_base_type(); + tg.script=script; + + return tg; + + +} void VisualScriptSelf::_bind_methods() { @@ -3247,13 +3480,29 @@ PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) cons String VisualScriptInputAction::get_caption() const { + return "Action"; } String VisualScriptInputAction::get_text() const { - return name; + switch(mode) { + case MODE_PRESSED: { + return name; + } break; + case MODE_RELEASED: { + return "not "+name; + } break; + case MODE_JUST_PRESSED: { + return String(name)+" "+TTR("just pressed"); + } break; + case MODE_JUST_RELEASED: { + return String(name)+" "+TTR("just released"); + } break; + } + + return String(); } @@ -3278,19 +3527,50 @@ StringName VisualScriptInputAction::get_action_name() const { return name; } +void VisualScriptInputAction::set_action_mode(Mode p_mode) { + + if (mode==p_mode) + return; + + mode=p_mode; + ports_changed_notify(); + +} +VisualScriptInputAction::Mode VisualScriptInputAction::get_action_mode() const { + + return mode; +} + class VisualScriptNodeInstanceInputAction : public VisualScriptNodeInstance { public: VisualScriptInstance* instance; StringName action; + VisualScriptInputAction::Mode mode; virtual int get_working_memory_size() const { return 1; } virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) { - *p_outputs[0]=Input::get_singleton()->is_action_pressed(action); + switch(mode) { + case VisualScriptInputAction::MODE_PRESSED: { + *p_outputs[0]=Input::get_singleton()->is_action_pressed(action); + } break; + case VisualScriptInputAction::MODE_RELEASED: { + *p_outputs[0]=!Input::get_singleton()->is_action_pressed(action); + } break; + case VisualScriptInputAction::MODE_JUST_PRESSED: { + *p_outputs[0]=Input::get_singleton()->is_action_just_pressed(action); + } break; + case VisualScriptInputAction:: MODE_JUST_RELEASED: { + *p_outputs[0]=Input::get_singleton()->is_action_just_released(action); + } break; + + } + + return 0; } @@ -3302,6 +3582,7 @@ VisualScriptNodeInstance* VisualScriptInputAction::instance(VisualScriptInstance VisualScriptNodeInstanceInputAction * instance = memnew(VisualScriptNodeInstanceInputAction ); instance->instance=p_instance; instance->action=name; + instance->mode=mode; return instance; } @@ -3348,13 +3629,18 @@ void VisualScriptInputAction::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_action_name","name"),&VisualScriptInputAction::set_action_name); ObjectTypeDB::bind_method(_MD("get_action_name"),&VisualScriptInputAction::get_action_name); + ObjectTypeDB::bind_method(_MD("set_action_mode","mode"),&VisualScriptInputAction::set_action_mode); + ObjectTypeDB::bind_method(_MD("get_action_mode"),&VisualScriptInputAction::get_action_mode); + ADD_PROPERTY( PropertyInfo(Variant::STRING,"action"),_SCS("set_action_name"),_SCS("get_action_name")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Pressed,Released,JustPressed,JustReleased"),_SCS("set_action_mode"),_SCS("get_action_mode")); } VisualScriptInputAction::VisualScriptInputAction() { name=""; + mode=MODE_PRESSED; } @@ -3593,6 +3879,7 @@ void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("constants/math_constant",create_node_generic<VisualScriptMathConstant>); VisualScriptLanguage::singleton->add_register_func("constants/class_constant",create_node_generic<VisualScriptClassConstant>); VisualScriptLanguage::singleton->add_register_func("constants/global_constant",create_node_generic<VisualScriptGlobalConstant>); + VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant",create_node_generic<VisualScriptBasicTypeConstant>); VisualScriptLanguage::singleton->add_register_func("custom/custom_node",create_node_generic<VisualScriptCustomNode>); VisualScriptLanguage::singleton->add_register_func("custom/sub_call",create_node_generic<VisualScriptSubCall>); diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index c0ffb17e15..94eeadca57 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -412,6 +412,48 @@ public: VisualScriptClassConstant(); }; +class VisualScriptBasicTypeConstant : public VisualScriptNode { + + OBJ_TYPE(VisualScriptBasicTypeConstant,VisualScriptNode) + + Variant::Type type; + StringName name; +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo& property) const; + +public: + + virtual int get_output_sequence_port_count() const; + virtual bool has_input_sequence_port() const; + + + virtual String get_output_sequence_port_text(int p_port) const; + + + virtual int get_input_value_port_count() const; + virtual int get_output_value_port_count() const; + + + virtual PropertyInfo get_input_value_port_info(int p_idx) const; + virtual PropertyInfo get_output_value_port_info(int p_idx) const; + + virtual String get_caption() const; + virtual String get_text() const; + virtual String get_category() const { return "constants"; } + + void set_basic_type_constant(const StringName& p_which); + StringName get_basic_type_constant() const; + + void set_basic_type(Variant::Type p_which); + Variant::Type get_basic_type() const; + + virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + + VisualScriptBasicTypeConstant(); +}; + + class VisualScriptMathConstant : public VisualScriptNode { @@ -496,6 +538,9 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; + + VisualScriptEngineSingleton(); }; @@ -535,6 +580,8 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; + VisualScriptSceneNode(); }; @@ -571,6 +618,8 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; + VisualScriptSceneTree(); }; @@ -644,6 +693,8 @@ public: virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); + virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const; + VisualScriptSelf(); }; @@ -906,8 +957,16 @@ public: class VisualScriptInputAction: public VisualScriptNode { OBJ_TYPE(VisualScriptInputAction,VisualScriptNode) +public: + enum Mode { + MODE_PRESSED, + MODE_RELEASED, + MODE_JUST_PRESSED, + MODE_JUST_RELEASED, + }; StringName name; + Mode mode; protected: @@ -936,12 +995,15 @@ public: void set_action_name(const StringName& p_name); StringName get_action_name() const; + void set_action_mode(Mode p_mode); + Mode get_action_mode() const; + virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance); VisualScriptInputAction(); }; - +VARIANT_ENUM_CAST( VisualScriptInputAction::Mode ) class VisualScriptDeconstruct: public VisualScriptNode { |