summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSmallJoker <SmallJoker@users.noreply.github.com>2018-04-30 18:43:49 +0200
committerGitHub <noreply@github.com>2018-04-30 18:43:49 +0200
commitba91624d8c354bac49c35a449029b6712022d0cb (patch)
tree8a6423ab354888691927f852754f86816f9c90c5
parent0b5b32b026291d5bd8e47acebca10259a379fc1c (diff)
downloadminetest-ba91624d8c354bac49c35a449029b6712022d0cb.tar.gz
minetest-ba91624d8c354bac49c35a449029b6712022d0cb.tar.bz2
minetest-ba91624d8c354bac49c35a449029b6712022d0cb.zip
Allow damage for attached objects, add attach/detach callbacks (#6786)
* Allow right-clicking on attached LuaEntities
-rw-r--r--doc/lua_api.txt7
-rw-r--r--src/content_cao.cpp24
-rw-r--r--src/content_sao.cpp78
-rw-r--r--src/content_sao.h7
-rw-r--r--src/script/cpp_api/s_entity.cpp53
-rw-r--r--src/script/cpp_api/s_entity.h9
-rw-r--r--src/script/lua_api/l_object.cpp24
-rw-r--r--src/server.cpp1
-rw-r--r--src/serverobject.h5
9 files changed, 133 insertions, 75 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<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;
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<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);
};
/*
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="<<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");
+}
diff --git a/src/script/cpp_api/s_entity.h b/src/script/cpp_api/s_entity.h
index 173e24c30..966c2745e 100644
--- a/src/script/cpp_api/s_entity.h
+++ b/src/script/cpp_api/s_entity.h
@@ -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);
};
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 0bef23541..b3c3a55bf 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -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;
}
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<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,