You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Fix NodeTransition initialization and AnimationNode remapping method
This commit is contained in:
@@ -187,9 +187,24 @@
|
|||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
<signals>
|
<signals>
|
||||||
|
<signal name="animation_node_removed">
|
||||||
|
<param index="0" name="object_id" type="int" />
|
||||||
|
<param index="1" name="name" type="String" />
|
||||||
|
<description>
|
||||||
|
Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes removes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
|
<signal name="animation_node_renamed">
|
||||||
|
<param index="0" name="object_id" type="int" />
|
||||||
|
<param index="1" name="old_name" type="String" />
|
||||||
|
<param index="2" name="new_name" type="String" />
|
||||||
|
<description>
|
||||||
|
Emitted by nodes that inherit from this class and that have an internal tree when one of their node names changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
<signal name="tree_changed">
|
<signal name="tree_changed">
|
||||||
<description>
|
<description>
|
||||||
Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
|
Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], [AnimationNodeBlendTree] and [AnimationNodeTransition].
|
||||||
</description>
|
</description>
|
||||||
</signal>
|
</signal>
|
||||||
</signals>
|
</signals>
|
||||||
|
|||||||
@@ -61,7 +61,15 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendSpace1D::_tree_changed() {
|
void AnimationNodeBlendSpace1D::_tree_changed() {
|
||||||
emit_signal(SNAME("tree_changed"));
|
AnimationRootNode::_tree_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendSpace1D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendSpace1D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendSpace1D::_bind_methods() {
|
void AnimationNodeBlendSpace1D::_bind_methods() {
|
||||||
@@ -137,6 +145,8 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_
|
|||||||
blend_points[p_at_index].position = p_position;
|
blend_points[p_at_index].position = p_position;
|
||||||
|
|
||||||
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
|
||||||
blend_points_used++;
|
blend_points_used++;
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
@@ -154,10 +164,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim
|
|||||||
|
|
||||||
if (blend_points[p_point].node.is_valid()) {
|
if (blend_points[p_point].node.is_valid()) {
|
||||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
|
||||||
}
|
}
|
||||||
|
|
||||||
blend_points[p_point].node = p_node;
|
blend_points[p_point].node = p_node;
|
||||||
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
@@ -177,12 +191,16 @@ void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
|
|||||||
|
|
||||||
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
||||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
|
||||||
|
|
||||||
for (int i = p_point; i < blend_points_used - 1; i++) {
|
for (int i = p_point; i < blend_points_used - 1; i++) {
|
||||||
blend_points[i] = blend_points[i + 1];
|
blend_points[i] = blend_points[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
blend_points_used--;
|
blend_points_used--;
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,20 +66,21 @@ protected:
|
|||||||
|
|
||||||
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
|
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
|
||||||
|
|
||||||
void _tree_changed();
|
|
||||||
|
|
||||||
StringName blend_position = "blend_position";
|
StringName blend_position = "blend_position";
|
||||||
StringName closest = "closest";
|
StringName closest = "closest";
|
||||||
StringName length_internal = "length_internal";
|
StringName length_internal = "length_internal";
|
||||||
|
|
||||||
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
||||||
|
|
||||||
protected:
|
|
||||||
bool sync = false;
|
bool sync = false;
|
||||||
|
|
||||||
void _validate_property(PropertyInfo &p_property) const;
|
void _validate_property(PropertyInfo &p_property) const;
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _tree_changed() override;
|
||||||
|
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||||
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_
|
|||||||
blend_points[p_at_index].position = p_position;
|
blend_points[p_at_index].position = p_position;
|
||||||
|
|
||||||
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
blend_points_used++;
|
blend_points_used++;
|
||||||
|
|
||||||
_queue_auto_triangles();
|
_queue_auto_triangles();
|
||||||
@@ -100,9 +102,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim
|
|||||||
|
|
||||||
if (blend_points[p_point].node.is_valid()) {
|
if (blend_points[p_point].node.is_valid()) {
|
||||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
|
||||||
}
|
}
|
||||||
blend_points[p_point].node = p_node;
|
blend_points[p_point].node = p_node;
|
||||||
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
@@ -122,6 +128,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
|
|||||||
|
|
||||||
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
||||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
|
||||||
|
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
|
||||||
|
|
||||||
for (int i = 0; i < triangles.size(); i++) {
|
for (int i = 0; i < triangles.size(); i++) {
|
||||||
bool erase = false;
|
bool erase = false;
|
||||||
@@ -144,6 +152,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
|
|||||||
blend_points[i] = blend_points[i + 1];
|
blend_points[i] = blend_points[i + 1];
|
||||||
}
|
}
|
||||||
blend_points_used--;
|
blend_points_used--;
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,10 +608,6 @@ Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName
|
|||||||
return get_blend_point_node(p_name.operator String().to_int());
|
return get_blend_point_node(p_name.operator String().to_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendSpace2D::_tree_changed() {
|
|
||||||
emit_signal(SNAME("tree_changed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
|
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
|
||||||
blend_mode = p_blend_mode;
|
blend_mode = p_blend_mode;
|
||||||
}
|
}
|
||||||
@@ -618,6 +624,18 @@ bool AnimationNodeBlendSpace2D::is_using_sync() const {
|
|||||||
return sync;
|
return sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendSpace2D::_tree_changed() {
|
||||||
|
AnimationRootNode::_tree_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendSpace2D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendSpace2D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendSpace2D::_bind_methods() {
|
void AnimationNodeBlendSpace2D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
|
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
|
||||||
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
|
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
|
||||||
|
|||||||
@@ -85,14 +85,15 @@ protected:
|
|||||||
void _update_triangles();
|
void _update_triangles();
|
||||||
void _queue_auto_triangles();
|
void _queue_auto_triangles();
|
||||||
|
|
||||||
void _tree_changed();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool sync = false;
|
bool sync = false;
|
||||||
|
|
||||||
void _validate_property(PropertyInfo &p_property) const;
|
void _validate_property(PropertyInfo &p_property) const;
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _tree_changed() override;
|
||||||
|
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||||
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||||
|
|||||||
@@ -721,12 +721,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
|
|||||||
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
|
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
|
||||||
if (p_parameter == time || p_parameter == prev_xfading) {
|
if (p_parameter == time || p_parameter == prev_xfading) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
} else if (p_parameter == prev_index) {
|
} else if (p_parameter == prev_index || p_parameter == current_index) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (p_parameter == transition_request || p_parameter == current_state) {
|
|
||||||
return String();
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return String();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,6 +746,10 @@ void AnimationNodeTransition::set_input_count(int p_inputs) {
|
|||||||
while (get_input_count() > p_inputs) {
|
while (get_input_count() > p_inputs) {
|
||||||
remove_input(get_input_count() - 1);
|
remove_input(get_input_count() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pending_update = true;
|
||||||
|
|
||||||
|
emit_signal(SNAME("tree_changed")); // For updating connect activity map.
|
||||||
notify_property_list_changed();
|
notify_property_list_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,6 +766,11 @@ void AnimationNodeTransition::remove_input(int p_index) {
|
|||||||
AnimationNode::remove_input(p_index);
|
AnimationNode::remove_input(p_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
|
||||||
|
pending_update = true;
|
||||||
|
return AnimationNode::set_input_name(p_input, p_name);
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
|
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
|
||||||
ERR_FAIL_INDEX(p_input, get_input_count());
|
ERR_FAIL_INDEX(p_input, get_input_count());
|
||||||
input_data.write[p_input].auto_advance = p_enable;
|
input_data.write[p_input].auto_advance = p_enable;
|
||||||
@@ -819,6 +826,22 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
|
|||||||
bool switched = false;
|
bool switched = false;
|
||||||
bool restart = false;
|
bool restart = false;
|
||||||
|
|
||||||
|
if (pending_update) {
|
||||||
|
if (cur_current_index < 0 || cur_current_index >= get_input_count()) {
|
||||||
|
set_parameter(prev_index, -1);
|
||||||
|
if (get_input_count() > 0) {
|
||||||
|
set_parameter(current_index, 0);
|
||||||
|
set_parameter(current_state, get_input_name(0));
|
||||||
|
} else {
|
||||||
|
set_parameter(current_index, -1);
|
||||||
|
set_parameter(current_state, StringName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set_parameter(current_state, get_input_name(cur_current_index));
|
||||||
|
}
|
||||||
|
pending_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cur_transition_request.is_empty()) {
|
if (!cur_transition_request.is_empty()) {
|
||||||
int new_idx = find_input(cur_transition_request);
|
int new_idx = find_input(cur_transition_request);
|
||||||
if (new_idx >= 0) {
|
if (new_idx >= 0) {
|
||||||
@@ -985,6 +1008,8 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
|
|||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
|
|
||||||
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
|
p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,6 +1072,8 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
|
|||||||
{
|
{
|
||||||
Ref<AnimationNode> node = nodes[p_name].node;
|
Ref<AnimationNode> node = nodes[p_name].node;
|
||||||
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
|
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
|
||||||
|
node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed));
|
||||||
|
node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed));
|
||||||
node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
|
node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,6 +1088,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
|
||||||
emit_changed();
|
emit_changed();
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
@@ -1087,6 +1115,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
|
|||||||
// Connection must be done with new name.
|
// Connection must be done with new name.
|
||||||
nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
|
nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1287,6 +1316,18 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
|
|||||||
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendTree::_tree_changed() {
|
||||||
|
AnimationRootNode::_tree_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeBlendTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendTree::reset_state() {
|
void AnimationNodeBlendTree::reset_state() {
|
||||||
graph_offset = Vector2();
|
graph_offset = Vector2();
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
@@ -1295,10 +1336,6 @@ void AnimationNodeBlendTree::reset_state() {
|
|||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeBlendTree::_tree_changed() {
|
|
||||||
emit_signal(SNAME("tree_changed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimationNodeBlendTree::_node_changed(const StringName &p_node) {
|
void AnimationNodeBlendTree::_node_changed(const StringName &p_node) {
|
||||||
ERR_FAIL_COND(!nodes.has(p_node));
|
ERR_FAIL_COND(!nodes.has(p_node));
|
||||||
nodes[p_node].connections.resize(nodes[p_node].node->get_input_count());
|
nodes[p_node].connections.resize(nodes[p_node].node->get_input_count());
|
||||||
|
|||||||
@@ -296,6 +296,8 @@ class AnimationNodeTransition : public AnimationNodeSync {
|
|||||||
Ref<Curve> xfade_curve;
|
Ref<Curve> xfade_curve;
|
||||||
bool allow_transition_to_self = false;
|
bool allow_transition_to_self = false;
|
||||||
|
|
||||||
|
bool pending_update = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||||
bool _set(const StringName &p_path, const Variant &p_value);
|
bool _set(const StringName &p_path, const Variant &p_value);
|
||||||
@@ -313,6 +315,7 @@ public:
|
|||||||
|
|
||||||
virtual bool add_input(const String &p_name) override;
|
virtual bool add_input(const String &p_name) override;
|
||||||
virtual void remove_input(int p_index) override;
|
virtual void remove_input(int p_index) override;
|
||||||
|
virtual bool set_input_name(int p_input, const String &p_name) override;
|
||||||
|
|
||||||
void set_input_as_auto_advance(int p_input, bool p_enable);
|
void set_input_as_auto_advance(int p_input, bool p_enable);
|
||||||
bool is_input_set_as_auto_advance(int p_input) const;
|
bool is_input_set_as_auto_advance(int p_input) const;
|
||||||
@@ -358,7 +361,6 @@ class AnimationNodeBlendTree : public AnimationRootNode {
|
|||||||
|
|
||||||
Vector2 graph_offset;
|
Vector2 graph_offset;
|
||||||
|
|
||||||
void _tree_changed();
|
|
||||||
void _node_changed(const StringName &p_node);
|
void _node_changed(const StringName &p_node);
|
||||||
|
|
||||||
void _initialize_node_tree();
|
void _initialize_node_tree();
|
||||||
@@ -369,6 +371,10 @@ protected:
|
|||||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
|
||||||
|
virtual void _tree_changed() override;
|
||||||
|
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||||
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||||
|
|
||||||
virtual void reset_state() override;
|
virtual void reset_state() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -791,6 +791,8 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
|
|||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
|
|
||||||
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
|
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
|
||||||
@@ -802,6 +804,8 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
|
|||||||
Ref<AnimationNode> node = states[p_name].node;
|
Ref<AnimationNode> node = states[p_name].node;
|
||||||
if (node.is_valid()) {
|
if (node.is_valid()) {
|
||||||
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
|
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
|
||||||
|
node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
|
||||||
|
node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,6 +815,8 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
|
|||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
|
|
||||||
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||||
|
p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeStateMachine::set_allow_transition_to_self(bool p_enable) {
|
void AnimationNodeStateMachine::set_allow_transition_to_self(bool p_enable) {
|
||||||
@@ -884,10 +890,13 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
|
|||||||
Ref<AnimationNode> node = states[p_name].node;
|
Ref<AnimationNode> node = states[p_name].node;
|
||||||
ERR_FAIL_COND(node.is_null());
|
ERR_FAIL_COND(node.is_null());
|
||||||
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
|
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
|
||||||
|
node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
|
||||||
|
node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
|
||||||
}
|
}
|
||||||
|
|
||||||
states.erase(p_name);
|
states.erase(p_name);
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
|
||||||
emit_changed();
|
emit_changed();
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
@@ -907,6 +916,7 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri
|
|||||||
|
|
||||||
_rename_transitions(p_name, p_new_name);
|
_rename_transitions(p_name, p_new_name);
|
||||||
|
|
||||||
|
emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
|
||||||
emit_changed();
|
emit_changed();
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
@@ -1365,7 +1375,15 @@ Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) c
|
|||||||
|
|
||||||
void AnimationNodeStateMachine::_tree_changed() {
|
void AnimationNodeStateMachine::_tree_changed() {
|
||||||
emit_changed();
|
emit_changed();
|
||||||
emit_signal(SNAME("tree_changed"));
|
AnimationRootNode::_tree_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeStateMachine::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationNodeStateMachine::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationNodeStateMachine::_bind_methods() {
|
void AnimationNodeStateMachine::_bind_methods() {
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ private:
|
|||||||
|
|
||||||
Vector2 graph_offset;
|
Vector2 graph_offset;
|
||||||
|
|
||||||
void _tree_changed();
|
|
||||||
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
|
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
|
||||||
void _rename_transitions(const StringName &p_name, const StringName &p_new_name);
|
void _rename_transitions(const StringName &p_name, const StringName &p_new_name);
|
||||||
bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
|
bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
|
||||||
@@ -221,6 +220,10 @@ protected:
|
|||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
|
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
|
||||||
|
|
||||||
|
virtual void _tree_changed() override;
|
||||||
|
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||||
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||||
|
|
||||||
virtual void reset_state() override;
|
virtual void reset_state() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -453,6 +453,8 @@ void AnimationNode::_bind_methods() {
|
|||||||
GDVIRTUAL_BIND(_has_filter);
|
GDVIRTUAL_BIND(_has_filter);
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("tree_changed"));
|
ADD_SIGNAL(MethodInfo("tree_changed"));
|
||||||
|
ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
|
||||||
|
ADD_SIGNAL(MethodInfo("animation_node_removed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "name")));
|
||||||
|
|
||||||
BIND_ENUM_CONSTANT(FILTER_IGNORE);
|
BIND_ENUM_CONSTANT(FILTER_IGNORE);
|
||||||
BIND_ENUM_CONSTANT(FILTER_PASS);
|
BIND_ENUM_CONSTANT(FILTER_PASS);
|
||||||
@@ -465,15 +467,33 @@ AnimationNode::AnimationNode() {
|
|||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
|
|
||||||
|
void AnimationRootNode::_tree_changed() {
|
||||||
|
emit_signal(SNAME("tree_changed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationRootNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationRootNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
|
void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
|
||||||
if (root.is_valid()) {
|
if (root.is_valid()) {
|
||||||
root->disconnect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
|
root->disconnect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
|
||||||
|
root->disconnect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
|
||||||
|
root->disconnect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
|
||||||
}
|
}
|
||||||
|
|
||||||
root = p_root;
|
root = p_root;
|
||||||
|
|
||||||
if (root.is_valid()) {
|
if (root.is_valid()) {
|
||||||
root->connect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
|
root->connect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
|
||||||
|
root->connect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
|
||||||
|
root->connect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
|
||||||
}
|
}
|
||||||
|
|
||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
@@ -1982,11 +2002,46 @@ void AnimationTree::_tree_changed() {
|
|||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
|
ERR_FAIL_COND(!property_reference_map.has(p_oid));
|
||||||
|
String base_path = property_reference_map[p_oid];
|
||||||
|
String old_base = base_path + p_old_name;
|
||||||
|
String new_base = base_path + p_new_name;
|
||||||
|
for (const PropertyInfo &E : properties) {
|
||||||
|
if (E.name.begins_with(old_base)) {
|
||||||
|
String new_name = E.name.replace_first(old_base, new_base);
|
||||||
|
property_map[new_name] = property_map[E.name];
|
||||||
|
property_map.erase(E.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//update tree second
|
||||||
|
properties_dirty = true;
|
||||||
|
_update_properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
|
ERR_FAIL_COND(!property_reference_map.has(p_oid));
|
||||||
|
String base_path = String(property_reference_map[p_oid]) + String(p_node);
|
||||||
|
for (const PropertyInfo &E : properties) {
|
||||||
|
if (E.name.begins_with(base_path)) {
|
||||||
|
property_map.erase(E.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//update tree second
|
||||||
|
properties_dirty = true;
|
||||||
|
_update_properties();
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node) {
|
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node) {
|
||||||
ERR_FAIL_COND(node.is_null());
|
ERR_FAIL_COND(node.is_null());
|
||||||
if (!property_parent_map.has(p_base_path)) {
|
if (!property_parent_map.has(p_base_path)) {
|
||||||
property_parent_map[p_base_path] = HashMap<StringName, StringName>();
|
property_parent_map[p_base_path] = HashMap<StringName, StringName>();
|
||||||
}
|
}
|
||||||
|
if (!property_reference_map.has(node->get_instance_id())) {
|
||||||
|
property_reference_map[node->get_instance_id()] = p_base_path;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->get_input_count() && !input_activity_map.has(p_base_path)) {
|
if (node->get_input_count() && !input_activity_map.has(p_base_path)) {
|
||||||
Vector<Activity> activity;
|
Vector<Activity> activity;
|
||||||
@@ -2032,6 +2087,7 @@ void AnimationTree::_update_properties() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
properties.clear();
|
properties.clear();
|
||||||
|
property_reference_map.clear();
|
||||||
property_parent_map.clear();
|
property_parent_map.clear();
|
||||||
input_activity_map.clear();
|
input_activity_map.clear();
|
||||||
input_activity_map_get.clear();
|
input_activity_map_get.clear();
|
||||||
|
|||||||
@@ -167,6 +167,11 @@ VARIANT_ENUM_CAST(AnimationNode::FilterAction)
|
|||||||
class AnimationRootNode : public AnimationNode {
|
class AnimationRootNode : public AnimationNode {
|
||||||
GDCLASS(AnimationRootNode, AnimationNode);
|
GDCLASS(AnimationRootNode, AnimationNode);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void _tree_changed();
|
||||||
|
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||||
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AnimationRootNode() {}
|
AnimationRootNode() {}
|
||||||
};
|
};
|
||||||
@@ -326,9 +331,12 @@ private:
|
|||||||
friend class AnimationNode;
|
friend class AnimationNode;
|
||||||
bool properties_dirty = true;
|
bool properties_dirty = true;
|
||||||
void _tree_changed();
|
void _tree_changed();
|
||||||
|
void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||||
|
void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||||
void _update_properties();
|
void _update_properties();
|
||||||
List<PropertyInfo> properties;
|
List<PropertyInfo> properties;
|
||||||
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
|
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
|
||||||
|
HashMap<ObjectID, StringName> property_reference_map;
|
||||||
HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
|
HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
|
||||||
|
|
||||||
struct Activity {
|
struct Activity {
|
||||||
|
|||||||
Reference in New Issue
Block a user