aboutsummaryrefslogtreecommitdiff
path: root/games/minimal/mods/experimental
Commit message (Expand)AuthorAge
* Private nodemeta (#5702)sfan52017-05-10
* Add particle animation, glowsfan52017-01-18
* Minimal game: Use field 'tiles' instead of 'tile_images'Rogier2016-12-24
* Minimal game: add /dummyball <count> commandKahrl2014-08-23
* Compress texturesDavid Gumberg2013-06-18
* Move scriptapi to separate folder (by sapier)sapier2013-05-25
* unkn own block -> unkn own nodekhonkhortisan2013-04-05
* Improve inventory callbacks a bitPerttu Ahola2012-07-25
* Detached inventory callbacks and reworked node metadata callbacksPerttu Ahola2012-07-25
* Detached inventoriesPerttu Ahola2012-07-24
* Add node timer test in minimal/experimentalPerttu Ahola2012-07-24
* Formspec button_exit[] and image_button_exit[]Perttu Ahola2012-07-22
* Add /test1 command to minimal for testing a more complicated player inventory...Perttu Ahola2012-07-22
* Actually fix facedir-rotated nodes placed using minetest.env:place_node()Perttu Ahola2012-07-21
* Allow defining player's inventory form in LuaPerttu Ahola2012-07-19
* Add experimental_tester_tool_1.png to minimal game (was accidentally left out)Perttu Ahola2012-06-08
* Allow groups in crafting recipesPerttu Ahola2012-06-06
* Add after_destruct and cache the existence of on_construct, on_destruct and a...Perttu Ahola2012-06-05
* place_node, dig_node and punch_node; an in-game tester tool; remove old codePerttu Ahola2012-06-05
* experimental:soundblock dig_immediate=3Perttu Ahola2012-04-06
* Make the minimal development test somewhat playable by adding ore generation ...Perttu Ahola2012-04-05
* Fix sound direction and add experimental:soundblock alias sb in minimal for t...Perttu Ahola2012-04-04
* initial_properties field in entity definitionPerttu Ahola2012-03-31
* Add minetest.get_connected_players()Perttu Ahola2012-03-30
* ObjectRef:set_armor_groups() and ObjectRef:set_properties() - works on player...Perttu Ahola2012-03-30
* Add minetest.after(time, func)Perttu Ahola2012-03-27
* Move games/minetest to games/minimal and update README.txtPerttu Ahola2012-03-26
75 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
/*
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)
{
	// 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_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) const
{
	*parent_id = m_attachment_parent_id;
	*bone = m_attachment_bone;
	*position = m_attachment_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);
}

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 << serializeString(m_attachment_bone);
	writeV3F32(os, m_attachment_position);
	writeV3F32(os, m_attachment_rotation);
	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 << serializeString(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 << serializeString(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()));
}