From ba91624d8c354bac49c35a449029b6712022d0cb Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 30 Apr 2018 18:43:49 +0200 Subject: [PATCH] Allow damage for attached objects, add attach/detach callbacks (#6786) * Allow right-clicking on attached LuaEntities --- doc/lua_api.txt | 7 +++ src/content_cao.cpp | 24 +++++----- src/content_sao.cpp | 78 ++++++++++++++++++++++++++++----- src/content_sao.h | 7 ++- src/script/cpp_api/s_entity.cpp | 59 ++++++++++++------------- src/script/cpp_api/s_entity.h | 9 +++- src/script/lua_api/l_object.cpp | 24 ++-------- src/server.cpp | 1 + src/serverobject.h | 5 +++ 9 files changed, 136 insertions(+), 78 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5442612af..dae3452a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4868,6 +4868,13 @@ Registered entities * Called when the object dies. * `killer`: an `ObjectRef` (can be `nil`) * `on_rightclick(self, clicker)` + * `on_attach_child(self, child)` + * `child`: an `ObjectRef` (can be `nil`) of the child that attaches + * `on_detach_child(self, child)` + * `child`: an `ObjectRef` (can be `nil`) of the child that detaches + * `on_detach(self, parent)` + * `parent`: an `ObjectRef` (can be `nil`) from where it got detached + * This happens before the parent object is removed from the world * `get_staticdata(self)` * Should return a string that will be passed to `on_activate` when the object is instantiated the next time. diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 4dde1b098..9344b3370 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -343,7 +343,7 @@ GenericCAO::~GenericCAO() bool GenericCAO::getSelectionBox(aabb3f *toset) const { if (!m_prop.is_visible || !m_is_visible || m_is_local_player - || !m_prop.pointable || getParent() != NULL) { + || !m_prop.pointable) { return false; } *toset = m_selection_box; @@ -430,6 +430,7 @@ void GenericCAO::removeFromScene(bool permanent) m_env->attachement_parent_ids[ci] = 0; } } + m_children.clear(); m_env->attachement_parent_ids[getId()] = 0; @@ -1409,17 +1410,18 @@ void GenericCAO::processMessage(const std::string &data) updateBonePosition(); } else if (cmd == GENERIC_CMD_ATTACH_TO) { - u16 parentID = readS16(is); - u16 oldparent = m_env->attachement_parent_ids[getId()]; - if (oldparent) { - m_children.erase(std::remove(m_children.begin(), m_children.end(), - getId()), m_children.end()); - } - m_env->attachement_parent_ids[getId()] = parentID; - GenericCAO *parentobj = m_env->getGenericCAO(parentID); + u16 parent_id = readS16(is); + u16 &old_parent_id = m_env->attachement_parent_ids[getId()]; + if (parent_id != old_parent_id) { + if (GenericCAO *old_parent = m_env->getGenericCAO(old_parent_id)) { + old_parent->m_children.erase(std::remove( + m_children.begin(), m_children.end(), + getId()), m_children.end()); + } + if (GenericCAO *new_parent = m_env->getGenericCAO(parent_id)) + new_parent->m_children.push_back(getId()); - if (parentobj) { - parentobj->m_children.push_back(getId()); + old_parent_id = parent_id; } m_attachment_bone = deSerializeString(is); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index c554b775d..1c049c727 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -187,11 +187,17 @@ void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position // This breaks some things so we also give the server the most accurate representation // even if players only see the client changes. + int old_parent = m_attachment_parent_id; m_attachment_parent_id = parent_id; m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; m_attachment_sent = false; + + if (parent_id != old_parent) { + onDetach(old_parent); + onAttach(parent_id); + } } void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, @@ -203,6 +209,30 @@ void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, *rotation = m_attachment_rotation; } +void UnitSAO::clearChildAttachments() +{ + for (int child_id : m_attachment_child_ids) { + // Child can be NULL if it was deleted earlier + if (ServerActiveObject *child = m_env->getActiveObject(child_id)) + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + m_attachment_child_ids.clear(); +} + +void UnitSAO::clearParentAttachment() +{ + ServerActiveObject *parent = nullptr; + if (m_attachment_parent_id) { + parent = m_env->getActiveObject(m_attachment_parent_id); + setAttachment(0, "", m_attachment_position, m_attachment_rotation); + } else { + setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + // Do it + if (parent) + parent->removeAttachmentChild(m_id); +} + void UnitSAO::addAttachmentChild(int child_id) { m_attachment_child_ids.insert(child_id); @@ -218,6 +248,38 @@ const std::unordered_set &UnitSAO::getAttachmentChildIds() return m_attachment_child_ids; } +void UnitSAO::onAttach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + // Call parent's on_attach field + m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this); + } +} + +void UnitSAO::onDetach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach(m_id, parent); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); +} + ObjectProperties* UnitSAO::accessObjectProperties() { return &m_prop; @@ -548,10 +610,6 @@ int LuaEntitySAO::punch(v3f dir, return 0; } - // It's best that attachments cannot be punched - if (isAttached()) - return 0; - ItemStack *punchitem = NULL; ItemStack punchitem_static; if (puncher) { @@ -588,8 +646,10 @@ int LuaEntitySAO::punch(v3f dir, } } - if (getHP() == 0) { + if (getHP() == 0 && !isGone()) { m_pending_removal = true; + clearParentAttachment(); + clearChildAttachments(); m_env->getScriptIface()->luaentity_on_death(m_id, puncher); } @@ -600,9 +660,7 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker) { if (!m_registered) return; - // It's best that attachments cannot be clicked - if (isAttached()) - return; + m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); } @@ -1187,10 +1245,6 @@ int PlayerSAO::punch(v3f dir, ServerActiveObject *puncher, float time_from_last_punch) { - // It's best that attachments cannot be punched - if (isAttached()) - return 0; - if (!toolcap) return 0; diff --git a/src/content_sao.h b/src/content_sao.h index 59e3b3d4b..486e2d252 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -52,6 +52,8 @@ public: void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); + void clearChildAttachments(); + void clearParentAttachment(); void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); const std::unordered_set &getAttachmentChildIds(); @@ -72,7 +74,7 @@ protected: float m_animation_blend = 0.0f; bool m_animation_loop = true; bool m_animation_sent = false; - bool m_animation_speed_sent = false; + bool m_animation_speed_sent = false; // Stores position and rotation for each bone name std::unordered_map> m_bone_position; @@ -84,6 +86,9 @@ protected: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; +private: + void onAttach(int parent_id); + void onDetach(int parent_id); }; /* diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index df4b77936..88dbcc620 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -262,7 +262,9 @@ bool ScriptApiEntity::luaentity_Punch(u16 id, return retval; } -bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer) +// Calls entity[field](ObjectRef self, ObjectRef sao) +bool ScriptApiEntity::luaentity_run_simple_callback(u16 id, + ServerActiveObject *sao, const char *field) { SCRIPTAPI_PRECHECKHEADER @@ -273,14 +275,14 @@ bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer) int object = lua_gettop(L); // State: object is at top of stack // Get function - lua_getfield(L, -1, "on_death"); + lua_getfield(L, -1, field); if (lua_isnil(L, -1)) { - lua_pop(L, 2); // Pop on_death and entity + lua_pop(L, 2); // Pop callback field and entity return false; } luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self - objectrefGetOrCreate(L, killer); // killer reference + objectrefGetOrCreate(L, sao); // killer reference setOriginFromTable(object); PCALL_RES(lua_pcall(L, 2, 1, error_handler)); @@ -290,33 +292,28 @@ bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer) return retval; } -// Calls entity:on_rightclick(ObjectRef clicker) -void ScriptApiEntity::luaentity_Rightclick(u16 id, - ServerActiveObject *clicker) +bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer) { - SCRIPTAPI_PRECHECKHEADER - - //infostream<<"scriptapi_luaentity_step: id="<getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - const std::unordered_set &child_ids = co->getAttachmentChildIds(); - for (int child_id : child_ids) { - // Child can be NULL if it was deleted earlier - if (ServerActiveObject *child = env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } + co->clearChildAttachments(); + co->clearParentAttachment(); verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl; co->m_pending_removal = true; @@ -721,21 +717,7 @@ int ObjectRef::l_set_detach(lua_State *L) if (co == NULL) return 0; - int parent_id = 0; - std::string bone; - v3f position; - v3f rotation; - co->getAttachment(&parent_id, &bone, &position, &rotation); - ServerActiveObject *parent = NULL; - if (parent_id) { - parent = env->getActiveObject(parent_id); - co->setAttachment(0, "", position, rotation); - } else { - co->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } - // Do it - if (parent != NULL) - parent->removeAttachmentChild(co->getId()); + co->clearParentAttachment(); return 0; } diff --git a/src/server.cpp b/src/server.cpp index dad8f1712..9d4c13325 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2504,6 +2504,7 @@ void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason) << " dies" << std::endl; playersao->setHP(0, reason); + playersao->clearParentAttachment(); // Trigger scripted stuff m_script->on_dieplayer(playersao, reason); diff --git a/src/serverobject.h b/src/serverobject.h index 77b701464..ba205f6a5 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -165,6 +165,8 @@ public: {} virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation) {} + virtual void clearChildAttachments() {} + virtual void clearParentAttachment() {} virtual void addAttachmentChild(int child_id) {} virtual void removeAttachmentChild(int child_id) @@ -250,6 +252,9 @@ public: std::queue m_messages_out; protected: + virtual void onAttach(int parent_id) {} + virtual void onDetach(int parent_id) {} + // Used for creating objects based on type typedef ServerActiveObject* (*Factory) (ServerEnvironment *env, v3f pos,