mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-27 17:28:41 +00:00
Fixes some other third person camera specific attachments. Implements a single new flag for entities to be forced visible in first person mode. Old mods do not need to be updated to use the new flag and are fully backwards compatible.
348 lines
9.5 KiB
C++
348 lines
9.5 KiB
C++
/*
|
|
Minetest
|
|
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
Copyright (C) 2013-2020 Minetest core developers & community
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "unit_sao.h"
|
|
#include "scripting_server.h"
|
|
#include "serverenvironment.h"
|
|
|
|
UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos)
|
|
{
|
|
// Initialize something to armor groups
|
|
m_armor_groups["fleshy"] = 100;
|
|
}
|
|
|
|
ServerActiveObject *UnitSAO::getParent() const
|
|
{
|
|
if (!m_attachment_parent_id)
|
|
return nullptr;
|
|
// Check if the parent still exists
|
|
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
|
|
|
|
return obj;
|
|
}
|
|
|
|
void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
|
|
{
|
|
m_armor_groups = armor_groups;
|
|
m_armor_groups_sent = false;
|
|
}
|
|
|
|
const ItemGroupList &UnitSAO::getArmorGroups() const
|
|
{
|
|
return m_armor_groups;
|
|
}
|
|
|
|
void UnitSAO::setAnimation(
|
|
v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
|
|
{
|
|
// store these so they can be updated to clients
|
|
m_animation_range = frame_range;
|
|
m_animation_speed = frame_speed;
|
|
m_animation_blend = frame_blend;
|
|
m_animation_loop = frame_loop;
|
|
m_animation_sent = false;
|
|
}
|
|
|
|
void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend,
|
|
bool *frame_loop)
|
|
{
|
|
*frame_range = m_animation_range;
|
|
*frame_speed = m_animation_speed;
|
|
*frame_blend = m_animation_blend;
|
|
*frame_loop = m_animation_loop;
|
|
}
|
|
|
|
void UnitSAO::setAnimationSpeed(float frame_speed)
|
|
{
|
|
m_animation_speed = frame_speed;
|
|
m_animation_speed_sent = false;
|
|
}
|
|
|
|
void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
|
|
{
|
|
// store these so they can be updated to clients
|
|
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
|
|
m_bone_position_sent = false;
|
|
}
|
|
|
|
void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
|
|
{
|
|
*position = m_bone_position[bone].X;
|
|
*rotation = m_bone_position[bone].Y;
|
|
}
|
|
|
|
// clang-format off
|
|
void UnitSAO::sendOutdatedData()
|
|
{
|
|
if (!m_armor_groups_sent) {
|
|
m_armor_groups_sent = true;
|
|
m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand());
|
|
}
|
|
|
|
if (!m_animation_sent) {
|
|
m_animation_sent = true;
|
|
m_animation_speed_sent = true;
|
|
m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand());
|
|
} else if (!m_animation_speed_sent) {
|
|
// Animation speed is also sent when 'm_animation_sent == false'
|
|
m_animation_speed_sent = true;
|
|
m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand());
|
|
}
|
|
|
|
if (!m_bone_position_sent) {
|
|
m_bone_position_sent = true;
|
|
for (const auto &bone_pos : m_bone_position) {
|
|
m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand(
|
|
bone_pos.first, bone_pos.second.X, bone_pos.second.Y));
|
|
}
|
|
}
|
|
|
|
if (!m_attachment_sent) {
|
|
m_attachment_sent = true;
|
|
m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand());
|
|
}
|
|
}
|
|
// clang-format on
|
|
|
|
void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position,
|
|
v3f rotation, bool force_visible)
|
|
{
|
|
// Attachments need to be handled on both the server and client.
|
|
// If we just attach on the server, we can only copy the position of the parent.
|
|
// Attachments are still sent to clients at an interval so players might see them
|
|
// lagging, plus we can't read and attach to skeletal bones. If we just attach on
|
|
// the client, the server still sees the child at its original location. 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_force_visible = force_visible;
|
|
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,
|
|
v3f *rotation, bool *force_visible) const
|
|
{
|
|
*parent_id = m_attachment_parent_id;
|
|
*bone = m_attachment_bone;
|
|
*position = m_attachment_position;
|
|
*rotation = m_attachment_rotation;
|
|
*force_visible = m_force_visible;
|
|
}
|
|
|
|
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), false);
|
|
}
|
|
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, false);
|
|
} else {
|
|
setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false);
|
|
}
|
|
// Do it
|
|
if (parent)
|
|
parent->removeAttachmentChild(m_id);
|
|
}
|
|
|
|
void UnitSAO::addAttachmentChild(int child_id)
|
|
{
|
|
m_attachment_child_ids.insert(child_id);
|
|
}
|
|
|
|
void UnitSAO::removeAttachmentChild(int child_id)
|
|
{
|
|
m_attachment_child_ids.erase(child_id);
|
|
}
|
|
|
|
const std::unordered_set<int> &UnitSAO::getAttachmentChildIds() const
|
|
{
|
|
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;
|
|
}
|
|
|
|
void UnitSAO::notifyObjectPropertiesModified()
|
|
{
|
|
m_properties_sent = false;
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdateAttachmentCommand() const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_ATTACH_TO);
|
|
// parameters
|
|
writeS16(os, m_attachment_parent_id);
|
|
os << serializeString16(m_attachment_bone);
|
|
writeV3F32(os, m_attachment_position);
|
|
writeV3F32(os, m_attachment_rotation);
|
|
writeU8(os, m_force_visible);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdateBonePositionCommand(
|
|
const std::string &bone, const v3f &position, const v3f &rotation)
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_SET_BONE_POSITION);
|
|
// parameters
|
|
os << serializeString16(bone);
|
|
writeV3F32(os, position);
|
|
writeV3F32(os, rotation);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdateAnimationSpeedCommand() const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_SET_ANIMATION_SPEED);
|
|
// parameters
|
|
writeF32(os, m_animation_speed);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdateAnimationCommand() const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_SET_ANIMATION);
|
|
// parameters
|
|
writeV2F32(os, m_animation_range);
|
|
writeF32(os, m_animation_speed);
|
|
writeF32(os, m_animation_blend);
|
|
// these are sent inverted so we get true when the server sends nothing
|
|
writeU8(os, !m_animation_loop);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdateArmorGroupsCommand() const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS);
|
|
writeU16(os, m_armor_groups.size());
|
|
for (const auto &armor_group : m_armor_groups) {
|
|
os << serializeString16(armor_group.first);
|
|
writeS16(os, armor_group.second);
|
|
}
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateUpdatePositionCommand(const v3f &position,
|
|
const v3f &velocity, const v3f &acceleration, const v3f &rotation,
|
|
bool do_interpolate, bool is_movement_end, f32 update_interval)
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_UPDATE_POSITION);
|
|
// pos
|
|
writeV3F32(os, position);
|
|
// velocity
|
|
writeV3F32(os, velocity);
|
|
// acceleration
|
|
writeV3F32(os, acceleration);
|
|
// rotation
|
|
writeV3F32(os, rotation);
|
|
// do_interpolate
|
|
writeU8(os, do_interpolate);
|
|
// is_end_position (for interpolation)
|
|
writeU8(os, is_movement_end);
|
|
// update_interval (for interpolation)
|
|
writeF32(os, update_interval);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generateSetPropertiesCommand(const ObjectProperties &prop) const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
writeU8(os, AO_CMD_SET_PROPERTIES);
|
|
prop.serialize(os);
|
|
return os.str();
|
|
}
|
|
|
|
std::string UnitSAO::generatePunchCommand(u16 result_hp) const
|
|
{
|
|
std::ostringstream os(std::ios::binary);
|
|
// command
|
|
writeU8(os, AO_CMD_PUNCHED);
|
|
// result_hp
|
|
writeU16(os, result_hp);
|
|
return os.str();
|
|
}
|
|
|
|
void UnitSAO::sendPunchCommand()
|
|
{
|
|
m_messages_out.emplace(getId(), true, generatePunchCommand(getHP()));
|
|
}
|