Add AnimationTree Advance Expressions
* Allows specifying an expression as a condition for state machine transitions. * For this to work safely (user not call queue_free or something in the expression), a const call mode was added to Object and Variant (and optionally Script). * This mode ensures only const functions can be called, making it safe to use from the editor. * Bonus: Fixed an animation import bug in Collada. This gives much greater flexibility for creating complex state machines. By directly interfacing with the script code, it is possible to create complex animation advance condition for switching between states.
This commit is contained in:
parent
d3547be9ae
commit
a6e72a971c
|
@ -539,6 +539,7 @@ void register_global_constants() {
|
|||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXPRESSION);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY);
|
||||
|
|
|
@ -152,7 +152,7 @@ typedef enum {
|
|||
GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */
|
||||
GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */
|
||||
GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL,
|
||||
|
||||
GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, /* used for const call */
|
||||
} GDNativeCallErrorType;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -1157,7 +1157,7 @@ bool Expression::_compile_expression() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) {
|
||||
bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str) {
|
||||
switch (p_node->type) {
|
||||
case Expression::ENode::TYPE_INPUT: {
|
||||
const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node);
|
||||
|
@ -1183,7 +1183,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node);
|
||||
|
||||
Variant a;
|
||||
bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1191,7 +1191,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
Variant b;
|
||||
|
||||
if (op->nodes[1]) {
|
||||
ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str);
|
||||
ret = _execute(p_inputs, p_instance, op->nodes[1], b, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1209,14 +1209,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node);
|
||||
|
||||
Variant base;
|
||||
bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Variant idx;
|
||||
|
||||
ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str);
|
||||
ret = _execute(p_inputs, p_instance, index->index, idx, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1233,7 +1233,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node);
|
||||
|
||||
Variant base;
|
||||
bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1253,7 +1253,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
arr.resize(array->array.size());
|
||||
for (int i = 0; i < array->array.size(); i++) {
|
||||
Variant value;
|
||||
bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, array->array[i], value, p_const_calls_only, r_error_str);
|
||||
|
||||
if (ret) {
|
||||
return true;
|
||||
|
@ -1270,14 +1270,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
Dictionary d;
|
||||
for (int i = 0; i < dictionary->dict.size(); i += 2) {
|
||||
Variant key;
|
||||
bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, p_const_calls_only, r_error_str);
|
||||
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Variant value;
|
||||
ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str);
|
||||
ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1297,7 +1297,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
|
||||
for (int i = 0; i < constructor->arguments.size(); i++) {
|
||||
Variant value;
|
||||
bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, p_const_calls_only, r_error_str);
|
||||
|
||||
if (ret) {
|
||||
return true;
|
||||
|
@ -1325,7 +1325,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
|
||||
for (int i = 0; i < bifunc->arguments.size(); i++) {
|
||||
Variant value;
|
||||
bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, p_const_calls_only, r_error_str);
|
||||
if (ret) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1346,7 +1346,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node);
|
||||
|
||||
Variant base;
|
||||
bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str);
|
||||
bool ret = _execute(p_inputs, p_instance, call->base, base, p_const_calls_only, r_error_str);
|
||||
|
||||
if (ret) {
|
||||
return true;
|
||||
|
@ -1359,7 +1359,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
|
||||
for (int i = 0; i < call->arguments.size(); i++) {
|
||||
Variant value;
|
||||
ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str);
|
||||
ret = _execute(p_inputs, p_instance, call->arguments[i], value, p_const_calls_only, r_error_str);
|
||||
|
||||
if (ret) {
|
||||
return true;
|
||||
|
@ -1369,7 +1369,11 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
|
|||
}
|
||||
|
||||
Callable::CallError ce;
|
||||
base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
|
||||
if (p_const_calls_only) {
|
||||
base.call_const(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
|
||||
} else {
|
||||
base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
|
||||
}
|
||||
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
r_error_str = vformat(RTR("On call to '%s':"), String(call->method));
|
||||
|
@ -1408,13 +1412,13 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu
|
|||
return OK;
|
||||
}
|
||||
|
||||
Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
|
||||
Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) {
|
||||
ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + ".");
|
||||
|
||||
execution_error = false;
|
||||
Variant output;
|
||||
String error_txt;
|
||||
bool err = _execute(p_inputs, p_base, root, output, error_txt);
|
||||
bool err = _execute(p_inputs, p_base, root, output, p_const_calls_only, error_txt);
|
||||
if (err) {
|
||||
execution_error = true;
|
||||
error_str = error_txt;
|
||||
|
@ -1434,7 +1438,7 @@ String Expression::get_error_text() const {
|
|||
|
||||
void Expression::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>()));
|
||||
ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error", "const_calls_only"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed);
|
||||
ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text);
|
||||
}
|
||||
|
|
|
@ -256,14 +256,14 @@ private:
|
|||
Vector<String> input_names;
|
||||
|
||||
bool execution_error = false;
|
||||
bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str);
|
||||
bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>());
|
||||
Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true);
|
||||
Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true, bool p_const_calls_only = false);
|
||||
bool has_execute_failed() const;
|
||||
String get_error_text() const;
|
||||
|
||||
|
|
|
@ -818,6 +818,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
|
|||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
return ret;
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
|
||||
}
|
||||
|
@ -837,6 +838,54 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
|
|||
return ret;
|
||||
}
|
||||
|
||||
Variant Object::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
if (p_method == CoreStringNames::get_singleton()->_free) {
|
||||
// Free is not const, so fail.
|
||||
r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
Variant ret;
|
||||
OBJ_DEBUG_LOCK
|
||||
|
||||
if (script_instance) {
|
||||
ret = script_instance->call_const(p_method, p_args, p_argcount, r_error);
|
||||
//force jumptable
|
||||
switch (r_error.error) {
|
||||
case Callable::CallError::CALL_OK:
|
||||
return ret;
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
return ret;
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//extension does not need this, because all methods are registered in MethodBind
|
||||
|
||||
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
|
||||
|
||||
if (method) {
|
||||
if (!method->is_const()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
|
||||
return ret;
|
||||
}
|
||||
ret = method->call(this, p_args, p_argcount, r_error);
|
||||
} else {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Object::notification(int p_notification, bool p_reversed) {
|
||||
_notificationv(p_notification, p_reversed);
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ enum PropertyHint {
|
|||
PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed
|
||||
PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type
|
||||
PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines
|
||||
PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines
|
||||
PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties
|
||||
PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
|
||||
PROPERTY_HINT_IMAGE_COMPRESS_LOSSY,
|
||||
|
@ -748,6 +749,7 @@ public:
|
|||
Variant callv(const StringName &p_method, const Array &p_args);
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
|
||||
virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
void notification(int p_notification, bool p_reversed = false);
|
||||
virtual String to_string();
|
||||
|
|
|
@ -294,6 +294,11 @@ void ScriptServer::save_global_classes() {
|
|||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
return call(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
|
||||
List<PropertyInfo> pinfo;
|
||||
get_property_list(&pinfo);
|
||||
|
|
|
@ -178,6 +178,7 @@ public:
|
|||
virtual bool has_method(const StringName &p_method) const = 0;
|
||||
virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST);
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
|
||||
virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions
|
||||
virtual void notification(int p_notification) = 0;
|
||||
virtual String to_string(bool *r_valid) {
|
||||
if (r_valid) {
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
CALL_ERROR_TOO_MANY_ARGUMENTS, // expected is number of arguments
|
||||
CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments
|
||||
CALL_ERROR_INSTANCE_IS_NULL,
|
||||
CALL_ERROR_METHOD_NOT_CONST,
|
||||
};
|
||||
Error error = Error::CALL_OK;
|
||||
int argument = 0;
|
||||
|
|
|
@ -3360,6 +3360,46 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
Variant Variant::call_const(const StringName &p_method, VARIANT_ARG_DECLARE) {
|
||||
VARIANT_ARGPTRS;
|
||||
int argc = 0;
|
||||
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
|
||||
if (argptr[i]->get_type() == Variant::NIL) {
|
||||
break;
|
||||
}
|
||||
argc++;
|
||||
}
|
||||
|
||||
Callable::CallError error;
|
||||
|
||||
Variant ret;
|
||||
call_const(p_method, argptr, argc, ret, error);
|
||||
|
||||
switch (error.error) {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
|
||||
String err = "Invalid type for argument #" + itos(error.argument) + ", expected '" + Variant::get_type_name(Variant::Type(error.expected)) + "'.";
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
|
||||
} break;
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD: {
|
||||
String err = "Invalid method '" + p_method + "' for type '" + Variant::get_type_name(type) + "'.";
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
} break;
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
|
||||
String err = "Too many arguments for method '" + p_method + "'";
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
} break;
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: {
|
||||
String err = "Method is not const '" + p_method + "' and call_const used";
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Variant::construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct, void *p_construct_ud) {
|
||||
r_value = Variant();
|
||||
}
|
||||
|
@ -3389,6 +3429,8 @@ String Variant::get_call_error_text(const StringName &p_method, const Variant **
|
|||
err_text = "Method not found.";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
err_text = "Instance is null";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
err_text = "Method not const in const instance";
|
||||
} else if (ce.error == Callable::CallError::CALL_OK) {
|
||||
return "Call OK";
|
||||
}
|
||||
|
@ -3413,6 +3455,8 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
|
|||
err_text = "Method not found.";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
err_text = "Instance is null";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
err_text = "Method not const in const instance";
|
||||
} else if (ce.error == Callable::CallError::CALL_OK) {
|
||||
return "Call OK";
|
||||
}
|
||||
|
@ -3443,6 +3487,8 @@ String Variant::get_callable_error_text(const Callable &p_callable, const Varian
|
|||
err_text = "Method not found.";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
err_text = "Instance is null";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
err_text = "Method not const in const instance";
|
||||
} else if (ce.error == Callable::CallError::CALL_OK) {
|
||||
return "Call OK";
|
||||
}
|
||||
|
|
|
@ -511,6 +511,9 @@ public:
|
|||
void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::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(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant());
|
||||
|
||||
void call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
|
||||
Variant call_const(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(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant());
|
||||
|
||||
static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
|
||||
|
||||
static String get_call_error_text(const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce);
|
||||
|
|
|
@ -1031,6 +1031,43 @@ void Variant::call(const StringName &p_method, const Variant **p_args, int p_arg
|
|||
}
|
||||
}
|
||||
|
||||
void Variant::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
|
||||
if (type == Variant::OBJECT) {
|
||||
//call object
|
||||
Object *obj = _get_obj().obj;
|
||||
if (!obj) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
r_ret = _get_obj().obj->call_const(p_method, p_args, p_argcount, r_error);
|
||||
|
||||
//else if (type==Variant::METHOD) {
|
||||
} else {
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
const VariantBuiltInMethodInfo *imf = builtin_method_info[type].lookup_ptr(p_method);
|
||||
|
||||
if (!imf) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imf->is_const) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
|
||||
return;
|
||||
}
|
||||
|
||||
imf->call(this, p_args, p_argcount, r_ret, imf->default_arguments, r_error);
|
||||
}
|
||||
}
|
||||
|
||||
void Variant::call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
|
|
|
@ -2318,21 +2318,23 @@
|
|||
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="19" enum="PropertyHint">
|
||||
Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="20" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_EXPRESSION" value="20" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="21" enum="PropertyHint">
|
||||
Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="21" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="22" enum="PropertyHint">
|
||||
Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="22" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="23" enum="PropertyHint">
|
||||
Hints that an image is compressed using lossy compression.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="23" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="24" enum="PropertyHint">
|
||||
Hints that an image is compressed using lossless compression.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_OBJECT_ID" value="24" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_OBJECT_ID" value="25" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_TYPE_STRING" value="25" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_TYPE_STRING" value="26" enum="PropertyHint">
|
||||
Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance:
|
||||
[codeblock]
|
||||
hint_string = "%s:" % [TYPE_INT] # Array of inteters.
|
||||
|
@ -2342,37 +2344,37 @@
|
|||
[/codeblock]
|
||||
[b]Note:[/b] The final colon is required to specify for properly detecting built-in types.
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="26" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="27" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="27" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="28" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="28" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="29" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="29" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="30" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="30" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="31" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="31" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="32" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="32" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="33" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="33" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="34" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="34" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="35" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="35" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="36" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="36" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="37" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_SAVE_FILE" value="37" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_SAVE_FILE" value="38" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="38" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="39" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_INT_IS_POINTER" value="40" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_INT_IS_POINTER" value="41" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_ARRAY_TYPE" value="40" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint">
|
||||
</constant>
|
||||
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
|
||||
</constant>
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
[/csharp]
|
||||
[/codeblocks]
|
||||
</member>
|
||||
<member name="advance_expression" type="String" setter="set_advance_expression" getter="get_advance_expression" default="""">
|
||||
</member>
|
||||
<member name="advance_expression_base_node" type="NodePath" setter="set_advance_expression_base_node" getter="get_advance_expression_base_node" default="NodePath("..")">
|
||||
</member>
|
||||
<member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance" default="false">
|
||||
Turn on the transition automatically when this state is reached. This works best with [constant SWITCH_MODE_AT_END].
|
||||
</member>
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<argument index="0" name="inputs" type="Array" default="[]" />
|
||||
<argument index="1" name="base_instance" type="Object" default="null" />
|
||||
<argument index="2" name="show_error" type="bool" default="true" />
|
||||
<argument index="3" name="const_calls_only" type="bool" default="false" />
|
||||
<description>
|
||||
Executes the expression that was previously parsed by [method parse] and returns the result. Before you use the returned object, you should check if the method failed by calling [method has_execute_failed].
|
||||
If you defined input variables in [method parse], you can specify their values in the inputs array, in the same order.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor_node.h"
|
||||
#include "editor_properties_array_dict.h"
|
||||
#include "editor_scale.h"
|
||||
|
@ -162,6 +163,9 @@ void EditorPropertyMultilineText::_notification(int p_what) {
|
|||
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
|
||||
text->set_custom_minimum_size(Vector2(0, font->get_height(font_size) * 6));
|
||||
|
||||
text->add_theme_font_override("font", get_theme_font("expression", "EditorFonts"));
|
||||
text->add_theme_font_size_override("font_size", get_theme_font_size("expression_size", "EditorFonts"));
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +173,7 @@ void EditorPropertyMultilineText::_notification(int p_what) {
|
|||
void EditorPropertyMultilineText::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyMultilineText::EditorPropertyMultilineText() {
|
||||
EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
add_child(hb);
|
||||
set_bottom_editor(hb);
|
||||
|
@ -185,6 +189,12 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() {
|
|||
hb->add_child(open_big_text);
|
||||
big_text_dialog = nullptr;
|
||||
big_text = nullptr;
|
||||
if (p_expression) {
|
||||
expression = true;
|
||||
Ref<EditorStandardSyntaxHighlighter> highlighter;
|
||||
highlighter.instantiate();
|
||||
text->set_syntax_highlighter(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////// TEXT ENUM /////////////////////////
|
||||
|
@ -3320,6 +3330,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
|
||||
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
|
||||
return editor;
|
||||
} else if (p_hint == PROPERTY_HINT_EXPRESSION) {
|
||||
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(true));
|
||||
return editor;
|
||||
} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {
|
||||
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
|
||||
editor->setup("Object", p_hint_text);
|
||||
|
|
|
@ -80,6 +80,7 @@ class EditorPropertyMultilineText : public EditorProperty {
|
|||
void _big_text_changed();
|
||||
void _text_changed();
|
||||
void _open_big_text();
|
||||
bool expression = false;
|
||||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
|
@ -88,7 +89,7 @@ protected:
|
|||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
EditorPropertyMultilineText();
|
||||
EditorPropertyMultilineText(bool p_expression = false);
|
||||
};
|
||||
|
||||
class EditorPropertyTextEnum : public EditorProperty {
|
||||
|
|
|
@ -513,7 +513,7 @@ void EditorSpinSlider::_evaluate_input_text() {
|
|||
return;
|
||||
}
|
||||
|
||||
Variant v = expr->execute(Array(), nullptr, false);
|
||||
Variant v = expr->execute(Array(), nullptr, false, true);
|
||||
if (v.get_type() == Variant::NIL) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1535,7 +1535,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
|||
bool has_rotation = false;
|
||||
bool has_scale = false;
|
||||
|
||||
for (int i = 0; cn->xform_list.size(); i++) {
|
||||
for (int i = 0; i < cn->xform_list.size(); i++) {
|
||||
switch (cn->xform_list[i].op) {
|
||||
case Collada::Node::XForm::OP_ROTATE: {
|
||||
has_rotation = true;
|
||||
|
|
|
@ -1133,7 +1133,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
String whitespace = line.substr(0, line.size() - line.strip_edges(true, false).size()); //extract the whitespace at the beginning
|
||||
|
||||
if (expression.parse(line) == OK) {
|
||||
Variant result = expression.execute(Array(), Variant(), false);
|
||||
Variant result = expression.execute(Array(), Variant(), false, true);
|
||||
if (expression.get_error_text() == "") {
|
||||
results.push_back(whitespace + result.get_construct_string());
|
||||
} else {
|
||||
|
|
|
@ -1446,7 +1446,7 @@ void CustomPropertyEditor::_modified(String p_string) {
|
|||
v = value_editor[0]->get_text().to_int();
|
||||
return;
|
||||
} else {
|
||||
v = expr->execute(Array(), nullptr, false);
|
||||
v = expr->execute(Array(), nullptr, false, false);
|
||||
}
|
||||
|
||||
if (v != prev_v) {
|
||||
|
@ -1622,7 +1622,7 @@ real_t CustomPropertyEditor::_parse_real_expression(String text) {
|
|||
if (err != OK) {
|
||||
out = value_editor[0]->get_text().to_float();
|
||||
} else {
|
||||
out = expr->execute(Array(), nullptr, false);
|
||||
out = expr->execute(Array(), nullptr, false, true);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -2154,6 +2154,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
break; // Can't happen in a builtin constructor.
|
||||
case Callable::CallError::CALL_OK:
|
||||
p_call->is_constant = true;
|
||||
|
@ -2256,6 +2257,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
break; // Can't happen in a builtin constructor.
|
||||
case Callable::CallError::CALL_OK:
|
||||
|
@ -2298,6 +2300,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
break; // Can't happen in a builtin constructor.
|
||||
case Callable::CallError::CALL_OK:
|
||||
|
|
|
@ -145,6 +145,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
|
|||
err_text = "Invalid call. Nonexistent " + p_where + ".";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
err_text = "Attempt to call " + p_where + " on a null instance.";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
err_text = "Attempt to call " + p_where + " on a const instance.";
|
||||
} else {
|
||||
err_text = "Bug, call error: #" + itos(p_err.error);
|
||||
}
|
||||
|
|
|
@ -1679,6 +1679,8 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
|
|||
error_str += "Expected " + itos(r_error.argument) + " arguments.";
|
||||
} else if (r_error.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
||||
error_str += "Invalid Call.";
|
||||
} else if (r_error.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
error_str += "Method not const in a const instance.";
|
||||
} else if (r_error.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
error_str += "Base Instance is null";
|
||||
}
|
||||
|
|
|
@ -68,6 +68,32 @@ StringName AnimationNodeStateMachineTransition::get_advance_condition_name() con
|
|||
return advance_condition_name;
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_advance_expression(const String &p_expression) {
|
||||
advance_expression = p_expression.strip_edges();
|
||||
if (advance_expression.strip_edges() == String()) {
|
||||
expression.unref();
|
||||
return;
|
||||
}
|
||||
|
||||
if (expression.is_null()) {
|
||||
expression.instantiate();
|
||||
}
|
||||
|
||||
expression->parse(advance_expression);
|
||||
}
|
||||
|
||||
String AnimationNodeStateMachineTransition::get_advance_expression() const {
|
||||
return advance_expression;
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_advance_expression_base_node(const NodePath &p_expression_base_node) {
|
||||
advance_expression_base_node = p_expression_base_node;
|
||||
}
|
||||
|
||||
NodePath AnimationNodeStateMachineTransition::get_advance_expression_base_node() const {
|
||||
return advance_expression_base_node;
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) {
|
||||
ERR_FAIL_COND(p_xfade < 0);
|
||||
xfade = p_xfade;
|
||||
|
@ -115,11 +141,22 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
|
||||
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_advance_expression", "text"), &AnimationNodeStateMachineTransition::set_advance_expression);
|
||||
ClassDB::bind_method(D_METHOD("get_advance_expression"), &AnimationNodeStateMachineTransition::get_advance_expression);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "path"), &AnimationNodeStateMachineTransition::set_advance_expression_base_node);
|
||||
ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationNodeStateMachineTransition::get_advance_expression_base_node);
|
||||
|
||||
ADD_GROUP("Switch", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
|
||||
ADD_GROUP("Advance", "advance_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
|
||||
ADD_GROUP("Disabling", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
||||
|
||||
BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
|
||||
|
@ -423,6 +460,19 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
|
|||
auto_advance = true;
|
||||
}
|
||||
|
||||
if (p_state_machine->transitions[i].from == current && p_state_machine->transitions[i].transition->expression.is_valid()) {
|
||||
Node *base = p_state_machine->get_animation_tree();
|
||||
ERR_CONTINUE(base == nullptr);
|
||||
Ref<Expression> exp = p_state_machine->transitions[i].transition->expression;
|
||||
base = base->get_node_or_null(p_state_machine->transitions[i].transition->advance_expression_base_node);
|
||||
if (base) {
|
||||
bool ret = exp->execute(Array(), base, false, Engine::get_singleton()->is_editor_hint()); // Avoid user from crashing the system with an expression by only allowing const calls when editor runs
|
||||
if (!exp->has_execute_failed()) {
|
||||
auto_advance = ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_state_machine->transitions[i].from == current && auto_advance) {
|
||||
if (p_state_machine->transitions[i].transition->get_priority() <= priority_best) {
|
||||
priority_best = p_state_machine->transitions[i].transition->get_priority();
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef ANIMATION_NODE_STATE_MACHINE_H
|
||||
#define ANIMATION_NODE_STATE_MACHINE_H
|
||||
|
||||
#include "core/math/expression.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeStateMachineTransition : public Resource {
|
||||
|
@ -51,6 +52,11 @@ private:
|
|||
float xfade = 0.0;
|
||||
bool disabled = false;
|
||||
int priority = 1;
|
||||
String advance_expression;
|
||||
NodePath advance_expression_base_node = NodePath(String(".."));
|
||||
|
||||
friend class AnimationNodeStateMachinePlayback;
|
||||
Ref<Expression> expression;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -67,6 +73,12 @@ public:
|
|||
|
||||
StringName get_advance_condition_name() const;
|
||||
|
||||
void set_advance_expression(const String &p_expression);
|
||||
String get_advance_expression() const;
|
||||
|
||||
void set_advance_expression_base_node(const NodePath &p_expression_base_node);
|
||||
NodePath get_advance_expression_base_node() const;
|
||||
|
||||
void set_xfade_time(float p_xfade);
|
||||
float get_xfade_time() const;
|
||||
|
||||
|
|
|
@ -133,6 +133,11 @@ real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode
|
|||
return t;
|
||||
}
|
||||
|
||||
AnimationTree *AnimationNode::get_animation_tree() const {
|
||||
ERR_FAIL_COND_V(!state, nullptr);
|
||||
return state->tree;
|
||||
}
|
||||
|
||||
void AnimationNode::make_invalid(const String &p_reason) {
|
||||
ERR_FAIL_COND(!state);
|
||||
state->valid = false;
|
||||
|
|
|
@ -102,6 +102,7 @@ protected:
|
|||
real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
|
||||
real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
|
||||
void make_invalid(const String &p_reason);
|
||||
AnimationTree *get_animation_tree() const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ void SpinBox::_text_submitted(const String &p_string) {
|
|||
return;
|
||||
}
|
||||
|
||||
Variant value = expr->execute(Array(), nullptr, false);
|
||||
Variant value = expr->execute(Array(), nullptr, false, true);
|
||||
if (value.get_type() != Variant::NIL) {
|
||||
set_value(value);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue