Allow damage for attached objects, add attach/detach callbacks (#6786)
authorSmallJoker <SmallJoker@users.noreply.github.com>
Mon, 30 Apr 2018 16:43:49 +0000 (18:43 +0200)
committerGitHub <noreply@github.com>
Mon, 30 Apr 2018 16:43:49 +0000 (18:43 +0200)
* Allow right-clicking on attached LuaEntities

doc/lua_api.txt
src/content_cao.cpp
src/content_sao.cpp
src/content_sao.h
src/script/cpp_api/s_entity.cpp
src/script/cpp_api/s_entity.h
src/script/lua_api/l_object.cpp
src/server.cpp
src/serverobject.h

index 5442612affe68a4caad6618ee0b05a55a8ede464..dae3452a1e67831e688cf9a4ccfda1d945b06959 100644 (file)
@@ -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.
index 4dde1b0985465db8a9a706f6b7520aaaa2520d1d..9344b3370669c69f5e6d2bd8a8e5960680a1f3e7 100644 (file)
@@ -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);
index c554b775d80daf3935d54a2ea1ef372cf5ecfe8e..1c049c727c208fdf28a848985e594fdd1cbef6aa 100644 (file)
@@ -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<int> &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;
 
index 59e3b3d4ba0edb3d17624e0c5a073261298fcb71..486e2d252f01e7b7f778a17ee9858f4fb8e09acc 100644 (file)
@@ -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<int> &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<std::string, core::vector2d<v3f>> 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);
 };
 
 /*
index df4b77936836fba837154c63e5a1d472c680a02c..88dbcc620757fa62b10ad3eb0cc8564b4ab7fae7 100644 (file)
@@ -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="<<id<<std::endl;
-
-       int error_handler = PUSH_ERROR_HANDLER(L);
+       return luaentity_run_simple_callback(id, killer, "on_death");
+}
 
-       // Get core.luaentities[id]
-       luaentity_get(L, id);
-       int object = lua_gettop(L);
-       // State: object is at top of stack
-       // Get function
-       lua_getfield(L, -1, "on_rightclick");
-       if (lua_isnil(L, -1)) {
-               lua_pop(L, 2); // Pop on_rightclick and entity
-               return;
-       }
-       luaL_checktype(L, -1, LUA_TFUNCTION);
-       lua_pushvalue(L, object); // self
-       objectrefGetOrCreate(L, clicker); // Clicker reference
+// Calls entity:on_rightclick(ObjectRef clicker)
+void ScriptApiEntity::luaentity_Rightclick(u16 id, ServerActiveObject *clicker)
+{
+       luaentity_run_simple_callback(id, clicker, "on_rightclick");
+}
 
-       setOriginFromTable(object);
-       PCALL_RES(lua_pcall(L, 2, 0, error_handler));
+void ScriptApiEntity::luaentity_on_attach_child(u16 id, ServerActiveObject *child)
+{
+       luaentity_run_simple_callback(id, child, "on_attach_child");
+}
 
-       lua_pop(L, 2); // Pop object and error handler
+void ScriptApiEntity::luaentity_on_detach_child(u16 id, ServerActiveObject *child)
+{
+       luaentity_run_simple_callback(id, child, "on_detach_child");
 }
 
+void ScriptApiEntity::luaentity_on_detach(u16 id, ServerActiveObject *parent)
+{
+       luaentity_run_simple_callback(id, parent, "on_detach");
+}
index 173e24c3082e53886c54d847bb3e372371ae98ae..966c2745ef54f7682d07ad2788730503912d292c 100644 (file)
@@ -41,6 +41,11 @@ public:
                        ServerActiveObject *puncher, float time_from_last_punch,
                        const ToolCapabilities *toolcap, v3f dir, s16 damage);
        bool luaentity_on_death(u16 id, ServerActiveObject *killer);
-       void luaentity_Rightclick(u16 id,
-                       ServerActiveObject *clicker);
+       void luaentity_Rightclick(u16 id, ServerActiveObject *clicker);
+       void luaentity_on_attach_child(u16 id, ServerActiveObject *child);
+       void luaentity_on_detach_child(u16 id, ServerActiveObject *child);
+       void luaentity_on_detach(u16 id, ServerActiveObject *parent);
+private:
+       bool luaentity_run_simple_callback(u16 id, ServerActiveObject *sao,
+               const char *field);
 };
index 0bef2354184b84766e34a3610a03f5144f78d80b..b3c3a55bf9597ccf441b7b81d860576e74848a04 100644 (file)
@@ -103,12 +103,8 @@ int ObjectRef::l_remove(lua_State *L)
        if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
                return 0;
 
-       const std::unordered_set<int> &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;
 }
 
index dad8f17121687e239eaf3ec1d88d5abeea7acd59..9d4c133255bf4ad61c1eb40d8c4aa7291751f1ce 100644 (file)
@@ -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);
index 77b70146426fd9f701d46a94b11091cfd9212c4a..ba205f6a5762849ba5110a08e4309b9a2f673a29 100644 (file)
@@ -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<ActiveObjectMessage> 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,