diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 40 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 624 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.cpp | 9 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 216 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 12 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 70 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 21 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 42 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 1 | ||||
-rw-r--r-- | modules/gridmap/grid_map.cpp | 79 | ||||
-rw-r--r-- | modules/gridmap/grid_map.h | 2 | ||||
-rw-r--r-- | modules/gridmap/grid_map_editor_plugin.cpp | 161 | ||||
-rw-r--r-- | modules/gridmap/grid_map_editor_plugin.h | 21 |
13 files changed, 1090 insertions, 208 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index b405555ec6..a62225f663 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -28,15 +28,6 @@ /*************************************************************************/ #include "gd_compiler.h" #include "gd_script.h" -/* TODO: - - *AND and OR need early abort - -Inheritance properly process (done?) - *create built in initializer and constructor - *assign operators - *build arrays and dictionaries - *call parent constructor - */ void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) { @@ -1397,13 +1388,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars int index_from=0; + Ref<GDNativeClass> native; if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; Ref<GDScript> script; - Ref<GDNativeClass> native; + if (path!="") { //path (and optionally subclasses) @@ -1573,7 +1565,35 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars //p_script->constants[constant->value].make_const(); } + for(int i=0;i<p_class->_signals.size();i++) { + + StringName name = p_class->_signals[i].name; + + GDScript *c = p_script; + while(c) { + + if (c->_signals.has(name)) { + _set_error("Signal '"+name+"' redefined (in current or parent class)",p_class); + return ERR_ALREADY_EXISTS; + } + + if (c->base.is_valid()) { + c=c->base.ptr(); + } else { + c=NULL; + } + } + + if (native.is_valid()) { + if (ObjectTypeDB::has_signal(native->get_name(),name)) { + _set_error("Signal '"+name+"' redefined (original in native class '"+String(native->get_name())+"')",p_class); + return ERR_ALREADY_EXISTS; + } + } + + p_script->_signals[name]=p_class->_signals[i].arguments; + } //parse sub-classes for(int i=0;i<p_class->subclasses.size();i++) { diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index f59dbd91d3..0d986e92a2 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -29,6 +29,7 @@ #include "gd_script.h" #include "gd_compiler.h" #include "globals.h" +#include "os/file_access.h" void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -238,26 +239,26 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level,List<String> *p if (_debug_parse_err_line>=0) return; - ERR_FAIL_INDEX(p_level,_debug_call_stack_pos); - int l = _debug_call_stack_pos - p_level -1; + ERR_FAIL_INDEX(p_level,_debug_call_stack_pos); + int l = _debug_call_stack_pos - p_level -1; - GDInstance *instance = _call_stack[l].instance; + GDInstance *instance = _call_stack[l].instance; - if (!instance) - return; + if (!instance) + return; - Ref<GDScript> script = instance->get_script(); - ERR_FAIL_COND( script.is_null() ); + Ref<GDScript> script = instance->get_script(); + ERR_FAIL_COND( script.is_null() ); - const Map<StringName,GDScript::MemberInfo>& mi = script->debug_get_member_indices(); + const Map<StringName,GDScript::MemberInfo>& mi = script->debug_get_member_indices(); - for(const Map<StringName,GDScript::MemberInfo>::Element *E=mi.front();E;E=E->next()) { + for(const Map<StringName,GDScript::MemberInfo>::Element *E=mi.front();E;E=E->next()) { - p_members->push_back(E->key()); - p_values->push_back( instance->debug_get_member_by_index(E->get().index)); - } + p_members->push_back(E->key()); + p_values->push_back( instance->debug_get_member_by_index(E->get().index)); + } } void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) { @@ -317,6 +318,7 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam struct GDCompletionIdentifier { StringName obj_type; + Ref<GDScript> script; Variant::Type type; Variant value; //im case there is a value, also return it }; @@ -381,7 +383,12 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) { path=context.base_path.plus_file(path); } - script = ResourceLoader::load(path); + + if (ScriptCodeCompletionCache::get_sigleton()) + script = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path); + else + script = ResourceLoader::load(path); + if (script.is_null()) { return REF(); } @@ -441,7 +448,7 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) { base_class=base_class->subclasses[subclass]; } else { - print_line("Could not find subclass: "+subclass); + //print_line("Could not find subclass: "+subclass); return _get_type_from_class(context); //fail please } } @@ -626,7 +633,9 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: //try calling the function if constant and all args are constant, should not crash.. Object *baseptr = base.value; - if (baseptr && mb->is_const() && pi.type==Variant::OBJECT) { + + if (mb->is_const() && pi.type==Variant::OBJECT) { + bool all_valid=true; Vector<Variant> args; for(int i=2;i<op->arguments.size();i++) { @@ -643,25 +652,88 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: all_valid=false; } } - if (all_valid) { - Vector<const Variant*> argptr; - for(int i=0;i<args.size();i++) { - argptr.push_back(&args[i]); - } - Variant::CallError ce; - Variant ret=mb->call(baseptr,argptr.ptr(),argptr.size(),ce); + if (all_valid && String(id)=="get_node" && ObjectTypeDB::is_type(base.obj_type,"Node") && args.size()) { + + String arg1=args[0]; + if (arg1.begins_with("/root/")) { + String which = arg1.get_slice("/",2); + if (which!="") { + List<PropertyInfo> props; + Globals::get_singleton()->get_property_list(&props); + //print_line("find singleton"); + + for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + //print_line("found "+s); + String name = s.get_slice("/",1); + //print_line("name: "+name+", which: "+which); + if (name==which) { + String script = Globals::get_singleton()->get(s); + if (!script.begins_with("res://")) { + script="res://"+script; + } - if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + if (!script.ends_with(".gd")) { + //not a script, try find the script anyway, + //may have some success + script=script.basename()+".gd"; + } - if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + if (FileAccess::exists(script)) { - r_type=_get_type_from_variant(ret); - return true; + //print_line("is a script"); + + + Ref<Script> scr; + if (ScriptCodeCompletionCache::get_sigleton()) + scr = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(script); + else + scr = ResourceLoader::load(script); + + + r_type.obj_type="Node"; + r_type.type=Variant::OBJECT; + r_type.script=scr; + r_type.value=Variant(); + + return true; + + } + } + } } } + } + + + + if (baseptr) { + + if (all_valid) { + Vector<const Variant*> argptr; + for(int i=0;i<args.size();i++) { + argptr.push_back(&args[i]); + } + + Variant::CallError ce; + Variant ret=mb->call(baseptr,argptr.ptr(),argptr.size(),ce); + + if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + + if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + + r_type=_get_type_from_variant(ret); + return true; + } + } + + } } } @@ -926,6 +998,44 @@ static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_l return false; } + +static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context,const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) { + + const GDParser::FunctionNode* func=NULL; + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->name==p_function) { + func=context._class->functions[i]; + break; + } + } + + if (!func) + return false; + + for(int i=0;i<func->body->statements.size();i++) { + + + + if (func->body->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { + const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(func->body->statements[i]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + + if (op->arguments.size() && op->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) { + + const GDParser::IdentifierNode *id = static_cast<const GDParser::IdentifierNode *>(op->arguments[0]); + + if (id->name==p_identifier) { + + return _guess_expression_type(context,op->arguments[1],func->body->statements[i]->line,r_type); + } + } + } + } + } + + return false; +} + static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { //go to block first @@ -1017,8 +1127,22 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const r_type=_get_type_from_pinfo(context._class->variables[i]._export); return true; } else if (context._class->variables[i].expression) { - return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); + + bool rtype = _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); + if (rtype && r_type.type!=Variant::NIL) + return true; + //return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); } + + //try to guess from assignment in construtor or _ready + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_ready",r_type)) + return true; + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_enter_tree",r_type)) + return true; + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_init",r_type)) + return true; + + return false; } } } @@ -1195,7 +1319,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl } static const char*_type_names[Variant::VARIANT_MAX]={ - "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", + "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Transform", "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", "Vector2Array","Vector3Array","ColorArray"}; @@ -1276,6 +1400,7 @@ static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argid static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) { + //print_line("find type arguments?"); if (id.type==Variant::INPUT_EVENT && String(p_method)=="is_action" && p_argidx==0) { List<PropertyInfo> pinfo; @@ -1296,78 +1421,300 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method); - if (!m) - return; + if (!m) { + //not in static method, see script + + //print_line("not in static: "+String(p_method)); + Ref<GDScript> on_script; + + if (id.value.get_type()) { + Object *obj=id.value; + + + if (obj) { + + + GDScript *scr = obj->cast_to<GDScript>(); + if (scr) { + while (scr) { + + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().is_static() && p_method==E->get().get_name()) { + arghint="static func "+String(p_method)+"("; + for(int i=0;i<E->get().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidx<E->get().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + arghint+=")"; + return; //found + } + } + + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } + } else { + on_script=obj->get_script(); + } + } + } + + //print_line("but it has a script?"); + if (!on_script.is_valid() && id.script.is_valid()) { + //print_line("yes"); + on_script=id.script; + } - if (p_method.operator String()=="connect") { + if (on_script.is_valid()) { + + GDScript *scr = on_script.ptr(); + if (scr) { + while (scr) { + + String code = scr->get_source_code(); + //print_line("has source code!"); + + if (code!="") { + //if there is code, parse it. This way is slower but updates in real-time + GDParser p; + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false); + + if (err==OK) { + //print_line("checking the functions..."); + //only if ok, otherwise use what is cached on the script + //GDParser::ClassNode *base = p. + const GDParser::Node *root = p.get_parse_tree(); + ERR_FAIL_COND(root->type!=GDParser::Node::TYPE_CLASS); + + const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root); + + const GDParser::FunctionNode* func=NULL; + bool st=false; + + for(int i=0;i<cl->functions.size();i++) { + //print_line(String(cl->functions[i]->name)+" vs "+String(p_method)); + if (cl->functions[i]->name==p_method) { + func=cl->functions[i]; + } + } + + for(int i=0;i<cl->static_functions.size();i++) { + + //print_line(String(cl->static_functions[i]->name)+" vs "+String(p_method)); + if (cl->static_functions[i]->name==p_method) { + func=cl->static_functions[i]; + st=true; + } + + } + + if (func) { + + arghint="func "+String(p_method)+"("; + if (st) + arghint="static "+arghint; + for(int i=0;i<func->arguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+String(func->arguments[i]); + int deffrom = func->arguments.size()-func->default_values.size(); + if (i>=deffrom) { + + int defidx = deffrom-i; + + if (defidx>=0 && defidx<func->default_values.size() && func->default_values[defidx]->type==GDParser::Node::TYPE_OPERATOR) { + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(func->default_values[defidx]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(op->arguments[1]); + arghint+="="+cn->value.get_construct_string(); + } + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + + arghint+=" )"; + return; + } + } else { + //print_line("failed parsing?"); + code=""; + } + + } + + if (code=="") { + + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (p_method==E->get().get_name()) { + arghint="func "+String(p_method)+"("; + for(int i=0;i<E->get().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidx<E->get().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + arghint+=")"; + return; //found + } + } +#if 0 + //use class directly, no code was found + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + options.insert(String(E->key())+"("); + } + for (const Set<StringName>::Element *E=scr->get_members().front();E;E=E->next()) { + options.insert(E->get()); + } +#endif + } - if (p_argidx==0) { - List<MethodInfo> sigs; - ObjectTypeDB::get_signal_list(id.obj_type,&sigs); - for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { - result.insert("\""+E->get().name+"\""); + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } } } - /*if (p_argidx==2) { - ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); - const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_node); - if (op->arguments.size()>) - }*/ } else { + //regular method - Object *obj=id.value; - if (obj) { - List<String> options; - obj->get_argument_options(p_method,p_argidx,&options); - for(List<String>::Element *E=options.front();E;E=E->next()) { + if (p_method.operator String()=="connect") { - result.insert(E->get()); + + if (p_argidx==0) { + List<MethodInfo> sigs; + ObjectTypeDB::get_signal_list(id.obj_type,&sigs); + for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { + result.insert("\""+E->get().name+"\""); + } } - } + /*if (p_argidx==2) { - } + ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_node); + if (op->arguments.size()>) - arghint = _get_visual_datatype(m->get_argument_info(-1),false)+" "+p_method.operator String()+String("("); + }*/ + } else { - for(int i=0;i<m->get_argument_count();i++) { - if (i>0) - arghint+=", "; - else - arghint+=" "; + if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ObjectTypeDB::is_type(id.obj_type,"Node")) { - if (i==p_argidx) { - arghint+=String::chr(0xFFFF); - } - String n = m->get_argument_info(i).name; - int dp = n.find(":"); - if (dp!=-1) - n=n.substr(0,dp); - arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; - int deffrom = m->get_argument_count()-m->get_default_argument_count(); + 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; + // print_line("found "+s); + String name = s.get_slice("/",1); + result.insert("\"/root/"+name+"\""); + } + } + Object *obj=id.value; + if (obj) { + List<String> options; + obj->get_argument_options(p_method,p_argidx,&options); - if (i>=deffrom) { - int defidx = i-deffrom; + for(List<String>::Element *E=options.front();E;E=E->next()) { - if (defidx>=0 && defidx<m->get_default_argument_count()) { - Variant v= m->get_default_argument(i); - arghint+="="+v.get_construct_string(); + result.insert(E->get()); + } } - } - if (i==p_argidx) { - arghint+=String::chr(0xFFFF); } - } - if (m->get_argument_count()>0) - arghint+=" "; + arghint = _get_visual_datatype(m->get_argument_info(-1),false)+" "+p_method.operator String()+String("("); + for(int i=0;i<m->get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; - arghint+=")"; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + String n = m->get_argument_info(i).name; + int dp = n.find(":"); + if (dp!=-1) + n=n.substr(0,dp); + arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; + int deffrom = m->get_argument_count()-m->get_default_argument_count(); + + + if (i>=deffrom) { + int defidx = i-deffrom; + + if (defidx>=0 && defidx<m->get_default_argument_count()) { + Variant v= m->get_default_argument(i); + arghint+="="+v.get_construct_string(); + } + } + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (m->get_argument_count()>0) + arghint+=" "; + + + arghint+=")"; + } } } @@ -1414,7 +1761,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No arghint+=")"; } else if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { - //complete built-in function + //complete constructor const GDParser::TypeNode *tn = static_cast<const GDParser::TypeNode*>(op->arguments[0]); List<MethodInfo> mil; @@ -1549,7 +1896,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No } } else { - + //indexed lookup GDCompletionIdentifier ci; if (_guess_expression_type(context,op->arguments[0],p_line,ci)) { @@ -1661,7 +2008,9 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>")); GDParser p; - Error err = p.parse(p_code,p_base_path,true); + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(p_code,p_base_path,false,"",true); bool isfunction=false; Set<String> options; @@ -1713,20 +2062,123 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + Ref<GDScript> on_script; if (t.value.get_type()) { Object *obj=t.value; + + if (obj) { + + GDScript *scr = obj->cast_to<GDScript>(); + if (scr) { + while (scr) { + + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().is_static()) + options.insert(E->key()); + } + + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } + } else { + on_script=obj->get_script(); + } + } + } + + + if (!on_script.is_valid() && t.script.is_valid()) { + on_script=t.script; + } + + if (on_script.is_valid()) { + + GDScript *scr = on_script.ptr(); + if (scr) { while (scr) { - if (!isfunction) { - for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { - options.insert(E->key()); + String code = scr->get_source_code(); + + if (code!="") { + //if there is code, parse it. This way is slower but updates in real-time + GDParser p; + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false); + + if (err==OK) { + //only if ok, otherwise use what is cached on the script + //GDParser::ClassNode *base = p. + const GDParser::Node *root = p.get_parse_tree(); + ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_PARSE_ERROR); + + const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root); + + for(int i=0;i<cl->functions.size();i++) { + + if (cl->functions[i]->arguments.size()) + options.insert(String(cl->functions[i]->name)+"("); + else + options.insert(String(cl->functions[i]->name)+"()"); + } + + for(int i=0;i<cl->static_functions.size();i++) { + + if (cl->static_functions[i]->arguments.size()) + options.insert(String(cl->static_functions[i]->name)+"("); + else + options.insert(String(cl->static_functions[i]->name)+"()"); + + } + + if (!isfunction) { + for(int i=0;i<cl->variables.size();i++) { + + options.insert(String(cl->variables[i].identifier)); + } + + for(int i=0;i<cl->constant_expressions.size();i++) { + + options.insert(String(cl->constant_expressions[i].identifier)); + } + + } + + + } else { + code=""; //well, then no code } + } - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - options.insert(E->key()); + + if (code=="") { + //use class directly, no code was found + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().get_argument_count()) + options.insert(String(E->key())+"()"); + else + options.insert(String(E->key())+"("); + + } + + for (const Set<StringName>::Element *E=scr->get_members().front();E;E=E->next()) { + options.insert(E->get()); + } } if (scr->get_base().is_valid()) @@ -1738,6 +2190,10 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base } + + + + if (!isfunction) { ObjectTypeDB::get_integer_constant_list(t.obj_type,r_options); } diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 37ddb2bc41..6f51ac5312 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -904,6 +904,15 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va r_ret = gdscr->_new(NULL,0,r_error); + GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance()); + Ref<GDScript> gd_ref = ins->get_script(); + + for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { + if(d.has(E->key())) { + ins->members[E->get().index] = d[E->key()]; + } + } + } break; case HASH: { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 6242400663..313fb57d0e 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -30,6 +30,7 @@ #include "print_string.h" #include "io/resource_loader.h" #include "os/file_access.h" +#include "script_language.h" template<class T> T* GDParser::alloc_node() { @@ -116,6 +117,14 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { _make_completable_call(argidx); completion_node=p_parent; + } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING && tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) { + //completing a string argument.. + completion_cursor=tokenizer->get_token_constant(); + + _make_completable_call(argidx); + completion_node=p_parent; + tokenizer->advance(1); + return false; } Node*arg = _parse_expression(p_parent,p_static); @@ -161,6 +170,7 @@ void GDParser::_make_completable_call(int p_arg) { completion_line=tokenizer->get_token_line(); completion_argument=p_arg; completion_block=current_block; + completion_found=true; tokenizer->advance(); } @@ -181,6 +191,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide completion_function=current_function; completion_line=tokenizer->get_token_line(); completion_block=current_block; + completion_found=true; tokenizer->advance(); if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { @@ -277,7 +288,11 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ if (!validating) { //this can be too slow for just validating code - res = ResourceLoader::load(path); + if (for_completion && ScriptCodeCompletionCache::get_sigleton()) { + res = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path); + } else { + res = ResourceLoader::load(path); + } if (!res.is_valid()) { _set_error("Can't preload resource at path: "+path); return NULL; @@ -1401,6 +1416,24 @@ GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_sta return expr; } +bool GDParser::_recover_from_completion() { + + if (!completion_found) { + return false; //can't recover if no completion + } + //skip stuff until newline + while(tokenizer->get_token()!=GDTokenizer::TK_NEWLINE && tokenizer->get_token()!=GDTokenizer::TK_EOF && tokenizer->get_token()!=GDTokenizer::TK_ERROR) { + tokenizer->advance(); + } + completion_found=false; + error_set=false; + if(tokenizer->get_token() == GDTokenizer::TK_ERROR){ + error_set = true; + } + + return true; +} + void GDParser::_parse_block(BlockNode *p_block,bool p_static) { int indent_level = tab_level.back()->get(); @@ -1498,8 +1531,14 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { Node *subexpr=NULL; subexpr = _parse_and_reduce_expression(p_block,p_static); - if (!subexpr) + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } + + lv->assign=subexpr; assigned=subexpr; @@ -1520,16 +1559,22 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { op->arguments.push_back(assigned); p_block->statements.push_back(op); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (var)"); + return; + } } break; case GDTokenizer::TK_CF_IF: { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_if = alloc_node<ControlFlowNode>(); @@ -1583,8 +1628,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { //condition Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } cf_else->arguments.push_back(condition); cf_else->cf_type=ControlFlowNode::CF_IF; @@ -1645,8 +1694,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_while = alloc_node<ControlFlowNode>(); @@ -1691,8 +1744,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *container = _parse_and_reduce_expression(p_block,p_static); - if (!container) + if (!container) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_for = alloc_node<ControlFlowNode>(); @@ -1756,8 +1813,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { } else { //expect expression Node *retexpr = _parse_and_reduce_expression(p_block,p_static); - if (!retexpr) + if (!retexpr) { + if (_recover_from_completion()) { + break; + } return; + } cf_return->arguments.push_back(retexpr); p_block->statements.push_back(cf_return); if (!_end_statement()) { @@ -1772,8 +1833,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } AssertNode *an = alloc_node<AssertNode>(); an->condition=condition; p_block->statements.push_back(an); @@ -1786,8 +1851,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { default: { Node *expression = _parse_and_reduce_expression(p_block,p_static,false,true); - if (!expression) + if (!expression) { + if (_recover_from_completion()) { + break; + } return; + } p_block->statements.push_back(expression); if (!_end_statement()) { _set_error("Expected end of statement after expression."); @@ -1946,8 +2015,10 @@ void GDParser::_parse_class(ClassNode *p_class) { _parse_extends(p_class); if (error_set) return; - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement after extends"); + return; + } } break; case GDTokenizer::TK_PR_TOOL: { @@ -2227,6 +2298,53 @@ void GDParser::_parse_class(ClassNode *p_class) { //arguments } break; + case GDTokenizer::TK_PR_SIGNAL: { + tokenizer->advance(); + + if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + _set_error("Expected identifier after 'signal'."); + return; + } + + ClassNode::Signal sig; + sig.name = tokenizer->get_token_identifier(); + tokenizer->advance(); + + + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) { + tokenizer->advance(); + while(true) { + + + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) { + tokenizer->advance(); + break; + } + + if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + _set_error("Expected identifier in signal argument."); + return; + } + + sig.arguments.push_back(tokenizer->get_token_identifier()); + tokenizer->advance(); + + if (tokenizer->get_token()==GDTokenizer::TK_COMMA) { + tokenizer->advance(); + } else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ',' or ')' after signal parameter identifier."); + return; + } + } + } + + p_class->_signals.push_back(sig); + + if (!_end_statement()) { + _set_error("Expected end of statement (signal)"); + return; + } + } break; case GDTokenizer::TK_PR_EXPORT: { tokenizer->advance(); @@ -2251,6 +2369,17 @@ void GDParser::_parse_class(ClassNode *p_class) { case Variant::INT: { + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="FLAGS") { + + current_export.hint=PROPERTY_HINT_ALL_FLAGS; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } + if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING) { //enumeration current_export.hint=PROPERTY_HINT_ENUM; @@ -2292,6 +2421,16 @@ void GDParser::_parse_class(ClassNode *p_class) { }; //fallthrough to use the same case Variant::REAL: { + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="EASE") { + current_export.hint=PROPERTY_HINT_EXP_EASING; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } + float sign=1.0; if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) { @@ -2442,6 +2581,17 @@ void GDParser::_parse_class(ClassNode *p_class) { } break; } + + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="MULTILINE") { + + current_export.hint=PROPERTY_HINT_MULTILINE_TEXT; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } } break; case Variant::COLOR: { @@ -2543,14 +2693,18 @@ void GDParser::_parse_class(ClassNode *p_class) { Node *subexpr=NULL; - subexpr = _parse_and_reduce_expression(p_class,false); - if (!subexpr) + subexpr = _parse_and_reduce_expression(p_class,false,autoexport); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } member.expression=subexpr; if (autoexport) { - if (subexpr->type==Node::TYPE_ARRAY) { + if (1)/*(subexpr->type==Node::TYPE_ARRAY) { member._export.type=Variant::ARRAY; @@ -2558,7 +2712,7 @@ void GDParser::_parse_class(ClassNode *p_class) { member._export.type=Variant::DICTIONARY; - } else { + } else*/ { if (subexpr->type!=Node::TYPE_CONSTANT) { @@ -2644,8 +2798,10 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->variables.push_back(member); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (continue)"); + return; + } } break; case GDTokenizer::TK_PR_CONST: { //variale declaration and (eventual) initialization @@ -2672,8 +2828,12 @@ void GDParser::_parse_class(ClassNode *p_class) { Node *subexpr=NULL; subexpr = _parse_and_reduce_expression(p_class,true,true); - if (!subexpr) + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } if (subexpr->type!=Node::TYPE_CONSTANT) { _set_error("Expected constant expression"); @@ -2682,8 +2842,10 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->constant_expressions.push_back(constant); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (constant)"); + return; + } } break; @@ -2759,11 +2921,14 @@ Error GDParser::_parse(const String& p_base_path) { Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path, const String &p_self_path) { + for_completion=false; + validating=false; completion_type=COMPLETION_NONE; completion_node=NULL; completion_class=NULL; completion_function=NULL; completion_block=NULL; + completion_found=false; current_block=NULL; current_class=NULL; current_function=NULL; @@ -2779,13 +2944,14 @@ Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p } -Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) { +Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path,bool p_for_completion) { completion_type=COMPLETION_NONE; completion_node=NULL; completion_class=NULL; completion_function=NULL; completion_block=NULL; + completion_found=false; current_block=NULL; current_class=NULL; @@ -2796,6 +2962,7 @@ Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_ju tt->set_code(p_code); validating=p_just_validate; + for_completion=p_for_completion; tokenizer=tt; Error ret = _parse(p_base_path); memdelete(tt); @@ -2828,9 +2995,12 @@ void GDParser::clear() { current_block=NULL; current_class=NULL; + completion_found=false; + current_function=NULL; validating=false; + for_completion=false; error_set=false; tab_level.clear(); tab_level.push_back(0); diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index bf2c8c9ef2..134279b6d8 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -76,6 +76,7 @@ public: StringName extends_file; Vector<StringName> extends_class; + struct Member { PropertyInfo _export; #ifdef TOOLS_ENABLED @@ -92,11 +93,17 @@ public: Node *expression; }; + struct Signal { + StringName name; + Vector<StringName> arguments; + }; + Vector<ClassNode*> subclasses; Vector<Member> variables; Vector<Constant> constant_expressions; Vector<FunctionNode*> functions; Vector<FunctionNode*> static_functions; + Vector<Signal> _signals; BlockNode *initializer; ClassNode *owner; //Vector<Node*> initializers; @@ -380,6 +387,7 @@ private: T* alloc_node(); bool validating; + bool for_completion; int parenthesis; bool error_set; String error; @@ -411,10 +419,12 @@ private: BlockNode *completion_block; int completion_line; int completion_argument; + bool completion_found; PropertyInfo current_export; void _set_error(const String& p_error, int p_line=-1, int p_column=-1); + bool _recover_from_completion(); bool _parse_arguments(Node* p_parent, Vector<Node*>& p_args, bool p_static, bool p_can_codecomplete=false); @@ -436,7 +446,7 @@ public: String get_error() const; int get_error_line() const; int get_error_column() const; - Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path=""); + Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); Error parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path="",const String& p_self_path=""); const Node *get_parse_tree() const; diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index ceca1ff2b9..99ddc74bb4 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -1756,6 +1756,12 @@ bool GDScript::_update_exports() { //print_line("found "+c->variables[i]._export.name); member_default_values_cache[c->variables[i].identifier]=c->variables[i].default_value; } + + _signals.clear(); + + for(int i=0;i<c->_signals.size();i++) { + _signals[c->_signals[i].name]=c->_signals[i].arguments; + } } } else { //print_line("unchaged is "+get_path()); @@ -1971,9 +1977,17 @@ void GDScript::_bind_methods() { ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo("new")); + ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code); + } +Vector<uint8_t> GDScript::get_as_byte_code() const { + + GDTokenizerBuffer tokenizer; + return tokenizer.parse_code_string(source); +}; + Error GDScript::load_byte_code(const String& p_path) { @@ -2100,6 +2114,47 @@ Ref<GDScript> GDScript::get_base() const { return base; } +bool GDScript::has_script_signal(const StringName& p_signal) const { + if (_signals.has(p_signal)) + return true; + if (base.is_valid()) { + return base->has_script_signal(p_signal); + } +#ifdef TOOLS_ENABLED + else if (base_cache.is_valid()){ + return base_cache->has_script_signal(p_signal); + } + +#endif + return false; +} +void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + + for(const Map<StringName,Vector<StringName> >::Element *E=_signals.front();E;E=E->next()) { + + MethodInfo mi; + mi.name=E->key(); + for(int i=0;i<E->get().size();i++) { + PropertyInfo arg; + arg.name=E->get()[i]; + mi.arguments.push_back(arg); + } + r_signals->push_back(mi); + } + + if (base.is_valid()) { + base->get_script_signal_list(r_signals); + } +#ifdef TOOLS_ENABLED + else if (base_cache.is_valid()){ + base_cache->get_script_signal_list(r_signals); + } + +#endif + +} + + GDScript::GDScript() { @@ -2131,7 +2186,6 @@ bool GDInstance::set(const StringName& p_name, const Variant& p_value) { { const Map<StringName,GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); if (E) { - members[E->get().index]=p_value; if (E->get().setter) { const Variant *val=&p_value; Variant::CallError err; @@ -2140,6 +2194,8 @@ bool GDInstance::set(const StringName& p_name, const Variant& p_value) { return true; //function exists, call was successful } } + else + members[E->get().index] = p_value; return true; } } @@ -2508,9 +2564,9 @@ void GDScriptLanguage::init() { //populate native classes - List<String> class_list; + List<StringName> class_list; ObjectTypeDB::get_type_list(&class_list); - for(List<String>::Element *E=class_list.front();E;E=E->next()) { + for(List<StringName>::Element *E=class_list.front();E;E=E->next()) { StringName n = E->get(); String s = String(n); @@ -2593,6 +2649,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "static", "float", "int", + "signal", 0}; @@ -2653,7 +2710,10 @@ GDScriptLanguage::~GDScriptLanguage() { /*************** RESOURCE ***************/ -RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_original_path) { +RES ResourceFormatLoaderGDScript::load(const String &p_path, const String& p_original_path, Error *r_error) { + + if (r_error) + *r_error=ERR_FILE_CANT_OPEN; GDScript *script = memnew( GDScript ); @@ -2685,6 +2745,8 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_orig script->reload(); } + if (r_error) + *r_error=OK; return scriptres; } diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index f505d51ba4..37ef47af6c 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -260,6 +260,7 @@ friend class GDScriptLanguage; Map<StringName,GDFunction> member_functions; Map<StringName,MemberInfo> member_indices; //members are just indices to the instanced script. Map<StringName,Ref<GDScript> > subclasses; + Map<StringName,Vector<StringName> > _signals; #ifdef TOOLS_ENABLED @@ -318,6 +319,9 @@ public: const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; } const Ref<GDNativeClass>& get_native() const { return native; } + virtual bool has_script_signal(const StringName& p_signal) const; + virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; + bool is_tool() const { return tool; } Ref<GDScript> get_base() const; @@ -345,6 +349,8 @@ public: Error load_source_code(const String& p_path); Error load_byte_code(const String& p_path); + Vector<uint8_t> get_as_byte_code() const; + virtual ScriptLanguage *get_language() const; GDScript(); @@ -469,6 +475,19 @@ public: } + virtual Vector<StackInfo> debug_get_current_stack_info() { + if (Thread::get_main_ID()!=Thread::get_caller_ID()) + return Vector<StackInfo>(); + + Vector<StackInfo> csi; + csi.resize(_debug_call_stack_pos); + for(int i=0;i<_debug_call_stack_pos;i++) { + csi[_debug_call_stack_pos-i-1].line=_call_stack[i].line?*_call_stack[i].line:0; + csi[_debug_call_stack_pos-i-1].script=Ref<GDScript>(_call_stack[i].function->get_script()); + } + return csi; + } + struct { StringName _init; @@ -538,7 +557,7 @@ public: class ResourceFormatLoaderGDScript : public ResourceFormatLoader { public: - virtual RES load(const String &p_path,const String& p_original_path=""); + virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String& p_type) const; virtual String get_resource_type(const String &p_path) const; diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 0745baafe6..b591ed3b4b 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -97,6 +97,7 @@ const char* GDTokenizer::token_names[TK_MAX]={ "preload", "assert", "yield", +"signal", "'['", "']'", "'{'", @@ -568,7 +569,10 @@ void GDTokenizerText::_advance() { } else if( string_mode!=STRING_MULTILINE && CharType(GETCHAR(i))=='\n') { _make_error("Unexpected EOL at String."); return; - + } else if( CharType(GETCHAR(i))==0xFFFF) { + //string ends here, next will be TK + i--; + break; } else if (CharType(GETCHAR(i))=='\\') { //escaped characters... i++; @@ -639,6 +643,11 @@ void GDTokenizerText::_advance() { str+=res; } else { + if (CharType(GETCHAR(i))=='\n') { + line++; + column=0; + } + str+=CharType(GETCHAR(i)); } i++; @@ -670,19 +679,19 @@ void GDTokenizerText::_advance() { while(true) { if (GETCHAR(i)=='.') { if (period_found || exponent_found) { - _make_error("Invalid numeric constant at '.'"); + _make_error("Invalid numeric constant at '.'"); return; } period_found=true; } else if (GETCHAR(i)=='x') { - if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) { - _make_error("Invalid numeric constant at 'x'"); + if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) { + _make_error("Invalid numeric constant at 'x'"); return; } hexa_found=true; - } else if (!hexa_found && GETCHAR(i)=='e') { + } else if (!hexa_found && GETCHAR(i)=='e') { if (hexa_found || exponent_found) { - _make_error("Invalid numeric constant at 'e'"); + _make_error("Invalid numeric constant at 'e'"); return; } exponent_found=true; @@ -692,7 +701,7 @@ void GDTokenizerText::_advance() { } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) { if (sign_found) { - _make_error("Invalid numeric constant at '-'"); + _make_error("Invalid numeric constant at '-'"); return; } sign_found=true; @@ -703,20 +712,20 @@ void GDTokenizerText::_advance() { i++; } - if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) { - _make_error("Invalid numeric constant: "+str); + if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) { + _make_error("Invalid numeric constant: "+str); return; } INCPOS(str.length()); - if (hexa_found) { - int val = str.hex_to_int(); - _make_constant(val); - } else if (period_found) { + if (hexa_found) { + int val = str.hex_to_int(); + _make_constant(val); + } else if (period_found) { real_t val = str.to_double(); //print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val)); _make_constant(val); - } else { + } else { int val = str.to_int(); _make_constant(val); @@ -825,7 +834,7 @@ void GDTokenizerText::_advance() { _make_built_in_func(GDFunctions::Function(i)); found=true; - break; + break; } } @@ -856,6 +865,7 @@ void GDTokenizerText::_advance() { {TK_PR_PRELOAD,"preload"}, {TK_PR_ASSERT,"assert"}, {TK_PR_YIELD,"yield"}, + {TK_PR_SIGNAL,"signal"}, {TK_PR_CONST,"const"}, //controlflow {TK_CF_IF,"if"}, @@ -1036,7 +1046,7 @@ void GDTokenizerText::advance(int p_amount) { ////////////////////////////////////////////////////////////////////////////////////////////////////// -#define BYTECODE_VERSION 4 +#define BYTECODE_VERSION 5 Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) { diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index b63687d2b4..d6bd63c5b8 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -104,6 +104,7 @@ public: TK_PR_PRELOAD, TK_PR_ASSERT, TK_PR_YIELD, + TK_PR_SIGNAL, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 121b23001d..ad7c392cd0 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -34,7 +34,7 @@ #include "scene/3d/baked_light_instance.h" #include "io/marshalls.h" #include "scene/scene_string_names.h" - +#include "os/os.h" bool GridMap::_set(const StringName& p_name, const Variant& p_value) { @@ -130,8 +130,8 @@ bool GridMap::_set(const StringName& p_name, const Variant& p_value) { } else if (name.begins_with("areas/")) { - int which = name.get_slice("/",1).to_int(); - String what=name.get_slice("/",2); + int which = name.get_slicec('/',1).to_int(); + String what=name.get_slicec('/',2); if (what=="bounds") { ERR_FAIL_COND_V(area_map.has(which),false); create_area(which,p_value); @@ -215,8 +215,8 @@ bool GridMap::_get(const StringName& p_name,Variant &r_ret) const { r_ret= d; } else if (name.begins_with("areas/")) { - int which = name.get_slice("/",1).to_int(); - String what=name.get_slice("/",2); + int which = name.get_slicec('/',1).to_int(); + String what=name.get_slicec('/',2); if (what=="bounds") r_ret= area_get_bounds(which); else if (what=="name") @@ -393,8 +393,12 @@ void GridMap::set_cell_item(int p_x,int p_y,int p_z, int p_item,int p_rot){ if (g.items.empty()) { PhysicsServer::get_singleton()->free(g.static_body); + if (g.collision_debug.is_valid()) { + PhysicsServer::get_singleton()->free(g.collision_debug); + PhysicsServer::get_singleton()->free(g.collision_debug_instance); + } - memdelete(&g); + memdelete(&g); octant_map.erase(octantkey); } else { @@ -422,6 +426,20 @@ void GridMap::set_cell_item(int p_x,int p_y,int p_z, int p_item,int p_rot){ if (is_inside_world()) PhysicsServer::get_singleton()->body_set_space(g->static_body,get_world()->get_space()); + SceneTree *st=SceneTree::get_singleton(); + + if (st && st->is_debugging_collisions_hint()) { + + g->collision_debug=VisualServer::get_singleton()->mesh_create(); + g->collision_debug_instance=VisualServer::get_singleton()->instance_create(); + VisualServer::get_singleton()->instance_set_base(g->collision_debug_instance,g->collision_debug); + if (is_inside_world()) { + VisualServer::get_singleton()->instance_set_scenario(g->collision_debug_instance,get_world()->get_scenario()); + VisualServer::get_singleton()->instance_set_transform(g->collision_debug_instance,get_global_transform()); + } + + } + octant_map[octantkey]=g; } @@ -512,6 +530,13 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { //print_line("BODYPOS: "+get_global_transform()); + if (g.collision_debug_instance.is_valid()) { + VS::get_singleton()->instance_set_scenario(g.collision_debug_instance,get_world()->get_scenario()); + VS::get_singleton()->instance_set_transform(g.collision_debug_instance,get_global_transform()); + if (area_map.has(p_key.area)) { + VS::get_singleton()->instance_set_room(g.collision_debug_instance,area_map[p_key.area]->instance); + } + } if (g.baked.is_valid()) { Transform xf = get_global_transform(); @@ -545,6 +570,10 @@ void GridMap::_octant_transform(const OctantKey &p_key) { Octant&g = *octant_map[p_key]; PhysicsServer::get_singleton()->body_set_state(g.static_body,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); + if (g.collision_debug_instance.is_valid()) { + VS::get_singleton()->instance_set_transform(g.collision_debug_instance,get_global_transform()); + } + if (g.baked.is_valid()) { Transform xf = get_global_transform(); @@ -572,6 +601,13 @@ void GridMap::_octant_update(const OctantKey &p_key) { PhysicsServer::get_singleton()->body_clear_shapes(g.static_body); + if (g.collision_debug.is_valid()) { + + VS::get_singleton()->mesh_clear(g.collision_debug); + } + + DVector<Vector3> col_debug; + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { Octant::ItemInstances &ii=E->get(); @@ -609,6 +645,7 @@ void GridMap::_octant_update(const OctantKey &p_key) { xform.basis.scale(Vector3(cell_scale,cell_scale,cell_scale)); ii.multimesh->set_instance_transform(idx,xform); + //ii.multimesh->set_instance_transform(idx,Transform() ); ii.multimesh->set_instance_color(idx,Color(1,1,1,1)); //print_line("MMINST: "+xform); @@ -624,8 +661,11 @@ void GridMap::_octant_update(const OctantKey &p_key) { if (ii.shape.is_valid()) { PhysicsServer::get_singleton()->body_add_shape(g.static_body,ii.shape->get_rid(),xform); - // print_line("PHIS x: "+xform); + if (g.collision_debug.is_valid()) { + ii.shape->add_vertices_to_array(col_debug,xform); + } + // print_line("PHIS x: "+xform); } idx++; @@ -636,6 +676,20 @@ void GridMap::_octant_update(const OctantKey &p_key) { } + if (col_debug.size()) { + + + Array arr; + arr.resize(VS::ARRAY_MAX); + arr[VS::ARRAY_VERTEX]=col_debug; + + VS::get_singleton()->mesh_add_surface(g.collision_debug,VS::PRIMITIVE_LINES,arr); + SceneTree *st=SceneTree::get_singleton(); + if (st) { + VS::get_singleton()->mesh_surface_set_material( g.collision_debug, 0,st->get_debug_collision_material()->get_rid() ); + } + } + g.dirty=false; } @@ -656,6 +710,12 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) { } + if (g.collision_debug_instance.is_valid()) { + + VS::get_singleton()->instance_set_room(g.collision_debug_instance,RID()); + VS::get_singleton()->instance_set_scenario(g.collision_debug_instance,RID()); + } + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { VS::get_singleton()->instance_set_scenario(E->get().multimesh_instance,RID()); @@ -959,6 +1019,11 @@ void GridMap::_clear_internal(bool p_keep_areas) { if (E->get()->bake_instance.is_valid()) VS::get_singleton()->free(E->get()->bake_instance); + if (E->get()->collision_debug.is_valid()) + VS::get_singleton()->free(E->get()->collision_debug); + if (E->get()->collision_debug_instance.is_valid()) + VS::get_singleton()->free(E->get()->collision_debug_instance); + PhysicsServer::get_singleton()->free(E->get()->static_body); memdelete(E->get()); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index dff9fe3d68..9d3b1dcf95 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -93,6 +93,8 @@ class GridMap : public Spatial { Ref<Mesh> baked; RID bake_instance; + RID collision_debug; + RID collision_debug_instance; bool dirty; RID static_body; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 39f83b806a..e4559ca100 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -61,7 +61,7 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CONFIGURE: { - + } break; case MENU_OPTION_LOCK_VIEW: { @@ -220,7 +220,9 @@ void GridMapEditor::_menu_option(int p_option) { } break; - + case MENU_OPTION_GRIDMAP_SETTINGS: { + settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); + } break; } } @@ -304,7 +306,7 @@ bool GridMapEditor::do_input_action(Camera* p_camera,const Point2& p_point,bool p.d=edit_floor[edit_axis]*node->get_cell_size(); Vector3 inters; - if (!p.intersects_segment(from,from+normal*500,&inters)) + if (!p.intersects_segment(from, from + normal * settings_pick_distance->get_val(), &inters)) return false; @@ -522,7 +524,9 @@ void GridMapEditor::_duplicate_paste() { } bool GridMapEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { - + if (!node) { + return false; + } if (edit_mode->get_selected()==0) { // regular click switch (p_event.type) { @@ -706,9 +710,40 @@ struct _CGMEItemSort { }; +void GridMapEditor::_set_display_mode(int p_mode) { + if (display_mode==p_mode) { + return; + } + + if (p_mode == DISPLAY_LIST) { + mode_list->set_pressed(true); + mode_thumbnail->set_pressed(false); + } else if (p_mode == DISPLAY_THUMBNAIL) { + mode_list->set_pressed(false); + mode_thumbnail->set_pressed(true); + } + + display_mode=p_mode; + + update_pallete(); +} + void GridMapEditor::update_pallete() { + int selected = theme_pallete->get_current(); theme_pallete->clear(); + if (display_mode == DISPLAY_THUMBNAIL) { + theme_pallete->set_max_columns(0); + theme_pallete->set_icon_mode(ItemList::ICON_MODE_TOP); + } else if (display_mode == DISPLAY_LIST){ + theme_pallete->set_max_columns(1); + theme_pallete->set_icon_mode(ItemList::ICON_MODE_LEFT); + } + + float min_size = EDITOR_DEF("grid_map/preview_size",64); + theme_pallete->set_min_icon_size(Size2(min_size, min_size)); + theme_pallete->set_fixed_column_width(min_size*3/2); + theme_pallete->set_max_text_lines(2); Ref<MeshLibrary> theme = node->get_theme(); @@ -720,10 +755,6 @@ void GridMapEditor::update_pallete() { Vector<int> ids; ids = theme->get_item_list(); - TreeItem *root = theme_pallete->create_item(NULL); - theme_pallete->set_hide_root(true); - TreeItem *selected=NULL; - List<_CGMEItemSort> il; for(int i=0;i<ids.size();i++) { @@ -734,45 +765,31 @@ void GridMapEditor::update_pallete() { } il.sort(); - int col=0; - TreeItem *ti=NULL; - int selected_col=0; + int item = 0; for(List<_CGMEItemSort>::Element *E=il.front();E;E=E->next()) { - int id = E->get().id; - if (col==0) { - ti = theme_pallete->create_item(root); - } + theme_pallete->add_item(""); String name=theme->get_item_name(id); Ref<Texture> preview = theme->get_item_preview(id); if (!preview.is_null()) { - - ti->set_cell_mode(col,TreeItem::CELL_MODE_ICON); - ti->set_icon(col,preview); - ti->set_tooltip(col,name); - } else { - - ti->set_text(col,name); + theme_pallete->set_item_icon(item, preview); + theme_pallete->set_item_tooltip(item, name); } - ti->set_metadata(col,id); - - if (selected_pallete==id) { - selected=ti; - selected_col=col; + if (name!="") { + theme_pallete->set_item_text(item,name); } + theme_pallete->set_item_metadata(item, id); - col++; - if (col==theme_pallete->get_columns()) - col=0; - + item++; } - if (selected) - selected->select(selected_col); + if (selected!=-1) { + theme_pallete->select(selected); + } last_theme=theme.operator->(); } @@ -842,6 +859,9 @@ void GridMapEditor::edit(GridMap *p_gridmap) { VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,false); } + + VisualServer::get_singleton()->instance_geometry_set_flag(cursor_instance, VS::INSTANCE_FLAG_VISIBLE,false); + _clear_areas(); return; @@ -951,7 +971,7 @@ void GridMapEditor::update_grid() { grid_xform.origin.x-=1; //force update in hackish way.. what do i care - VS *vs = VS::get_singleton(); + //VS *vs = VS::get_singleton(); grid_ofs[edit_axis]=edit_floor[edit_axis]*node->get_cell_size(); @@ -976,7 +996,7 @@ void GridMapEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { - theme_pallete->connect("cell_selected", this,"_item_selected_cbk"); + theme_pallete->connect("item_selected", this,"_item_selected_cbk"); edit_mode->connect("item_selected", this,"_edit_mode_changed"); area_list->connect("item_edited", this,"_area_renamed"); area_list->connect("item_selected", this,"_area_selected"); @@ -1014,7 +1034,7 @@ void GridMapEditor::_notification(int p_what) { if (xf!=grid_xform) { for(int i=0;i<3;i++) { - + VS::get_singleton()->instance_set_transform(grid_instance[i],xf * edit_grid_xform); } grid_xform=xf; @@ -1043,7 +1063,9 @@ void GridMapEditor::_notification(int p_what) { } void GridMapEditor::_update_cursor_instance() { - + if (!node) { + return; + } if (cursor_instance.is_valid()) VisualServer::get_singleton()->free(cursor_instance); @@ -1063,18 +1085,8 @@ void GridMapEditor::_update_cursor_instance() { } -void GridMapEditor::_item_selected_cbk() { - - TreeItem *it = theme_pallete->get_selected(); - if (it) { - - selected_pallete=it->get_metadata(theme_pallete->get_selected_column()); - - } else { - - selected_pallete=-1; - - } +void GridMapEditor::_item_selected_cbk(int idx) { + selected_pallete=theme_pallete->get_item_metadata(idx); _update_cursor_instance(); @@ -1092,7 +1104,9 @@ void GridMapEditor::_clear_areas() { } void GridMapEditor::_update_areas_display() { - + if (!node) { + return; + } _clear_areas(); List<int> areas; @@ -1179,7 +1193,7 @@ void GridMapEditor::_bind_methods() { ObjectTypeDB::bind_method("_area_selected",&GridMapEditor::_area_selected); ObjectTypeDB::bind_method("_floor_changed",&GridMapEditor::_floor_changed); - + ObjectTypeDB::bind_method(_MD("_set_display_mode","mode"), &GridMapEditor::_set_display_mode); } @@ -1237,9 +1251,30 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { //options->get_popup()->add_separator(); //options->get_popup()->add_item("Configure",MENU_OPTION_CONFIGURE); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Settings", MENU_OPTION_GRIDMAP_SETTINGS); + + settings_dialog = memnew(ConfirmationDialog); + settings_dialog->set_title("GridMap Settings"); + add_child(settings_dialog); + settings_vbc = memnew(VBoxContainer); + settings_vbc->set_custom_minimum_size(Size2(200, 0)); + settings_dialog->add_child(settings_vbc); + settings_dialog->set_child_rect(settings_vbc); + + settings_pick_distance = memnew(SpinBox); + settings_pick_distance->set_max(10000.0f); + settings_pick_distance->set_min(500.0f); + settings_pick_distance->set_step(1.0f); + settings_pick_distance->set_val(EDITOR_DEF("gridmap_editor/pick_distance", 5000.0)); + settings_vbc->add_margin_child("Pick Distance:", settings_pick_distance); + clip_mode=CLIP_DISABLED; options->get_popup()->connect("item_pressed", this,"_menu_option"); + HBoxContainer *hb = memnew( HBoxContainer ); + add_child(hb); + hb->set_h_size_flags(SIZE_EXPAND_FILL); edit_mode = memnew(OptionButton); edit_mode->set_area_as_parent_rect(); @@ -1247,13 +1282,27 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { edit_mode->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,14);; edit_mode->add_item("Tiles"); edit_mode->add_item("Areas"); - add_child(edit_mode); - + hb->add_child(edit_mode); + edit_mode->set_h_size_flags(SIZE_EXPAND_FILL); + + mode_thumbnail = memnew( ToolButton ); + mode_thumbnail->set_toggle_mode(true); + mode_thumbnail->set_pressed(true); + mode_thumbnail->set_icon(p_editor->get_gui_base()->get_icon("FileThumbnail","EditorIcons")); + hb->add_child(mode_thumbnail); + mode_thumbnail->connect("pressed", this, "_set_display_mode", varray(DISPLAY_THUMBNAIL)); + + mode_list = memnew( ToolButton ); + mode_list->set_toggle_mode(true); + mode_list->set_pressed(false); + mode_list->set_icon(p_editor->get_gui_base()->get_icon("FileList", "EditorIcons")); + hb->add_child(mode_list); + mode_list->connect("pressed", this, "_set_display_mode", varray(DISPLAY_LIST)); + + display_mode = DISPLAY_THUMBNAIL; selected_area=-1; - - theme_pallete = memnew( Tree ); - theme_pallete->set_columns(3); + theme_pallete = memnew( ItemList ); add_child(theme_pallete); theme_pallete->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 716ef66f0d..03b2d4226e 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -40,10 +40,8 @@ class SpatialEditorPlugin; class GridMapEditor : public VBoxContainer { - OBJ_TYPE(GridMapEditor, VBoxContainer ); - enum { GRID_CURSOR_SIZE=50 @@ -66,6 +64,10 @@ class GridMapEditor : public VBoxContainer { CLIP_BELOW }; + enum DisplayMode { + DISPLAY_THUMBNAIL, + DISPLAY_LIST + }; UndoRedo *undo_redo; InputAction input_action; @@ -73,7 +75,12 @@ class GridMapEditor : public VBoxContainer { MenuButton * options; SpinBox *floor; OptionButton *edit_mode; + ToolButton *mode_thumbnail; + ToolButton *mode_list; HBoxContainer *spatial_editor_hb; + ConfirmationDialog *settings_dialog; + VBoxContainer *settings_vbc; + SpinBox *settings_pick_distance; struct SetItem { @@ -132,6 +139,7 @@ class GridMapEditor : public VBoxContainer { Vector3 cursor_origin; Vector3 last_mouseover; + int display_mode; int selected_pallete; int selected_area; int cursor_rot; @@ -160,8 +168,8 @@ class GridMapEditor : public VBoxContainer { MENU_OPTION_SELECTION_MAKE_AREA, MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR, MENU_OPTION_SELECTION_CLEAR, - MENU_OPTION_REMOVE_AREA - + MENU_OPTION_REMOVE_AREA, + MENU_OPTION_GRIDMAP_SETTINGS }; @@ -183,9 +191,10 @@ class GridMapEditor : public VBoxContainer { void _configure(); void _menu_option(int); void update_pallete(); - Tree *theme_pallete; + void _set_display_mode(int p_mode); + ItemList *theme_pallete; Tree *area_list; - void _item_selected_cbk(); + void _item_selected_cbk(int idx); void _update_cursor_transform(); void _update_cursor_instance(); void _update_clip(); |