Allow damage for attached objects, add attach/detach callbacks (#6786)
* Allow right-clicking on attached LuaEntitiesmaster
parent
0b5b32b026
commit
ba91624d8c
|
@ -4868,6 +4868,13 @@ Registered entities
|
||||||
* Called when the object dies.
|
* Called when the object dies.
|
||||||
* `killer`: an `ObjectRef` (can be `nil`)
|
* `killer`: an `ObjectRef` (can be `nil`)
|
||||||
* `on_rightclick(self, clicker)`
|
* `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)`
|
* `get_staticdata(self)`
|
||||||
* Should return a string that will be passed to `on_activate` when
|
* Should return a string that will be passed to `on_activate` when
|
||||||
the object is instantiated the next time.
|
the object is instantiated the next time.
|
||||||
|
|
|
@ -343,7 +343,7 @@ GenericCAO::~GenericCAO()
|
||||||
bool GenericCAO::getSelectionBox(aabb3f *toset) const
|
bool GenericCAO::getSelectionBox(aabb3f *toset) const
|
||||||
{
|
{
|
||||||
if (!m_prop.is_visible || !m_is_visible || m_is_local_player
|
if (!m_prop.is_visible || !m_is_visible || m_is_local_player
|
||||||
|| !m_prop.pointable || getParent() != NULL) {
|
|| !m_prop.pointable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*toset = m_selection_box;
|
*toset = m_selection_box;
|
||||||
|
@ -430,6 +430,7 @@ void GenericCAO::removeFromScene(bool permanent)
|
||||||
m_env->attachement_parent_ids[ci] = 0;
|
m_env->attachement_parent_ids[ci] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_children.clear();
|
||||||
|
|
||||||
m_env->attachement_parent_ids[getId()] = 0;
|
m_env->attachement_parent_ids[getId()] = 0;
|
||||||
|
|
||||||
|
@ -1409,17 +1410,18 @@ void GenericCAO::processMessage(const std::string &data)
|
||||||
|
|
||||||
updateBonePosition();
|
updateBonePosition();
|
||||||
} else if (cmd == GENERIC_CMD_ATTACH_TO) {
|
} else if (cmd == GENERIC_CMD_ATTACH_TO) {
|
||||||
u16 parentID = readS16(is);
|
u16 parent_id = readS16(is);
|
||||||
u16 oldparent = m_env->attachement_parent_ids[getId()];
|
u16 &old_parent_id = m_env->attachement_parent_ids[getId()];
|
||||||
if (oldparent) {
|
if (parent_id != old_parent_id) {
|
||||||
m_children.erase(std::remove(m_children.begin(), m_children.end(),
|
if (GenericCAO *old_parent = m_env->getGenericCAO(old_parent_id)) {
|
||||||
getId()), m_children.end());
|
old_parent->m_children.erase(std::remove(
|
||||||
}
|
m_children.begin(), m_children.end(),
|
||||||
m_env->attachement_parent_ids[getId()] = parentID;
|
getId()), m_children.end());
|
||||||
GenericCAO *parentobj = m_env->getGenericCAO(parentID);
|
}
|
||||||
|
if (GenericCAO *new_parent = m_env->getGenericCAO(parent_id))
|
||||||
|
new_parent->m_children.push_back(getId());
|
||||||
|
|
||||||
if (parentobj) {
|
old_parent_id = parent_id;
|
||||||
parentobj->m_children.push_back(getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_attachment_bone = deSerializeString(is);
|
m_attachment_bone = deSerializeString(is);
|
||||||
|
|
|
@ -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
|
// This breaks some things so we also give the server the most accurate representation
|
||||||
// even if players only see the client changes.
|
// even if players only see the client changes.
|
||||||
|
|
||||||
|
int old_parent = m_attachment_parent_id;
|
||||||
m_attachment_parent_id = parent_id;
|
m_attachment_parent_id = parent_id;
|
||||||
m_attachment_bone = bone;
|
m_attachment_bone = bone;
|
||||||
m_attachment_position = position;
|
m_attachment_position = position;
|
||||||
m_attachment_rotation = rotation;
|
m_attachment_rotation = rotation;
|
||||||
m_attachment_sent = false;
|
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,
|
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;
|
*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)
|
void UnitSAO::addAttachmentChild(int child_id)
|
||||||
{
|
{
|
||||||
m_attachment_child_ids.insert(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;
|
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()
|
ObjectProperties* UnitSAO::accessObjectProperties()
|
||||||
{
|
{
|
||||||
return &m_prop;
|
return &m_prop;
|
||||||
|
@ -548,10 +610,6 @@ int LuaEntitySAO::punch(v3f dir,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's best that attachments cannot be punched
|
|
||||||
if (isAttached())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ItemStack *punchitem = NULL;
|
ItemStack *punchitem = NULL;
|
||||||
ItemStack punchitem_static;
|
ItemStack punchitem_static;
|
||||||
if (puncher) {
|
if (puncher) {
|
||||||
|
@ -588,8 +646,10 @@ int LuaEntitySAO::punch(v3f dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getHP() == 0) {
|
if (getHP() == 0 && !isGone()) {
|
||||||
m_pending_removal = true;
|
m_pending_removal = true;
|
||||||
|
clearParentAttachment();
|
||||||
|
clearChildAttachments();
|
||||||
m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
|
m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,9 +660,7 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
|
||||||
{
|
{
|
||||||
if (!m_registered)
|
if (!m_registered)
|
||||||
return;
|
return;
|
||||||
// It's best that attachments cannot be clicked
|
|
||||||
if (isAttached())
|
|
||||||
return;
|
|
||||||
m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
|
m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1187,10 +1245,6 @@ int PlayerSAO::punch(v3f dir,
|
||||||
ServerActiveObject *puncher,
|
ServerActiveObject *puncher,
|
||||||
float time_from_last_punch)
|
float time_from_last_punch)
|
||||||
{
|
{
|
||||||
// It's best that attachments cannot be punched
|
|
||||||
if (isAttached())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!toolcap)
|
if (!toolcap)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ public:
|
||||||
void getBonePosition(const std::string &bone, v3f *position, v3f *rotation);
|
void getBonePosition(const std::string &bone, v3f *position, v3f *rotation);
|
||||||
void setAttachment(int parent_id, 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 getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
|
||||||
|
void clearChildAttachments();
|
||||||
|
void clearParentAttachment();
|
||||||
void addAttachmentChild(int child_id);
|
void addAttachmentChild(int child_id);
|
||||||
void removeAttachmentChild(int child_id);
|
void removeAttachmentChild(int child_id);
|
||||||
const std::unordered_set<int> &getAttachmentChildIds();
|
const std::unordered_set<int> &getAttachmentChildIds();
|
||||||
|
@ -72,7 +74,7 @@ protected:
|
||||||
float m_animation_blend = 0.0f;
|
float m_animation_blend = 0.0f;
|
||||||
bool m_animation_loop = true;
|
bool m_animation_loop = true;
|
||||||
bool m_animation_sent = false;
|
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
|
// Stores position and rotation for each bone name
|
||||||
std::unordered_map<std::string, core::vector2d<v3f>> m_bone_position;
|
std::unordered_map<std::string, core::vector2d<v3f>> m_bone_position;
|
||||||
|
@ -84,6 +86,9 @@ protected:
|
||||||
v3f m_attachment_position;
|
v3f m_attachment_position;
|
||||||
v3f m_attachment_rotation;
|
v3f m_attachment_rotation;
|
||||||
bool m_attachment_sent = false;
|
bool m_attachment_sent = false;
|
||||||
|
private:
|
||||||
|
void onAttach(int parent_id);
|
||||||
|
void onDetach(int parent_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -262,7 +262,9 @@ bool ScriptApiEntity::luaentity_Punch(u16 id,
|
||||||
return retval;
|
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
|
SCRIPTAPI_PRECHECKHEADER
|
||||||
|
|
||||||
|
@ -273,14 +275,14 @@ bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer)
|
||||||
int object = lua_gettop(L);
|
int object = lua_gettop(L);
|
||||||
// State: object is at top of stack
|
// State: object is at top of stack
|
||||||
// Get function
|
// Get function
|
||||||
lua_getfield(L, -1, "on_death");
|
lua_getfield(L, -1, field);
|
||||||
if (lua_isnil(L, -1)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
luaL_checktype(L, -1, LUA_TFUNCTION);
|
luaL_checktype(L, -1, LUA_TFUNCTION);
|
||||||
lua_pushvalue(L, object); // self
|
lua_pushvalue(L, object); // self
|
||||||
objectrefGetOrCreate(L, killer); // killer reference
|
objectrefGetOrCreate(L, sao); // killer reference
|
||||||
|
|
||||||
setOriginFromTable(object);
|
setOriginFromTable(object);
|
||||||
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
|
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
|
||||||
|
@ -290,33 +292,28 @@ bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calls entity:on_rightclick(ObjectRef clicker)
|
bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer)
|
||||||
void ScriptApiEntity::luaentity_Rightclick(u16 id,
|
|
||||||
ServerActiveObject *clicker)
|
|
||||||
{
|
{
|
||||||
SCRIPTAPI_PRECHECKHEADER
|
return luaentity_run_simple_callback(id, killer, "on_death");
|
||||||
|
|
||||||
//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
|
|
||||||
|
|
||||||
int error_handler = PUSH_ERROR_HANDLER(L);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
setOriginFromTable(object);
|
|
||||||
PCALL_RES(lua_pcall(L, 2, 0, error_handler));
|
|
||||||
|
|
||||||
lua_pop(L, 2); // Pop object and error handler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calls entity:on_rightclick(ObjectRef clicker)
|
||||||
|
void ScriptApiEntity::luaentity_Rightclick(u16 id, ServerActiveObject *clicker)
|
||||||
|
{
|
||||||
|
luaentity_run_simple_callback(id, clicker, "on_rightclick");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptApiEntity::luaentity_on_attach_child(u16 id, ServerActiveObject *child)
|
||||||
|
{
|
||||||
|
luaentity_run_simple_callback(id, child, "on_attach_child");
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,11 @@ public:
|
||||||
ServerActiveObject *puncher, float time_from_last_punch,
|
ServerActiveObject *puncher, float time_from_last_punch,
|
||||||
const ToolCapabilities *toolcap, v3f dir, s16 damage);
|
const ToolCapabilities *toolcap, v3f dir, s16 damage);
|
||||||
bool luaentity_on_death(u16 id, ServerActiveObject *killer);
|
bool luaentity_on_death(u16 id, ServerActiveObject *killer);
|
||||||
void luaentity_Rightclick(u16 id,
|
void luaentity_Rightclick(u16 id, ServerActiveObject *clicker);
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -103,12 +103,8 @@ int ObjectRef::l_remove(lua_State *L)
|
||||||
if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
|
if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const std::unordered_set<int> &child_ids = co->getAttachmentChildIds();
|
co->clearChildAttachments();
|
||||||
for (int child_id : child_ids) {
|
co->clearParentAttachment();
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl;
|
verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl;
|
||||||
co->m_pending_removal = true;
|
co->m_pending_removal = true;
|
||||||
|
@ -721,21 +717,7 @@ int ObjectRef::l_set_detach(lua_State *L)
|
||||||
if (co == NULL)
|
if (co == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int parent_id = 0;
|
co->clearParentAttachment();
|
||||||
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());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2504,6 +2504,7 @@ void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
|
||||||
<< " dies" << std::endl;
|
<< " dies" << std::endl;
|
||||||
|
|
||||||
playersao->setHP(0, reason);
|
playersao->setHP(0, reason);
|
||||||
|
playersao->clearParentAttachment();
|
||||||
|
|
||||||
// Trigger scripted stuff
|
// Trigger scripted stuff
|
||||||
m_script->on_dieplayer(playersao, reason);
|
m_script->on_dieplayer(playersao, reason);
|
||||||
|
|
|
@ -165,6 +165,8 @@ public:
|
||||||
{}
|
{}
|
||||||
virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation)
|
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 addAttachmentChild(int child_id)
|
||||||
{}
|
{}
|
||||||
virtual void removeAttachmentChild(int child_id)
|
virtual void removeAttachmentChild(int child_id)
|
||||||
|
@ -250,6 +252,9 @@ public:
|
||||||
std::queue<ActiveObjectMessage> m_messages_out;
|
std::queue<ActiveObjectMessage> m_messages_out;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void onAttach(int parent_id) {}
|
||||||
|
virtual void onDetach(int parent_id) {}
|
||||||
|
|
||||||
// Used for creating objects based on type
|
// Used for creating objects based on type
|
||||||
typedef ServerActiveObject* (*Factory)
|
typedef ServerActiveObject* (*Factory)
|
||||||
(ServerEnvironment *env, v3f pos,
|
(ServerEnvironment *env, v3f pos,
|
||||||
|
|
Loading…
Reference in New Issue