372 lines
9.8 KiB
C++
372 lines
9.8 KiB
C++
#include <cstdio>
|
|
#include <cassert>
|
|
#include <core/engine.h>
|
|
#include <scene/animation/animation_tree.h>
|
|
#include <scene/animation/animation_node_state_machine.h>
|
|
#include <scene/animation/animation_blend_tree.h>
|
|
#include "smart_object.h"
|
|
|
|
SmartObject::SmartObject(): anim_state(""),
|
|
anim_finish_state(""),
|
|
enabled(false), teleport(false),
|
|
distance(0.0f), state(STATE_INIT)
|
|
{
|
|
#ifdef TOOLS_ENABLED
|
|
run_in_editor = false;
|
|
#endif
|
|
}
|
|
|
|
void SmartObject::_notification(int p_what)
|
|
{
|
|
}
|
|
|
|
void SmartObject::_bind_methods()
|
|
{
|
|
}
|
|
#ifdef TOOLS_ENABLED
|
|
void SmartObject::draw_debug()
|
|
{
|
|
if (!character.ptr())
|
|
return;
|
|
Node *node = character->instance();
|
|
if (!node)
|
|
return;
|
|
add_child(node);
|
|
Spatial *sp = Object::cast_to<Spatial>(node);
|
|
if (sp)
|
|
sp->set_transform(Transform());
|
|
AnimationTree *anim = find_animation_tree(node);
|
|
assert(anim);
|
|
node->set_meta("animation_tree", anim);
|
|
if (anim_state.length() > 0)
|
|
SmartObjectManager::play_animation(node, anim_state);
|
|
else
|
|
SmartObjectManager::play_animation(node, "locomotion");
|
|
set_meta("debug", node);
|
|
}
|
|
void SmartObject::clear_debug()
|
|
{
|
|
if (!has_meta("debug"))
|
|
return;
|
|
Node *node = get_meta("debug");
|
|
if (node) {
|
|
remove_child(node);
|
|
node->queue_delete();
|
|
}
|
|
remove_meta("debug");
|
|
}
|
|
#endif
|
|
|
|
void SmartObject::_get_property_list(List<PropertyInfo> *p_list) const
|
|
{
|
|
#if TOOLS_ENABLED
|
|
String animations;
|
|
if (character.ptr()) {
|
|
animations = get_animation_list();
|
|
}
|
|
if (animations.length() == 0)
|
|
animations = "default";
|
|
#endif
|
|
p_list->push_back(PropertyInfo(Variant::BOOL, "enabled"));
|
|
p_list->push_back(PropertyInfo(Variant::STRING, "animation_state", PROPERTY_HINT_ENUM, animations));
|
|
p_list->push_back(PropertyInfo(Variant::STRING, "animation_finish_state", PROPERTY_HINT_ENUM, animations));
|
|
p_list->push_back(PropertyInfo(Variant::BOOL, "teleport"));
|
|
p_list->push_back(PropertyInfo(Variant::REAL, "distance"));
|
|
#ifdef TOOLS_ENABLED
|
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "character", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"));
|
|
p_list->push_back(PropertyInfo(Variant::BOOL, "run_in_editor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR));
|
|
#endif
|
|
}
|
|
|
|
bool SmartObject::_get(const StringName &p_name, Variant &r_ret) const
|
|
{
|
|
if (p_name == "enabled") {
|
|
r_ret = enabled;
|
|
return true;
|
|
} else if (p_name == "animation_state") {
|
|
r_ret = anim_state;
|
|
return true;
|
|
} else if (p_name == "animation_finish_state") {
|
|
r_ret = anim_finish_state;
|
|
return true;
|
|
} else if (p_name == "teleport") {
|
|
r_ret = teleport;
|
|
return true;
|
|
} else if (p_name == "distance") {
|
|
r_ret = distance;
|
|
return true;
|
|
#ifdef TOOLS_ENABLED
|
|
} else if (p_name == "character") {
|
|
r_ret = character;
|
|
return true;
|
|
} else if (p_name == "run_in_editor") {
|
|
r_ret = run_in_editor;
|
|
return true;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
#ifdef TOOLS_ENABLED
|
|
String SmartObject::dump_subnodes(Ref<AnimationNode> anode) const
|
|
{
|
|
String animations = "";
|
|
List<AnimationNode::ChildNode> child_nodes;
|
|
anode->get_child_nodes(&child_nodes);
|
|
List<AnimationNode::ChildNode>::Element *c;
|
|
for (c = child_nodes.front(); c; c = c->next()) {
|
|
AnimationNode::ChildNode cn = c->get();
|
|
animations += cn.name;
|
|
if (c->next())
|
|
animations += ",";
|
|
}
|
|
return animations;
|
|
}
|
|
#endif
|
|
|
|
AnimationTree *SmartObject::find_animation_tree(Node *node) const
|
|
{
|
|
int i;
|
|
List<Node *> queue;
|
|
queue.push_back(node);
|
|
List<Node *>::Element *e;
|
|
while(!queue.empty()) {
|
|
e = queue.front();
|
|
Node *item = e->get();
|
|
queue.pop_front();
|
|
AnimationTree *at = Object::cast_to<AnimationTree>(item);
|
|
if (at) {
|
|
if (!at->is_in_group("hair") &&
|
|
!at->is_in_group("hair"))
|
|
return at;
|
|
}
|
|
for (i = 0; i < item->get_child_count(); i++)
|
|
queue.push_back(item->get_child(i));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
String SmartObject::get_animation_list() const
|
|
{
|
|
Node *node = character->instance();
|
|
assert(node);
|
|
String animations;
|
|
if (node) {
|
|
AnimationTree *anim = find_animation_tree(node);
|
|
assert(anim);
|
|
if (anim) {
|
|
List<PropertyInfo> plist;
|
|
Ref<AnimationNodeBlendTree> anim_root = get_anode<AnimationNodeBlendTree>(anim->get_tree_root());
|
|
assert(anim_root.ptr());
|
|
Ref<AnimationNodeStateMachine> anim_state = get_anode<AnimationNodeStateMachine>(anim_root->get_child_by_name("state"));
|
|
assert(anim_state.ptr());
|
|
animations = dump_subnodes(anim_state);
|
|
printf("ANIMATIONS: %ls\n", animations.c_str());
|
|
#if 0
|
|
anim_root->get_property_list(&plist);
|
|
List<PropertyInfo>::Element *e;
|
|
for (e = plist.front(); e; e = e->next()) {
|
|
PropertyInfo pi = e->get();
|
|
String name = pi.name;
|
|
printf("node: %ls\n", name.c_str());
|
|
}
|
|
#endif
|
|
}
|
|
node->queue_delete();
|
|
}
|
|
return animations;
|
|
}
|
|
|
|
bool SmartObject::_set(const StringName &p_name, const Variant &p_value)
|
|
{
|
|
bool update = false;
|
|
if (p_name == "enabled") {
|
|
enabled = p_value;
|
|
update = true;
|
|
} else if (p_name == "animation_state") {
|
|
anim_state = p_value;
|
|
update = true;
|
|
} else if (p_name == "animation_finish_state") {
|
|
anim_finish_state = p_value;
|
|
update = true;
|
|
} else if (p_name == "teleport") {
|
|
teleport = p_value;
|
|
update = true;
|
|
} else if (p_name == "distance") {
|
|
distance = p_value;
|
|
update = true;
|
|
#ifdef TOOLS_ENABLED
|
|
} else if (p_name == "character") {
|
|
character = p_value;
|
|
update = true;
|
|
} else if (p_name == "run_in_editor") {
|
|
run_in_editor = p_value;
|
|
update = true;
|
|
#endif
|
|
}
|
|
if (update) {
|
|
if (enabled) {
|
|
if (!is_in_group("_smart_object"))
|
|
add_to_group("_smart_object");
|
|
} else {
|
|
if (is_in_group("_smart_object"))
|
|
remove_from_group("_smart_object");
|
|
}
|
|
_change_notify();
|
|
#ifdef TOOLS_ENABLED
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
if (run_in_editor)
|
|
draw_debug();
|
|
else
|
|
clear_debug();
|
|
}
|
|
#endif
|
|
}
|
|
return update;
|
|
}
|
|
|
|
void SmartObjectManager::play_animation(Node *cnode, const String &anim_state)
|
|
{
|
|
Node *anode = cnode->get_meta("animation_tree");
|
|
assert(anode);
|
|
AnimationTree *anim = Object::cast_to<AnimationTree>(anode);
|
|
assert(anim);
|
|
Ref<AnimationNodeStateMachinePlayback> playback;
|
|
playback = anim->get("parameters/state/playback");
|
|
assert(playback.ptr());
|
|
playback->travel(anim_state);
|
|
printf("ANIMATION play %ls\n", anim_state.c_str());
|
|
}
|
|
|
|
void SmartObjectManager::update_state(SmartObject *sm)
|
|
{
|
|
if (sm->state == SmartObject::STATE_INIT)
|
|
return;
|
|
Object *obj = ObjectDB::get_instance(sm->npc_id);
|
|
assert(obj);
|
|
Node *ch = Object::cast_to<Node>(obj);
|
|
assert(ch);
|
|
Spatial *csp = Object::cast_to<Spatial>(obj);
|
|
assert(csp);
|
|
Transform oxform = sm->get_global_transform();
|
|
Transform cxform = csp->get_global_transform();
|
|
switch(sm->state) {
|
|
case SmartObject::STATE_INIT:
|
|
break;
|
|
case SmartObject::STATE_SELECTED:
|
|
printf("state == SELECTED\n");
|
|
if (sm->teleport) {
|
|
csp->set_global_transform(oxform);
|
|
Transform orientation = ch->get_meta("orientation");
|
|
orientation.basis = oxform.basis;
|
|
ch->set_meta("orientation", orientation);
|
|
printf("arrived: teleport\n");
|
|
sm->state = SmartObject::STATE_ACTIVE;
|
|
} else {
|
|
if (!cxform.origin.is_equal_approx(oxform.origin))
|
|
sm->state = SmartObject::STATE_CLOSE;
|
|
else
|
|
sm->state = SmartObject::STATE_ACTIVE;
|
|
}
|
|
break;
|
|
case SmartObject::STATE_CLOSE:
|
|
printf("state == CLOSE\n");
|
|
if (!cxform.origin.is_equal_approx(oxform.origin)) {
|
|
csp->set_global_transform(cxform.interpolate_with(oxform, get_process_delta_time()));
|
|
printf("arrived: interpolate pos\n");
|
|
} else
|
|
sm->state = SmartObject::STATE_ACTIVE;
|
|
break;
|
|
case SmartObject::STATE_ACTIVE:
|
|
printf("state == ACTIVE\n");
|
|
printf("arrived: at position\n");
|
|
printf("SMART object active\n");
|
|
ch->set_meta("smart_object", Object::cast_to<Object>(sm));
|
|
play_animation(ch, sm->anim_state);
|
|
sm->state = SmartObject::STATE_PROGRESS;
|
|
break;
|
|
case SmartObject::STATE_PROGRESS:
|
|
break;
|
|
case SmartObject::STATE_FINISH:
|
|
play_animation(ch, sm->anim_finish_state);
|
|
ch->remove_meta("smart_object");
|
|
ch->remove_meta("_target");
|
|
ch->remove_meta("_target_node");
|
|
sm->state = SmartObject::STATE_INIT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SmartObjectManager::arrived(SmartObject *sm, Node *ch)
|
|
{
|
|
assert(sm);
|
|
assert(ch);
|
|
assert(!ch->has_meta("smart_object"));
|
|
Spatial *csp = Object::cast_to<Spatial>(ch);
|
|
assert(csp);
|
|
Node *onode = Object::cast_to<Node>(sm);
|
|
assert(onode);
|
|
Transform oxform = sm->get_global_transform();
|
|
Transform cxform = csp->get_global_transform();
|
|
switch(sm->state) {
|
|
case SmartObject::STATE_INIT:
|
|
printf("arrived: warning: STATE_INIT\n");
|
|
sm->state = SmartObject::STATE_SELECTED;
|
|
sm->npc_id = ch->get_instance_id();
|
|
/* no break - transition to next state */
|
|
case SmartObject::STATE_SELECTED:
|
|
case SmartObject::STATE_CLOSE:
|
|
case SmartObject::STATE_ACTIVE:
|
|
update_state(sm);
|
|
break;
|
|
case SmartObject::STATE_PROGRESS:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SmartObjectManager::_notification(int p_what)
|
|
{
|
|
List<Node *> char_node_list;
|
|
List<Node *> smart_object_list;
|
|
List<Node *>::Element *e, *c;
|
|
switch(p_what) {
|
|
case NOTIFICATION_PHYSICS_PROCESS:
|
|
/* don't run in editor */
|
|
if (Engine::get_singleton()->is_editor_hint())
|
|
return;
|
|
get_tree()->get_nodes_in_group("character", &char_node_list);
|
|
get_tree()->get_nodes_in_group("_smart_object", &smart_object_list);
|
|
for (e = char_node_list.front(); e; e = e->next()) {
|
|
Node *cnode = e->get();
|
|
if (!cnode->has_meta("_target"))
|
|
continue;
|
|
if (cnode->has_meta("smart_object"))
|
|
continue;
|
|
for (c = smart_object_list.front(); c; c = c->next()) {
|
|
Node *onode = c->get();
|
|
if (!onode)
|
|
continue;
|
|
if (cnode->has_meta("_target_node"))
|
|
continue;
|
|
Vector3 ctarget = cnode->get_meta("_target");
|
|
SmartObject *obj = Object::cast_to<SmartObject>(onode);
|
|
if (!obj)
|
|
continue;
|
|
cnode->set_meta("_target_node", onode);
|
|
#if 0
|
|
if (ctarget.is_equal_approx(oxform.origin)) {
|
|
float d = cxform.origin.distance_squared_to(oxform.origin);
|
|
if (d < obj->distance * obj->distance) {
|
|
arrived(obj, cnode);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
case NOTIFICATION_READY:
|
|
set_physics_process(true);
|
|
break;
|
|
}
|
|
}
|
|
|