Browse Source

-Display on animation editor which keys are invalid and which tracks are unresolved
-Added a tool to clean up unresolved tracks and unused keys

Juan Linietsky 9 years ago
parent
commit
200b7bb87c

+ 5 - 0
core/error_macros.h

@@ -207,6 +207,11 @@ extern bool _err_error_exists;
 		_err_error_exists=false;\
 		_err_error_exists=false;\
 	} \
 	} \
 
 
+#define ERR_PRINTS(m_string) \
+	{ \
+		_err_print_error(FUNCTION_STR,__FILE__,__LINE__,String(m_string).utf8().get_data());	\
+		_err_error_exists=false;\
+	} \
 
 
 /** Print a warning string.
 /** Print a warning string.
  */
  */

+ 24 - 3
core/object.cpp

@@ -314,6 +314,7 @@ void Object::set(const StringName& p_name, const Variant& p_value, bool *r_valid
 
 
 	_edited=true;
 	_edited=true;
 #endif
 #endif
+
 	if (script_instance) {
 	if (script_instance) {
 
 
 		if (script_instance->set(p_name,p_value)) {
 		if (script_instance->set(p_name,p_value)) {
@@ -326,9 +327,9 @@ void Object::set(const StringName& p_name, const Variant& p_value, bool *r_valid
 
 
 	//try built-in setgetter
 	//try built-in setgetter
 	{
 	{
-		if (ObjectTypeDB::set_property(this,p_name,p_value)) {
-			if (r_valid)
-				*r_valid=true;
+		if (ObjectTypeDB::set_property(this,p_name,p_value,r_valid)) {
+			//if (r_valid)
+			//	*r_valid=true;
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -1694,6 +1695,26 @@ void Object::get_translatable_strings(List<String> *p_strings) const {
 
 
 }
 }
 
 
+Variant::Type Object::get_static_property_type(const StringName& p_property, bool *r_valid) const {
+
+	bool valid;
+	Variant::Type t = ObjectTypeDB::get_property_type(get_type_name(),p_property,&valid);
+	if (valid) {
+		if (r_valid)
+			*r_valid=true;
+		return t;
+	}
+
+	if (get_script_instance()) {
+		return get_script_instance()->get_property_type(p_property,r_valid);
+	}
+	if (r_valid)
+		*r_valid=false;
+
+	return Variant::NIL;
+
+}
+
 bool Object::is_queued_for_deletion() const {
 bool Object::is_queued_for_deletion() const {
 	return _is_queued_for_deletion;
 	return _is_queued_for_deletion;
 }
 }

+ 2 - 0
core/object.h

@@ -606,6 +606,8 @@ public:
 	void set_block_signals(bool p_block);
 	void set_block_signals(bool p_block);
 	bool is_blocking_signals() const;
 	bool is_blocking_signals() const;
 
 
+	Variant::Type get_static_property_type(const StringName& p_property,bool *r_valid=NULL) const;
+
 	virtual void get_translatable_strings(List<String> *p_strings) const;
 	virtual void get_translatable_strings(List<String> *p_strings) const;
 
 
 	virtual void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
 	virtual void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;

+ 35 - 4
core/object_type_db.cpp

@@ -612,6 +612,7 @@ void ObjectTypeDB::add_property(StringName p_type,const PropertyInfo& p_pinfo, c
 	psg._setptr=mb_set;
 	psg._setptr=mb_set;
 	psg._getptr=mb_get;
 	psg._getptr=mb_get;
 	psg.index=p_index;
 	psg.index=p_index;
+	psg.type=p_pinfo.type;
 
 
 	type->property_setget[p_pinfo.name]=psg;
 	type->property_setget[p_pinfo.name]=psg;
 
 
@@ -634,7 +635,7 @@ void ObjectTypeDB::get_property_list(StringName p_type,List<PropertyInfo> *p_lis
 	}
 	}
 
 
 }
 }
-bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, const Variant& p_value) {
+bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, const Variant& p_value,bool *r_valid) {
 
 
 
 
 	TypeInfo *type=types.getptr(p_object->get_type_name());
 	TypeInfo *type=types.getptr(p_object->get_type_name());
@@ -643,13 +644,17 @@ bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, c
 		const PropertySetGet *psg = check->property_setget.getptr(p_property);
 		const PropertySetGet *psg = check->property_setget.getptr(p_property);
 		if (psg) {
 		if (psg) {
 
 
-			if (!psg->setter)
+			if (!psg->setter) {
+				if (r_valid)
+					*r_valid=false;
 				return true; //return true but do nothing
 				return true; //return true but do nothing
+			}
+
+			Variant::CallError ce;
 
 
 			if (psg->index>=0) {
 			if (psg->index>=0) {
 				Variant index=psg->index;
 				Variant index=psg->index;
 				const Variant* arg[2]={&index,&p_value};
 				const Variant* arg[2]={&index,&p_value};
-				Variant::CallError ce;
 //				p_object->call(psg->setter,arg,2,ce);
 //				p_object->call(psg->setter,arg,2,ce);
 				if (psg->_setptr) {
 				if (psg->_setptr) {
 					psg->_setptr->call(p_object,arg,2,ce);
 					psg->_setptr->call(p_object,arg,2,ce);
@@ -660,13 +665,16 @@ bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, c
 
 
 			} else {
 			} else {
 				const Variant* arg[1]={&p_value};
 				const Variant* arg[1]={&p_value};
-				Variant::CallError ce;
 				if (psg->_setptr) {
 				if (psg->_setptr) {
 					psg->_setptr->call(p_object,arg,1,ce);
 					psg->_setptr->call(p_object,arg,1,ce);
 				} else {
 				} else {
 					p_object->call(psg->setter,arg,1,ce);
 					p_object->call(psg->setter,arg,1,ce);
 				}
 				}
 			}
 			}
+
+			if (r_valid)
+				*r_valid=ce.error==Variant::CallError::CALL_OK;
+
 			return true;
 			return true;
 		}
 		}
 
 
@@ -718,6 +726,29 @@ bool ObjectTypeDB::get_property(Object* p_object,const StringName& p_property, V
 	return false;
 	return false;
 }
 }
 
 
+Variant::Type ObjectTypeDB::get_property_type(const StringName& p_type, const StringName& p_property,bool *r_is_valid) {
+
+	TypeInfo *type=types.getptr(p_type);
+	TypeInfo *check=type;
+	while(check) {
+		const PropertySetGet *psg = check->property_setget.getptr(p_property);
+		if (psg) {
+
+			if (r_is_valid)
+				*r_is_valid=true;
+
+			return psg->type;
+		}
+
+		check=check->inherits_ptr;
+	}
+	if (r_is_valid)
+		*r_is_valid=false;
+
+	return Variant::NIL;
+
+}
+
 
 
 void ObjectTypeDB::set_method_flags(StringName p_type,StringName p_method,int p_flags) {
 void ObjectTypeDB::set_method_flags(StringName p_type,StringName p_method,int p_flags) {
 
 

+ 3 - 1
core/object_type_db.h

@@ -117,6 +117,7 @@ class ObjectTypeDB {
 		StringName getter;
 		StringName getter;
 		MethodBind *_setptr;
 		MethodBind *_setptr;
 		MethodBind *_getptr;
 		MethodBind *_getptr;
+		Variant::Type type;
 	};
 	};
 
 
 	struct TypeInfo {
 	struct TypeInfo {
@@ -456,8 +457,9 @@ public:
 
 
 	static void add_property(StringName p_type,const PropertyInfo& p_pinfo, const StringName& p_setter, const StringName& p_getter, int p_index=-1);
 	static void add_property(StringName p_type,const PropertyInfo& p_pinfo, const StringName& p_setter, const StringName& p_getter, int p_index=-1);
 	static void get_property_list(StringName p_type,List<PropertyInfo> *p_list,bool p_no_inheritance=false);
 	static void get_property_list(StringName p_type,List<PropertyInfo> *p_list,bool p_no_inheritance=false);
-	static bool set_property(Object* p_object,const StringName& p_property, const Variant& p_value);
+	static bool set_property(Object* p_object, const StringName& p_property, const Variant& p_value, bool *r_valid=NULL);
 	static bool get_property(Object* p_object,const StringName& p_property, Variant& r_value);
 	static bool get_property(Object* p_object,const StringName& p_property, Variant& r_value);
+	static Variant::Type get_property_type(const StringName& p_type, const StringName& p_property,bool *r_is_valid=NULL);
 
 
 
 
 
 

+ 14 - 0
core/script_language.cpp

@@ -267,6 +267,20 @@ void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properti
 	}
 	}
 }
 }
 
 
+Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const {
+
+	if (values.has(p_name))	{
+		if (r_is_valid)
+			*r_is_valid=true;
+		return values[p_name].get_type();
+	}
+	if (r_is_valid)
+		*r_is_valid=false;
+
+	return Variant::NIL;
+}
+
+
 void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties,const Map<StringName,Variant>& p_values) {
 void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties,const Map<StringName,Variant>& p_values) {
 
 
 
 

+ 3 - 0
core/script_language.h

@@ -82,6 +82,7 @@ public:
 	virtual StringName get_instance_base_type() const=0; // this may not work in all scripts, will return empty if so
 	virtual StringName get_instance_base_type() const=0; // this may not work in all scripts, will return empty if so
 	virtual ScriptInstance* instance_create(Object *p_this)=0;
 	virtual ScriptInstance* instance_create(Object *p_this)=0;
 	virtual bool instance_has(const Object *p_this) const=0;
 	virtual bool instance_has(const Object *p_this) const=0;
+
 	
 	
 	virtual bool has_source_code() const=0;
 	virtual bool has_source_code() const=0;
 	virtual String get_source_code() const=0;
 	virtual String get_source_code() const=0;
@@ -109,6 +110,7 @@ public:
 	virtual bool set(const StringName& p_name, const Variant& p_value)=0;
 	virtual bool set(const StringName& p_name, const Variant& p_value)=0;
 	virtual bool get(const StringName& p_name, Variant &r_ret) const=0;
 	virtual bool get(const StringName& p_name, Variant &r_ret) const=0;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const=0;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const=0;
+	virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const=0;
 
 
 	virtual void get_method_list(List<MethodInfo> *p_list) const=0;
 	virtual void get_method_list(List<MethodInfo> *p_list) const=0;
 	virtual bool has_method(const StringName& p_method) const=0;
 	virtual bool has_method(const StringName& p_method) const=0;
@@ -208,6 +210,7 @@ public:
 	virtual bool set(const StringName& p_name, const Variant& p_value);
 	virtual bool set(const StringName& p_name, const Variant& p_value);
 	virtual bool get(const StringName& p_name, Variant &r_ret) const;
 	virtual bool get(const StringName& p_name, Variant &r_ret) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+	virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const;
 
 
 	virtual void get_method_list(List<MethodInfo> *p_list) const {}
 	virtual void get_method_list(List<MethodInfo> *p_list) const {}
 	virtual bool has_method(const StringName& p_method) const { return false; }
 	virtual bool has_method(const StringName& p_method) const { return false; }

+ 1 - 1
core/variant.h

@@ -390,7 +390,7 @@ public:
 
 
 	Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,CallError &r_error);
 	Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,CallError &r_error);
 	Variant call(const StringName& p_method,const Variant& p_arg1=Variant(),const Variant& p_arg2=Variant(),const Variant& p_arg3=Variant(),const Variant& p_arg4=Variant(),const Variant& p_arg5=Variant());
 	Variant call(const StringName& p_method,const Variant& p_arg1=Variant(),const Variant& p_arg2=Variant(),const Variant& p_arg3=Variant(),const Variant& p_arg4=Variant(),const Variant& p_arg5=Variant());
-	static Variant construct(const Variant::Type,const Variant** p_args,int p_argcount,CallError &r_error);
+	static Variant construct(const Variant::Type,const Variant** p_args,int p_argcount,CallError &r_error,bool p_strict=true);
 
 
 	void get_method_list(List<MethodInfo> *p_list) const;
 	void get_method_list(List<MethodInfo> *p_list) const;
 	bool has_method(const StringName& p_method) const;
 	bool has_method(const StringName& p_method) const;

+ 2 - 2
core/variant_call.cpp

@@ -959,7 +959,7 @@ Variant Variant::call(const StringName& p_method,const Variant** p_args,int p_ar
 #define VCALL(m_type,m_method) _VariantCall::_call_##m_type##_##m_method
 #define VCALL(m_type,m_method) _VariantCall::_call_##m_type##_##m_method
 
 
 
 
-Variant Variant::construct(const Variant::Type p_type,const Variant** p_args,int p_argcount,CallError &r_error) {
+Variant Variant::construct(const Variant::Type p_type, const Variant** p_args, int p_argcount, CallError &r_error, bool p_strict) {
 
 
 	r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
 	r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
 	ERR_FAIL_INDEX_V(p_type,VARIANT_MAX,Variant());
 	ERR_FAIL_INDEX_V(p_type,VARIANT_MAX,Variant());
@@ -1035,7 +1035,7 @@ Variant Variant::construct(const Variant::Type p_type,const Variant** p_args,int
 
 
 	} else if (p_argcount==1 && p_args[0]->type==p_type) {
 	} else if (p_argcount==1 && p_args[0]->type==p_type) {
 		return *p_args[0]; //copy construct
 		return *p_args[0]; //copy construct
-	} else if (p_argcount==1 && Variant::can_convert(p_args[0]->type,p_type)) {
+	} else if (p_argcount==1 && (!p_strict || Variant::can_convert(p_args[0]->type,p_type))) {
 		//near match construct
 		//near match construct
 
 
 		switch(p_type) {
 		switch(p_type) {

+ 20 - 0
modules/gdscript/gd_script.cpp

@@ -2274,6 +2274,26 @@ bool GDInstance::get(const StringName& p_name, Variant &r_ret) const {
 	return false;
 	return false;
 
 
 }
 }
+
+Variant::Type GDInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const {
+
+
+	const GDScript *sptr=script.ptr();
+	while(sptr) {
+
+		if (sptr->member_info.has(p_name)) {
+			if (r_is_valid)
+				*r_is_valid=true;
+			return sptr->member_info[p_name].type;
+		}
+		sptr = sptr->_base;
+	}
+
+	if (r_is_valid)
+		*r_is_valid=false;
+	return Variant::NIL;
+}
+
 void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 	// exported members, not doen yet!
 	// exported members, not doen yet!
 
 

+ 2 - 0
modules/gdscript/gd_script.h

@@ -373,6 +373,8 @@ public:
 	virtual bool set(const StringName& p_name, const Variant& p_value);
 	virtual bool set(const StringName& p_name, const Variant& p_value);
 	virtual bool get(const StringName& p_name, Variant &r_ret) const;
 	virtual bool get(const StringName& p_name, Variant &r_ret) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+	virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const;
+
 
 
 	virtual void get_method_list(List<MethodInfo> *p_list) const;
 	virtual void get_method_list(List<MethodInfo> *p_list) const;
 	virtual bool has_method(const StringName& p_method) const;
 	virtual bool has_method(const StringName& p_method) const;

+ 81 - 6
scene/animation/animation_player.cpp

@@ -268,6 +268,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
 		TrackNodeCacheKey key;
 		TrackNodeCacheKey key;
 		key.id=id;
 		key.id=id;
 		key.bone_idx=bone_idx;
 		key.bone_idx=bone_idx;
+
 		
 		
 		if (node_cache_map.has(key)) {
 		if (node_cache_map.has(key)) {
 		
 		
@@ -278,6 +279,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
 			node_cache_map[key]=TrackNodeCache();
 			node_cache_map[key]=TrackNodeCache();
 			
 			
 			p_anim->node_cache[i]=&node_cache_map[key];
 			p_anim->node_cache[i]=&node_cache_map[key];
+			p_anim->node_cache[i]->path=a->track_get_path(i);
 			p_anim->node_cache[i]->node=child;
 			p_anim->node_cache[i]->node=child;
 			p_anim->node_cache[i]->resource=resource;
 			p_anim->node_cache[i]->resource=resource;
 			p_anim->node_cache[i]->node_2d=child->cast_to<Node2D>();
 			p_anim->node_cache[i]->node_2d=child->cast_to<Node2D>();
@@ -320,6 +322,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
 				pa.prop=property;
 				pa.prop=property;
 				pa.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child;
 				pa.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child;
 				pa.special=SP_NONE;
 				pa.special=SP_NONE;
+				pa.owner=p_anim->node_cache[i];
 				if (false && p_anim->node_cache[i]->node_2d) {
 				if (false && p_anim->node_cache[i]->node_2d) {
 
 
 					if (pa.prop==SceneStringNames::get_singleton()->transform_pos)
 					if (pa.prop==SceneStringNames::get_singleton()->transform_pos)
@@ -410,7 +413,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData* p_anim,float p
 				TrackNodeCache::PropertyAnim *pa = &E->get();
 				TrackNodeCache::PropertyAnim *pa = &E->get();
 
 
 
 
-				if (a->value_track_is_continuous(i) || p_delta==0) {
+				if (a->value_track_is_continuous(i) || p_delta==0) { //delta == 0 means seek
 
 
 
 
 					Variant value=a->value_track_interpolate(i,p_time);
 					Variant value=a->value_track_interpolate(i,p_time);
@@ -436,10 +439,42 @@ void AnimationPlayer::_animation_process_animation(AnimationData* p_anim,float p
 						Variant value=a->track_get_key_value(i,F->get());
 						Variant value=a->track_get_key_value(i,F->get());
 						switch(pa->special) {
 						switch(pa->special) {
 
 
-							case SP_NONE: pa->object->set(pa->prop,value); break; //you are not speshul
-							case SP_NODE2D_POS: static_cast<Node2D*>(pa->object)->set_pos(value); break;
-							case SP_NODE2D_ROT: static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(value)); break;
-							case SP_NODE2D_SCALE: static_cast<Node2D*>(pa->object)->set_scale(value); break;
+							case SP_NONE: {
+								bool valid;
+								pa->object->set(pa->prop,value,&valid);  //you are not speshul
+#ifdef DEBUG_ENABLED
+								if (!valid) {
+									ERR_PRINTS("Failed setting track value '"+String(pa->owner->path)+"'. Check if property exists or the type of key is valid");
+								}
+#endif
+
+							} break;
+							case SP_NODE2D_POS: {
+#ifdef DEBUG_ENABLED
+								if (value.get_type()!=Variant::VECTOR2) {
+									ERR_PRINTS("Position key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not of type Vector2()");
+								}
+#endif
+								static_cast<Node2D*>(pa->object)->set_pos(value);
+							} break;
+							case SP_NODE2D_ROT: {
+#ifdef DEBUG_ENABLED
+								if (value.is_num()) {
+									ERR_PRINTS("Rotation key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not numerical");
+								}
+#endif
+
+								static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(value));
+							} break;
+							case SP_NODE2D_SCALE: {
+#ifdef DEBUG_ENABLED
+								if (value.get_type()!=Variant::VECTOR2) {
+									ERR_PRINTS("Scale key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not of type Vector2()");
+								}
+#endif
+
+								static_cast<Node2D*>(pa->object)->set_scale(value);
+							} break;
 						}
 						}
 
 
 					}
 					}
@@ -607,13 +642,53 @@ void AnimationPlayer::_animation_update_transforms() {
 		ERR_CONTINUE( pa->accum_pass!=accum_pass );
 		ERR_CONTINUE( pa->accum_pass!=accum_pass );
 
 
 #if 1
 #if 1
-		switch(pa->special) {
+/*		switch(pa->special) {
 
 
 
 
 			case SP_NONE: pa->object->set(pa->prop,pa->value_accum); break; //you are not speshul
 			case SP_NONE: pa->object->set(pa->prop,pa->value_accum); break; //you are not speshul
 			case SP_NODE2D_POS: static_cast<Node2D*>(pa->object)->set_pos(pa->value_accum); break;
 			case SP_NODE2D_POS: static_cast<Node2D*>(pa->object)->set_pos(pa->value_accum); break;
 			case SP_NODE2D_ROT: static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(pa->value_accum)); break;
 			case SP_NODE2D_ROT: static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(pa->value_accum)); break;
 			case SP_NODE2D_SCALE: static_cast<Node2D*>(pa->object)->set_scale(pa->value_accum); break;
 			case SP_NODE2D_SCALE: static_cast<Node2D*>(pa->object)->set_scale(pa->value_accum); break;
+		}*/
+
+		switch(pa->special) {
+
+			case SP_NONE: {
+				bool valid;
+				pa->object->set(pa->prop,pa->value_accum,&valid);  //you are not speshul
+#ifdef DEBUG_ENABLED
+				if (!valid) {
+					ERR_PRINTS("Failed setting key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"'. Check if property exists or the type of key is right for the property");
+				}
+#endif
+
+			} break;
+			case SP_NODE2D_POS: {
+#ifdef DEBUG_ENABLED
+				if (pa->value_accum.get_type()!=Variant::VECTOR2) {
+					ERR_PRINTS("Position key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not of type Vector2()");
+				}
+#endif
+				static_cast<Node2D*>(pa->object)->set_pos(pa->value_accum);
+			} break;
+			case SP_NODE2D_ROT: {
+#ifdef DEBUG_ENABLED
+				if (pa->value_accum.is_num()) {
+					ERR_PRINTS("Rotation key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not numerical");
+				}
+#endif
+
+				static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(pa->value_accum));
+			} break;
+			case SP_NODE2D_SCALE: {
+#ifdef DEBUG_ENABLED
+				if (pa->value_accum.get_type()!=Variant::VECTOR2) {
+					ERR_PRINTS("Scale key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not of type Vector2()");
+				}
+#endif
+
+				static_cast<Node2D*>(pa->object)->set_scale(pa->value_accum);
+			} break;
 		}
 		}
 #else
 #else
 
 

+ 2 - 0
scene/animation/animation_player.h

@@ -68,6 +68,7 @@ private:
 
 
 	struct TrackNodeCache {
 	struct TrackNodeCache {
 
 
+		NodePath path;
 		uint32_t id;
 		uint32_t id;
 		RES resource;
 		RES resource;
 		Node *node;
 		Node *node;
@@ -84,6 +85,7 @@ private:
 
 
 		struct PropertyAnim {
 		struct PropertyAnim {
 
 
+			TrackNodeCache *owner;
 			SpecialProperty special; //small optimization
 			SpecialProperty special; //small optimization
 			StringName prop;
 			StringName prop;
 			Object *object;
 			Object *object;

+ 168 - 3
tools/editor/animation_editor.cpp

@@ -34,6 +34,7 @@
 #include "pair.h"
 #include "pair.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "editor_node.h"
 #include "editor_node.h"
+#include "tools/editor/plugins/animation_player_editor_plugin.h"
 /* Missing to fix:
 /* Missing to fix:
 
 
   *Set
   *Set
@@ -903,6 +904,23 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 
 			optimize_dialog->popup_centered(Size2(250,180));
 			optimize_dialog->popup_centered(Size2(250,180));
 		} break;
 		} break;
+		case TRACK_MENU_CLEAN_UP: {
+
+			cleanup_dialog->popup_centered_minsize(Size2(300,0));
+		} break;
+		case TRACK_MENU_CLEAN_UP_CONFIRM: {
+
+			if (cleanup_all->is_pressed()) {
+				List<StringName> names;
+				AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
+				for (List<StringName>::Element *E=names.front();E;E=E->next()) {
+					_cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E->get()));
+				}
+			} else {
+				_cleanup_animation(animation);
+
+			}
+		} break;
 		case CURVE_SET_LINEAR: {
 		case CURVE_SET_LINEAR: {
 			curve_edit->force_transition(1.0);
 			curve_edit->force_transition(1.0);
 
 
@@ -933,6 +951,57 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 
 }
 }
 
 
+void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) {
+
+
+	for(int i=0;i<p_animation->get_track_count();i++) {
+
+		bool prop_exists=false;
+		Variant::Type valid_type=Variant::NIL;
+		Object *obj=NULL;
+
+		RES res;
+		Node *node = root->get_node_and_resource(animation->track_get_path(i),res);
+
+		if (res.is_valid()) {
+			obj=res.ptr();
+		} else if (node) {
+			obj=node;
+		}
+
+		if (obj && animation->track_get_type(i)==Animation::TYPE_VALUE) {
+			valid_type=obj->get_static_property_type(animation->track_get_path(i).get_property(),&prop_exists);
+		}
+
+		if (!obj && cleanup_tracks->is_pressed()) {
+
+			animation->remove_track(i);
+			i--;
+			continue;
+		}
+
+		if (!prop_exists || animation->track_get_type(i)!=Animation::TYPE_VALUE || cleanup_keys->is_pressed()==false)
+			continue;
+
+		for(int j=0;j<animation->track_get_key_count(i);j++) {
+
+			Variant v = animation->track_get_key_value(i,j);
+
+			if (!Variant::can_convert(v.get_type(),valid_type)) {
+				animation->track_remove_key(i,j);
+				j--;
+			}
+		}
+
+		if (animation->track_get_key_count(i)==0 && cleanup_tracks->is_pressed()) {
+			animation->remove_track(i);
+			i--;
+		}
+	}
+
+	undo_redo->clear_history();
+	_update_paths();
+}
 
 
 void AnimationKeyEditor::_animation_optimize()  {
 void AnimationKeyEditor::_animation_optimize()  {
 
 
@@ -1042,6 +1111,7 @@ void AnimationKeyEditor::_track_editor_draw() {
 	timecolor = Color::html("ff4a414f");
 	timecolor = Color::html("ff4a414f");
 	Color hover_color = Color(1,1,1,0.05);
 	Color hover_color = Color(1,1,1,0.05);
 	Color select_color = Color(1,1,1,0.1);
 	Color select_color = Color(1,1,1,0.1);
+	Color invalid_path_color = Color(1,0.6,0.4,0.5);
 	Color track_select_color =Color::html("ffbd8e8e");
 	Color track_select_color =Color::html("ffbd8e8e");
 
 
 	Ref<Texture> remove_icon = get_icon("Remove","EditorIcons");
 	Ref<Texture> remove_icon = get_icon("Remove","EditorIcons");
@@ -1068,6 +1138,9 @@ void AnimationKeyEditor::_track_editor_draw() {
 		get_icon("KeyCall","EditorIcons")
 		get_icon("KeyCall","EditorIcons")
 	};
 	};
 
 
+	Ref<Texture> invalid_icon = get_icon("KeyInvalid","EditorIcons");
+	Ref<Texture> invalid_icon_hover = get_icon("KeyInvalidHover","EditorIcons");
+
 	Ref<Texture> hsize_icon = get_icon("Hsize","EditorIcons");
 	Ref<Texture> hsize_icon = get_icon("Hsize","EditorIcons");
 
 
 	Ref<Texture> type_hover=get_icon("KeyHover","EditorIcons");
 	Ref<Texture> type_hover=get_icon("KeyHover","EditorIcons");
@@ -1254,6 +1327,23 @@ void AnimationKeyEditor::_track_editor_draw() {
 			break;
 			break;
 		int y = h+i*h+sep;
 		int y = h+i*h+sep;
 
 
+		bool prop_exists=false;
+		Variant::Type valid_type=Variant::NIL;
+		Object *obj=NULL;
+
+		RES res;
+		Node *node = root->get_node_and_resource(animation->track_get_path(idx),res);
+
+		if (res.is_valid()) {
+			obj=res.ptr();
+		} else if (node) {
+			obj=node;
+		}
+
+		if (obj && animation->track_get_type(idx)==Animation::TYPE_VALUE) {
+			valid_type=obj->get_static_property_type(animation->track_get_path(idx).get_property(),&prop_exists);
+		}
+
 
 
 		if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx==mouse_over.track) {
 		if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx==mouse_over.track) {
 			Color sepc=hover_color;
 			Color sepc=hover_color;
@@ -1274,6 +1364,8 @@ void AnimationKeyEditor::_track_editor_draw() {
 			ncol=track_select_color;
 			ncol=track_select_color;
 		te->draw_string(font,Point2(ofs+Point2(type_icon[0]->get_width()+sep,y+font->get_ascent()+(sep/2))).floor(),np,ncol,name_limit-(type_icon[0]->get_width()+sep)-5);
 		te->draw_string(font,Point2(ofs+Point2(type_icon[0]->get_width()+sep,y+font->get_ascent()+(sep/2))).floor(),np,ncol,name_limit-(type_icon[0]->get_width()+sep)-5);
 
 
+		if (!obj)
+			te->draw_line(ofs+Point2(0,y+h/2),ofs+Point2(name_limit,y+h/2),invalid_path_color);
 
 
 		te->draw_line(ofs+Point2(0,y+h),ofs+Point2(size.width,y+h),sepcolor);
 		te->draw_line(ofs+Point2(0,y+h),ofs+Point2(size.width,y+h),sepcolor);
 
 
@@ -1339,6 +1431,8 @@ void AnimationKeyEditor::_track_editor_draw() {
 		int kc=animation->track_get_key_count(idx);
 		int kc=animation->track_get_key_count(idx);
 		bool first=true;
 		bool first=true;
 
 
+
+
 		for(int i=0;i<kc;i++) {
 		for(int i=0;i<kc;i++) {
 
 
 
 
@@ -1386,7 +1480,21 @@ void AnimationKeyEditor::_track_editor_draw() {
 
 
 			}
 			}
 
 
-			te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+			if (prop_exists && !Variant::can_convert(value.get_type(),valid_type)) {
+				te->draw_texture(invalid_icon,ofs+Point2(x,y+key_vofs).floor());
+			}
+
+			if (prop_exists && !Variant::can_convert(value.get_type(),valid_type)) {
+				if (tex==type_hover)
+					te->draw_texture(invalid_icon_hover,ofs+Point2(x,y+key_vofs).floor());
+				else
+					te->draw_texture(invalid_icon,ofs+Point2(x,y+key_vofs).floor());
+			} else {
+
+				te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+			}
+
+
 			first=false;
 			first=false;
 		}
 		}
 
 
@@ -2555,6 +2663,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
 
 						String text;
 						String text;
 						text="time: "+rtos(animation->track_get_key_time(idx,mouse_over.over_key))+"\n";
 						text="time: "+rtos(animation->track_get_key_time(idx,mouse_over.over_key))+"\n";
+
+
 						switch(animation->track_get_type(idx)) {
 						switch(animation->track_get_type(idx)) {
 
 
 							case Animation::TYPE_TRANSFORM: {
 							case Animation::TYPE_TRANSFORM: {
@@ -2569,8 +2679,33 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 							} break;
 							} break;
 							case Animation::TYPE_VALUE: {
 							case Animation::TYPE_VALUE: {
 
 
-								Variant v = animation->track_get_key_value(idx,mouse_over.over_key);
-								text+="value: "+String(v)+"\n";
+								Variant v = animation->track_get_key_value(idx,mouse_over.over_key);;
+								//text+="value: "+String(v)+"\n";
+
+								bool prop_exists=false;
+								Variant::Type valid_type=Variant::NIL;
+								Object *obj=NULL;
+
+								RES res;
+								Node *node = root->get_node_and_resource(animation->track_get_path(idx),res);
+
+								if (res.is_valid()) {
+									obj=res.ptr();
+								} else if (node) {
+									obj=node;
+								}
+
+								if (obj) {
+									valid_type=obj->get_static_property_type(animation->track_get_path(idx).get_property(),&prop_exists);
+								}
+
+								text+="type: "+Variant::get_type_name(v.get_type())+"\n";
+								if (prop_exists && !Variant::can_convert(v.get_type(),valid_type)) {
+									text+="value: "+String(v)+"  (Invalid, expected type: "+Variant::get_type_name(valid_type)+")\n";
+								} else {
+									text+="value: "+String(v)+"\n";
+								}
+
 							} break;
 							} break;
 							case Animation::TYPE_METHOD: {
 							case Animation::TYPE_METHOD: {
 
 
@@ -2593,6 +2728,9 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 							} break;
 							} break;
 						}
 						}
 						text+="easing: "+rtos(animation->track_get_key_transition(idx,mouse_over.over_key));
 						text+="easing: "+rtos(animation->track_get_key_transition(idx,mouse_over.over_key));
+
+
+
 						track_editor->set_tooltip(text);
 						track_editor->set_tooltip(text);
 						return;
 						return;
 
 
@@ -2703,6 +2841,7 @@ void AnimationKeyEditor::_notification(int p_what) {
 				//menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
 				//menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
 				//menu_track->get_popup()->add_separator();
 				//menu_track->get_popup()->add_separator();
 				menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
 				menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
+				menu_track->get_popup()->add_item("Clean-Up Animation",TRACK_MENU_CLEAN_UP);
 
 
 				curve_linear->set_icon(get_icon("CurveLinear","EditorIcons"));
 				curve_linear->set_icon(get_icon("CurveLinear","EditorIcons"));
 				curve_in->set_icon(get_icon("CurveIn","EditorIcons"));
 				curve_in->set_icon(get_icon("CurveIn","EditorIcons"));
@@ -3862,6 +4001,32 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	add_child(call_select);
 	add_child(call_select);
 	call_select->set_title("Call Functions in Which Node?");
 	call_select->set_title("Call Functions in Which Node?");
 
 
+	cleanup_dialog = memnew( ConfirmationDialog );
+	add_child(cleanup_dialog);
+	VBoxContainer *cleanup_vb = memnew( VBoxContainer );
+	cleanup_dialog->add_child(cleanup_vb);
+	cleanup_dialog->set_child_rect(cleanup_vb);
+	cleanup_keys = memnew( CheckButton );
+	cleanup_keys->set_text("Remove invalid keys");
+	cleanup_keys->set_pressed(true);
+	cleanup_vb->add_child(cleanup_keys);
+
+	cleanup_tracks = memnew( CheckButton );
+	cleanup_tracks->set_text("Remove unresolved and empty tracks");
+	cleanup_tracks->set_pressed(true);
+	cleanup_vb->add_child(cleanup_tracks);
+
+	cleanup_all = memnew( CheckButton );
+	cleanup_all->set_text("Clean-Up all animations");
+	cleanup_vb->add_child(cleanup_all);
+
+	cleanup_dialog->set_title("Clean up Animation(s) (NO UNDO!)");
+	cleanup_dialog->get_ok()->set_text("Clean-Up");
+
+	cleanup_dialog->connect("confirmed",this,"_menu_track",varray(TRACK_MENU_CLEAN_UP_CONFIRM));
+
+
+
 }
 }
 
 
 AnimationKeyEditor::~AnimationKeyEditor() {
 AnimationKeyEditor::~AnimationKeyEditor() {

+ 8 - 0
tools/editor/animation_editor.h

@@ -89,6 +89,8 @@ class AnimationKeyEditor : public VBoxContainer  {
 		TRACK_MENU_NEXT_STEP,
 		TRACK_MENU_NEXT_STEP,
 		TRACK_MENU_PREV_STEP,
 		TRACK_MENU_PREV_STEP,
 		TRACK_MENU_OPTIMIZE,
 		TRACK_MENU_OPTIMIZE,
+		TRACK_MENU_CLEAN_UP,
+		TRACK_MENU_CLEAN_UP_CONFIRM,
 		CURVE_SET_LINEAR,
 		CURVE_SET_LINEAR,
 		CURVE_SET_IN,
 		CURVE_SET_IN,
 		CURVE_SET_OUT,
 		CURVE_SET_OUT,
@@ -190,6 +192,11 @@ class AnimationKeyEditor : public VBoxContainer  {
 	SpinBox *optimize_angular_error;
 	SpinBox *optimize_angular_error;
 	SpinBox *optimize_max_angle;
 	SpinBox *optimize_max_angle;
 
 
+	ConfirmationDialog *cleanup_dialog;
+	CheckButton *cleanup_keys;
+	CheckButton *cleanup_tracks;
+	CheckButton *cleanup_all;
+
 	SpinBox *step;
 	SpinBox *step;
 
 
 	MenuButton *menu_add_track;
 	MenuButton *menu_add_track;
@@ -284,6 +291,7 @@ class AnimationKeyEditor : public VBoxContainer  {
 
 
 	void _animation_changed();
 	void _animation_changed();
 	void _animation_optimize();
 	void _animation_optimize();
+	void _cleanup_animation(Ref<Animation> p_animation);
 
 
 	void _scroll_changed(double);
 	void _scroll_changed(double);
 
 

BIN
tools/editor/icons/icon_key_invalid.png


BIN
tools/editor/icons/icon_key_invalid_hover.png


+ 7 - 0
tools/editor/plugins/animation_player_editor_plugin.cpp

@@ -1250,8 +1250,15 @@ void AnimationPlayerEditor::_bind_methods() {
 
 
 }
 }
 
 
+AnimationPlayerEditor *AnimationPlayerEditor::singleton=NULL;
+
+AnimationPlayer *AnimationPlayerEditor::get_player() const {
+
+	return player;
+}
 AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 	editor=p_editor;
 	editor=p_editor;
+	singleton=this;
 
 
 	updating=false;
 	updating=false;
 
 

+ 4 - 0
tools/editor/plugins/animation_player_editor_plugin.h

@@ -162,6 +162,7 @@ class AnimationPlayerEditor : public VBoxContainer {
 	void _animation_tool_menu(int p_option);
 	void _animation_tool_menu(int p_option);
 	void _animation_save_menu(int p_option);
 	void _animation_save_menu(int p_option);
 
 
+
 	AnimationPlayerEditor();
 	AnimationPlayerEditor();
 protected:
 protected:
 
 
@@ -171,6 +172,9 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 public:
 public:
 
 
+	AnimationPlayer *get_player() const;
+	static AnimationPlayerEditor *singleton;
+
 	Dictionary get_state() const;
 	Dictionary get_state() const;
 	void set_state(const Dictionary& p_state);
 	void set_state(const Dictionary& p_state);